-
-
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 concrete implementations be required to implement protocol? #8926
Comments
I ran into exactly this today. Mypy doesn't recognize the problem. It should! |
Yeah, this behavior is not what one would expect. Note that if There is a question about when exactly should we require a method to be implemented in the concrete derived class. The method @ilevkivskyi What do you think? |
This behavior seems to be as-expected according to the PEP.
I don't see any reason for a type error in the class declaration or the
That's expected due to how class inheritance works. If you want to make
That's how it will be treated at runtime, so the type checker should reflect this. Maybe there could be a warning if the body is |
When the PEP says: "To explicitly declare that a certain class implements a given protocol, it can be used as a regular base class. " ... IMO, it's a statement about the class, not the instance. If it's about the instance, then that doesn't fit (again, IMO) with the zen of Python type hinting. Why warn me about a function's return type not matching the type of the But I recognize that's a small part of the PEP and I doubt it really reflects the intent. I confess I'm trying to shoe-horn |
It seems like with the current behavior, a class that extends a protocol class without implementing its abstract members is treated like an abstract class. Defining an abstract class isn't a type error, but attempting to instantiate one is. If MyPy were to require protocol subclasses to implement all abstract protocol members, this would rule out defining an abstract subclass of a protocol, other than a subprotocol.
I can see a couple potential issues with this.
One possible solution would be if there were a way to explicitly mark a class as non-abstract, which would signal to the type-checker that all abstract members should be implemented. (This could be useful for ABCs as well.) For practical purposes, you can do something like this (with or without explicitly subclassing the protocol):
Note, however, that the PEP explicitly discourages this pattern. |
I also ran into this testing out the following snippet: class A(Protocol):
def a(self) -> int: ...
class B(A): ...
b = B()
b.a() mypy sees no problem with this, which is wrong. At runtime, Adding an incorrect implementation of class B(A):
def b(self):
return "not an int" |
@ktbarrett Your first example passes type-checking due to a quirk of MyPy unrelated to protocols. MyPy doesn't warn about methods/functions whose body is In your second example it looks like you meant to override |
That's quite the "quirk".
I did. Transliteration error.
Ugh, mypy complains if you provide an incompatible type in a subclass, but just ignores the definition in the base class if you don't annotate? That's not consistent or intuitive IMO. I really like how all these "expected" corner cases team up and seemingly misbehave. There are FAR too many gotchas in mypy. It should be convention to avoid them. |
There were two previous issues for the same problem: #8005 and #8395 but this one has the most discussion, so it probably makes sense to close the other two and keep this one. I was thinking that a method in a protocol should be treated as abstract if all of the following holds:
You could extend the first rule to also cover functions that only raise a |
Closes python#8005 Closes python#8926 Methods in Protocols are considered abstract if they have an empty function body, have a return type that is not compatible with `None`, and are not in a stub file.
This is not a good idea. The relevant part of PEP544.
I think the primary issue from the OP, myself, and others is the assumption of the opposite of 2. You see this in other languages like Java's interfaces and Rust's traits when you "implement" them. However, inheritance here isn't "implementing" the interface, it's more like using the Protocol as an ABC. ABCs allow for partial implementation. Deciding that when inheriting from Protocol the user must fully implement the interface will likely break a lot of users and changes the semantics of abstractmethod, violating the PEP. You could argue that the semantics of abstractmethod should have been different, which would make this change valid, but I think that ship sailed long ago... |
Just for reference, I recently implemented this feature in pyright in response to user requests, and pyright users have been happy with the functionality. My implementation uses the following rules. If these conditions are met, an error is generated for the child class declaration.
It also checks for the case where the base class contains an instance variable or class variable declaration without a value assignment and the child class also assigns no value for that variable. FWIW, pyright doesn't treat a This has been implemented in pyright for about a month now, and we haven't received any negative feedback so far. I think that's evidence that this check will not break users. It would be atypical to subclass from a protocol and leave some members unimplemented. In the rare case where that's desired and intended, it would be appropriate to mark the subclass as a protocol also, simply by including |
Ehhhh. I'm not sure about the trivial body being implicitly abstract part. Protocol being an ABCMeta and If you haven't seen anyone break because of the change to check Protocol immediate subclasses, it's a good idea in my book. I might extend the functionality a bit:
I'd really like to see the last sentence applied to subclasses of ABCs as well; "Concrete subclasses or ABCs must implement all abstract members". This would make the rules between the two universal: Protocol and ABC being two different ways of saying abstract, and all other classes being concrete. I really don't like implicitly abstract classes. I want to be able to look at the class definition and see immediately if it's abstract or not. If we can't do both, I'm less sure about it being a good idea. One should be like the other. Since |
Closes python#8005 Closes python#8926 Methods in Protocols are considered abstract if they have an empty function body, have a return type that is not compatible with `None`, and are not in a stub file.
Closes python#8005 Closes python#8926 Methods in Protocols are considered abstract if they have an empty function body, have a return type that is not compatible with `None`, and are not in a stub file.
Closes python#8005 Closes python#8926 Methods in Protocols are considered abstract if they have an empty function body, have a return type that is not compatible with `None`, and are not in a stub file.
Bug
or a mock-up repro if the source is private. We would appreciate
if you try to simplify your case to a minimal repro.
PEP 544 says you can state that an implementation supports a protocol by
subclassing the protocol.
It also says: "If one omits Protocol in the base class list, this would
be a regular (non-protocol) class that must implement Sized."
I would hope
mypy
would, either at declaration time or in theprint_person
function, spot that something claimed to be aPerson
but had neither the protocol variablename
nor the methodgreeting
.It's also a shame that
FrenchPerson
got thegreeting
method added to it from the subclassing.mypy
0.770 and Python 3.8.1None.
The text was updated successfully, but these errors were encountered: