Skip to content

Commit 4b0291e

Browse files
committed
Use TypeGuard in IsImplementation
However due to python/mypy#4717, this breaks IsImplemenation with errors like this one: Only concrete class can be given where "Type[_InterfM3]" is expected [type-abstract] Which is unfortunate.
1 parent 09a9953 commit 4b0291e

File tree

3 files changed

+38
-4
lines changed

3 files changed

+38
-4
lines changed

CHANGELOG.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
------------------
33

44
* Added support for Python 3.10.
5+
* ``interface.IsImplementation`` now uses `TypeGuard`_ so ``mypy`` understands that if it returns ``True``, the
6+
passed object can be considered an implementation of that interface.
7+
8+
.. _TypeGuard: https://docs.python.org/3/library/typing.html#typing.TypeGuard
59

610
2.1.0 (2021-03-19)
711
------------------

src/oop_ext/interface/_interface.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ class MyCalculatorImpl(object):
4242
from functools import lru_cache
4343
from typing import Any
4444
from typing import Callable
45-
from typing import cast
4645
from typing import Dict
4746
from typing import FrozenSet
4847
from typing import Generic
@@ -57,6 +56,8 @@ class MyCalculatorImpl(object):
5756
from typing import TypeVar
5857
from typing import Union
5958

59+
from typing_extensions import TypeGuard
60+
6061
from oop_ext.foundation.cached_method import ImmutableParamsCachedMethod
6162
from oop_ext.foundation.decorators import Deprecated
6263
from oop_ext.foundation.is_frozen import IsDevelopment
@@ -99,7 +100,7 @@ class BadImplementationError(InterfaceError):
99100
pass
100101

101102

102-
# InterfaceType should be changed to ``Type[Interface]`` after https://github.com/python/mypy/issues/5374
103+
# InterfaceType should be changed to ``Type[Interface]`` after https://github.com/python/mypy/issues/4717
103104
# is fixed.
104105
InterfaceType = Type[Any]
105106

@@ -314,10 +315,10 @@ def _CheckIsInterfaceSubclass(interface: Any) -> None:
314315

315316
def IsImplementation(
316317
class_or_instance: Any,
317-
interface: InterfaceType,
318+
interface: Type[T],
318319
*,
319320
requires_declaration: bool = True,
320-
) -> bool:
321+
) -> TypeGuard[T]:
321322
"""
322323
:param class_or_instance: type or classobj or object
323324

src/oop_ext/interface/_tests/test_interface.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1019,6 +1019,35 @@ def Foo(a: IAcme) -> int:
10191019
result.assert_ok()
10201020

10211021

1022+
def testIsImplementation(type_checker) -> None:
1023+
"""
1024+
Check that IsImplementation correctly tells mypy that the type now has that narrowed type
1025+
inside the if-block.
1026+
"""
1027+
type_checker.make_file(
1028+
"""
1029+
from oop_ext.interface import Interface, IsImplementation
1030+
class IAcme(Interface):
1031+
def Foo(self, a, b=None) -> int: # type:ignore[empty-body]
1032+
...
1033+
1034+
class Acme:
1035+
def Foo(self, a, b=None) -> int:
1036+
return 40 + a
1037+
1038+
def GetIt(a: object) -> int:
1039+
if IsImplementation(a, IAcme):
1040+
return a.Foo(10)
1041+
return 0
1042+
1043+
GetIt(Acme())
1044+
GetIt("hello")
1045+
"""
1046+
)
1047+
result = type_checker.run()
1048+
result.assert_ok()
1049+
1050+
10221051
def testAttributeTypeChecking(type_checker) -> None:
10231052
class IFoo(Interface, TypeCheckingSupport):
10241053
value: int = Attribute(int)

0 commit comments

Comments
 (0)