-
-
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
Edge case: mypy
ignores Never
being passed as an argument to another function.
#15821
Comments
Mypy is correct in not generating an error here. The type Practically speaking, if the function If this were a common source of bugs, I suppose that a type checker could emit a warning in this case even though it doesn't violate any type rules. However, I don't think this is a common source of bugs. Your sample looks pretty contrived to me. |
@erictraut I completely agree that Python docs:
In other words, it is an axiom that no instances of type
If a type can have no members, a type-checker should probably raise an error if such a member was provably brought into existence. A bare |
But even under this view, shouldn't it at least produce an unreachable-error? Since the calling of the outer function is never reached? |
I don't think it's a bug, so I would categorize it as an enhancement request. The maintainers of mypy can decide what they would like to do here. As for pyright, I don't plan to emit any errors here because it's not a type violation and it's not a common bug pattern. |
So the second half sentence in "The bottom type, a type that has no members." is basically meaningless to type-checkers? Or do they intentionally ignore it for some pragmatic reason? |
The fact that a type has no members doesn't imply that it is an error to use it. |
It's a logical contradiction to simultaneously assume (1) instances of |
To chime in with my opinion:
We're better off for all of those. As such, practicality over purity and this is allowed. At least that's how it seems to me. If this is actually causing you errors, maybe things can be rethought. (Additionally, I don't see why you seem to expect type checkers to consider whether a function can be called when saying if it's type safe...? Like I don't think this violates purity either, I am providing an argument that doesn't rely on purity) |
@A5rocks Can you give an example?
Not sure how the proposal here would stop you from doing exhaustive checks.
We'll have https://peps.python.org/pep-0702/ for that.
When and how is this useful? |
Being strict about ② is really useful, for example, consider a function @overload
def get_last(x: list[Never]) -> None: ...
@overload
def get_last(x: list[T]) -> T: ... Given input
That is, I argue that I can't see any case where something like ¹: except possibly for unit tests that check that the error raising happens in the first place, but even then this is likely covered by allowing bare |
Here's an example of exhaustiveness checking, with a deprecation: import enum
import typing
class A(enum.Enum):
x = "X"
y = "Y"
def f(x: A) -> str:
if not isinstance(x, A):
# this function used to take "X" or "Y", maybe?
print(f"Make sure you pass `A.x` or `A.y`; not {x!r}")
x = A(x) # we're using a Never here
if x is A.x:
return "A"
elif x is A.y:
return "B"
else:
typing.assert_never(x) # we're using another Never here |
For the first part, @overload
def f(x: A) -> str: ...
@overload
@deprecated("Only A will be allowed in the future")
def f(x: Other) -> str: ... |
|
Then the docs need to be changed, as
Clearly is violated by the major type-checkers. |
We can discuss a documentation change over on the CPython repo, but I wrote that documentation and I don't think "has no members" implies that type checkers should error on usage of Never. |
@JelleZijlstra The following doesn't emit any errors, even with from typing import Never
def f() -> Never:
while True:
pass
x: Never = f() So the type checker has allowed me to create a member of |
Bug Report
This is kind of a version 2.0 for #15818, as I found an edge-case where
mypy
silently passes an instance ofNever
.(mypy-play with
--strict
and--warn-unreachable
)Expected Behavior
The above code should raise on lines containing
g(never_returns())
, as instances ofNever
cannot exist, and hence cannot be passed as arguments to other functions. Therefore, the only safe behavior is that functions that returnNever
can only appear "on their own", their output cannot be assigned to variables (the same code as above withx=never_returns()
in the last line gives the unhelpful message "Need type annotation for "x"") or be part of an expression:The
--warn-unreachable
flag is useless when the offending code appears in the last line of a script / function definition.The text was updated successfully, but these errors were encountered: