-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Should __class_getitem__
be considered part of the interface of Protocol classes?
#11886
Comments
__class_getitem__
be considered __class_getitem__
be considered as part of the interface of Protocol classes?
__class_getitem__
be considered as part of the interface of Protocol classes?__class_getitem__
be considered part of the interface of Protocol classes?
How it works right now: from typing import Protocol, runtime_checkable
@runtime_checkable
class ClassGetItem(Protocol):
def __class_getitem__(cls, val) -> object: # or `types.GenericAlias`
pass
class Example:
pass
def func(klass: ClassGetItem):
pass
print(issubclass(Example, ClassGetItem))
func(Example()) # E: Argument 1 to "func" has incompatible type "Example"; expected "ClassGetItem" What runtime does? It prints
So, it is complicated! |
I am going to CC @erictraut as well |
It looks as though |
Git blame indicates that |
Currently pyright ignores any variables without type annotations when performing protocol comparisons, but if you add a type annotation to It sounds like we should probably exclude both I'm thinking we should leave the check for Does that sound good? Here's a small unit test to clarify the proposal. from typing import Any, Final, Iterable, Protocol
class B:
...
class C:
def __class_getitem__(cls, __item: Any) -> Any:
...
class SupportsClassGetItem(Protocol):
__slots__: str | Iterable[str] = ()
def __class_getitem__(cls, __item: Any) -> Any:
...
b1: SupportsClassGetItem = B() # OK (missing __class_getitem__ is ignored)
c1: SupportsClassGetItem = C() # OK
b2: SupportsClassGetItem = B # Error (missing __class_getitem__)
c2: SupportsClassGetItem = C # OK |
To clarify, @erictraut, would the following code be supported under your proposal? from types import GenericAlias
from typing import Any, Protocol, TypeVar
AnyStr_co = TypeVar("AnyStr_co", str, bytes, covariant=True)
class PathLike(Protocol[AnyStr_co ]):
def __fspath__(self) -> AnyStr_co : ...
def __class_getitem__(self, __item: Any) -> GenericAlias: ...
class StrPath:
def __fspath__(self) -> str: ...
x: type[PathLike[str]] = StrPath
y = x() This snippet currently passes mypy (with or without |
Yes, the above example would pass with my proposal. It sounds like this meets the requirements for your use case, so I've implemented my proposal in pyright, and it will be included in the next release. For completeness, this sample should also pass (if and when mypy adds support for class object protocol matching): class PathLikeClass1(Protocol[AnyStr_co ]):
def __fspath__(__self, self) -> AnyStr_co : ...
z1: PathLikeClass1[str] = StrPath But this sample should not pass: class PathLikeClass2(Protocol[AnyStr_co ]):
def __fspath__(__self, self) -> AnyStr_co : ...
def __class_getitem__(cls, __item: Any) -> GenericAlias: ...
z2: PathLikeClass2[str] = StrPath |
Great, then it sounds like we're in agreement. Thanks! 😀 |
Should class objects with a
|
I'd argue using At least, that's what the docs on |
Great, less special casing it is. |
In python/typeshed#5869,
__class_getitem__
was added to the stub foros.PathLike
. The method exists on the class at runtime; however, the addition of the method caused regressions in mypy 0.920.PathLike
is defined in typeshed as a protocol, and so the addition of the method to the stub led mypy to erroneously believe that a custom class could only be considered a subtype ofPathLike
if it implemented__class_getitem__
. The change to the stub had to be reverted in python/typeshed#6591, in time for mypy 0.930.The question is: is there a situation where it is ever useful for
__class_getitem__
to be considered part of a protocol interface? Or, would it be better for__class_getitem__
to be special-cased, such that mypy does not consider it to be part of the interface, even if it is defined on aProtocol
class?Related: #11884, where a similar issue is being discussed w.r.t.
__slots__
. Cc. @sobolevn, who is working on the__slots__
issue, and @hauntsaninja, who filed the PR to fix thePathLike
regression.The text was updated successfully, but these errors were encountered: