Skip to content

Commit

Permalink
feat: support CLI, add pytests, and publish to PyPi
Browse files Browse the repository at this point in the history
  • Loading branch information
agusmakmun committed Apr 4, 2024
1 parent ab0f1bf commit 35ff506
Show file tree
Hide file tree
Showing 9 changed files with 359 additions and 55 deletions.
27 changes: 19 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# ScanReq

[![PyPI - Version](https://img.shields.io/pypi/v/scanreq.svg)](https://pypi.org/project/scanreq)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/scanreq.svg)](https://pypi.org/project/scanreq)

**ScanReq** - Python tool to scan all unused packages in requirements.txt file for your project.

## Background
Expand All @@ -22,10 +25,18 @@ So, this tool comes in handy for easily identifying which exact packages are act
10. **Code Better**: Keep your documentation and codebase cleaner for improved quality.


## Installation


```console
pip3 install scanreq
```


## Usage

```bash
(env-myproject) ➜ myproject git:(development) ✗ python scan.py -r requirements.txt -p .
```console
(env-myproject) ➜ myproject git:(development) ✗ scanreq -r requirements.txt -p .

[i] Please wait! It may take few minutes to complete...
[i] Scanning unused packages:
Expand All @@ -37,8 +48,8 @@ So, this tool comes in handy for easily identifying which exact packages are act

Cool right? 😎

```bash
(env-myproject) ➜ scan-unused-requirements git:(master) ✗ python scan.py --help
```console
(env-myproject) ➜ myproject git:(development) ✗ scanreq --help
usage: scan.py [-h] [-r REQUIREMENTS] [-p PATH]

Scan for unused Python packages.
Expand All @@ -62,9 +73,9 @@ optional arguments:
- [x] Requirement file to scan
- [ ] 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
- [x] Write some tests
- [x] Publish to PyPi
- [ ] Support scan the `pyproject.toml`
- [ ] Support multiple python versions
- [ ] Support CLI - make it as a command
- [ ] Support multiple devices (Linux, Macbook, and Windows)
- [ ] Write some tests
- [ ] Publish to PyPi
- [ ] Support scan the `pyproject.toml`
97 changes: 97 additions & 0 deletions docs/build-publish.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
### 1. Installation

Modern, extensible Python project management using Hatch:
https://hatch.pypa.io/latest/install/

```console
$ pip3 install hatch
```

> Ensure were working on environment.
Just for knowledge how this project generated using `hatch new {project_name}`:

```
$ hatch new scanreq
```

### 2. Testing

```console
$ hatch run test -vv
```


### 3. Versioning

https://hatch.pypa.io/latest/version/#updating

**To check the current version:**

```console
$ hatch version
0.0.1
```

**To tag new release:**

```console
$ hatch version "0.1.0"
Old: 0.0.1
New: 0.1.0
```

**Release micro version:**

```console
$ hatch version micro
Old: 0.0.1
New: 0.0.2
```


### 4. Building

https://hatch.pypa.io/latest/build/

```console
$ hatch build
[sdist]
dist/hatch_demo-1rc0.tar.gz

[wheel]
dist/hatch_demo-1rc0-py3-none-any.whl
```


### 5. Publishing

https://hatch.pypa.io/latest/publish/

Ensure we already setup the api token:
https://pypi.org/help/#apitoken

To make it easy, you can save inside `~/.pypirc` file:

```console
~ cat .pypirc
[pypi]
username = __token__
password = pypi-XXXXX
```


For the first time, `hatch` will require user to fill above PyPi token,
but it will be caching for the next publishments:

```console
$ hatch publish

Enter your username [__token__]:
Enter your credentials:
dist/scanreq-0.0.1.tar.gz ... success
dist/scanreq-0.0.1-py3-none-any.whl ... success

[scanreq]
https://pypi.org/project/scanreq/0.0.1/
```
73 changes: 73 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "scanreq"
dynamic = ["version"]
description = "Python tool to scan all unused packages in requirements.txt file for your project."
readme = "README.md"
requires-python = ">=3.8"
license = "MIT"
keywords = ["scan", "unused", "packages", "requirements"]
authors = [{ name = "agusmakmun", email = "[email protected]" }]
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
dependencies = ["importlib_metadata>=7.1.0"]
scripts.scanreq = "scanreq.__main__:main"

[project.urls]
Changelog = "https://github.com/agusmakmun/scan-unused-requirements/releases"
Documentation = "https://github.com/agusmakmun/scanreq"
Issues = "https://github.com/agusmakmun/scanreq/issues"
Source = "https://github.com/agusmakmun/scanreq"

[tool.hatch.version]
path = "src/scanreq/__about__.py"

[tool.hatch.envs.default]
dependencies = ["coverage[toml]>=6.5", "pytest"]
[tool.hatch.envs.default.scripts]
test = "pytest {args:tests}"
test-cov = "coverage run -m pytest {args:tests}"
cov-report = ["- coverage combine", "coverage report"]
cov = ["test-cov", "cov-report"]

[[tool.hatch.envs.all.matrix]]
python = ["3.8", "3.9", "3.10", "3.11", "3.12"]

[tool.hatch.envs.types]
dependencies = ["mypy>=1.0.0"]
[tool.hatch.envs.types.scripts]
check = "mypy --install-types --non-interactive {args:src/scanreq tests}"

[tool.coverage.run]
source_pkgs = ["scanreq", "tests"]
branch = true
parallel = true
omit = ["src/scanreq/__about__.py"]

[tool.coverage.paths]
scanreq = ["src/scanreq", "*/scanreq/src/scanreq"]
tests = ["tests", "*/scanreq/tests"]

[tool.coverage.report]
exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"]

[tool.hatch.build.targets.sdist]
exclude = ["/.github", "/.vscode", "/docs"]

[tool.hatch.build.targets.wheel]
packages = ["src/scanreq"]
1 change: 1 addition & 0 deletions src/scanreq/__about__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "0.0.5"
Empty file added src/scanreq/__init__.py
Empty file.
56 changes: 56 additions & 0 deletions src/scanreq/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import argparse
from typing import List

from scanreq.scanner import (
get_main_packages,
read_requirements,
search_string_in_python_files,
)


def main():
parser = argparse.ArgumentParser(description="Scan for unused Python packages.")
parser.add_argument(
"-r",
"--requirements",
type=str,
default="requirements.txt",
help="Path to the requirements.txt file to read packages from.",
)
parser.add_argument(
"-p",
"--path",
type=str,
default=".",
help="Project path to scan for unused packages (default: current directory).",
)
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


if __name__ == "__main__":
main()
47 changes: 0 additions & 47 deletions scan.py → src/scanreq/scanner.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import argparse
import multiprocessing
import os
import re
Expand Down Expand Up @@ -127,49 +126,3 @@ 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 main(project_path: str, requirement_file: str):
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


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Scan for unused Python packages.")
parser.add_argument(
"-r",
"--requirements",
type=str,
default="requirements.txt",
help="Path to the requirements.txt file to read packages from.",
)
parser.add_argument(
"-p",
"--path",
type=str,
default=".",
help="Project path to scan for unused packages (default: current directory).",
)

args = parser.parse_args()
main(project_path=args.path, requirement_file=args.requirements)
Empty file added tests/__init__.py
Empty file.
Loading

0 comments on commit 35ff506

Please sign in to comment.