Skip to content

Commit 0c46e6c

Browse files
committed
Fix lack of support on checking generic interfaces
Previosly verifying non-generic classes that correctly implements generic interfaces used to fail, as the interface holds an special method coming from the typing.Generic inheritance (`__class_getitem__`). The changes here solves this limitation.
1 parent e03adf1 commit 0c46e6c

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

src/oop_ext/interface/_interface.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -747,7 +747,7 @@ def AssertImplementsFullChecking(
747747

748748

749749
# set of methods that might be declared in interfaces but should be not be required by implementations
750-
_INTERFACE_METHODS_TO_IGNORE = {"__init_subclass__"}
750+
_INTERFACE_METHODS_TO_IGNORE = {"__init_subclass__", "__class_getitem__"}
751751

752752

753753
def _AssertImplementsFullChecking(

src/oop_ext/interface/_tests/test_interface.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1077,3 +1077,36 @@ def AfterCaption(*args):
10771077

10781078
assert Foo.GetCaption() == "Foo"
10791079
assert Foo().GetValues("m") == [0.1, 10.0]
1080+
1081+
1082+
def testGenericInterface() -> None:
1083+
"""Generic interfaces need to support checking on both generic and non-generic implementers"""
1084+
from typing import Generic, TypeVar
1085+
1086+
T = TypeVar("T", covariant=True)
1087+
1088+
class IFoo(Interface, Generic[T], TypeCheckingSupport):
1089+
def GetOutput(self) -> T: # type:ignore[empty-body]
1090+
...
1091+
1092+
@ImplementsInterface(IFoo, no_check=True) # Will check later.
1093+
class GenericFoo(Generic[T]):
1094+
def __init__(self, output: T) -> None:
1095+
self.output = output
1096+
1097+
@Implements(IFoo.GetOutput)
1098+
def GetOutput(self) -> T:
1099+
return self.output
1100+
1101+
@ImplementsInterface(IFoo, no_check=True)
1102+
class NonGenericFoo:
1103+
@Implements(IFoo.GetOutput)
1104+
def GetOutput(self) -> int:
1105+
return 1
1106+
1107+
# This works out of the box.
1108+
AssertImplements(GenericFoo, IFoo)
1109+
AssertImplements(GenericFoo[str](output="foo"), IFoo)
1110+
1111+
# This only works if we skip the verification of `__class_getitem__` method.
1112+
AssertImplements(NonGenericFoo, IFoo)

0 commit comments

Comments
 (0)