Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use TypeGuard in IsImplementation #92

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
------------------

* Added support for Python 3.10.
* ``interface.IsImplementation`` now uses `TypeGuard`_ so ``mypy`` understands that if it returns ``True``, the
passed object can be considered an implementation of that interface.

.. _TypeGuard: https://docs.python.org/3/library/typing.html#typing.TypeGuard

2.1.0 (2021-03-19)
------------------
Expand Down
9 changes: 5 additions & 4 deletions src/oop_ext/interface/_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ class MyCalculatorImpl(object):
from functools import lru_cache
from typing import Any
from typing import Callable
from typing import cast
from typing import Dict
from typing import FrozenSet
from typing import Generic
Expand All @@ -57,6 +56,8 @@ class MyCalculatorImpl(object):
from typing import TypeVar
from typing import Union

from typing_extensions import TypeGuard

from oop_ext.foundation.cached_method import ImmutableParamsCachedMethod
from oop_ext.foundation.decorators import Deprecated
from oop_ext.foundation.is_frozen import IsDevelopment
Expand Down Expand Up @@ -99,7 +100,7 @@ class BadImplementationError(InterfaceError):
pass


# InterfaceType should be changed to ``Type[Interface]`` after https://github.com/python/mypy/issues/5374
# InterfaceType should be changed to ``Type[Interface]`` after https://github.com/python/mypy/issues/4717
# is fixed.
InterfaceType = Type[Any]

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

def IsImplementation(
class_or_instance: Any,
interface: InterfaceType,
interface: Type[T],
*,
requires_declaration: bool = True,
) -> bool:
) -> TypeGuard[T]:
"""
:param class_or_instance: type or classobj or object

Expand Down
29 changes: 29 additions & 0 deletions src/oop_ext/interface/_tests/test_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -1019,6 +1019,35 @@ def Foo(a: IAcme) -> int:
result.assert_ok()


def testIsImplementation(type_checker) -> None:
"""
Check that IsImplementation correctly tells mypy that the type now has that narrowed type
inside the if-block.
"""
type_checker.make_file(
"""
from oop_ext.interface import Interface, IsImplementation
class IAcme(Interface):
def Foo(self, a, b=None) -> int: # type:ignore[empty-body]
...

class Acme:
def Foo(self, a, b=None) -> int:
return 40 + a

def GetIt(a: object) -> int:
if IsImplementation(a, IAcme):
return a.Foo(10)
return 0

GetIt(Acme())
GetIt("hello")
"""
)
result = type_checker.run()
result.assert_ok()


def testAttributeTypeChecking(type_checker) -> None:
class IFoo(Interface, TypeCheckingSupport):
value: int = Attribute(int)
Expand Down