diff --git a/argbind/argbind.py b/argbind/argbind.py index 54e2fb2..5afa3e2 100644 --- a/argbind/argbind.py +++ b/argbind/argbind.py @@ -99,11 +99,18 @@ def bind(*args, without_prefix=False, positional=False, group: Union[list, str] def decorator(object_or_func): func = object_or_func + prefix = func.__qualname__ # get prefix before a potential monkey patch below changes it to a superclass's prefix is_class = inspect.isclass(func) if is_class: + # If the class has no __init__ method, find the __init__ method from the closest superclass + # that defines one. Then monkey patch that __init__ onto the class. + if '__init__' not in func.__dict__: + for base in func.__mro__[1:]: + if '__init__' in base.__dict__: + func.__init__ = base.__init__ # monkey patch + break func = getattr(func, "__init__") - prefix = func.__qualname__ if "__init__" in prefix: prefix = prefix.split(".")[0] diff --git a/examples/bind_existing/with_argbind.py b/examples/bind_existing/with_argbind.py index a7a17cf..69af05b 100644 --- a/examples/bind_existing/with_argbind.py +++ b/examples/bind_existing/with_argbind.py @@ -25,8 +25,8 @@ class BoundClass(MyClass): argbind.parse_args() # add for help text, though it isn't used here. args = { - 'MyClass.x': 'from binding', - 'pattern/MyClass.x': 'from binding in scoping pattern', + 'BoundClass.x': 'from binding', + 'pattern/BoundClass.x': 'from binding in scoping pattern', 'my_func.x': 123, 'args.debug': True # for printing arguments passed to each function } diff --git a/examples/bind_existing/with_argbind_abstract_classes.py b/examples/bind_existing/with_argbind_abstract_classes.py new file mode 100644 index 0000000..cf79692 --- /dev/null +++ b/examples/bind_existing/with_argbind_abstract_classes.py @@ -0,0 +1,42 @@ +import abc +from typing import Any, Dict + +import argbind + + +class Base(abc.ABC): + + def __init__(self, config: Dict[str, Any] = None): + self.config = config + + @abc.abstractmethod + def action(self): + """Do something""" + + +@argbind.bind() +class Sub1(Base): + + def action(self): + print('hello', self.config) + + +@argbind.bind() +class Sub2(Base): + + def action(self): + print('goodbye', self.config) + + +if __name__ == "__main__": + + argbind.parse_args() # add for help text, though it isn't used here. + + args = { + 'Sub1.config': {"a": "apple", "b": "banana"}, + 'Sub2.config': {"c": "cherry", "d": "durian"}, + } + + with argbind.scope(args): + Sub1().action() # prints "hello {'a': 'apple', 'b': 'banana'}" + Sub2().action() # prints "goodbye {'c': 'cherry', 'd': 'durian'}"