import inspect
class ContainerDefinition:
__slots__ = 'val',
def __init__(self, val):
self.val = val
class Container:
def __init__(self):
self._map = {}
self._cache = {}
for key, fn in self.__class__.__dict__.items():
if not isinstance(fn, ContainerDefinition):
continue
try:
spec = inspect.getfullargspec(fn.val)
if len(spec.annotations) != len(spec.args):
raise TypeError('%s does not have all arguments annotated' % key)
if spec.varargs or spec.varkw:
raise TypeError('%s must not use variadic arguments' % key)
return_type = spec.annotations['return']
self._map[return_type] = fn.val, spec
except KeyError as e:
raise TypeError('%s does not have annotated return value' % key) from e
def definition():
def wrapper(fn):
return ContainerDefinition(fn)
return wrapper
def _get_method_args(self, fn, spec):
if len(spec.args) == 0:
return []
args = []
diff = len(spec.args) - (1 + 0 if not spec.defaults else len(spec.defaults))
for i, arg in enumerate(spec.args):
if i == 0: # "self"
continue
if arg in spec.annotations:
argtype = spec.annotations[arg]
if not inspect.isclass(argtype):
raise TypeError('Argument %s of %s() is not a class' % (
arg, fn.__qualname__
))
append = self._get_class(argtype)
elif spec.defaults and i >= diff:
append = spec.defaults[diff - i]
else:
raise TypeError('Argument %s of %s() neither has typehint nor a default value' % (
arg, fn.__qualname__
))
args.append(append)
return args
def _get_class(self, cls):
try:
return self._cache[cls]
except KeyError:
pass
try:
fn = self._map[cls]
args = self._get_method_args(fn[0], fn[1])
rv = self._cache[cls] = fn[0](self, *args)
return rv
except KeyError:
pass
for type, fnparams in self._map.items():
if issubclass(type, cls):
fn = self._map[type]
args = self._get_method_args(fn[0], fn[1])
rv = self._cache[cls] = fn[0](self, *args)
return rv
if inspect.isbuiltin(cls):
raise ValueError('No known method to construct %s' % cls)
fn = cls.__init__
spec = inspect.getfullargspec(fn)
args = self._get_method_args(fn, spec)
return cls(*args)
def get(self, cls):
if inspect.isclass(cls):
return self._get_class(cls)
else:
raise TypeError('non-class object requested from container')
class Test1:
def __init__(self, a):
self.a = a
class Test2:
def __init__(self, x:Test1, b=0, c=-1):
self.a = x.a + b + c
class Test3:
def __init__(self, x:Test1, y:Test2, c=400, d=-50):
self.a = x.a + y.a + c + d
class MyContainer(Container):
@Container.definition()
def new1(self) -> Test1:
return Test1(100)
print(MyContainer().get(Test3).a)
# class MyContainer(Container):
# @Container.definition()
# def new_int(self) -> int:
# return 104
# @Container.definition()
# def new_str(self) -> str:
# return "hello"
# @Container.definition()
# def new_tuple(self, a:int, b:str) -> tuple:
# return a * 2, b
# class Testing:
# pass
# class Testing2(Testing):
# pass
# class Testing3(Testing):
# def __init__(self, a:int, b:tuple):
# self.a = a
# self.b = b
# class Testing4:
# def __init__(self, x:Testing3):
# self.x = x
# x = MyContainer()
# t2 = x.get(Testing2)
# t = x.get(Testing)
# t4 = x.get(Testing4)
# print(t2)
# print(t)
# print(t4.x.a, t4.x.b)
To embed this program on your website, copy the following code and paste it into your website's HTML: