Skip to content

Commit

Permalink
Add support for partial/full specialization of Generic interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed May 15, 2023
1 parent 8ae9e76 commit e885427
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
2.2.0 (UNRELEASED)
------------------

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

2.1.0 (2021-03-19)
Expand Down
10 changes: 10 additions & 0 deletions src/oop_ext/interface/_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,16 @@ def __GetInterfaceMethodsAndAttrs(
if attr in self.INTERFACE_OWN_METHODS:
continue

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

val = getattr(interface, attr)

if type(val) in self._ATTRIBUTE_CLASSES:
Expand Down
32 changes: 32 additions & 0 deletions src/oop_ext/interface/_tests/test_interface.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import re
import sys
import textwrap
from typing import Any
from typing import List

import pytest
Expand Down Expand Up @@ -1077,3 +1079,33 @@ def AfterCaption(*args):

assert Foo.GetCaption() == "Foo"
assert Foo().GetValues("m") == [0.1, 10.0]


@pytest.mark.skipif(
sys.version_info[:2] <= (3, 6), reason="Only supported in Python 3.7+"
)
def testGenericSupport() -> None:
from typing import Generic, TypeVar

T = TypeVar("T")

class AnInterface(Generic[T], interface.Interface, interface.TypeCheckingSupport):
def foo(self, v: T) -> T:
...

@interface.ImplementsInterface(AnInterface)
class Specialization:
def foo(self, v: str) -> str:
return v + "bar"

@interface.ImplementsInterface(AnInterface)
class FooGeneric(Generic[T]):
def foo(self, v: T) -> T:
return v

spec = Specialization()
assert spec.foo("hello") == "hellobar"

generic = FooGeneric[Any]()
assert generic.foo("hello") == "hello"
assert generic.foo(10) == 10

0 comments on commit e885427

Please sign in to comment.