diff --git a/src/python/jw/pkg/lib/Types.py b/src/python/jw/pkg/lib/Types.py new file mode 100644 index 00000000..eb74c773 --- /dev/null +++ b/src/python/jw/pkg/lib/Types.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- + +from typing import TypeVar, Generic +import abc, re, sys +from collections.abc import Iterable, Iterator + +from .log import * + +T = TypeVar("T") + +class Types(abc.ABC, Iterable[T], Generic[T]): # export + + def __iter__(self) -> Iterator[T]: + return iter(self._classes()) + + @abc.abstractmethod + def _classes(self) -> Iterable[T]: + pass + + @property + def classes(self) -> Iterable[T]: + return self._classes() + + @abc.abstractmethod + def _stringify(self) -> list[str]: + pass + + def dump(self, prio: int, *args, **kwargs) -> None: + contents = self._stringify() + log(prio, ",--- ", *args, **kwargs) + for line in contents: + log(prio, "| " + line) + log(prio, "`--- ", *args, **kwargs) + +class LoadTypes(Types): # export + + def __init__(self, mod_names: list[str], type_name_filter: str=None, type_filter: list[T]=[]): + self.__type_name_filter = type_name_filter + self.__type_filter = type_filter + self.__mod_names = mod_names + self.__classes: list[type[Any]]|None = None + + def _stringify(self): + return [ + "type_name_filter: " + str(self.__type_name_filter), + "type_filter: " + ', '.join([str(f) for f in self.__type_filter]), + "mod_names: " + ', '.join(self.__mod_names) + ] + + def _classes(self) -> Iterable[T]: + if self.__classes is None: + import importlib, inspect + rx: Any|None = None + if self.__type_name_filter is not None: + rx = re.compile(self.__type_name_filter) + ret: list[Any] = [] + for mod_name in self.__mod_names: + if mod_name != '__main__': + importlib.import_module(mod_name) + for member_name, c in inspect.getmembers(sys.modules[mod_name], inspect.isclass): + if rx is not None and not re.match(rx, member_name): + log(DEBUG, 'o "{}.{}" has wrong name'.format(mod_name, member_name)) + continue + if inspect.isabstract(c): + log(DEBUG, 'o "{}.{}" is abstract'.format(mod_name, member_name)) + continue + if self.__type_filter: + for tp in self.__type_filter: + if issubclass(c, tp): + break + log(DEBUG, 'o "{}.{}" is not of type {}'.format(mod_name, member_name, tp)) + else: + log(DEBUG, 'o "{}.{}" doesn\'t match type filter'.format(mod_name, member_name)) + breakpoint() + continue + log(DEBUG, 'o "{}.{}" is fine, adding'.format(mod_name, member_name)) + ret.append(c) + self.__classes = ret + return self.__classes