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

Add support for partial/full specialization of Generic interfaces #97

Closed
wants to merge 1 commit into from

Conversation

nicoddemus
Copy link
Member

No description provided.

@nicoddemus nicoddemus requested review from kfasolin and mv-per May 15, 2023 13:44
...

@interface.ImplementsInterface(AnInterface)
class Specialization:
Copy link
Member Author

@nicoddemus nicoddemus May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is 100% correct: the interface declares that every implementation should support any type as the parameter to foo, like FooGeneric does below.

Specialization is specializing T to str, which violates the AnInterface interface.

FooGeneric works out of the box now, without the changes in _interface.py in this PR.

I'm not sure the use case for Specialization is totally valid.

Copy link

@mv-per mv-per May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does work, but let's say I wrongfully implement Specialization as:

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

The above code works on runtime and on type_checking. However, it's clear that foo(self, v: int) -> str differs from foo(self, v: T) -> T but neither mypy or interface can see this Liskov substitution principle violation.

If we use Protocol directly, we would have something like:

    from typing import TypeVar, Protocol

    T = TypeVar("T")

    class AnInterface(Protocol[T]):
        def foo(self, v: T) -> T:
            ...

    class Specialization(AnInterface[str]):
        def foo(self, v: int) -> str:
            return str(v) + "bar"

    spec = Specialization()
    assert spec.foo(1) == "1bar"

what would cause:

src/oop_ext/interface/_tests/test_interface.py:1129: error: Argument 1 of "foo" is incompatible with supertype "AnInterface"; supertype defines the argument type as "str"  [override]
src/oop_ext/interface/_tests/test_interface.py:1129: note: This violates the Liskov substitution principle
src/oop_ext/interface/_tests/test_interface.py:1129: note: See https://mypy.readthedocs.io/en/stable/common_issues.html#incompatible-overrides

Seems like mypy is not properly checking the interface's structure

Copy link
Member Author

@nicoddemus nicoddemus May 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but neither mypy or interface can see this Liskov substitution principle violation.

mypy does not know about our Interface implementation at all, that's why -- it cannot konw that Specialization and AnInterface are related at all.

For new interface hierarchies, we should consider using plain ABC classes and/or Protocols, as they are natively interpreted by mypy.

(Btw in you example you are not supposed to subclass from AnInterface).

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!    Quality Gate passed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 1 Code Smell

No Coverage information No Coverage information
0.0% 0.0% Duplication

@nicoddemus
Copy link
Member Author

As we discussed, in the end this does not seem to be the right approach. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants