diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..ae06a3f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,29 @@ +on: + release: + types: + - published + +name: release + +jobs: + pypi: + name: upload release to PyPI + runs-on: ubuntu-latest + environment: release + + permissions: + # Used to authenticate to PyPI via OIDC. + id-token: write + + steps: + - uses: actions/checkout@v3 + + - uses: actions/setup-python@v4 + with: + python-version: ">= 3.7" + + - name: build + run: pipx run build + + - name: publish + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/news/.bugfix b/news/.bugfix new file mode 100644 index 0000000..35a9e22 --- /dev/null +++ b/news/.bugfix @@ -0,0 +1 @@ +Preserve kwargs values in class defintion to ensure to properly create a concrete class with the same attributes as the original one. diff --git a/src/extendable/main.py b/src/extendable/main.py index e4d61a2..a0da920 100644 --- a/src/extendable/main.py +++ b/src/extendable/main.py @@ -27,6 +27,7 @@ class ExtendableClassDef: hierarchy: List["ExtendableClassDef"] metaclass: "ExtendableMeta" original_cls: Type["ExtendableMeta"] + kwargs: Dict[str, Any] def __init__( self, @@ -34,6 +35,7 @@ def __init__( bases: List[Any], namespace: Dict[str, Any], metaclass: "ExtendableMeta", + kwargs: Dict[str, Any], ) -> None: self.namespace = namespace self.name = namespace["__xreg_name__"] @@ -42,6 +44,7 @@ def __init__( self.base_names = namespace["__xreg_base_names__"] or [] self.hierarchy = [self] self.metaclass = metaclass + self.kwargs = kwargs def add_child(self, cls_def: "ExtendableClassDef") -> None: self.hierarchy.append(cls_def) @@ -69,7 +72,11 @@ def clone(self) -> "ExtendableClassDef": hierarchy. """ clone = ExtendableClassDef( - self.original_name, self.others_bases, self.namespace, self.metaclass + self.original_name, + self.others_bases, + self.namespace, + self.metaclass, + self.kwargs, ) clone.original_cls = self.original_cls return clone @@ -154,8 +161,9 @@ def _collect_class_def(metacls, name, bases, namespace, extends=None, **kwargs): cls_def = ExtendableClassDef( original_name=name, bases=tuple(other_bases), - namespace=namespace, + namespace=namespace.copy(), metaclass=metacls, + kwargs=kwargs, ) __register_class_def__( namespace["__module__"], @@ -191,9 +199,12 @@ def new_method( # ensure that arggs and kwargs are conform to the # initial signature inspect.signature(_initial_func).bind(cls, *args, **kwargs) - return getattr(cls._get_assembled_cls(), _method_name)( - *args, **kwargs - ) + try: + return getattr(cls._get_assembled_cls(), _method_name)( + *args, **kwargs + ) + except KeyError: + return _initial_func(cls, *args, **kwargs) new_method_def = functools.partial( new_method, _method_name=key, _initial_func=func diff --git a/src/extendable/registry.py b/src/extendable/registry.py index f88ea77..b80c27c 100644 --- a/src/extendable/registry.py +++ b/src/extendable/registry.py @@ -130,7 +130,7 @@ def build_extendable_class( extendableClass = types.new_class( simple_name, tuple(bases), - kwds={"metaclass": cls_def.metaclass}, + kwds=dict(class_def.kwargs, metaclass=cls_def.metaclass), exec_body=( lambda ns, namespace=namespace: ns.update(namespace) # type: ignore ),