clients-protocol/clients_protocol/autoload.py
Jon Michael Aanes 87c30b08c6
Some checks failed
Run Python tests (through Pytest) / Test (push) Failing after 24s
Verify Python project can be installed, loaded and have version checked / Test (push) Failing after 23s
Moved autoload to protocol.
2025-06-30 00:43:31 +02:00

69 lines
1.9 KiB
Python

import abc
import functools
import importlib
import logging
from collections.abc import Iterator
from pathlib import Path
logger = logging.getLogger(__name__)
def load_module_with_name(name: str) -> object | None:
try:
module = importlib.import_module(name)
except Exception:
logger.exception('Module %s failed to load!', name)
return None
logger.info('Loaded module: %s', name)
return module
def is_loadable_python(f: Path) -> bool:
if f.is_file() and f.suffix == '.py':
return True
if f.is_dir():
return is_loadable_python(f / '__init__.py')
return False
def get_modules_in_directory(module_directory: Path) -> Iterator[str]:
for f in module_directory.iterdir():
name = f.parts[-1].removesuffix('.py')
if is_loadable_python(f):
if name != '__init__':
yield name
@functools.cache
def load_submodules(module_name: str, index_module_path: str | Path) -> None:
"""Loads the submodules for the given module name.
This method is not recursive, as it is the submodule's responsibility to
manage it's own submodules. That said: The load_submodules system works
when invoked from an auto-loaded package.
"""
module_directory = Path(index_module_path)
submodules = list(get_modules_in_directory(module_directory))
logger.info(
'Detected %d submodules for %s',
len(submodules),
module_name,
)
for submodule_name in submodules:
load_module_with_name(f'{module_name}.{submodule_name}')
def _find_subclasses(class_: type, class_accum: list[type]) -> None:
is_abstract = abc.ABC in class_.__bases__
if not is_abstract:
class_accum.append(class_)
for subclass in class_.__subclasses__():
_find_subclasses(subclass, class_accum)
def find_subclasses(abstract_class: type) -> list[type]:
subclasses = []
_find_subclasses(abstract_class, subclasses)
return subclasses