from __future__ import annotations import pkgutil from importlib import import_module from typing import TYPE_CHECKING if TYPE_CHECKING: from collections.abc import MutableMapping from typing import Any def detect_modules( namespace: MutableMapping[str, Any], prefix: str | None = None, skip: set[str] | None = None, *, extend_namespace: bool = True, ) -> list[str]: package_name = namespace.get("__name__") package_path = namespace.get("__path__") if not isinstance(package_name, str): raise TypeError("namespace must contain string __name__") if package_path is None: raise TypeError("namespace must be a package namespace with __path__") if extend_namespace: package_path = pkgutil.extend_path(package_path, package_name) namespace["__path__"] = package_path ret: list[str] = [] skip = skip or set() for _finder, module_name, _ispkg in pkgutil.iter_modules(package_path): if prefix is not None and not module_name.startswith(prefix): continue if module_name in skip: continue module = import_module(f".{module_name}", package_name) cls = getattr(module, module_name) namespace[module_name] = cls ret.append(module_name) return ret