Skip to content

Commit

Permalink
fix(annots): must consider other base classes for abstractmethod impl…
Browse files Browse the repository at this point in the history
…ementations (#26)
  • Loading branch information
kszucs authored Sep 2, 2024
1 parent fd8a65a commit e75bb77
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 4 deletions.
15 changes: 11 additions & 4 deletions koerce/annots.py
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,7 @@ def __new__(

namespace: dict[str, Any] = {}
parameters: dict[str, Parameter] = {}
abstractmethods: set = set()
for name, value in dct.items():
if isinstance(value, Parameter):
parameters[name] = value
Expand All @@ -697,9 +698,7 @@ def __new__(
slots.append(name)
else:
if getattr(value, "__isabstractmethod__", False):
abstracts.add(name)
else:
abstracts.discard(name)
abstractmethods.add(name)
namespace[name] = value

# merge the annotations with the parent annotations
Expand All @@ -724,7 +723,15 @@ def __new__(
__spec__=spec,
)
klass = super().__new__(metacls, clsname, bases, namespace, **kwargs)
klass.__abstractmethods__ = frozenset(abstracts)

# check whether the inherited abstract methods are implemented by
# any of the parent classes, basically recalculating the abstractmethods
for name in abstracts:
value = getattr(klass, name, None)
if getattr(value, "__isabstractmethod__", False):
abstractmethods.add(name)
klass.__abstractmethods__ = frozenset(abstractmethods)

return klass

def __call__(cls, *args, **kwargs):
Expand Down
23 changes: 23 additions & 0 deletions koerce/tests/test_annots.py
Original file line number Diff line number Diff line change
Expand Up @@ -1961,6 +1961,29 @@ def bar(self):
assert Bar.__abstractmethods__ == frozenset()


def test_annotable_recalculates_inherited_abstractmethods():
class Abstract(Annotable):
@abstractmethod
def foo(self): ...

@property
@abstractmethod
def bar(self): ...

class Mixin:
def foo(self):
return 1

def bar(self):
return 2

class Foo(Mixin, Abstract):
pass

assert Abstract.__abstractmethods__ == frozenset({"foo", "bar"})
assert Foo.__abstractmethods__ == frozenset()


# TODO(kszucs): test __new__ as well
def test_annotable_with_custom_init():
called_with = None
Expand Down

0 comments on commit e75bb77

Please sign in to comment.