Skip to content

import_parser

setuptools.import_parser

parse_compile_list

parse_compile_list(module: str) -> List[str]

returns python files to be built by recursively parsing import phrases.

Reference

https://github.com/andrewp-as-is/list-imports.py/blob/master/list_imports/init.py

Example
from SaigeToolkit.setuptools.import_parser import parse_compile_list
compile_list = parse_compile_list("deploy_demo.py")
Note

file/dir의 존재 유무를 isfile/isdir 함수에서 glob의 string comapre로 변경한 이유.

이슈 window에서는 filename의 대소문자를 구분하지 않음, "A.py", "a.Py", "a.PY"가 모두 같은 file. os.path.isfile/isdir 함수 또한 대소문자를 구분하지 않음, "A"가 존재할 때 isfile("a")을 호출시 True return. 따라서 폴더 내부가 다음과 같이 되어있고, import A를 했다면 ├── A │ └── init.py └── a.py linux의 경우는 A.py가 없기 때문에, A directory를 잘 찾지만, window의 경우에는 isfile(A.py)가 True이기 때문에 logic에 문제가 발생.

해결 방법 glob로 모든 file name, directory name을 가져온 뒤, string compare 진행.

unresolved issue glob가 모든 파일 리스트를 읽어오기 때문에, 폴더 내에 파일이 많은 경우 속도가 느려지는 이슈.

Source code in SaigeToolkit/setuptools/import_parser.py
def parse_compile_list(module: str) -> List[str]:
    """returns python files to be built by recursively parsing `import` phrases.

    Reference:
        https://github.com/andrewp-as-is/list-imports.py/blob/master/list_imports/__init__.py

    Example:
        ``` python
        from SaigeToolkit.setuptools.import_parser import parse_compile_list
        compile_list = parse_compile_list("deploy_demo.py")
        ```

    Note:
        file/dir의 존재 유무를 isfile/isdir 함수에서 glob의 string comapre로 변경한 이유.

        이슈
            window에서는 filename의 대소문자를 구분하지 않음,
                "A.py", "a.Py", "a.PY"가 모두 같은 file.
            os.path.isfile/isdir 함수 또한 대소문자를 구분하지 않음,
                "A"가 존재할 때 isfile("a")을 호출시 True return.
            따라서 폴더 내부가 다음과 같이 되어있고, import A를 했다면
            ├── A
            │   └── __init__.py
            └── a.py
            linux의 경우는 A.py가 없기 때문에, A directory를 잘 찾지만,
            window의 경우에는 isfile(A.py)가 True이기 때문에 logic에 문제가 발생.

        해결 방법
            glob로 모든 file name, directory name을 가져온 뒤, string compare 진행.

        unresolved issue
            glob가 모든 파일 리스트를 읽어오기 때문에, 폴더 내에 파일이 많은 경우 속도가 느려지는 이슈.
    """
    compile_list = []
    all_existing_files = glob("**", recursive=True)
    all_existing_directories = glob("**" + os.sep, recursive=True)

    def _parse_compile_list(module: str) -> None:
        import_list = get_import_module_list(module)

        # recursively update compile list
        for imp in import_list:
            import_path = imp.replace(".", os.path.sep)

            if import_path + ".py" in all_existing_files and imp not in compile_list:
                compile_list.append(imp)
                _parse_compile_list(imp)

            elif (
                import_path + os.sep in all_existing_directories
                and imp + ".__init__" not in compile_list
            ):
                compile_list.append(imp + ".__init__")
                _parse_compile_list(imp + ".__init__")

    _parse_compile_list(module)

    return sorted(list(set(compile_list)))

get_import_module_list

get_import_module_list(module: str) -> List[str]

Get importing module list from single python file

Source code in SaigeToolkit/setuptools/import_parser.py
def get_import_module_list(module: str) -> List[str]:
    """Get importing module list from single python file"""
    path = module.replace(".", os.path.sep)
    path = path[:-3] + ".py" if path.endswith(os.path.sep + "py") else path + ".py"
    assert os.path.isfile(path), f"should be file path: {path}"

    directory = os.path.dirname(path)
    with open(path, encoding="utf-8") as f:
        code = f.read()
    tree = ast.parse(code)

    # Get importing module list
    import_list = []
    for node in ast.walk(tree):
        if isinstance(node, ast.Import):
            # style of [import A]
            for subnode in node.names:
                import_list.append(subnode.name)
        if isinstance(node, ast.ImportFrom):
            if node.module is None:
                # style of [from .. import A]
                import_list.extend(["." * node.level + n.name for n in node.names])
            else:
                # style of [from A import B] or [from ..A import B]
                # add "A" or "..A"
                import_list.append("." * node.level + node.module)
                # add "A.B" or "..A.B"
                import_list.extend(["." * node.level + node.module + "." + n.name for n in node.names])

    # Interpret dot-representation
    for idx, imp in enumerate(import_list):
        if imp[0] != ".":
            continue
        dot_directory = directory
        while imp[1] == ".":
            dot_directory = os.path.dirname(dot_directory)
            imp = imp[1:]
        import_list[idx] = dot_directory.replace(os.path.sep, ".") + imp

    return import_list