Fix implementation checks on generic interfaces#109
Fix implementation checks on generic interfaces#109ro-oliveira95 wants to merge 1 commit intoESSS:masterfrom
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.
@nicoddemus
Do you think we could drop python 3.6 support in oop-ext?
There was a problem hiding this comment.
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: |
| ------------------ | ||
|
|
||
| * Added support for Python 3.10. | ||
| * #109: Non-generic implementers of generic interfaces are now correctly checked. |
| 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.
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.
| ------------------ | ||
|
|
||
| * Added support for Python 3.10. | ||
| * #109: Non-generic implementers of generic interfaces are now correctly checked. |
There was a problem hiding this comment.
| * #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.
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.
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.
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
- 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
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.
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.
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.
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.
Just a question.
What about AssertImplements(GenericFoo[str], IFoo)?
There was a problem hiding this comment.
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.
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.