Skip to content

Commit 0a2356a

Browse files
find and compare used and unused dependencies in python project
1 parent af17805 commit 0a2356a

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

compare_dependencies.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import ast
2+
import importlib.metadata
3+
import os
4+
5+
# Set PROJECT_DIR to the script's directory
6+
PROJECT_DIR = os.path.dirname(os.path.abspath(__file__))
7+
VENV_DIR = os.path.join(PROJECT_DIR, ".venv")
8+
9+
10+
def get_installed_packages() -> set:
11+
"""Get installed packages inside .venv using importlib.metadata."""
12+
installed = {pkg.metadata["Name"].lower() for pkg in importlib.metadata.distributions()}
13+
return installed
14+
15+
16+
def get_imported_modules(project_path) -> set:
17+
"""Scan all Python files in the project for imported modules, ignoring .venv."""
18+
imported_modules = set()
19+
20+
for root, _, files in os.walk(project_path):
21+
# Skip the virtual environment directory
22+
if ".venv" in root:
23+
continue
24+
25+
for file in files:
26+
if file.endswith(".py"):
27+
file_path = os.path.join(root, file)
28+
print(f"Scanning {file_path}...")
29+
try:
30+
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
31+
tree = ast.parse(f.read(), filename=file_path)
32+
for node in ast.walk(tree):
33+
if isinstance(node, ast.Import):
34+
for alias in node.names:
35+
imported_modules.add(alias.name.split(".")[0])
36+
elif isinstance(node, ast.ImportFrom):
37+
if node.module:
38+
imported_modules.add(node.module.split(".")[0])
39+
except SyntaxError:
40+
print(f"Skipping {file_path} due to syntax error.")
41+
42+
return imported_modules
43+
44+
45+
def compare_dependencies() -> None:
46+
"""Compare installed vs imported dependencies."""
47+
installed_packages = get_installed_packages()
48+
imported_modules = get_imported_modules(PROJECT_DIR)
49+
50+
used_dependencies = installed_packages.intersection(imported_modules)
51+
unused_dependencies = installed_packages - imported_modules
52+
missing_dependencies = imported_modules - installed_packages
53+
54+
print("\n📌 Dependency Comparison:\n")
55+
print("✅ Used Dependencies (Installed & Imported):")
56+
print(sorted(used_dependencies))
57+
58+
print("\n❌ Unused Dependencies (Installed but NOT used):")
59+
print(sorted(unused_dependencies))
60+
61+
print("\n⚠️ Missing Dependencies (Used but NOT installed):")
62+
print(sorted(missing_dependencies))
63+
64+
65+
if __name__ == "__main__":
66+
compare_dependencies()

find_dependencies.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import ast
2+
import os
3+
4+
import importlib.metadata
5+
6+
7+
def get_installed_packages() -> set:
8+
"""Get installed packages inside .venv using importlib.metadata."""
9+
installed = {pkg.metadata["Name"].lower() for pkg in importlib.metadata.distributions()}
10+
return installed
11+
12+
13+
def get_imported_modules(project_path) -> set:
14+
"""Scan all Python files in the project for imported modules, ignoring .venv."""
15+
imported_modules = set()
16+
17+
for root, _, files in os.walk(project_path):
18+
# Skip the virtual environment directory
19+
if ".venv" in root:
20+
continue
21+
22+
for file in files:
23+
if file.endswith(".py"):
24+
file_path = os.path.join(root, file)
25+
print(f"Scanning {file_path}...")
26+
try:
27+
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
28+
tree = ast.parse(f.read(), filename=file_path)
29+
for node in ast.walk(tree):
30+
if isinstance(node, ast.Import):
31+
for alias in node.names:
32+
imported_modules.add(alias.name.split(".")[0])
33+
elif isinstance(node, ast.ImportFrom):
34+
if node.module:
35+
imported_modules.add(node.module.split(".")[0])
36+
except SyntaxError:
37+
print(f"Skipping {file_path} due to syntax error.")
38+
39+
return imported_modules
40+
41+
def analyze_dependencies(project_path) -> tuple[set, set, set]:
42+
installed_packages = get_installed_packages()
43+
imported_modules = get_imported_modules(project_path)
44+
45+
used_dependencies = installed_packages.intersection(imported_modules)
46+
unused_dependencies = installed_packages - imported_modules
47+
missing_dependencies = imported_modules - installed_packages
48+
49+
return used_dependencies, unused_dependencies, missing_dependencies
50+
51+
52+
if __name__ == "__main__":
53+
project_directory = os.path.dirname(os.path.abspath(__file__))
54+
used, unused, missing = analyze_dependencies(project_directory)
55+
56+
print("\nUsed Dependencies:\n", used)
57+
print("\nUnused Dependencies:\n", unused)
58+
print("\nMissing Dependencies:\n", missing)

0 commit comments

Comments
 (0)