-
Notifications
You must be signed in to change notification settings - Fork 2
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
Fix implementation checks on generic interfaces #109
base: master
Are you sure you want to change the base?
Fix implementation checks on generic interfaces #109
Conversation
Previously checking if a non-generic class implements a generic interface always used to fail, as the latter has a special method (`__class_getitem__`) that the former does't have, which led to false negatives.
32edb03
to
9ee227d
Compare
class IFoo(Interface, Generic[T], TypeCheckingSupport): | ||
# Class instance defined here as a workaround for this class to work in Python 3.6. | ||
__abstractmethods__: FrozenSet = frozenset() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nicoddemus
Do you think we could drop python 3.6 support in oop-ext?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely, IMO we can do this for all of our OS projects, supporting >=3.7 or even >=3.10 only, if the former is too much work.
The sonar cloud token is a organization wide configuration? |
There was an early attempt at this in the past: |
@@ -2,6 +2,28 @@ | |||
------------------ | |||
|
|||
* Added support for Python 3.10. | |||
* #109: Non-generic implementers of generic interfaces are now correctly checked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great changelog entry. 👍
class IFoo(Interface, Generic[T], TypeCheckingSupport): | ||
# Class instance defined here as a workaround for this class to work in Python 3.6. | ||
__abstractmethods__: FrozenSet = frozenset() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Definitely, IMO we can do this for all of our OS projects, supporting >=3.7 or even >=3.10 only, if the former is too much work.
@@ -2,6 +2,28 @@ | |||
------------------ | |||
|
|||
* Added support for Python 3.10. | |||
* #109: Non-generic implementers of generic interfaces are now correctly checked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
* #109: Non-generic implementers of generic interfaces are now correctly checked. | |
* `#109 <https://github.com/ESSS/oop-ext/pull/109>`_: Non-generic implementers of generic interfaces are now correctly checked. |
def GetOutput(self) -> T: # type:ignore[empty-body] | ||
... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To solve this new warning we are decorating interface methods with @abstractmethod
, so mypy won't complain. Also let's make the example more comprehensive by also having an input parameter:
def GetOutput(self) -> T: # type:ignore[empty-body] | |
... | |
@abstractmethod | |
def GetOutput(self, v: T) -> T: | |
... |
# Class instance defined here as a workaround for this class to work in Python 3.6. | ||
__abstractmethods__: FrozenSet = frozenset() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's drop Python 3.6 first.
# Class instance defined here as a workaround for this class to work in Python 3.6. | |
__abstractmethods__: FrozenSet = frozenset() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To drop Python 3.6:
- Review CI: https://github.com/ESSS/oop-ext/blob/master/.github/workflows/main.yml
- Update
setup.py
: - Update
tox.ini
:Line 2 in 9b6f944
envlist = py36, py37, py38, py39, docs - Update
CHANGELOG
: https://github.com/ESSS/oop-ext/blob/master/CHANGELOG.rst - Update
environment.devenv.yml
:oop-ext/environment.devenv.yml
Line 7 in 9b6f944
- python>=3.6
I think 3.7 has reached EOL. Yep. It did. |
You are right, let's drop that one too. |
class IFoo(Interface, typing.Generic[T]): | ||
def Bar(self) -> T: | ||
... | ||
|
||
|
||
@ImplementsInterface(IFoo, no_check=True) | ||
class Foo: | ||
@Implements(IFoo.Bar) | ||
def Bar(self) -> str: | ||
return "baz" | ||
|
||
|
||
AssertImplements(Foo, IFoo) # Ok now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And without the no_check
? Does the interface infra complain?
To appease mypy do we still have to declare TypeCheckingSupport
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And without the no_check? Does the interface infra complain?
Yes, it does complain.
To appease mypy do we still have to declare TypeCheckingSupport?
Yeah we still have to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And without the no_check? Does the interface infra complain?
Yes, it does complain.
Why? How?
AssertImplements(GenericFoo, IFoo) | ||
AssertImplements(GenericFoo[str](output="foo"), IFoo) | ||
|
||
# This only works if we skip the verification of `__class_getitem__` method. | ||
AssertImplements(NonGenericFoo, IFoo) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a question.
What about AssertImplements(GenericFoo[str], IFoo)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes it raise an AssertionError with the following error:
E AssertionError: Method 'GetOutput' is missing in class '_GenericAlias' (required by interface 'IFoo')
E type object '_GenericAlias' has no attribute 'GetOutput'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we want to handle that?
AssertImplements(GenericFoo[str], IFoo[str])
also fail?
Previously checking if a non-generic class implements a generic interface always used to fail, as the latter has a special method (
__class_getitem__
) that the former does't have, which led to false negatives.