From 472fe517aca58c3d512a637c58a6d9ff72250e58 Mon Sep 17 00:00:00 2001 From: agusmakmun Date: Fri, 5 Apr 2024 23:55:19 +0700 Subject: [PATCH] feat: support output for unused requirements --- README.md | 35 +++++++++++++++++++++++++------- docs/build-publish.md | 6 ++++++ src/scanreq/__about__.py | 2 +- src/scanreq/__main__.py | 43 ++++++++++++---------------------------- src/scanreq/scanner.py | 41 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index b151671..a92e754 100644 --- a/README.md +++ b/README.md @@ -37,22 +37,40 @@ pip3 install scanreq ## Usage +> **Note:** Ensure you're working on python environment. + ```console -(env-myproject) ➜ myproject git:(development) ✗ scanreq -r requirements.txt -p . +scanreq -r requirements.txt -p . -o unused-requirements.txt +``` +``` [i] Please wait! It may take few minutes to complete... [i] Scanning unused packages: - 1. Module: rcssmin ---> Package: rcssmin - 2. Module: model_utils ---> Package: django-model-utils - 3. Module: pinax_theme_bootstrap ---> Package: pinax-theme-bootstrap - 4. Module: phonenumbers ---> Package: phonenumbers + 1. Module: rcssmin -> Package: rcssmin + 2. Module: model_utils -> Package: django-model-utils + 3. Module: pinax_theme_bootstrap -> Package: pinax-theme-bootstrap + 4. Module: phonenumbers -> Package: phonenumbers +``` + +```console +cat unused-requirements.txt +``` + +``` +rcssmin +django-model-utils +pinax-theme-bootstrap +phonenumbers ``` Cool right? 😎 ```console -(env-myproject) ➜ myproject git:(development) ✗ scanreq --help -usage: scan.py [-h] [-r REQUIREMENTS] [-p PATH] +scanreq --help +``` + +``` +usage: scan.py [-h] [-r REQUIREMENTS] [-p PATH] [-o OUTPUT] Scan for unused Python packages. @@ -61,6 +79,8 @@ optional arguments: -r REQUIREMENTS, --requirements REQUIREMENTS Path to the requirements.txt file to read packages from. -p PATH, --path PATH Project path to scan for unused packages (default: current directory). + -o OUTPUT, --output OUTPUT + Path to the output file where unused packages will be saved. ``` > **Note:** Don't forget to cross-check the unused packages after finding them, @@ -73,6 +93,7 @@ optional arguments: - [x] Support argument parser (command arguments) - [x] Directory to scan - [x] Requirement file to scan + - [x] Option to write the output of unused packages - [ ] Option to auto replace the package from requirements.txt file - [ ] Option to exclude or ignore some packages - [x] Support CLI - make it as a command diff --git a/docs/build-publish.md b/docs/build-publish.md index 5777ba0..7e9972f 100644 --- a/docs/build-publish.md +++ b/docs/build-publish.md @@ -21,6 +21,12 @@ $ hatch new scanreq $ hatch run test -vv ``` +Executing the script: + +```console +$ hatch run python src/scanreq/__main__.py -r requirements.txt -p . +``` + ### 3. Versioning diff --git a/src/scanreq/__about__.py b/src/scanreq/__about__.py index 034f46c..6526deb 100644 --- a/src/scanreq/__about__.py +++ b/src/scanreq/__about__.py @@ -1 +1 @@ -__version__ = "0.0.6" +__version__ = "0.0.7" diff --git a/src/scanreq/__main__.py b/src/scanreq/__main__.py index 2060c8c..c67905e 100644 --- a/src/scanreq/__main__.py +++ b/src/scanreq/__main__.py @@ -1,11 +1,6 @@ import argparse -from typing import List -from scanreq.scanner import ( - get_main_packages, - read_requirements, - search_string_in_python_files, -) +from scanreq.scanner import scan def main(): @@ -24,32 +19,20 @@ def main(): default=".", help="Project path to scan for unused packages (default: current directory).", ) + parser.add_argument( + "-o", + "--output", + type=str, + default=None, + help="Path to the output file where unused packages will be saved.", + ) args = parser.parse_args() - project_path: str = args.path - requirement_file: str = args.requirements - - print("\n[i] Please wait! It may take few minutes to complete...") - main_packages: dict = get_main_packages() - package_names: List[str] = read_requirements(requirement_file) - - print("[i] Scanning unused packages:") - unused_packages: List[str] = [] - number: int = 1 - for package_name in package_names: - for module_name, package_names in main_packages.items(): - if package_name in package_names: - results: list = search_string_in_python_files(project_path, module_name) - if not results and (module_name not in unused_packages): - unused_packages.append(package_name) - print( - f" {number}. Module: {module_name} ---> Package: {package_name}" - ) - number += 1 - - if len(unused_packages) < 1: - print("[i] Great! No unused packages found.") - return unused_packages + scan( + requirement_file=args.requirements, + project_path=args.path, + output_path=args.output, + ) if __name__ == "__main__": diff --git a/src/scanreq/scanner.py b/src/scanreq/scanner.py index 25493e1..572e020 100644 --- a/src/scanreq/scanner.py +++ b/src/scanreq/scanner.py @@ -23,6 +23,7 @@ ".cfg", ".yml", ".yaml", + "Dockerfile", ) @@ -126,3 +127,43 @@ def read_requirements(file_path: str) -> List[str]: package_name: str = line.split("==")[0].strip().lower() package_names.append(package_name) return package_names + + +def scan(requirement_file: str, project_path: str, output_path: str = None) -> None: + """ + A function that scans for unused packages in a project based on a given requirements file. + + Parameters: + - requirement_file (str): the path to the requirements file to be scanned. + - project_path (str): the path to the project to be scanned. + - output_path (str, optional): the path to the output file where unused packages will be saved. Defaults to None. + + Returns: + - None + """ + print("\n[i] Please wait! It may take few minutes to complete...") + + package_names: List[str] = read_requirements(requirement_file) + main_packages: dict = get_main_packages() + + print("[i] Scanning unused packages:") + unused_packages: List[str] = [] + number: int = 1 + for package_name in package_names: + for module_name, package_names in main_packages.items(): + if package_name in package_names: + results: list = search_string_in_python_files(project_path, module_name) + if not results and (module_name not in unused_packages): + unused_packages.append(package_name) + number_str: str = f"{number}." + print( + f" {number_str: <4}Module: {module_name: <30}-> Package: {package_name}" + ) + number += 1 + + if len(unused_packages) < 1: + print("[i] Great! No unused packages found.") + + elif unused_packages and output_path: + with open(output_path, "w") as _file: + _file.writelines("\n".join(unused_packages) + "\n")