Created
March 11, 2019 01:50
-
-
Save zooba/f34089baed4397f6be1a4af8b301720e to your computer and use it in GitHub Desktop.
Tool for seeing which Python stdlib modules have dependencies on others
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ast | |
import json | |
import sys | |
import sysconfig | |
from pathlib import Path | |
def remove(data, key): | |
removed = {key} | |
removed.update(k for k in data if k.startswith(key + ".")) | |
report = {} | |
any_removed = bool(removed) | |
while any_removed: | |
for k in removed: | |
data.pop(k, None) | |
any_removed = False | |
for k, v in data.items(): | |
cause = removed & set(v) | |
if not cause: | |
continue | |
any_removed = True | |
report[k] = cause | |
removed.add(k) | |
removed.update(k2 for k2 in data if k2.startswith(f"{k}.")) | |
return report | |
def find_deps(file): | |
try: | |
with open(file, "rb") as f: | |
tree = ast.parse(f.read(), str(file)) | |
except SyntaxError: | |
return | |
# for n in ast.walk(tree): | |
for n in ast.iter_child_nodes(tree): | |
if isinstance(n, ast.ImportFrom): | |
yield n.module | |
elif isinstance(n, ast.Import): | |
yield from (nom.name for nom in n.names) | |
def module_name(root, file): | |
p = file.with_suffix("").relative_to(root) | |
parts = p.parts[:-1] if p.parts[-1] == "__init__" else p.parts | |
return ".".join(parts) | |
SKIP_DIRS = {"test", "tests", "site-packages", "idle_test"} | |
def search(root): | |
found = {} | |
for p in Path(root).resolve().rglob("**/*.py"): | |
if SKIP_DIRS & set(p.parts): | |
continue | |
deps = set(find_deps(p)) | |
deps.discard(None) | |
found[module_name(root, p)] = sorted(deps) | |
return found | |
if __name__ == "__main__": | |
if len(sys.argv) < 2: | |
print("Usage: try-remove MODULE ...") | |
print() | |
sys.exit(1) | |
try: | |
with open("try-remove.json", "r", encoding="utf-8") as f: | |
data = json.load(f) | |
except FileNotFoundError: | |
print("Scanning sysconfig.get_path('stdlib')") | |
print(" -", sysconfig.get_path("stdlib")) | |
print(" - caching to to", Path("try-remove.json").resolve()) | |
data = search(sysconfig.get_path("stdlib")) | |
print() | |
with open("try-remove.json", "w", encoding="utf-8") as f: | |
json.dump(data, f) | |
report = {} | |
for m in sys.argv[1:]: | |
report.update(remove(data, m)) | |
print("Removed:") | |
for k in sorted(report): | |
v = report[k] | |
print(f" {k} because {sorted(v)}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment