From b8467b961e5c52ecc8a10c2e0c8f07a626b5a187 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 4 Jul 2019 12:45:30 +0100 Subject: [PATCH 1/5] New semantic analyzer: reverse the order of modules within SCC --- mypy/newsemanal/semanal_main.py | 7 ++++++ test-data/unit/check-classes.test | 42 +++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/mypy/newsemanal/semanal_main.py b/mypy/newsemanal/semanal_main.py index aa54c84ba76d..88c609c9defb 100644 --- a/mypy/newsemanal/semanal_main.py +++ b/mypy/newsemanal/semanal_main.py @@ -66,6 +66,9 @@ def semantic_analysis_for_scc(graph: 'Graph', scc: List[str], errors: Errors) -> """Perform semantic analysis for all modules in a SCC (import cycle). Assume that reachability analysis has already been performed. + + The scc will be processed roughly in the order the modules are included + in the list. """ patches = [] # type: Patches # Note that functions can't define new module-level attributes @@ -152,6 +155,10 @@ def restore_saved_attrs(saved_attrs: SavedAttributes) -> None: def process_top_levels(graph: 'Graph', scc: List[str], patches: Patches) -> None: # Process top levels until everything has been bound. + # Reverse order of the scc so the first modules in the original list will be + # be processed first. This helps with performance. + scc = list(reversed(scc)) + # Initialize ASTs and symbol tables. for id in scc: state = graph[id] diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index 563149956ea1..ada855359ef5 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -5925,3 +5925,45 @@ class B: class C(A, B): pass [out] + +[case testAttributeDefOrder1] +import a + +[file a.py] +from b import C + +class D(C): + def g(self) -> None: + self.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + + def f(self) -> None: + reveal_type(self.x) # N: Revealed type is 'builtins.int' + + +[file b.py] +import a + +class C: + def __init__(self) -> None: + self.x = 0 + +[case testAttributeDefOrder2] +class D(C): + def g(self) -> None: + self.x = '' + + def f(self) -> None: + # https://github.com/python/mypy/issues/7162 + reveal_type(self.x) # N: Revealed type is 'builtins.str' + + +class C: + def __init__(self) -> None: + self.x = 0 + +class E(C): + def g(self) -> None: + self.x = '' # E: Incompatible types in assignment (expression has type "str", variable has type "int") + + def f(self) -> None: + reveal_type(self.x) # N: Revealed type is 'builtins.int' From 44ee1fe976aea660ee804acdd50610af38900944 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 4 Jul 2019 16:16:31 +0100 Subject: [PATCH 2/5] Update test cases --- test-data/unit/check-attr.test | 4 +++- test-data/unit/check-dataclasses.test | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test-data/unit/check-attr.test b/test-data/unit/check-attr.test index c98433cf742d..bc5ad3293290 100644 --- a/test-data/unit/check-attr.test +++ b/test-data/unit/check-attr.test @@ -1156,7 +1156,9 @@ C(0).total = 1 # E: Property "total" defined in "C" is read-only import lib [file lib.py] import attr -from other import * +MYPY = False +if MYPY: # Force deferral + from other import * @attr.s class C: diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 288c37664d22..87c4c850bed0 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -670,7 +670,9 @@ c.x = 1 # E: Property "x" defined in "C" is read-only import lib [file lib.py] from dataclasses import dataclass -from other import * +MYPY = False +if MYPY: # Force deferral + from other import * @dataclass class C: From 42f040feac792570615daee4608c21e474dda2f7 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 5 Jul 2019 12:18:11 +0100 Subject: [PATCH 3/5] Fix tests --- test-data/unit/check-incremental.test | 6 +++--- test-data/unit/check-newsemanal.test | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 02a7077f4fe2..bee93d77f404 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -4521,12 +4521,12 @@ B = List[A] [builtins fixtures/list.pyi] [out] -tmp/other.pyi:2: error: Module 'lib' has no attribute 'A' +tmp/lib.pyi:4: error: Module 'other' has no attribute 'B' tmp/other.pyi:3: error: Cannot resolve name "B" (possible cyclic definition) [out2] -tmp/other.pyi:2: error: Module 'lib' has no attribute 'A' +tmp/lib.pyi:4: error: Module 'other' has no attribute 'B' tmp/other.pyi:3: error: Cannot resolve name "B" (possible cyclic definition) -tmp/a.py:3: note: Revealed type is 'builtins.list[builtins.list[Any]]' +tmp/a.py:3: note: Revealed type is 'builtins.list[Any]' [case testRecursiveNamedTupleTypedDict] # https://github.com/python/mypy/issues/7125 diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 6eed0400479d..91200a3f9c12 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -74,10 +74,10 @@ from a import bad2 # E: Module 'a' has no attribute 'bad2'; maybe "bad"? [case testNewAnalyzerTypeAnnotationCycle4] import b [file a.py] -# TODO: Could we generate an error here as well? -from b import bad +from b import bad # E: Module 'b' has no attribute 'bad' [file b.py] -from a import bad # E: Module 'a' has no attribute 'bad' +# TODO: Could we generate an error here as well? +from a import bad [case testNewAnalyzerExportedValuesInImportAll] from m import * @@ -436,11 +436,11 @@ def main() -> None: import b [file a.py] import b -x = b.x # E: Cannot determine type of 'x' +x = b.x # E: Cannot resolve attribute "x" (possible cyclic definition) \ + # E: Module has no attribute "x" [file b.py] import a -x = a.x # E: Cannot resolve attribute "x" (possible cyclic definition) \ - # E: Module has no attribute "x" +x = a.x [builtins fixtures/module.pyi] [case testNewAnalyzerMutuallyRecursiveOverloadedFunctions] From 245c02dfe28bed18ba04ebaac287b3d40e31cb1a Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 8 Jul 2019 15:01:21 +0100 Subject: [PATCH 4/5] Fix test case --- test-data/unit/check-newsemanal.test | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 867581eaab8d..57b2affbe68a 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -3081,4 +3081,4 @@ class Yes: ... import a def func() -> int: ... -[targets a, b, a, b.func, a.func, __main__] +[targets b, a, a, b.func, a.func, __main__] From ff260bb0fc4280afabae64b67ad43d25c9af08bc Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Mon, 8 Jul 2019 15:08:40 +0100 Subject: [PATCH 5/5] Check order of processing --- test-data/unit/check-classes.test | 4 ++++ test-data/unit/check-newsemanal.test | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index ada855359ef5..4bed558ead0d 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -5947,6 +5947,8 @@ class C: def __init__(self) -> None: self.x = 0 +[targets b, a, b.C.__init__, a.D.g, a.D.f, __main__] + [case testAttributeDefOrder2] class D(C): def g(self) -> None: @@ -5967,3 +5969,5 @@ class E(C): def f(self) -> None: reveal_type(self.x) # N: Revealed type is 'builtins.int' + +[targets __main__, __main__, __main__.D.g, __main__.D.f, __main__.C.__init__, __main__.E.g, __main__.E.f] diff --git a/test-data/unit/check-newsemanal.test b/test-data/unit/check-newsemanal.test index 57b2affbe68a..d7030cf07932 100644 --- a/test-data/unit/check-newsemanal.test +++ b/test-data/unit/check-newsemanal.test @@ -78,6 +78,7 @@ from b import bad # E: Module 'b' has no attribute 'bad' [file b.py] # TODO: Could we generate an error here as well? from a import bad +[targets a, b, a, b, a, b, a, b, __main__] [case testNewAnalyzerExportedValuesInImportAll] from m import * @@ -225,6 +226,8 @@ class A: class C(B): c: int +[targets b, a, b, a, __main__] + [case testNewAnalyzerTypedDictClass] from mypy_extensions import TypedDict import a @@ -298,6 +301,8 @@ from a import x def f(): pass +[targets a, b, a, a.y, b.f, __main__] + [case testNewAnalyzerRedefinitionAndDeferral1b] import a @@ -323,6 +328,8 @@ if MYPY: # Tweak processing order def f(): pass +[targets b, a, b, a, b.f, a.y, __main__] + [case testNewAnalyzerRedefinitionAndDeferral2a] import a