-
Notifications
You must be signed in to change notification settings - Fork 241
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
Typing spec should be clearer that type checkers are not expected to support PEP 3141 #1663
Comments
It could be also worth calling out that even though consistent subtyping/compatibility does allow def f(x: float) -> bool:
return x.is_integer()
f(42) # type checks, but fails at runtime |
I think that's already touched on where the spec says:
But perhaps it could be more explicitly stated, yes |
They are not subtypes, but they are part of the consistency (or rather the consistent subtyping) relation, which is what is normally used by type checkers for checking function calls. Am I interpreting the spec wording in the wrong way? |
I believe the |
Note that regardless of the terminology, this feature is unsound since it is not type safe generally to pass an |
A discussion on the merits and demerits of the current behaviour of type checkers in this regard is outside of the scope of this issue. |
The typing spec is not meant to be a tutorial or reference for users of the type system. It's a specification that clarifies how type checkers should behave. As such, I think it's unnecessary (and arguably inappropriate) for it to discuss PEP 3141, since it has no bearing on the type system. This topic could fit within the Typing Reference section of the docs. Contributions are welcome.
As @AlexWaygood said above, that statement is subtly incorrect. The type
That's not necessarily true. It depends on the type checker implementation. If a type checker assumes that I attempted to implement this suggestion in pyright but needed to back out part of it for compatibility reasons. See this issue for details. The problem is that there's currently no way in the type system to provide an annotation that means "only Here's an illustrative code sample that generates a runtime error. Pyright detects this error but mypy does not currently. Code sample in pyright playground def func1(f: float):
if not isinstance(f, float):
f.hex() # Runtime error
func1(1) |
Thanks for an extended reply, Eric.
I think it would be useful if the spec clarified that
I agree that this interpretation is sound, but I also find it confusing to treat Do you think we could re-evaluate this as part of the work in the spec? For example, we could explore an alternative design where literals have flexible types, so 42 would be |
Looking at
as a user, my first reaction would be to conclude that the body of the |
The spec is a living document, and the community welcomes new proposals. If this is a topic that's important to you and you'd like to champion a modification, feel free to create a new thread in the typing forum. This is an area where backward compatibility is really important, so any such proposals will be reviewed with that in mind. |
I'm not sure I agree. PEP-3141 is a PEP that has been accepted for 17 years, and has not been deprecated. At runtime, Python recognises I'm fully aware of the reasons why neither typeshed nor type checkers support PEP-3141, I don't believe that should change, and I don't want to restart a conversation about whether they should or not. But the decision by PEP-484 to state that PEP-3141 is a "runtime-only" numeric tower, and to instead implement a parallel "static-only" numeric tower that is understood by type checkers but is not understood by |
I don't really have anything to add to this conversation, except that I believe that historically when we wrote and accepted PEP 484, I personally believed that we were specifying that |
I was probably the person who cut out the mention of PEP 3141 from the spec. I agree with @erictraut above that a discussion of this topic is a better fit for the user-facing type system reference (which unfortunately we haven't yet done much work on). If all we are saying is that there are no special cases for type checkers, that doesn't feel like it's worth saying in the spec. Eric's suggestion above about the interpretation of the float/int special case is interesting and it does seem to be a more sensible interpretation than treating int directly as a subtype of float. |
From my perspective, there is a special case when it comes to PEP-3141. For all other stdlib ABCs, we've taken pains in typeshed to ensure that type checkers understand runtime virtual subclasses of those ABCs as static subtypes of those ABCs. |
I thought about this a bit more, and unfortunately flexible types have the same drawbacks as existing special-casing -- they allow for unsoundness (unless supplemented with runtime type checking). I wonder if the "right" thing to do here would be to abandon the idea of special-casing of |
But we always have runtime type checking, right? |
Because Python users have been doing that for the last 30 years. |
Raising
I agree that the type system should be designed around existing Python code, but given that it is probably impossible to account for every Python dynamic feature in a reasonable way, there will always be compromises. Could you give an example which stops working if 42 is statically modelled as just |
This passes for me, BTW. (Apparently this was a CPython bug and it was fixed in 3.12.) The same applies to Eric's example using
This sounds like a trick question. I'd say that in 3.12 the above example works at runtime, but it seems you would like it to fail the static check? Why? Because it fails at runtime in 3.11? |
It was not intended as one, actually. If we can guarantee that |
That would be my preferred approach (and what I had in mind when we created PEP 484, nearly 10 years ago). |
Perhaps it is orthogonal to the core concern of this issue, but if we amend this paragraph of the spec, I think it would also be better to use clearer terms for the relation that this special case applies to (e.g. "consistent with"), rather than discussing only argument annotations, as the current text does, which leaves underspecified whether the described behavior is also supposed to apply to non-argument annotations (e.g. |
With regards to the numeric tower, PEP 484 states:
This is a very useful passage to link to, because:
The parallel passage in the typing spec currently states this:
Since PEP-484 is a historical document rather than a piece of living documentation, it would be great if the typing spec could state as clearly as PEP-484 that the PEP-3141 ABCs are not the recommended way of annotating numeric types in Python, and that type checkers may not necessarily (and in fact probably won't ever) support them.
The text was updated successfully, but these errors were encountered: