From 1eb99b4ca07c04eaa6e237ca17db2f98bb5fc5b9 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Fri, 28 Apr 2017 17:17:16 +0100 Subject: [PATCH 1/5] Add additional quick mode test cases Some of these are currently skipped because they are crashing. Add more descriptive names to existing --quick test cases. Also minor tweaks to existing test cases. --- test-data/unit/check-incremental.test | 335 ++++++++++++++++++++++++-- 1 file changed, 311 insertions(+), 24 deletions(-) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index f14cc4c97b2d..c04e0b430a8a 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -1948,7 +1948,7 @@ main:3: error: Revealed type is 'builtins.int' main:5: error: Revealed type is 'builtins.int' -- TODO: Add another test for metaclass in import cycle (reversed from the above test). --- This currently doesn't work. +-- This currently does not work. [case testDeleteFile] import n @@ -2035,7 +2035,11 @@ def foo(x) -> int: tmp/n.py:2: error: Too many arguments for "foo" [out3] -[case testQuickAndDirty1] +-- +-- Quick mode +-- + +[case testQuickAndDirtyInterfaceChangeDoesNotPropagate] # flags: --quick-and-dirty import b, c [file a.py] @@ -2051,7 +2055,7 @@ def a(x): pass [rechecked a] [stale a] -[case testQuickAndDirty2] +[case testQuickAndDirtyDoesNotInvalidateImportCycle] # flags: --quick-and-dirty import b, c [file a.py] @@ -2069,7 +2073,7 @@ x = 0 [rechecked b] [stale b] -[case testQuickAndDirty3] +[case testQuickAndDirtySwitchToIncrementalMode] # flags: --quick-and-dirty # flags2: --incremental import a, b @@ -2080,7 +2084,7 @@ import a [rechecked a, b, builtins] [stale a, b, builtins] -[case testQuickAndDirty4] +[case testQuickAndDirtyIntroduceErrorToExistingFunction] # flags: --quick-and-dirty import a, b [file a.py] @@ -2096,7 +2100,7 @@ tmp/a.py:2: error: Incompatible return value type (got "str", expected "int") [rechecked a] [stale a] -[case testQuickAndDirty5] +[case testQuickAndDirtyIntroduceErrorInNewFunction] # flags: --quick-and-dirty import a, b [file a.py] @@ -2112,7 +2116,7 @@ tmp/a.py:2: error: Incompatible return value type (got "str", expected "int") [rechecked a] [stale] -[case testQuickAndDirty6] +[case testQuickAndDirtyExistingError] # flags: --quick-and-dirty import a, b [file a.py] @@ -2130,7 +2134,7 @@ tmp/a.py:2: error: Incompatible return value type (got "float", expected "int") [rechecked a] [stale] -[case testQuickAndDirty7] +[case testQuickAndDirtyIntroduceReferencesWithinCycle] # flags: --quick-and-dirty import a, b [file a.py] @@ -2150,7 +2154,7 @@ tmp/a.py:3: error: Revealed type is 'def () -> builtins.int' [rechecked a] [stale] -[case testQuickAndDirty8] +[case testQuickAndDirtyIntroduceReferencesWithinCycle2] # flags: --quick-and-dirty import a, b [file a.py] @@ -2171,13 +2175,13 @@ tmp/b.py:3: error: Revealed type is 'def () -> builtins.int' [stale] -- (The behavior for blockers is actually no different than in regular incremental mode) -[case testQuickAndDirty9] +[case testQuickAndDirtyBlockerOnFirstRound] # flags: --quick-and-dirty import a, b [file a.py] import b -class B: pass -class C(B, B): pass # blocker +class B(C): pass +class C(B): pass # blocker [file b.py] import a [file a.py.2] @@ -2185,12 +2189,12 @@ import b class B: pass class C(B): pass [out1] -tmp/a.py:3: error: Duplicate base class "B" +tmp/a.py:3: error: Cycle in inheritance hierarchy [out2] [rechecked a, b] [stale a, b] -[case testQuickAndDirty10] +[case testQuickAndDirtyBlockerOnSecondRound] # flags: --quick-and-dirty import a, b [file a.py] @@ -2201,15 +2205,15 @@ class C(B): pass import a [file a.py.2] import b -class B: pass -class C(B, B): pass # blocker +class B(C): pass +class C(B): pass # blocker [out1] [out2] -tmp/a.py:3: error: Duplicate base class "B" +tmp/a.py:3: error: Cycle in inheritance hierarchy [rechecked a, b] [stale a, b] -[case testQuickAndDirty11] +[case testQuickAndDirtyRenameFunctionInTwoModules] # flags: --quick-and-dirty import a, b, c, d [file a.py] @@ -2227,7 +2231,7 @@ def g(): pass # renamed f to g [file c.py.2] from a import g -[case testQuickAndDirty12] +[case testQuickAndDirtyUnmodifiedModuleDoesNotGenerateError] # flags: --quick-and-dirty import a, b, c, d [file a.py] @@ -2240,7 +2244,7 @@ from a import C from b import C [file d.py] from c import C -C().f() +C().f() # no error because unmodified [file a.py.2] import d class C: @@ -2250,7 +2254,7 @@ from a import C [out1] [out2] -[case testQuickAndDirty13] +[case testQuickAndDirtyUnmodifiedModuleDoesNotGenerateError2] # flags: --quick-and-dirty import a, b, c [file a.py] @@ -2272,7 +2276,7 @@ class C: [rechecked a] [stale a] -[case testQuickAndDirty14] +[case testQuickAndDirtyTypeAliasReference] # flags: --quick-and-dirty import a, b [file a.py] @@ -2285,7 +2289,7 @@ S = str import b def f(x: b.S) -> int: return 0 -[case testQuickAndDirty15] +[case testQuickAndDirtyNamedTupleReference] # flags: --quick-and-dirty import a, b [file a.py] @@ -2299,7 +2303,7 @@ P = NamedTuple('P', (('x', int),)) import b def f(x: b.P) -> int: return 0 -[case testQuickAndDirty16] +[case testQuickAndDirtyTypeVarReference] # flags: --quick-and-dirty import a, b [file a.py] @@ -2312,3 +2316,286 @@ T = TypeVar('T') [file a.py.2] import b def f(x: b.T) -> int: return 0 + +[case testQuickAndDirtyDeleteFunctionUsedByOtherModule] +# flags: --quick-and-dirty +import a +[file a.py] +from b import f +[file b.py] +import a +def f() -> int: pass +a.f() +[file b.py.2] +import a +reveal_type(a.f) +[out2] +tmp/b.py:2: error: Revealed type is 'Any' + +[case testQuickAndDirtyDeleteClassUsedInAnnotation] +# flags: --quick-and-dirty +import a +[file a.py] +import b +def f() -> b.C: pass +[file b.py] +import a +class C: pass +[file b.py.2] +import a +reveal_type(a.f) +a.f().x +[out2] +tmp/b.py:2: error: Revealed type is 'def () -> ' +tmp/b.py:3: error: "" has no attribute "x" + +[case testQuickAndDirtyDeleteClassUsedAsBase] +# flags: --quick-and-dirty +import a +[file a.py] +import b +class D(b.C): pass +[file b.py] +import a +class C: pass +[file b.py.2] +import a +reveal_type(a.D) +a.D().x +[out2] +tmp/b.py:2: error: Revealed type is 'Any' + +[case testQuickAndDirtyDeleteNestedClassUsedInAnnotation] +# flags: --quick-and-dirty +import a +[file a.py] +import b +def f() -> b.C.D: pass +[file b.py] +import a +class C: + class D: pass +[file b.py.2] +import a +class C: + pass +reveal_type(a.f) +a.f().x +[out2] +tmp/b.py:4: error: Revealed type is 'def () -> ' +tmp/b.py:5: error: "" has no attribute "x" + +[case testQuickAndDirtyTurnGenericClassIntoNonGeneric-skip] +# flags: --quick-and-dirty +import a +[file a.py] +import b +def f() -> b.C[int]: pass +[file b.py] +from typing import TypeVar, Generic +import a + +T = TypeVar('T') +class C(Generic[T]): pass + +[file b.py.2] +import a +class C: pass +reveal_type(a.f) +c: C +d = a.f() +c = d +d = c +[out2] +# TODO: Crashes (https://github.com/python/mypy/issues/3279) + +[case testQuickAndDirtyTurnClassIntoGenericOne-skip] +# flags: --quick-and-dirty +import a +[file a.py] +import b +def f() -> b.C: pass +[file b.py] +import a +class C: pass +[file b.py.2] +from typing import TypeVar, Generic +import a + +T = TypeVar('T') +class C(Generic[T]): pass + +reveal_type(a.f) +c: C[int] +d = a.f() +d = c +c = d +[out2] +# TODO: Crashes (https://github.com/python/mypy/issues/3279) + +[case testQuickAndDirtyDeleteTypeVarUsedInAnnotation] +# flags: --quick-and-dirty +import a +[file a.py] +import b +def f(x: b.T) -> b.T: return x +[file b.py] +from typing import TypeVar +import a +T = TypeVar('T') +[file b.py.2] +import a +reveal_type(a.f) +reveal_type(a.f(1)) +[out2] +tmp/b.py:2: error: Revealed type is 'def [b.T] (x: b.T`-1) -> b.T`-1' +tmp/b.py:3: error: Revealed type is 'builtins.int*' + +[case testQuickAndDirtyDeleteNewTypeUsedInAnnotation] +# flags: --quick-and-dirty +import a +[file a.py] +import b +def f() -> b.C: pass +[file b.py] +from typing import NewType +import a +C = NewType('C', int) +[file b.py.2] +import a +reveal_type(a.f) +a.f().x +[out2] +tmp/b.py:2: error: Revealed type is 'def () -> ' +tmp/b.py:3: error: "" has no attribute "x" + +[case testQuickAndDirtyChangeClassIntoFunction] +# flags: --quick-and-dirty +import a +[file a.py] +import b +def f() -> b.C: pass +[file b.py] +import a +class C: pass +[file b.py.2] +import a +def C() -> None: pass +reveal_type(a.f) +a.f().x +[out2] +tmp/b.py:3: error: Revealed type is 'def () -> ' +tmp/b.py:4: error: "" has no attribute "x" + +[case testQuickAndDirtyChangeClassIntoVariable] +# flags: --quick-and-dirty +import a +[file a.py] +import b +def f() -> b.C: pass +[file b.py] +import a +class C: pass +[file b.py.2] +import a +C = 0 +reveal_type(a.f) +a.f().x +[out2] +tmp/b.py:3: error: Revealed type is 'def () -> ' +tmp/b.py:4: error: "" has no attribute "x" + +[case testQuickAndDirtyAddFile] +# flags: --quick-and-dirty +import a +[file a.py] +import b +x = '' +[file b.py] +import a +[file b.py.2] +import c +reveal_type(c.x) +[file c.py.2] +import a +x = 1 +reveal_type(a.x) +[rechecked b, c] +[stale] +[out2] +tmp/c.py:3: error: Revealed type is 'builtins.str' +tmp/b.py:2: error: Revealed type is 'builtins.int' + +[case testQuickAndDirtyDeleteFile] +# flags: --quick-and-dirty +import b +[file a.py] +def f() -> None: pass +[file b.py] +import a +a.f() +[delete a.py.2] +[file b.py.3] +import a +a.f() # Comment change +[file b.py.4] +# Remove import +[rechecked b] +[stale] +[rechecked2 b] +[stale2] +[rechecked3 b] +[stale3 b] +[out2] +tmp/b.py:1: error: Cannot find module named 'a' +tmp/b.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) +[out3] +tmp/b.py:1: error: Cannot find module named 'a' +tmp/b.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) +[out4] + +[case testQuickAndDirtyRenameModule] +# flags: --quick-and-dirty +import a +[file a.py] +import b +b.f() +[file b.py] +def f() -> None: pass +[delete b.py.2] +[file c.py] +def f() -> None: pass +[file a.py.2] +import c +c.f(1) +[file c.py.3] +def f() -> None: pass # comment change +[file c.py.4] +def f(x) -> None: pass +[out] +[out2] +tmp/a.py:2: error: Too many arguments for "f" +[out3] +tmp/a.py:2: error: Too many arguments for "f" +[out4] + +[case testQuickAndDirtyMultiplePasses] +# flags: --quick-and-dirty +import a +[file a.py] +import b +b.f() +[file b.py] +def f() -> None: pass +[file b.py.2] +# Write cache file but the error in a is not caught yet. +def f(x) -> None: pass +[file a.py.3] +# Editing a triggers the error. +import b +b.f() +[rechecked b] +[rechecked2 a] +[out2] +[out3] +tmp/a.py:3: error: Too few arguments for "f" From bcdfeb71a48810340515872648e13986ab81ec7e Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 1 Jun 2017 11:39:12 +0100 Subject: [PATCH 2/5] Address feedback --- test-data/unit/check-incremental.test | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index c04e0b430a8a..73f129bfe3f2 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -2084,7 +2084,7 @@ import a [rechecked a, b, builtins] [stale a, b, builtins] -[case testQuickAndDirtyIntroduceErrorToExistingFunction] +[case testQuickAndDirtyFixErrorInExistingFunction] # flags: --quick-and-dirty import a, b [file a.py] @@ -2116,7 +2116,7 @@ tmp/a.py:2: error: Incompatible return value type (got "str", expected "int") [rechecked a] [stale] -[case testQuickAndDirtyExistingError] +[case testQuickAndDirtyPersistingError] # flags: --quick-and-dirty import a, b [file a.py] @@ -2394,10 +2394,8 @@ def f() -> b.C[int]: pass [file b.py] from typing import TypeVar, Generic import a - T = TypeVar('T') class C(Generic[T]): pass - [file b.py.2] import a class C: pass @@ -2421,10 +2419,8 @@ class C: pass [file b.py.2] from typing import TypeVar, Generic import a - T = TypeVar('T') class C(Generic[T]): pass - reveal_type(a.f) c: C[int] d = a.f() From 16d54a04b567c0c35ff5e3a2a10a9ce8672e41dd Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 1 Jun 2017 11:59:06 +0100 Subject: [PATCH 3/5] Add test cases based on feedback --- test-data/unit/check-incremental.test | 56 +++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 73f129bfe3f2..7b6d237c3918 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -2174,6 +2174,62 @@ tmp/b.py:3: error: Revealed type is 'def () -> builtins.int' [rechecked b] [stale] +[case testQuickAndDirtyIntroduceReferencesWithinCycleNoError] +# flags: --quick-and-dirty +import a, b, c +[file a.py] +import b +[file b.py] +import a +class C: pass +def f() -> int: pass +[file c.py] +[file a.py.2] +import b +def g() -> b.C: pass +h = b.f +[file c.py.3] +import a +reveal_type(a.g) +reveal_type(a.h) +[out1] +[out2] +[out3] +tmp/c.py:2: error: Revealed type is 'def () -> b.C' +tmp/c.py:3: error: Revealed type is 'def () -> builtins.int' +[rechecked a] +[stale a] +[rechecked2 c] +[stale2] + +[case testQuickAndDirtyIntroduceReferencesWithinCycleNoError2] +# flags: --quick-and-dirty +import a, b, c +[file a.py] +import b +class C: pass +def f() -> int: pass +[file b.py] +import a +[file c.py] +[file b.py.2] +import a +def g() -> a.C: pass +h = a.f +[file c.py.3] +import b +reveal_type(b.g) +reveal_type(b.h) +[out1] +[out2] +[out3] +tmp/c.py:2: error: Revealed type is 'def () -> a.C' +tmp/c.py:3: error: Revealed type is 'def () -> builtins.int' +[rechecked b] +[stale b] +[rechecked2 c] +[stale2] + -- (The behavior for blockers is actually no different than in regular incremental mode) [case testQuickAndDirtyBlockerOnFirstRound] # flags: --quick-and-dirty From 14b752cfd907b845b4c57fa7e49ae2d2c3831f85 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 1 Jun 2017 17:57:09 +0100 Subject: [PATCH 4/5] Add test case --- test-data/unit/check-incremental.test | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 7b6d237c3918..0f749955a0ec 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -2651,3 +2651,30 @@ b.f() [out2] [out3] tmp/a.py:3: error: Too few arguments for "f" + +[case testQuickAndDirtySerializeStaleType] +# flags: --quick-and-dirty +import a, c +[file a.py] +import b +def f() -> b.C: pass +[file b.py] +import a +class C: pass +[file c.py] +[file b.py.2] +import a +x = a.f() +[file c.py.3] +import b +reveal_type(b.x) +def g(x: object) -> None: pass +g(b.x) +b.x.y +[rechecked b] +[stale b] +[rechecked2 c] +[stale2] +[out3] +tmp/c.py:2: error: Revealed type is '' +tmp/c.py:5: error: "" has no attribute "y" From deb3593fe1c7c0759d676bf447597ba86cca006b Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Thu, 1 Jun 2017 18:04:39 +0100 Subject: [PATCH 5/5] Minor updates based on feedback --- test-data/unit/check-incremental.test | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test-data/unit/check-incremental.test b/test-data/unit/check-incremental.test index 0f749955a0ec..5256c362531b 100644 --- a/test-data/unit/check-incremental.test +++ b/test-data/unit/check-incremental.test @@ -2615,7 +2615,7 @@ b.f() [file b.py] def f() -> None: pass [delete b.py.2] -[file c.py] +[file c.py.2] def f() -> None: pass [file a.py.2] import c @@ -2630,6 +2630,12 @@ tmp/a.py:2: error: Too many arguments for "f" [out3] tmp/a.py:2: error: Too many arguments for "f" [out4] +[rechecked a, c] +[stale c] +[rechecked2 a, c] +[stale2] +[rechecked3 a, c] +[stale3 a, c] [case testQuickAndDirtyMultiplePasses] # flags: --quick-and-dirty