packagelister.packagelister
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:
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.