-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Prohibit list[int], etc (those fail at runtime) #2869
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
Conversation
mypy/nodes.py
Outdated
@@ -2143,17 +2152,20 @@ class SymbolTableNode: | |||
# For deserialized MODULE_REF nodes, the referenced module name; | |||
# for other nodes, optionally the name of the referenced object. | |||
cross_ref = None # type: Optional[str] | |||
# Was this node created by normaloze_type_alias? |
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.
normalize
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.
Thanks! Fixed.
Oh, that's great! Sorry for missing that. |
Thanks! This found tons of bad annotations in our internal codebase. FYI I am currently in a state where I can still write code but cannot focus long enough to review anything more complex than simple typeshed fixes. Hopefully this is not a permanent condition, but I expect it to persist for a few weeks... |
I am glad this is helpful. I hope you get better soon. |
@gvanrossum It turns out mypy itself uses |
Hold on. What's the value added from making this change? The "failure at runtime" in this case is actually "failure at import" which is pretty obvious to the user. In fact, I've been quietly hoping that once type checks get uncontroversial enough, we can revisit the ideas for Python 3.7 to either:
In both cases, we could start using generics on builtin collections without requiring runtime changes to the relevant C code. This makes for a big improvement in how obvious type hints look like to the reader. The proliferation of this problem in typeshed, internal codebases using type checks, proves that this is a problem. I'm getting support requests around this sometimes, and sometimes I myself make mistakes (either making a lowercase dict or an uppercase Str!). So, I'll open a new issue on python/typing about the 3.7 enhancement. In the mean time, can we defer merging this? |
@ambv from collections import defaultdict, Counter, ChainMap
from typing import Any
class MyDDict(defaultdict[int, str]): # Fails at runtime, mypy is silent
...
class CustomList(list[int]): # Same here, fails at runtime, mypy is silent
...
c = Counter[int]() # Same situation here
m = ChainMap[str, Any]() # ... and here In principle I am in favour of making annotations just strings (but rather via a flag or This PR will make all these errors, I think it is reasonable. |
Suggestion reported as python/typing#400. Nice round number. Your examples are valid concerns. Especially your generic base class examples are tricky. He we will require users to continue using the typing variants of collections even if python/typing#400 gets implemented. Regarding your last two examples, mypy should warn here too but ultimately they are solved by using variable annotations or type comments. The suggestion in PEP 484 to instantiate like that needs revision, we should probably discourage further use of that syntax. It's confusing to users why Does it make sense for your PR to only focus on raising errors for those cases? |
var: DefaultDict[int, str] = defaultdict() The PEP already makes the full form preferable: "It is not recommended to use the subscripted class (e.g.
It is a bit tricky but possible. However, I think it could be surprising for a user why this is flagged in some places but not in others. Also Jukka have pointed few more places where this will fail: in |
I really want this to land. It's the right thing from the PEP's POV -- writing I'm going to review the code now and unless I see something terrible I will merge it. Re: DefaultDict etc., I think we should really stick to our guns and disallow instantiation just like we disallow it for List etc., the reasons are the same -- these are generic "aliases" for concrete implementations (and that sets them apart from user-defined generic classes like Node as well as ABC's like Sequence). But that doesn't need to happen in the same PR. |
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.
Just some nits, really.
@@ -1283,13 +1286,19 @@ def process_import_over_existing_name(self, | |||
|
|||
def normalize_type_alias(self, node: SymbolTableNode, | |||
ctx: Context) -> SymbolTableNode: | |||
normalized = False | |||
if node.fullname in type_aliases: |
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.
This is the only place I can find where frozenset is treated differently -- do you understand why?
mypy/nodes.py
Outdated
collections_type_aliases.items()) # type: Dict[str, str] | ||
|
||
nongen_builtins = {'builtins.tuple': 'typing.Tuple', | ||
'builtins.frozenset': 'typing.FrozenSet', |
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.
I'm curious why this isn't in type_aliases
above. How is frozenset different from set?
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.
Oh, it looks like an old oversight. I just checked stubs for builtins.frozenset
and typing.FrozenSet
, the former is much better (it has all actual methods). I can easily fix this, but this requires a tiny PR to typeshed to be merged first and synced python/typeshed#979
Could you please merge it?
test-data/unit/check-generics.test
Outdated
[case testNoSubscriptionOfBuiltinAliases] | ||
from typing import List, TypeVar | ||
|
||
list[int]() # E: "list" is not subscriptable, use "typing.List" instead |
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.
This is somewhat sad, because if you follow the instructions in the message, you get List[int]()
which elicits a different error ("Type List cannot be instantiated; use list() instead"), but at runtime.
test-data/unit/check-generics.test
Outdated
reveal_type(fun()) # E: Revealed type is 'builtins.list[builtins.int]' | ||
|
||
BuiltinAlias = list | ||
BuiltinAlias[int]() # E: "list" is not subscriptable, use "typing.List" instead |
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.
Ditto.
test-data/unit/pythoneval.test
Outdated
_program.py:5: error: "defaultdict" is not subscriptable, use "typing.DefaultDict" instead | ||
_program.py:6: error: "Counter" is not subscriptable, use "typing.Counter" instead | ||
_program.py:9: error: "defaultdict" is not subscriptable, use "typing.DefaultDict" instead | ||
_program.py:12: error: Invalid index type "int" for "dict"; expected type "str" |
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.
Hm, why does this refer to dict instead of to DefaultDict?
As I am chugging through our internal codebase fixing the issues found here, I've noticed that the most common is |
I have made a PR to typeshed fixing some stubs python/typeshed#993 In the meantime I have found something more, something like this from collections.abc import Iterable
it: Iterable[int] is not flagged even with this PR. I understand the reason, but it is a bit more tedious to fix. I propose to make it in a separate PR. |
Merged! I am about halfway through changing our internal codebase and have not found additional complications. |
Thanks! I will open a separate issue about |
Hm. There's another issue, which is that this introduces the use of TYPE_CHECKING in mypy itself. This is unfortunately a problem with our internal installation that I can't easily fix, and we've in the past worked around this (using |
This is really a temporary measure until the new version of Python 3.6.1 final is scheduled for Monday, but on the other hand we don't plan to make any changes to |
No, the use of |
Also the reason (which I suddenly recall) is that we're running mypy with Python 3.5.1 which forces one to use the stdlib typing.py which doesn't even have TYPE_CHECKING. |
Fix in #3008 |
Fixes #2428
All of the following are now allowed, but fail at runtime:
I prohibit those by simply tracking whether a corresponding symbol table node was normalized or not.
@gvanrossum I make an exclusion for stubs, because a have found dozens of places where this is used in
typeshed
, if you think that it also makes sense to prohibit this in stubs, then I will make an additional PR totypeshed
.