Skip to content

Instantly share code, notes, and snippets.

@mindeng
Forked from weixinfree/deps.py
Created May 10, 2022 09:54
Show Gist options
  • Save mindeng/ada04c81bcac0211d692992449b20d19 to your computer and use it in GitHub Desktop.
Save mindeng/ada04c81bcac0211d692992449b20d19 to your computer and use it in GitHub Desktop.

Revisions

  1. @weixinfree weixinfree revised this gist Nov 1, 2021. 1 changed file with 23 additions and 21 deletions.
    44 changes: 23 additions & 21 deletions deps.py
    Original file line number Diff line number Diff line change
    @@ -4,6 +4,7 @@
    Prerequirements:
    1. brew install graphviz
    2. pip3 install fire
    3. pip3 install simpletemplate
    Usage:
    deps [path to code]
    @@ -21,21 +22,13 @@
    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"
    DEFAULT_EXLUDE = r"(accessids|logtag|widget|listener|response|layout|view|model|utils|util|helper|logger|pagelist|constant|constants|api|config|type|wrapper|source|item|reason|dialog|object|info|name|status|data|apiservice|drawable|callback|viewV\d+|delegate)$"


    ENGINE = [
    "dot",
    "neato",
    "twopi",
    "circo",
    "osage",
    "fdp",
    "sfdp"
    ]
    ENGINE = ["dot", "neato", "twopi", "circo", "osage", "fdp", "sfdp"]


    TEMPLATE = '''\
    TEMPLATE = """\
    digraph Deps {
    node [color=lightblue2, style=filled];
    %{for node in deps.keys():}%
    @@ -48,26 +41,33 @@
    %{end}%
    %{end}%
    }\
    '''
    """


    def _parse_file(file: Path):
    content = file.read_text()
    return file.stem, set(re.findall(r'\b[A-Z$][\w.]+\b', content))
    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"):
    def _should_exlude(file: Path, exclude: str):
    r = re.search(exclude, file.stem, re.I)
    if r:
    print(f"exlude: {file.stem} {r}")
    return r


    def main(path: str, exclude: 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))
    if exclude:
    files = (f for f in files if not _should_exlude(f, exclude=exclude))

    t2 = time()
    print(f">>> 1. scan files finishd, cost: {t2 - t1:.2f}s")
    @@ -76,20 +76,22 @@ def main(path: str, exlude: str = DEFAULT_EXLUDE, engine: str = "dot"):

    # _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()}
    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:
    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)
    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__':
    if __name__ == "__main__":
    fire.Fire(main)
  2. @weixinfree weixinfree created this gist Feb 6, 2021.
    95 changes: 95 additions & 0 deletions deps.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,95 @@
    #! /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)