-
-
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
Fix crashes in synthetic types #3322
Conversation
Using unions as fallbacks for typed dicts has the problem that the union could be arbitrarily complex for a typed dict with multiple nested typed dicts, etc. It's actually just the kind of situation where unions may not be a good option. My current thinking is basically like this:
Also, we use |
I am marking this as work-in-progress until we resolve the question of third/fourth pass, also I have discovered some spurious errors with this PR in |
Can you resolve the merge conflict? |
Resolved. |
@gvanrossum I have fixed the merge conflict, do you have any comments on this? |
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.
Really, all looks fine, just style nits. Thanks!
@@ -731,6 +731,7 @@ class ClassDef(Statement): | |||
info = None # type: TypeInfo # Related TypeInfo | |||
metaclass = '' # type: Optional[str] | |||
decorators = None # type: List[Expression] | |||
analyzed = None # type: Optional[Expression] |
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.
Does this need an update to serialize() (perhaps in the comment)?
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 think we don't need to serialize this (since it is needed only for semantic analysis). I updated the comment.
mypy/semanal.py
Outdated
@@ -3686,6 +3691,11 @@ def visit_class_def(self, tdef: ClassDef) -> None: | |||
if tdef.info.mro: | |||
tdef.info.mro = [] # Force recomputation | |||
calculate_class_mro(tdef, self.fail_blocker) | |||
if tdef.analyzed: |
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.
Add is not None
?
mypy/semanal.py
Outdated
if tdef.analyzed: | ||
if isinstance(tdef.analyzed, TypedDictExpr): | ||
self.analyze(tdef.analyzed.info.typeddict_type) | ||
if isinstance(tdef.analyzed, NamedTupleExpr): |
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.
Probably use elif
to make it totally clear that these are separate cases. Also, nothing to do for NewType here?
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.
We (intentionally) support only functional syntax for NewType
, therefore it can't appear in ClassDef
analyzed.
test-data/unit/check-classes.test
Outdated
from typing import List, NamedTuple | ||
from mypy_extensions import TypedDict | ||
|
||
class NM(NamedTuple): |
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'd order the classes and the test expressions the same way.
y1 = NM(x=[]) | ||
reveal_type(x) # E: Revealed type is 'TypedDict(x=builtins.list[Any], _fallback=__main__.TD)' | ||
reveal_type(x1) # E: Revealed type is 'TypedDict(x=builtins.list[Any], _fallback=typing.Mapping[builtins.str, builtins.list[Any]])' | ||
reveal_type(y) # E: Revealed type is 'Tuple[builtins.list[Any], fallback=__main__.NM]' |
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.
No reveal for y1?
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.
Added it.
[out] | ||
|
||
-- The two tests below will not crash after | ||
-- https://github.com/python/mypy/issues/3319 is fixed |
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.
Please remember to remove the -skip
then.
test-data/unit/check-classes.test
Outdated
[case testCrashInvalidArgsSyntheticClassSyntax] | ||
from typing import List, NamedTuple | ||
from mypy_extensions import TypedDict | ||
|
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 vote for fewer (or no) blank lines in tests.
@gvanrossum Thanks for review! I fixed all points. (For some reasons Travis is now much slower than AppVeyor.) |
Will merge once tests pass. Perhaps Travis is squeezing the python project because the sprints cause many runs for many repos in the project.. |
UPDATE: see #3322 (comment) below.
Fixes #3308
This PR adds better processing of "synthetic" types (
NewType
,NamedTuple
,TypedDict
) to the third pass and moves some processing from the second to the third pass. As @JukkaL noticed, this is still not perfect, since some MROs are recomputed in the third pass. I think the right way to fix this is just use unions in this case, the point is that users will practically never see these fallbacks, and this will also allow us to have better type expansions for fallbacks of generic tuple types. @JukkaL what do you think?Additional fourth pass seems to be an overkill for this. Although, I have a big project in my future plans (probably after we are more or less done with protocols): providing better support for forward references (there are a dozen similar issues about this) and recursive types. Even for this fourth pass seems to be a bit an overkill, and I would rather proceed as Jukka proposed in option (3) in #1701 (comment)