Skip to content

Commit e885427

Browse files
committed
Add support for partial/full specialization of Generic interfaces
1 parent 8ae9e76 commit e885427

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
2.2.0 (UNRELEASED)
22
------------------
33

4+
* Added support for interfaces that subclass ``Generic`` and partial/full implementations (meaning the implementation itself is not ``Generic``). Only for Python 3.7+.
45
* Added support for Python 3.10.
56

67
2.1.0 (2021-03-19)

src/oop_ext/interface/_interface.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,16 @@ def __GetInterfaceMethodsAndAttrs(
684684
if attr in self.INTERFACE_OWN_METHODS:
685685
continue
686686

687+
# error: Argument 2 to "issubclass" has incompatible type "<typing special form>"; expected "_ClassInfo" [arg-type]
688+
# This fails during type-checking, but is valid at runtime; let's keep this
689+
# check to ensure we do not break existing cases by accident.
690+
if issubclass(interface, Generic): # type:ignore[arg-type]
691+
# Do not check some specific methods that Generic declares,
692+
# in case we have a Generic interface and a partial/complete
693+
# specialization as implementation (see testGenericSupport).
694+
if attr in ("__class_getitem__", "__init_subclass__"):
695+
continue
696+
687697
val = getattr(interface, attr)
688698

689699
if type(val) in self._ATTRIBUTE_CLASSES:

src/oop_ext/interface/_tests/test_interface.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import re
2+
import sys
23
import textwrap
4+
from typing import Any
35
from typing import List
46

57
import pytest
@@ -1077,3 +1079,33 @@ def AfterCaption(*args):
10771079

10781080
assert Foo.GetCaption() == "Foo"
10791081
assert Foo().GetValues("m") == [0.1, 10.0]
1082+
1083+
1084+
@pytest.mark.skipif(
1085+
sys.version_info[:2] <= (3, 6), reason="Only supported in Python 3.7+"
1086+
)
1087+
def testGenericSupport() -> None:
1088+
from typing import Generic, TypeVar
1089+
1090+
T = TypeVar("T")
1091+
1092+
class AnInterface(Generic[T], interface.Interface, interface.TypeCheckingSupport):
1093+
def foo(self, v: T) -> T:
1094+
...
1095+
1096+
@interface.ImplementsInterface(AnInterface)
1097+
class Specialization:
1098+
def foo(self, v: str) -> str:
1099+
return v + "bar"
1100+
1101+
@interface.ImplementsInterface(AnInterface)
1102+
class FooGeneric(Generic[T]):
1103+
def foo(self, v: T) -> T:
1104+
return v
1105+
1106+
spec = Specialization()
1107+
assert spec.foo("hello") == "hellobar"
1108+
1109+
generic = FooGeneric[Any]()
1110+
assert generic.foo("hello") == "hello"
1111+
assert generic.foo(10) == 10

0 commit comments

Comments
 (0)