mirror of
ssh://git.janware.com/janware/proj/jw-pkg
synced 2026-04-24 17:23:36 +02:00
lib.Types: Add module
Types is a container for types, notably classes, which are
dynamically loaded from other modules. Which modules are loaded is
based on the following criteria passed to its constructor:
- mod_names: A list of modules to load and investigate
- type_name_filter: A regular filter expression or None (default).
If it's None, all types pass this filter.
- type_filter: A list of types the returned types must match.
Defaults to [], in which case all types pass this filter
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
parent
f7cc364be2
commit
18467a6500
1 changed files with 79 additions and 0 deletions
79
src/python/jw/pkg/lib/Types.py
Normal file
79
src/python/jw/pkg/lib/Types.py
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue