from fnmatch import fnmatch
from functools import partial
from importlib import import_module
from pathlib import Path
from types import ModuleType
from typing import Iterable, List, Optional, Union
from .model import Module
INIT_PY = '__init__.py'
MAIN_PY = '__main__.py'
def _path_to_str(path: Path, base_path: Path) -> str:
path_str = str(path).replace(str(base_path.parent), '').replace('/', '.')
if path_str.startswith('.'):
path_str = path_str[1:]
if path_str.endswith('.py'):
path_str = path_str[:-3]
return path_str
def _is_ignored(check_path: Path, ignore: List[str], base_path: Path) -> bool:
path_str = _path_to_str(check_path, base_path=base_path)
for ignored_item in ignore:
if fnmatch(path_str, ignored_item):
return True
return False
def _get_submodules(base_path: Path, ignore: List[str]) -> Iterable[Module]:
is_ignored = partial(_is_ignored, ignore=ignore, base_path=base_path)
path_to_str = partial(_path_to_str, base_path=base_path)
sub_modules = [
p for p in sorted(base_path.rglob(INIT_PY)) if not is_ignored(p.parent)
]
for sub_module in sub_modules:
sub_module = sub_module.parent
sub_sub_modules = [
sub_sub_module
for sub_sub_module in sub_module.glob('*.py')
if sub_sub_module.name not in (INIT_PY, MAIN_PY)
and not is_ignored(sub_sub_module)
]
submodule_str = path_to_str(sub_module)
yield Module(
module_str=submodule_str,
is_package=True,
has_submodules=bool(len(sub_sub_modules)),
parent_str='.'.join(submodule_str.split('.')[:-1]),
)
for sub_sub_module in sub_sub_modules:
yield Module(
module_str=path_to_str(sub_sub_module),
is_package=False,
has_submodules=False,
parent_str=submodule_str,
)
[docs]def get_module_hierarchy(
name: Union[str, ModuleType], ignore: Optional[List[str]] = None
) -> Iterable[Module]:
if ignore is None:
ignore = []
if isinstance(name, str):
module = import_module(name)
else:
module = name
assert isinstance(module, ModuleType) and module.__file__ is not None
path = Path(module.__file__).parent
yield from _get_submodules(path, ignore)