-
-
Save gemire/0f323c8ef81ca3764906683d75ec943f to your computer and use it in GitHub Desktop.
快速的分析一个复杂模块的依赖关系 并且 可视化
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
#! /usr/bin/env python3 | |
""" | |
Prerequirements: | |
1. brew install graphviz | |
2. pip3 install fire | |
Usage: | |
deps [path to code] | |
""" | |
import fire | |
from pathlib import Path | |
import re | |
import os | |
from pprint import pprint | |
from typing import Dict, Iterable, List | |
from simpletemplate import Template | |
from subprocess import check_call | |
from concurrent.futures import ThreadPoolExecutor | |
from time import time | |
DEFAULT_EXLUDE = r"accessids|logtag|widget|bar|callback|item|listener|callback|response|layout|view|model|utils|util|helper|logger|pagelist|constant|api|config|type|wrapper|source" | |
ENGINE = [ | |
"dot", | |
"neato", | |
"twopi", | |
"circo", | |
"osage", | |
"fdp", | |
"sfdp" | |
] | |
TEMPLATE = '''\ | |
digraph Deps { | |
node [color=lightblue2, style=filled]; | |
%{for node in deps.keys():}% | |
{{node}}; | |
%{end}% | |
%{for node, values in deps.items():}% | |
%{for v in values:}% | |
{{node}} -> {{v}}; | |
%{end}% | |
%{end}% | |
}\ | |
''' | |
def _parse_file(file: Path): | |
content = file.read_text() | |
return file.stem, set(re.findall(r'\b[A-Z$][\w.]+\b', content)) | |
def _files(root: Path) -> Iterable[Path]: | |
for d, _, files in os.walk(root): | |
yield from (Path(os.path.join(d, f)) for f in files) | |
def main(path: str, exlude: str = DEFAULT_EXLUDE, engine: str = "dot"): | |
dot = Path(path).stem | |
t1 = time() | |
files = _files(Path(path)) | |
if exlude: | |
files = (f for f in files if not re.search(exlude, str(f), re.I)) | |
t2 = time() | |
print(f">>> 1. scan files finishd, cost: {t2 - t1:.2f}s") | |
with ThreadPoolExecutor() as pool: | |
_deps = dict(pool.map(_parse_file, list(files))) | |
# _deps = dict(_parse_file(f) for f in files) | |
valid_keys = set(_deps.keys()) | |
deps = {k: [v for v in values if v in valid_keys and v != k] | |
for k, values in _deps.items()} | |
t3 = time() | |
print(f">>> 2. parse files finishd, cost: {t3 - t2:.2f}s") | |
code = Template(TEMPLATE).render({"deps": deps}) | |
with open(f'{dot}.dot', 'w') as f: | |
f.write(code) | |
check_call(f'{engine} -Tpng {dot}.dot -o {dot}.png && open {dot}.png', shell=True) | |
t4 = time() | |
print(f">>> 3. render finishd, cost: {t4 - t3:.2f}s, total: {t4 - t1:.2f}s") | |
if __name__ == '__main__': | |
fire.Fire(main) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment