packagelister.packagelister API documentation

# packagelister.packagelister

View Source

     1import importlib.metadata
     2import sys
     3from pathlib import Path
     4
     5from pathcrawler import crawl
     6from printbuddies import ProgBar
     7
     8
     9def scan(project_dir: Path | str = None, include_builtins: bool = False) -> dict:
    10    """Recursively scans a directory for python files to determine
    11    what packages are in use, as well as the version number
    12    if applicable.
    13
    14    Returns a dictionary where the keys are package
    15    names and the values are the version number of the package if there is one
    16    (None if there isn't) and a list of the files that import the package.
    17
    18    :param project_dir: Can be an absolute or relative path
    19    to a directory or a single file (.py).
    20    If it is relative, it will be assumed to be relative to
    21    the current working directory.
    22    If an argument isn't given, the current working directory
    23    will be scanned.
    24    If the path doesn't exist, an empty dictionary is returned."""
    25    if not project_dir:
    26        project_dir = Path.cwd()
    27    elif type(project_dir) is str:
    28        project_dir = Path(project_dir)
    29    if not project_dir.is_absolute():
    30        project_dir = project_dir.absolute()
    31
    32    # Return empty dict if project_dir doesn't exist
    33    if not project_dir.exists():
    34        return {}
    35    # You can scan a non python file one at a time if you reeeally want to.
    36    if project_dir.is_file():
    37        files = [project_dir]
    38    else:
    39        files = [file for file in crawl(project_dir) if file.suffix == ".py"]
    40
    41    bar = ProgBar(len(files) - 1, width_ratio=0.33)
    42    # If scanning one file, the progress bar will show 0% complete if bar.counter == 0
    43    if len(files) == 1:
    44        bar.counter = 1
    45    packages = {}
    46    standard_lib = list(sys.stdlib_module_names) if not include_builtins else []
    47    for file in files:
    48        bar.display(suffix=f"Scanning {file.name}")
    49        contents = [
    50            line.split()[1]
    51            for line in file.read_text(encoding="utf-8").splitlines()
    52            if line.startswith(("from", "import"))
    53        ]
    54        for package in contents:
    55            if package.startswith("."):
    56                package = package[1:]
    57            if "." in package:
    58                package = package[: package.find(".")]
    59            if "," in package:
    60                package = package[:-1]
    61            if file.with_stem(package) not in files and package not in standard_lib:
    62                if package in packages and str(file) not in packages[package]["files"]:
    63                    packages[package]["files"].append(str(file))
    64                else:
    65                    try:
    66                        package_version = importlib.metadata.version(package)
    67                    except Exception as e:
    68                        package_version = None
    69                    packages[package] = {
    70                        "files": [str(file)],
    71                        "version": package_version,
    72                    }
    73    return packages

def scan( project_dir: pathlib.Path | str = None, include_builtins: bool = False) -> dict: View Source

    10def scan(project_dir: Path | str = None, include_builtins: bool = False) -> dict:
    11    """Recursively scans a directory for python files to determine
    12    what packages are in use, as well as the version number
    13    if applicable.
    14
    15    Returns a dictionary where the keys are package
    16    names and the values are the version number of the package if there is one
    17    (None if there isn't) and a list of the files that import the package.
    18
    19    :param project_dir: Can be an absolute or relative path
    20    to a directory or a single file (.py).
    21    If it is relative, it will be assumed to be relative to
    22    the current working directory.
    23    If an argument isn't given, the current working directory
    24    will be scanned.
    25    If the path doesn't exist, an empty dictionary is returned."""
    26    if not project_dir:
    27        project_dir = Path.cwd()
    28    elif type(project_dir) is str:
    29        project_dir = Path(project_dir)
    30    if not project_dir.is_absolute():
    31        project_dir = project_dir.absolute()
    32
    33    # Return empty dict if project_dir doesn't exist
    34    if not project_dir.exists():
    35        return {}
    36    # You can scan a non python file one at a time if you reeeally want to.
    37    if project_dir.is_file():
    38        files = [project_dir]
    39    else:
    40        files = [file for file in crawl(project_dir) if file.suffix == ".py"]
    41
    42    bar = ProgBar(len(files) - 1, width_ratio=0.33)
    43    # If scanning one file, the progress bar will show 0% complete if bar.counter == 0
    44    if len(files) == 1:
    45        bar.counter = 1
    46    packages = {}
    47    standard_lib = list(sys.stdlib_module_names) if not include_builtins else []
    48    for file in files:
    49        bar.display(suffix=f"Scanning {file.name}")
    50        contents = [
    51            line.split()[1]
    52            for line in file.read_text(encoding="utf-8").splitlines()
    53            if line.startswith(("from", "import"))
    54        ]
    55        for package in contents:
    56            if package.startswith("."):
    57                package = package[1:]
    58            if "." in package:
    59                package = package[: package.find(".")]
    60            if "," in package:
    61                package = package[:-1]
    62            if file.with_stem(package) not in files and package not in standard_lib:
    63                if package in packages and str(file) not in packages[package]["files"]:
    64                    packages[package]["files"].append(str(file))
    65                else:
    66                    try:
    67                        package_version = importlib.metadata.version(package)
    68                    except Exception as e:
    69                        package_version = None
    70                    packages[package] = {
    71                        "files": [str(file)],
    72                        "version": package_version,
    73                    }
    74    return packages

Recursively scans a directory for python files to determine what packages are in use, as well as the version number if applicable.

Returns a dictionary where the keys are package names and the values are the version number of the package if there is one (None if there isn't) and a list of the files that import the package.

###### Parameters

- project_dir: Can be an absolute or relative path to a directory or a single file (.py). If it is relative, it will be assumed to be relative to the current working directory. If an argument isn't given, the current working directory will be scanned. If the path doesn't exist, an empty dictionary is returned.