From 471bf152c52cb237a645c30f6d05c7e37fb6c8aa Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 2 Jul 2019 20:11:15 +0100 Subject: [PATCH 1/3] Fix crash in plugins when star import blocks adding a symbol --- mypy/plugins/attrs.py | 5 ++++- mypy/plugins/dataclasses.py | 8 +++++++- test-data/unit/check-attr.test | 16 ++++++++++++++++ test-data/unit/check-dataclasses.test | 16 ++++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index d97e92875977..9fb9d39bb200 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -212,7 +212,10 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext', if ctx.api.options.new_semantic_analyzer: # Check if attribute types are ready. for attr in attributes: - if info[attr.name].type is None and not ctx.api.final_iteration: + node = info.get(attr.name) + if node is None: + return + if node.type is None and not ctx.api.final_iteration: ctx.api.defer() return diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 5e9a895d803a..21c5ed63d69c 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -213,7 +213,13 @@ def collect_attributes(self) -> List[DataclassAttribute]: if not isinstance(lhs, NameExpr): continue - node = cls.info.names[lhs.name].node + sym = cls.info.names.get(lhs.name) + if sym is None: + # This name is likely blocked by a star import. + assert ctx.api.options.new_semantic_analyzer + continue + + node = sym.node if isinstance(node, PlaceholderNode): # This node is not ready yet. continue diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index 070528b7495a..c98433cf742d 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -1151,3 +1151,19 @@ class C: C(0).total = 1 # E: Property "total" defined in "C" is read-only [builtins fixtures/bool.pyi] + +[case testTypeInAttrDeferredStar] +import lib +[file lib.py] +import attr +from other import * + +@attr.s +class C: + total = attr.ib(type=int) + +C() # E: Too few arguments for "C" +C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int" +[file other.py] +import lib +[builtins fixtures/bool.pyi] diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 5150c257b2fd..288c37664d22 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -665,3 +665,19 @@ def func() -> int: ... c: C c.x = 1 # E: Property "x" defined in "C" is read-only [builtins fixtures/bool.pyi] + +[case testTypeInDataclassDeferredStar] +import lib +[file lib.py] +from dataclasses import dataclass +from other import * + +@dataclass +class C: + total: int + +C() # E: Too few arguments for "C" +C('no') # E: Argument 1 to "C" has incompatible type "str"; expected "int" +[file other.py] +import lib +[builtins fixtures/bool.pyi] From df44f728d9a02cc112b71c7161495048678f6e72 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Tue, 2 Jul 2019 20:14:52 +0100 Subject: [PATCH 2/3] Add a comment --- mypy/newsemanal/semanal.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypy/newsemanal/semanal.py b/mypy/newsemanal/semanal.py index 772f8e838c1d..51a0ddae02cc 100644 --- a/mypy/newsemanal/semanal.py +++ b/mypy/newsemanal/semanal.py @@ -201,6 +201,10 @@ class NewSemanticAnalyzer(NodeVisitor[None], # unbound names due to cyclic definitions and should not defer)? _final_iteration = False # These names couldn't be added to the symbol table due to incomplete deps. + # Note that missing names are per module, _not_ per namespace. This means that e.g. + # a missing name at global scope will block adding same name at a class scope. + # This should not affect correctness and is purely a performance issue, + # since it can cause unnecessary deferrals. missing_names = None # type: Set[str] # Callbacks that will be called after semantic analysis to tweak things. patches = None # type: List[Tuple[int, Callable[[], None]]] From 696e407e27d7d08f4b25304f6ded02b161dad0f3 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Wed, 3 Jul 2019 14:25:41 +0100 Subject: [PATCH 3/3] Address CR --- mypy/newsemanal/semanal.py | 2 ++ mypy/plugins/attrs.py | 2 ++ mypy/plugins/dataclasses.py | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/mypy/newsemanal/semanal.py b/mypy/newsemanal/semanal.py index 51a0ddae02cc..002030076be4 100644 --- a/mypy/newsemanal/semanal.py +++ b/mypy/newsemanal/semanal.py @@ -205,6 +205,8 @@ class NewSemanticAnalyzer(NodeVisitor[None], # a missing name at global scope will block adding same name at a class scope. # This should not affect correctness and is purely a performance issue, # since it can cause unnecessary deferrals. + # Note that a star import adds a special name '*' to the set, this blocks + # adding _any_ names in the current file. missing_names = None # type: Set[str] # Callbacks that will be called after semantic analysis to tweak things. patches = None # type: List[Tuple[int, Callable[[], None]]] diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py index 9fb9d39bb200..ae316f3d2730 100644 --- a/mypy/plugins/attrs.py +++ b/mypy/plugins/attrs.py @@ -214,6 +214,8 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext', for attr in attributes: node = info.get(attr.name) if node is None: + # This name is likely blocked by a star import. We don't need to defer because + # defer() is already called by mark_incomplete(). return if node.type is None and not ctx.api.final_iteration: ctx.api.defer() diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 21c5ed63d69c..7646c1dfacc4 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -215,7 +215,8 @@ def collect_attributes(self) -> List[DataclassAttribute]: sym = cls.info.names.get(lhs.name) if sym is None: - # This name is likely blocked by a star import. + # This name is likely blocked by a star import. We don't need to defer because + # defer() is already called by mark_incomplete(). assert ctx.api.options.new_semantic_analyzer continue