-
-
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
Support narrowing unions that include type[None]. #16315
Conversation
# Conflicts: # test-data/unit/check-narrowing.test
This comment has been minimized.
This comment has been minimized.
The primer's results show that the proposed changes also have other benefits. However, I have no idea what the failed mypyc runtime test is about. (I have no experience with Mypyc, so any hint is welcome.) |
The mypyc failure is likely spurious; it's been failing randomly for some time. I retriggered the job. |
Yes, you were right, thanks! |
NoneType_ = type(None) | ||
|
||
def f(cls: Type[Union[None, int]]) -> None: | ||
if issubclass(cls, NoneType_): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One case is still broken, I believe: (python3.10+)
if issubclass(cls, types.Nonetype)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. The definition in types.pyi is not helpful. I assumed something similar to mentioned test:
NoneType = type(None)
But it is:
@final
class NoneType:
def __bool__(self) -> Literal[False]: ...
My suggestion is to let ExpressionChecker.analyze_ref_expr
directly return TypeType(NoneType())
when it encounters the full name "types.NoneType". (already pushed)
This comment has been minimized.
This comment has been minimized.
The new unreachable error for clinic.py also seems correct because neither |
The clinic code in question is here: https://github.com/python/cpython/blob/1262e41842957c3b402fc0cf0a6eb2ea230c828f/Tools/clinic/clinic.py#L5828-L5831 I agree that the It looks to me like there's a bug in the annotations for diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index 5dd7900851..51964fb38e 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -5840,6 +5840,7 @@ def bad_node(self, node: ast.AST) -> None:
value = unknown
else:
value = ast.literal_eval(expr)
+ reveal_type(value)
py_default = repr(value)
if isinstance(value, (bool, NoneType)):
c_default = "Py_" + py_default And then run
That's because of an incorrect type declaration in |
Thanks for checking and confirming the error report is "true positive". But I see no contradiction between your and mine opinion (except that I definitely should have used more words; sorry for that). Hm, I just wrote the following example to explain my thinking (the note and error messages are generated by Mypy 1.6): from typing import final
class Null: ...
@final
class NoneType1: ...
class NoneType2: ...
class Impossible(Null, NoneType1): ... # error: Cannot inherit from final class "NoneType" [misc] (right)
class Possible(Null, NoneType2): ...
x: Null
if isinstance(x, NoneType1):
reveal_type(x) # note: Revealed type is "temp.<subclass of "Null" and "NoneType1">" (wrong)
if isinstance(x, NoneType2):
reveal_type(x) # note: Revealed type is "temp.<subclass of "Null" and "NoneType2">" (right) I intended to show that the unreachable warning is only correct for final types. (I somehow defined So, either I am confused, or this example reveals another Mypy limitation (that could possibly be easily fixed in this pull request as well). |
already reported here: #15148 |
I decided to open #16330. So, in my opinion, the pull request here is ready. |
Yeah, wise to defer that to another PR, imo! |
@tyralla, looks like there might be some merge conflicts here now — think you'd be able to merge in |
# Conflicts: # test-data/unit/check-narrowing.test
@AlexWaygood No big deal; just two tests inserted at the same place. |
This comment has been minimized.
This comment has been minimized.
Hmm, any idea why these errors are going away? I see mypy still emits an |
Yes, something is still missing in this pull request. Smaller repro: from types import NoneType
from typing import final
class X: ...
class Y: ...
@final
class Z: ...
x: X
if isinstance(x, (Y, Z)):
reveal_type(x) # N: note: Revealed type is "__main__.<subclass of "X" and "Y">" (right)
if isinstance(x, (Y, NoneType)):
reveal_type(x) # E: Statement is unreachable (wrong) I will have a look at it later. |
…oneType` and modify (fix?) the second algorithm tried in `conditional_types_with_intersection`.
This comment has been minimized.
This comment has been minimized.
…ot exist: "NoneType" is final` messages.
for more information, see https://pre-commit.ci
Diff from mypy_primer, showing the effect of this PR on open source code: alerta (https://github.com/alerta/alerta)
+ alerta/models/alert.py:38: error: Unused "type: ignore" comment [unused-ignore]
discord.py (https://github.com/Rapptz/discord.py)
- discord/app_commands/transformers.py:729: error: Dict entry 0 has incompatible type "AppCommandOptionType": "tuple[type[str], <typing special form>]"; expected "AppCommandOptionType": "tuple[type[Any], ...]" [dict-item]
- discord/app_commands/transformers.py:730: error: Dict entry 1 has incompatible type "AppCommandOptionType": "tuple[type[int], <typing special form>]"; expected "AppCommandOptionType": "tuple[type[Any], ...]" [dict-item]
- discord/app_commands/transformers.py:731: error: Dict entry 2 has incompatible type "AppCommandOptionType": "tuple[type[bool], <typing special form>]"; expected "AppCommandOptionType": "tuple[type[Any], ...]" [dict-item]
- discord/app_commands/transformers.py:732: error: Dict entry 3 has incompatible type "AppCommandOptionType": "tuple[type[float], <typing special form>]"; expected "AppCommandOptionType": "tuple[type[Any], ...]" [dict-item]
- discord/app_commands/transformers.py:739: error: Incompatible default for argument "_none" (default has type "<typing special form>", argument has type "type") [assignment]
- discord/app_commands/commands.py:235: error: Incompatible default for argument "_none" (default has type "<typing special form>", argument has type "type") [assignment]
|
@AlexWaygood This has been ready for a while now. Can you merge it, or is there anything left to do? |
I'm not a maintainer, just a triager, I'm afraid, so I can review but I can't merge :/ I'm also afraid I'm unusually busy at the moment, so I probably won't be able to take another look at this soon :( |
Ah, okay. I didn't know Mypy has triagers (to be honest, I didn't even know that "triager" is an English word...). So, this means this pull request has to wait until you find time to finish your review and give the maintainers your okay? (No need for a rush; I just wanted to make sure this won't be forgotten.) |
Not necessarily; it can be merged as soon as a maintainer approves it. That's maybe slightly more likely to happen if a triager approves it first, but it's very marginal — most PRs at mypy are just reviewed by a maintainer before being merged, as we actually have more maintainers than triagers. But as with most open-source projects, there aren't enough reviewers, so it can unfortunately take some time sometimes :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, thank you for the improvement!
Fixes #16279
See my comment in the referenced issue.