| -- Checks for incremental mode (see testcheck.py). |
| -- Each test is run twice, once with a cold cache, once with a warm cache. |
| -- Before the tests are run the second time, any *.py.next files are copied to *.py. |
| -- |
| -- Errors expected in the first run should be in the `[out1]` section, and |
| -- errors expected in the second run should be in the `[out2]` section. If a |
| -- section is omitted, it is expected there are no errors on that run. |
| -- |
| -- Any files that we expect to be rechecked should be annotated in the [rechecked] |
| -- annotation, and any files expect to be stale (aka have a modified interface) |
| -- should be annotated in the [stale] annotation. Note that a file that ends up |
| -- producing an error does not create a new cache file and so is not considered stale. |
| -- |
| -- The test suite will automatically assume that __main__ is stale and rechecked in |
| -- all cases so we can avoid constantly having to annotate it. The list of |
| -- rechecked/stale files can be in any arbitrary order, or can be left empty |
| -- if no files should be rechecked/stale. |
| |
| [case testIncrementalEmpty] |
| [rechecked] |
| [stale] |
| |
| [case testIncrementalBasics] |
| import m |
| [file m.py] |
| def foo(): |
| pass |
| [file m.py.next] |
| def foo() -> None: |
| pass |
| [rechecked m] |
| [stale m] |
| |
| [case testIncrementalError] |
| import m |
| [file m.py] |
| def foo() -> None: |
| pass |
| [file m.py.next] |
| def foo() -> None: |
| bar() |
| [rechecked m] |
| [stale] |
| [out2] |
| tmp/m.py:2: error: Name 'bar' is not defined |
| |
| [case testIncrementalSimpleImportSequence] |
| import mod1 |
| mod1.func1() |
| |
| [file mod1.py] |
| import mod2 |
| def func1() -> None: mod2.func2() |
| |
| [file mod2.py] |
| import mod3 |
| def func2() -> None: mod3.func3() |
| |
| [file mod3.py] |
| def func3() -> None: pass |
| |
| [rechecked] |
| [stale] |
| |
| |
| [case testIncrementalInternalChangeOnly] |
| import mod1 |
| mod1.func1() |
| |
| [file mod1.py] |
| import mod2 |
| def func1() -> None: mod2.func2() |
| |
| [file mod2.py] |
| import mod3 |
| def func2() -> None: mod3.func3() |
| |
| [file mod3.py] |
| def func3() -> None: pass |
| |
| [file mod3.py.next] |
| def func3() -> None: 3 + 2 |
| |
| [rechecked mod3] |
| [stale] |
| |
| |
| [case testIncrementalImportGone] |
| import mod1 |
| |
| [file mod1.py] |
| from mod2 import A |
| def func1() -> A: pass |
| |
| [file mod2.py] |
| class A: pass |
| |
| [file mod1.py.next] |
| def func1() -> A: pass |
| |
| [rechecked mod1] |
| [stale] |
| [out2] |
| tmp/mod1.py:1: error: Name 'A' is not defined |
| |
| [case testIncrementalSameNameChange] |
| import mod1 |
| |
| [file mod1.py] |
| from mod2 import A |
| def func1() -> A: pass |
| |
| [file mod2.py] |
| class A: pass |
| |
| [file mod2.py.next] |
| class Parent: pass |
| class A(Parent): pass |
| |
| [rechecked mod1, mod2] |
| [stale mod2] |
| |
| [case testIncrementalPartialInterfaceChange] |
| import mod1 |
| mod1.func1() |
| |
| [file mod1.py] |
| import mod2 |
| def func1() -> None: mod2.func2() |
| |
| [file mod2.py] |
| import mod3 |
| def func2() -> None: mod3.func3() |
| |
| [file mod3.py] |
| def func3() -> None: pass |
| |
| [file mod3.py.next] |
| def func3() -> int: return 2 |
| |
| [rechecked mod2, mod3] |
| [stale mod3] |
| |
| [case testIncrementalInternalFunctionDefinitionChange] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| def accepts_int(a: int) -> int: return a |
| accepts_int(mod2.foo()) |
| |
| [file mod2.py] |
| def foo() -> int: |
| def inner() -> int: |
| return 42 |
| return inner() |
| |
| [file mod2.py.next] |
| def foo() -> int: |
| def inner2() -> str: |
| return "foo" |
| return inner2() |
| |
| [rechecked mod2] |
| [stale] |
| [out2] |
| tmp/mod2.py:4: error: Incompatible return value type (got "str", expected "int") |
| |
| [case testIncrementalInternalScramble] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| mod2.foo() |
| |
| [file mod2.py] |
| def baz() -> int: |
| return 3 |
| |
| def bar() -> int: |
| return baz() |
| |
| def foo() -> int: |
| return bar() |
| |
| [file mod2.py.next] |
| def foo() -> int: |
| return baz() |
| |
| def bar() -> int: |
| return bar() |
| |
| def baz() -> int: |
| return 42 |
| [rechecked mod2] |
| [stale] |
| |
| [case testIncrementalMethodInterfaceChange] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| |
| [file mod2.py] |
| class Foo: |
| def bar(self, a: str) -> str: |
| return "a" |
| |
| [file mod2.py.next] |
| class Foo: |
| def bar(self, a: float) -> str: |
| return "a" |
| |
| [rechecked mod1, mod2] |
| [stale mod2] |
| |
| [case testIncrementalBaseClassChange] |
| import mod1 |
| |
| [file mod1.py] |
| from mod2 import Child |
| Child().good_method() |
| |
| [file mod2.py] |
| class Good: |
| def good_method(self) -> int: return 1 |
| class Bad: pass |
| class Child(Good): pass |
| |
| [file mod2.py.next] |
| class Good: |
| def good_method(self) -> int: return 1 |
| class Bad: pass |
| class Child(Bad): pass |
| |
| [rechecked mod1, mod2] |
| [stale mod2] |
| [out2] |
| tmp/mod1.py:2: error: "Child" has no attribute "good_method" |
| |
| [case testIncrementalCascadingChange] |
| import mod1 |
| |
| [file mod1.py] |
| from mod2 import A |
| def accepts_int(a: int) -> None: pass |
| accepts_int(A) |
| |
| [file mod2.py] |
| from mod3 import B |
| A = B |
| |
| [file mod3.py] |
| from mod4 import C |
| B = C |
| |
| [file mod4.py] |
| C = 3 |
| |
| [file mod4.py.next] |
| C = "A" |
| |
| [rechecked mod1, mod2, mod3, mod4] |
| [stale mod2, mod3, mod4] |
| [out2] |
| tmp/mod1.py:3: error: Argument 1 to "accepts_int" has incompatible type "str"; expected "int" |
| |
| [case testIncrementalBrokenCascade] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| def accept_int(a: int) -> int: return a |
| accept_int(mod2.mod3.mod4.const) |
| |
| [file mod2.py] |
| import mod3 |
| |
| [file mod3.py] |
| import mod4 |
| |
| [file mod4.py] |
| const = 3 |
| |
| [file mod3.py.next] |
| # Import to mod4 is gone! |
| |
| [rechecked mod1, mod2, mod3] |
| [stale mod3] |
| [builtins fixtures/module.pyi] |
| [out2] |
| tmp/mod1.py:3: error: "module" has no attribute "mod4" |
| |
| [case testIncrementalLongBrokenCascade] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| def accept_int(a: int) -> int: return a |
| accept_int(mod2.mod3.mod4.mod5.mod6.mod7.const) |
| |
| [file mod2.py] |
| import mod3 |
| |
| [file mod3.py] |
| import mod4 |
| |
| [file mod4.py] |
| import mod5 |
| |
| [file mod5.py] |
| import mod6 |
| |
| [file mod6.py] |
| import mod7 |
| |
| [file mod7.py] |
| const = 3 |
| |
| [file mod6.py.next] |
| # Import to mod7 is gone! |
| |
| [rechecked mod1, mod5, mod6] |
| [stale mod6] |
| [builtins fixtures/module.pyi] |
| [out2] |
| tmp/mod1.py:3: error: "module" has no attribute "mod7" |
| |
| [case testIncrementalNestedBrokenCascade] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| def accept_int(a: int) -> int: return a |
| accept_int(mod2.mod3.mod4.const) |
| |
| [file mod2/__init__.py] |
| import mod2.mod3 as mod3 |
| |
| [file mod2/mod3/__init__.py] |
| import mod2.mod3.mod4 as mod4 |
| |
| [file mod2/mod3/__init__.py.next] |
| # Import is gone! |
| |
| [file mod2/mod3/mod4.py] |
| const = 3 |
| |
| [rechecked mod1, mod2, mod2.mod3] |
| [stale mod2.mod3] |
| [builtins fixtures/module.pyi] |
| [out2] |
| tmp/mod1.py:3: error: "module" has no attribute "mod4" |
| |
| [case testIncrementalNestedBrokenCascadeWithType1] |
| import mod1, mod2.mod3.mod5 |
| |
| [file mod1.py] |
| import mod2 |
| def accept_int(x: int) -> None: pass |
| def produce() -> mod2.CustomType: |
| return mod2.CustomType() |
| a = produce() |
| accept_int(a.foo()) |
| |
| [file mod2/__init__.py] |
| from mod2.mod3 import CustomType |
| |
| [file mod2/mod3/__init__.py] |
| from mod2.mod3.mod4 import CustomType |
| |
| [file mod2/mod3/__init__.py.next] |
| # Import a different class that also happens to be called 'CustomType' |
| from mod2.mod3.mod5 import CustomType |
| def produce() -> CustomType: |
| return CustomType() |
| |
| [file mod2/mod3/mod4.py] |
| class CustomType: |
| def foo(self) -> int: return 1 |
| |
| [file mod2/mod3/mod5.py] |
| class CustomType: |
| def foo(self) -> str: return "a" |
| |
| [rechecked mod1, mod2, mod2.mod3] |
| [stale mod2, mod2.mod3] |
| [builtins fixtures/module.pyi] |
| [out1] |
| [out2] |
| tmp/mod1.py:6: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" |
| |
| [case testIncrementalNestedBrokenCascadeWithType2] |
| import mod1, mod2.mod3.mod5 |
| |
| [file mod1.py] |
| from mod2 import produce |
| def accept_int(x: int) -> None: pass |
| a = produce() |
| accept_int(a.foo()) |
| |
| [file mod2/__init__.py] |
| from mod2.mod3 import produce |
| |
| [file mod2/mod3/__init__.py] |
| from mod2.mod3.mod4 import CustomType |
| def produce() -> CustomType: |
| return CustomType() |
| |
| [file mod2/mod3/__init__.py.next] |
| # Import a different class that also happens to be called 'CustomType' |
| from mod2.mod3.mod5 import CustomType |
| def produce() -> CustomType: |
| return CustomType() |
| |
| [file mod2/mod3/mod4.py] |
| class CustomType: |
| def foo(self) -> int: return 1 |
| |
| [file mod2/mod3/mod5.py] |
| class CustomType: |
| def foo(self) -> str: return "a" |
| |
| [rechecked mod1, mod2, mod2.mod3] |
| [stale mod2.mod3] |
| [builtins fixtures/module.pyi] |
| [out1] |
| [out2] |
| tmp/mod1.py:4: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" |
| |
| [case testIncrementalRemoteChange] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| def accepts_int(a: int) -> None: pass |
| accepts_int(mod2.mod3.mod4.const) |
| |
| [file mod2.py] |
| import mod3 |
| |
| [file mod3.py] |
| import mod4 |
| |
| [file mod4.py] |
| const = 3 |
| |
| [file mod4.py.next] |
| const = "foo" |
| |
| [rechecked mod1, mod3, mod4] |
| [stale mod4] |
| [out2] |
| tmp/mod1.py:3: error: Argument 1 to "accepts_int" has incompatible type "str"; expected "int" |
| |
| [case testIncrementalBadChange] |
| import mod1 |
| |
| [file mod1.py] |
| from mod2 import func2 |
| |
| def func1() -> int: |
| return func2() |
| |
| [file mod2.py] |
| def func2() -> int: |
| return 1 |
| |
| [file mod2.py.next] |
| def func2() -> str: |
| return "foo" |
| |
| [rechecked mod1, mod2] |
| [stale mod2] |
| [out2] |
| tmp/mod1.py:4: error: Incompatible return value type (got "str", expected "int") |
| |
| [case testIncrementalBadChangeWithSave] |
| import mod0 |
| |
| [file mod0.py] |
| import mod1 |
| A = mod1.func2() |
| |
| [file mod1.py] |
| from mod2 import func2 |
| |
| def func1() -> int: |
| return func2() |
| |
| [file mod2.py] |
| def func2() -> int: |
| return 1 |
| |
| [file mod2.py.next] |
| def func2() -> str: |
| return "foo" |
| |
| [rechecked mod0, mod1, mod2] |
| [stale mod2] |
| [out2] |
| tmp/mod1.py:4: error: Incompatible return value type (got "str", expected "int") |
| |
| [case testIncrementalOkChangeWithSave] |
| import mod0 |
| |
| [file mod0.py] |
| import mod1 |
| A = mod1.func2() |
| |
| [file mod1.py] |
| from mod2 import func2 |
| |
| def func1() -> int: |
| func2() |
| return 1 |
| |
| [file mod2.py] |
| def func2() -> int: |
| return 1 |
| |
| [file mod2.py.next] |
| def func2() -> str: |
| return "foo" |
| |
| [rechecked mod0, mod1, mod2] |
| [stale mod0, mod2] |
| [out2] |
| |
| [case testIncrementalWithComplexDictExpression] |
| import mod1 |
| |
| [file mod1.py] |
| import mod1_private |
| |
| [file mod1_private.py] |
| my_dict = { |
| 'a': [1, 2, 3], |
| 'b': [4, 5, 6] |
| } |
| |
| [file mod1_private.py.next] |
| my_dict = { |
| 'a': [1, 2, 3], |
| 'b': [4, 5, 'a'] |
| } |
| |
| [rechecked mod1, mod1_private] |
| [stale mod1_private] |
| [builtins fixtures/dict.pyi] |
| |
| [case testIncrementalWithComplexConstantExpressionNoAnnotation] |
| import mod1 |
| |
| [file mod1.py] |
| import mod1_private |
| |
| [file mod1_private.py] |
| def foobar() -> int: return 1 |
| def baz() -> int: return 2 |
| const = 1 + foobar() |
| |
| [file mod1_private.py.next] |
| def foobar() -> int: return 1 |
| def baz() -> int: return 2 |
| const = 1 + baz() |
| |
| [rechecked mod1_private] |
| [stale] |
| |
| [case testIncrementalWithComplexConstantExpressionWithAnnotation] |
| import mod1 |
| |
| [file mod1.py] |
| import mod1_private |
| |
| [file mod1_private.py] |
| def foobar() -> int: return 1 |
| def baz() -> int: return 2 |
| const = 1 + foobar() # type: int |
| |
| [file mod1_private.py.next] |
| def foobar() -> int: return 1 |
| def baz() -> int: return 2 |
| const = 1 + baz() # type: int |
| |
| [rechecked mod1_private] |
| [stale] |
| |
| [case testIncrementalSmall] |
| import mod1 |
| |
| [file mod1.py] |
| import mod1_private |
| def accepts_int(a: int) -> None: pass |
| accepts_int(mod1_private.some_func(12)) |
| |
| [file mod1_private.py] |
| def some_func(a: int) -> int: |
| return 1 |
| |
| [file mod1_private.py.next] |
| def some_func(a: int) -> str: |
| return "a" |
| |
| [rechecked mod1, mod1_private] |
| [stale mod1_private] |
| [builtins fixtures/ops.pyi] |
| [out2] |
| tmp/mod1.py:3: error: Argument 1 to "accepts_int" has incompatible type "str"; expected "int" |
| |
| [case testIncrementalWithDecorators] |
| import mod1 |
| |
| [file mod1.py] |
| import mod1_private |
| def accepts_int(a: int) -> None: pass |
| accepts_int(mod1_private.some_func(12)) |
| |
| [file mod1_private.py] |
| from typing import Callable |
| def multiply(f: Callable[[int], int]) -> Callable[[int], int]: |
| return lambda a: f(a) * 10 |
| |
| def stringify(f: Callable[[int], int]) -> Callable[[int], str]: |
| return lambda a: str(f(a)) |
| |
| @multiply |
| def some_func(a: int) -> int: |
| return a + 2 |
| |
| [file mod1_private.py.next] |
| from typing import Callable |
| def multiply(f: Callable[[int], int]) -> Callable[[int], int]: |
| return lambda a: f(a) * 10 |
| |
| def stringify(f: Callable[[int], int]) -> Callable[[int], str]: |
| return lambda a: str(f(a)) |
| |
| @stringify |
| def some_func(a: int) -> int: |
| return a + 2 |
| [rechecked mod1, mod1_private] |
| [stale mod1_private] |
| [builtins fixtures/ops.pyi] |
| [out2] |
| tmp/mod1.py:3: error: Argument 1 to "accepts_int" has incompatible type "str"; expected "int" |
| |
| [case testIncrementalChangingClassAttributes] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| mod2.Foo.A |
| |
| [file mod2.py] |
| class Foo: |
| A = 3 |
| |
| [file mod2.py.next] |
| class Foo: |
| A = "hello" |
| |
| [rechecked mod1, mod2] |
| [stale mod2] |
| |
| [case testIncrementalChangingFields] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| f = mod2.Foo() |
| f.A |
| |
| [file mod2.py] |
| class Foo: |
| def __init__(self) -> None: |
| self.A = 3 |
| |
| [file mod2.py.next] |
| class Foo: |
| def __init__(self) -> None: |
| self.A = "hello" |
| |
| [rechecked mod1, mod2] |
| [stale mod2] |
| [out2] |
| |
| [case testIncrementalChangingFieldsWithAssignment] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| f = mod2.Foo() |
| B = f.A |
| |
| [file mod2.py] |
| class Foo: |
| def __init__(self) -> None: |
| self.A = 3 |
| |
| [file mod2.py.next] |
| class Foo: |
| def __init__(self) -> None: |
| self.A = "hello" |
| |
| [rechecked mod1, mod2] |
| [stale mod1, mod2] |
| |
| [case testIncrementalCheckingChangingFields] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| def accept_int(a: int) -> int: return a |
| f = mod2.Foo() |
| accept_int(f.A) |
| |
| [file mod2.py] |
| class Foo: |
| def __init__(self) -> None: |
| self.A = 3 |
| |
| [file mod2.py.next] |
| class Foo: |
| def __init__(self) -> None: |
| self.A = "hello" |
| |
| [rechecked mod1, mod2] |
| [stale mod2] |
| [out2] |
| tmp/mod1.py:4: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" |
| |
| [case testIncrementalNestedClassDefinition] |
| import mod1 |
| |
| [file mod1.py] |
| import mod2 |
| b = mod2.Foo.Bar() |
| b.attr |
| |
| [file mod2.py] |
| class Foo: |
| class Bar: |
| attr = 3 |
| |
| [file mod2.py.next] |
| class Foo: |
| class Bar: |
| attr = "foo" |
| |
| [rechecked mod1, mod2] |
| [stale mod2] |
| |
| [case testIncrementalSimpleBranchingModules] |
| import mod1 |
| import mod2 |
| |
| [file mod1.py] |
| def func() -> None: pass |
| |
| [file mod2.py] |
| def func() -> None: pass |
| |
| [file mod1.py.next] |
| def func() -> int: return 1 |
| |
| [rechecked mod1] |
| [stale mod1] |
| |
| [case testIncrementalSubmoduleImport] |
| from parent.childA import Foo |
| |
| def func1() -> Foo: |
| return Foo() |
| |
| [file parent/__init__.py] |
| from parent.childA import Foo |
| from parent.childB import Bar |
| |
| __all__ = ['Foo', 'Bar'] |
| |
| [file parent/childA.py] |
| import parent |
| |
| class Foo: |
| def test(self) -> int: |
| return parent.Bar().test() |
| |
| [file parent/childB.py] |
| class Bar: |
| def test(self) -> int: return 3 |
| |
| [builtins fixtures/module_all.pyi] |
| [rechecked] |
| [stale] |
| |
| [case testIncrementalSubmoduleWithAttr] |
| import mod.child |
| x = mod.child.Foo() |
| x.bar() |
| |
| [file mod/__init__.py] |
| |
| [file mod/child.py] |
| class Foo: |
| def bar(self) -> None: pass |
| [builtins fixtures/module.pyi] |
| [rechecked] |
| [stale] |
| |
| [case testIncrementalNestedSubmoduleImportFromWithAttr] |
| from mod1.mod2 import mod3 |
| def accept_int(a: int) -> None: pass |
| |
| accept_int(mod3.val3) |
| |
| [file mod1/__init__.py] |
| val1 = 1 |
| |
| [file mod1/mod2/__init__.py] |
| val2 = 1 |
| |
| [file mod1/mod2/mod3.py] |
| val3 = 1 |
| |
| [builtins fixtures/module.pyi] |
| [rechecked] |
| [stale] |
| |
| [case testIncrementalNestedSubmoduleWithAttr] |
| import mod1.mod2.mod3 |
| def accept_int(a: int) -> None: pass |
| |
| accept_int(mod1.mod2.mod3.val3) |
| accept_int(mod1.mod2.val2) |
| accept_int(mod1.val1) |
| |
| [file mod1/__init__.py] |
| val1 = 1 |
| |
| [file mod1/mod2/__init__.py] |
| val2 = 1 |
| |
| [file mod1/mod2/mod3.py] |
| val3 = 1 |
| |
| [builtins fixtures/module.pyi] |
| [rechecked] |
| [stale] |
| |
| [case testIncrementalSubmoduleParentWithImportFrom] |
| import parent |
| |
| [file parent/__init__.py] |
| from parent import a |
| |
| [file parent/a.py] |
| val = 3 |
| |
| [builtins fixtures/args.pyi] |
| [stale] |
| |
| [case testIncrementalSubmoduleParentBackreference] |
| import parent |
| |
| [file parent/__init__.py] |
| from parent import a |
| |
| [file parent/a.py] |
| import parent.b |
| |
| [file parent/b.py] |
| |
| [builtins fixtures/args.pyi] |
| [stale] |
| |
| [case testIncrementalSubmoduleParentBackreferenceComplex] |
| import parent |
| |
| [file parent/__init__.py] |
| import parent.a |
| |
| [file parent/a.py] |
| import parent.b |
| import parent.c |
| |
| [file parent/b.py] |
| import parent.a |
| |
| [file parent/c.py] |
| import parent.a |
| |
| [builtins fixtures/args.pyi] |
| [stale] |
| |
| [case testIncrementalReferenceNewFileWithImportFrom] |
| from parent import a |
| |
| [file parent/__init__.py] |
| |
| [file parent/a.py] |
| |
| [file parent/a.py.next] |
| from parent import b |
| |
| [file parent/b.py.next] |
| |
| [stale parent, parent.a, parent.b] |
| |
| [case testIncrementalReferenceExistingFileWithImportFrom] |
| from parent import a, b |
| |
| [file parent/__init__.py] |
| |
| [file parent/a.py] |
| |
| [file parent/b.py] |
| |
| [file parent/a.py.next] |
| from parent import b |
| |
| [stale parent.a] |
| |
| [case testIncrementalWithTypeIgnoreOnDirectImport] |
| import a, b |
| |
| [file a.py] |
| import b # type: ignore |
| |
| [file b.py] |
| import c |
| |
| [file c.py] |
| |
| [stale] |
| |
| [case testIncrementalWithTypeIgnoreOnImportFrom] |
| import a, b |
| |
| [file a.py] |
| from b import something # type: ignore |
| |
| [file b.py] |
| import c |
| something = 3 |
| |
| [file c.py] |
| |
| [stale] |
| |
| [case testIncrementalWithPartialTypeIgnore] |
| import a # type: ignore |
| import a.b |
| |
| [file a/__init__.py] |
| |
| [file a/b.py] |
| |
| [stale] |
| |
| [case testIncrementalAnyIsDifferentFromIgnore] |
| import b |
| |
| [file b.py] |
| from typing import Any |
| import a.b |
| |
| [file b.py.next] |
| from typing import Any |
| |
| a = 3 # type: Any |
| import a.b |
| |
| [file a/__init__.py] |
| |
| [file a/b.py] |
| |
| [rechecked b] |
| [stale] |
| [out2] |
| tmp/b.py:4: error: Name 'a' already defined |
| |
| [case testIncrementalSilentImportsAndImportsInClass] |
| # flags: --ignore-missing-imports |
| class MyObject(object): |
| from bar import FooBar |
| [stale] |
| |
| [case testIncrementalSameFileSize] |
| import m |
| |
| [file m.py] |
| def foo(a: int) -> None: pass |
| def bar(a: str) -> None: pass |
| |
| foo(3) |
| |
| [file m.py.next] |
| def foo(a: int) -> None: pass |
| def bar(a: str) -> None: pass |
| |
| bar(3) |
| |
| [rechecked m] |
| [stale] |
| [out2] |
| tmp/m.py:4: error: Argument 1 to "bar" has incompatible type "int"; expected "str" |
| |
| [case testIncrementalUnsilencingModule] |
| # cmd: mypy -m main package.subpackage.mod2 |
| # cmd2: mypy -m main package.subpackage.mod1 |
| # flags: --follow-imports=skip |
| |
| [file main.py] |
| from package.subpackage.mod1 import Class |
| |
| def handle(c: Class) -> None: |
| c.some_attribute |
| |
| [file package/__init__.py] |
| # empty |
| |
| [file package/subpackage/__init__.py] |
| # empty |
| |
| [file package/subpackage/mod1.py] |
| import collections # Any previously unloaded package works here |
| |
| class Class: pass |
| |
| [file package/subpackage/mod2.py] |
| # empty |
| |
| [builtins fixtures/args.pyi] |
| [rechecked collections, main, package.subpackage.mod1] |
| [stale collections, package.subpackage.mod1] |
| [out2] |
| tmp/main.py:4: error: "Class" has no attribute "some_attribute" |
| |
| [case testIncrementalWithIgnores] |
| import foo # type: ignore |
| |
| [builtins fixtures/module.pyi] |
| [stale] |
| |
| [case testIncrementalWithSilentImportsAndIgnore] |
| # cmd: mypy -m main b |
| # cmd2: mypy -m main c c.submodule |
| # flags: --follow-imports=skip |
| |
| [file main.py] |
| import a # type: ignore |
| import b |
| import c |
| |
| a.A().foo() |
| b.B().foo() |
| c.C().foo() |
| |
| [file b.py] |
| class B: |
| def foo(self) -> None: pass |
| |
| [file b.py.next] |
| |
| [file c/__init__.py] |
| class C: pass |
| |
| [file c/submodule.py] |
| val = 3 # type: int |
| val = "foo" |
| |
| [builtins fixtures/module_all.pyi] |
| [rechecked main, c, c.submodule] |
| [stale] |
| [out2] |
| tmp/c/submodule.py:2: error: Incompatible types in assignment (expression has type "str", variable has type "int") |
| tmp/main.py:7: error: "C" has no attribute "foo" |
| |
| [case testIncrementalRemoteError] |
| import m |
| m.C().foo().bar() |
| [file m.py] |
| import n |
| class C: |
| def foo(self) -> n.A: pass |
| [file n.py] |
| class A: |
| def bar(self): pass |
| [file n.py.next] |
| class A: |
| pass |
| [rechecked m, n] |
| [stale n] |
| [out2] |
| main:2: error: "A" has no attribute "bar" |
| |
| [case testIncrementalRemoteErrorFixed] |
| import m |
| m.C().foo().bar() |
| [file m.py] |
| import n |
| class C: |
| def foo(self) -> n.A: pass |
| [file n.py] |
| class A: |
| pass |
| [file n.py.next] |
| class A: |
| def bar(self): pass |
| [rechecked m, n] |
| [stale n] |
| [out1] |
| main:2: error: "A" has no attribute "bar" |
| |
| [case testIncrementalChangedError] |
| import m |
| [file m.py] |
| import n |
| def accept_int(x: int) -> None: pass |
| accept_int(n.foo) |
| [file n.py] |
| foo = "hello" |
| reveal_type(foo) |
| [file n.py.next] |
| foo = 3.14 |
| reveal_type(foo) |
| [rechecked m, n] |
| [stale] |
| [out1] |
| tmp/n.py:2: error: Revealed type is 'builtins.str' |
| tmp/m.py:3: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" |
| [out2] |
| tmp/n.py:2: error: Revealed type is 'builtins.float' |
| tmp/m.py:3: error: Argument 1 to "accept_int" has incompatible type "float"; expected "int" |
| |
| [case testIncrementalReplacingImports] |
| import good, bad, client |
| |
| [file good.py] |
| def foo(a: int) -> None: pass |
| |
| [file bad.py] |
| def foo(a: str) -> None: pass |
| |
| [file client.py] |
| import good |
| import bad |
| from good import foo |
| foo(3) |
| |
| [file client.py.next] |
| import good |
| import bad |
| from bad import foo |
| foo(3) |
| |
| [rechecked client] |
| [stale] |
| [out2] |
| tmp/client.py:4: error: Argument 1 to "foo" has incompatible type "int"; expected "str" |
| |
| [case testIncrementalChangingAlias] |
| import m1, m2, m3, m4, m5 |
| |
| [file m1.py] |
| from m2 import A |
| def accepts_int(x: int) -> None: pass |
| accepts_int(A()) |
| |
| [file m2.py] |
| from m3 import A |
| |
| [file m3.py] |
| from m4 import B |
| A = B |
| |
| [file m3.py.next] |
| from m5 import C |
| A = C |
| |
| [file m4.py] |
| def B() -> int: |
| return 42 |
| |
| [file m5.py] |
| def C() -> str: |
| return "hello" |
| |
| [rechecked m1, m2, m3] |
| [stale m3] |
| [out2] |
| tmp/m1.py:3: error: Argument 1 to "accepts_int" has incompatible type "str"; expected "int" |
| |
| [case testIncrementalSilentImportsWithBlatantError] |
| # cmd: mypy -m main |
| # flags: --follow-imports=skip |
| |
| [file main.py] |
| from evil import Hello |
| |
| [file main.py.next] |
| from evil import Hello |
| reveal_type(Hello()) |
| |
| [file evil.py] |
| def accept_int(x: int) -> None: pass |
| accept_int("not an int") |
| |
| [rechecked main] |
| [stale] |
| [out2] |
| tmp/main.py:2: error: Revealed type is 'Any' |
| |
| [case testIncrementalImportIsNewlySilenced] |
| # cmd: mypy -m main foo |
| # cmd2: mypy -m main |
| # flags: --follow-imports=skip |
| |
| [file main.py] |
| from foo import bar |
| def accept_int(x: int) -> None: pass |
| accept_int(bar) |
| |
| [file foo.py] |
| bar = 3 |
| |
| [file foo.py.next] |
| # Empty! |
| |
| [rechecked main] |
| [stale main] |
| |
| [case testIncrementalSilencedModuleNoLongerCausesError] |
| # cmd: mypy -m main evil |
| # cmd2: mypy -m main |
| # flags: --follow-imports=skip |
| |
| [file main.py] |
| from evil import bar |
| def accept_int(x: int) -> None: pass |
| accept_int(bar) |
| reveal_type(bar) |
| |
| [file evil.py] |
| bar = "str" |
| |
| [rechecked main] |
| [stale] |
| [out1] |
| tmp/main.py:3: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" |
| tmp/main.py:4: error: Revealed type is 'builtins.str' |
| [out2] |
| tmp/main.py:4: error: Revealed type is 'Any' |
| |
| [case testIncrementalFixedBugCausesPropagation] |
| import mod1 |
| |
| [file mod1.py] |
| from mod2 import A |
| val = A().makeB().makeC().foo() |
| reveal_type(val) |
| |
| [file mod2.py] |
| from mod3 import B |
| class A: |
| def makeB(self) -> B: return B() |
| |
| [file mod3.py] |
| from mod4 import C |
| class B: |
| def makeC(self) -> C: |
| val = 3 # type: int |
| val = "str" # deliberately triggering error |
| return C() |
| |
| [file mod3.py.next] |
| from mod4 import C |
| class B: |
| def makeC(self) -> C: return C() |
| |
| [file mod4.py] |
| class C: |
| def foo(self) -> int: return 1 |
| |
| [rechecked mod3, mod2, mod1] |
| [stale mod3, mod2] |
| [out1] |
| tmp/mod3.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") |
| tmp/mod1.py:3: error: Revealed type is 'builtins.int' |
| |
| [out2] |
| tmp/mod1.py:3: error: Revealed type is 'builtins.int' |
| |
| [case testIncrementalIncidentalChangeWithBugCausesPropagation] |
| import mod1 |
| |
| [file mod1.py] |
| from mod2 import A |
| val = A().makeB().makeC().foo() |
| reveal_type(val) |
| |
| [file mod2.py] |
| from mod3 import B |
| class A: |
| def makeB(self) -> B: return B() |
| |
| [file mod3.py] |
| from mod4 import C |
| class B: |
| def makeC(self) -> C: |
| val = 3 # type: int |
| val = "str" # deliberately triggering error |
| return C() |
| |
| [file mod4.py] |
| class C: |
| def foo(self) -> int: return 1 |
| |
| [file mod4.py.next] |
| class C: |
| def foo(self) -> str: return 'a' |
| |
| [rechecked mod4, mod3, mod2, mod1] |
| [stale mod4] |
| [out1] |
| tmp/mod3.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") |
| tmp/mod1.py:3: error: Revealed type is 'builtins.int' |
| |
| [out2] |
| tmp/mod3.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") |
| tmp/mod1.py:3: error: Revealed type is 'builtins.str' |
| |
| [case testIncrementalIncidentalChangeWithBugFixCausesPropagation] |
| import mod1 |
| |
| [file mod1.py] |
| from mod2 import A |
| val = A().makeB().makeC().foo() |
| reveal_type(val) |
| |
| [file mod2.py] |
| from mod3 import B |
| class A: |
| def makeB(self) -> B: return B() |
| |
| [file mod3.py] |
| from mod4 import C |
| class B: |
| def makeC(self) -> C: |
| val = 3 # type: int |
| val = "str" # deliberately triggering error |
| return C() |
| |
| [file mod3.py.next] |
| from mod4 import C |
| class B: |
| def makeC(self) -> C: return C() |
| |
| [file mod4.py] |
| class C: |
| def foo(self) -> int: return 1 |
| |
| [file mod4.py.next] |
| class C: |
| def foo(self) -> str: return 'a' |
| |
| [rechecked mod4, mod3, mod2, mod1] |
| [stale mod4, mod3, mod2] |
| [out1] |
| tmp/mod3.py:5: error: Incompatible types in assignment (expression has type "str", variable has type "int") |
| tmp/mod1.py:3: error: Revealed type is 'builtins.int' |
| |
| [out2] |
| tmp/mod1.py:3: error: Revealed type is 'builtins.str' |
| |
| [case testIncrementalSilentImportsWithInnerImports] |
| # cmd: mypy -m main foo |
| # flags: --ignore-missing-imports |
| |
| [file main.py] |
| from foo import MyClass |
| m = MyClass() |
| |
| [file main.py.next] |
| from foo import MyClass |
| m = MyClass() |
| reveal_type(m.val) |
| |
| [file foo.py] |
| class MyClass: |
| def __init__(self) -> None: |
| import unrelated |
| self.val = unrelated.test() |
| |
| [rechecked main] |
| [stale] |
| [out2] |
| tmp/main.py:3: error: Revealed type is 'Any' |
| |
| [case testIncrementalSilentImportsWithInnerImportsAndNewFile] |
| # cmd: mypy -m main foo |
| # cmd2: mypy -m main foo unrelated |
| # flags: --follow-imports=skip |
| |
| [file main.py] |
| from foo import MyClass |
| m = MyClass() |
| |
| [file main.py.next] |
| from foo import MyClass |
| m = MyClass() |
| reveal_type(m.val) |
| |
| [file foo.py] |
| class MyClass: |
| def __init__(self) -> None: |
| import unrelated |
| self.val = unrelated.test() |
| |
| [file unrelated.py] |
| def test() -> str: return "foo" |
| |
| [rechecked main, foo, unrelated] |
| [stale foo, unrelated] |
| [out2] |
| tmp/main.py:3: error: Revealed type is 'builtins.str' |
| |
| [case testIncrementalWorksWithNestedClasses] |
| import foo |
| |
| [file foo.py] |
| class MyClass: |
| class NestedClass: |
| pass |
| |
| class_attr = NestedClass() |
| |
| [rechecked] |
| [stale] |
| |
| [case testIncrementalWorksWithNamedTuple] |
| import foo |
| |
| [file foo.py] |
| from mid import MyTuple |
| def accept_int(x: int) -> None: pass |
| accept_int(MyTuple(1, "b", "c").a) |
| |
| [file mid.py] |
| from bar import MyTuple |
| |
| [file bar.py] |
| from typing import NamedTuple |
| MyTuple = NamedTuple('MyTuple', [ |
| ('a', int), |
| ('b', str), |
| ('c', str) |
| ]) |
| |
| [file bar.py.next] |
| from typing import NamedTuple |
| MyTuple = NamedTuple('MyTuple', [ |
| ('b', int), # a and b are swapped |
| ('a', str), |
| ('c', str) |
| ]) |
| |
| [rechecked bar, mid, foo] |
| [stale bar] |
| [out2] |
| tmp/foo.py:3: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" |
| |
| [case testIncrementalWorksWithNestedNamedTuple] |
| import foo |
| |
| [file foo.py] |
| from mid import Outer |
| def accept_int(x: int) -> None: pass |
| accept_int(Outer.MyTuple(1, "b", "c").a) |
| |
| [file mid.py] |
| from bar import Outer |
| |
| [file bar.py] |
| from typing import NamedTuple |
| class Outer: |
| MyTuple = NamedTuple('MyTuple', [ |
| ('a', int), |
| ('b', str), |
| ('c', str) |
| ]) |
| |
| [file bar.py.next] |
| from typing import NamedTuple |
| class Outer: |
| MyTuple = NamedTuple('MyTuple', [ |
| ('b', int), # a and b are swapped |
| ('a', str), |
| ('c', str) |
| ]) |
| |
| [rechecked bar, mid, foo] |
| [stale bar] |
| [out2] |
| tmp/foo.py:3: error: Argument 1 to "accept_int" has incompatible type "str"; expected "int" |
| |
| [case testIncrementalPartialSubmoduleUpdate] |
| # cmd: mypy -m a |
| # cmd2: mypy -m a a.c |
| # flags: --follow-imports=skip |
| |
| [file a/__init__.py] |
| from .b import B |
| from .c import C |
| |
| [file a/b.py] |
| class B: pass |
| |
| [file a/c.py] |
| class C: pass |
| |
| [file a/c.py.next] |
| class C: pass |
| pass |
| |
| [rechecked a, a.c] |
| [stale a, a.c] |
| [out] |
| |
| [case testIncrementalNestedClassRef] |
| import top |
| |
| [file top.py] |
| from funcs import callee |
| from classes import Outer |
| def caller(a: Outer.Inner) -> None: |
| callee(a) |
| |
| [file funcs.py] |
| from classes import Outer |
| def callee(a: Outer.Inner) -> None: |
| pass |
| |
| [file classes.py] |
| class Outer: |
| class Inner: |
| pass |
| |
| [file top.py.next] |
| from funcs import callee |
| from classes import Outer |
| def caller(a: Outer.Inner) -> int: |
| callee(a) |
| |
| [case testIncrementalLoadsParentAfterChild] |
| # cmd: mypy -m r.s |
| |
| [file r/__init__.py] |
| from . import s |
| |
| [file r/m.py] |
| class R: pass |
| |
| [file r/s.py] |
| from . import m |
| R = m.R |
| a = None # type: R |
| |
| [file r/s.py.next] |
| from . import m |
| R = m.R |
| a = None # type: R |
| |
| [case testIncrementalBaseClassAttributeConflict] |
| class A: pass |
| class B: pass |
| |
| class X: |
| attr = None # type: A |
| class Y: |
| attr = None # type: B |
| class Z(X, Y): pass |
| [stale] |
| [out] |
| main:8: error: Definition of "attr" in base class "X" is incompatible with definition in base class "Y" |
| [out2] |
| main:8: error: Definition of "attr" in base class "X" is incompatible with definition in base class "Y" |
| |
| [case testIncrementalFollowImportsSilent] |
| # flags: --follow-imports=silent |
| import a |
| [file a.py] |
| x = 0 |
| [file a.py.next] |
| x = 0 |
| x + '' |
| |
| [case testIncrementalFollowImportsSkip] |
| # flags: --follow-imports=skip |
| import a |
| reveal_type(a.x) |
| [file a.py] |
| / |
| [file a.py.next] |
| // |
| [out] |
| main:3: error: Revealed type is 'Any' |
| [out2] |
| main:3: error: Revealed type is 'Any' |
| |
| [case testIncrementalFollowImportsError] |
| # flags: --follow-imports=error |
| import a |
| [file a.py] |
| / |
| [file a.py.next] |
| // |
| [out1] |
| main:2: note: Import of 'a' ignored |
| main:2: note: (Using --follow-imports=error, module not passed on command line) |
| [out2] |
| main:2: note: Import of 'a' ignored |
| main:2: note: (Using --follow-imports=error, module not passed on command line) |
| |
| [case testIncrementalFollowImportsVariable] |
| # flags: --config-file tmp/mypy.ini |
| import a |
| reveal_type(a.x) |
| [file a.py] |
| x = 0 |
| [file mypy.ini] |
| [[mypy] |
| follow_imports = normal |
| [file mypy.ini.next] |
| [[mypy] |
| follow_imports = skip |
| [out1] |
| main:3: error: Revealed type is 'builtins.int' |
| [out2] |
| main:3: error: Revealed type is 'Any' |
| |
| [case testIncrementalNamedTupleInMethod] |
| from ntcrash import nope |
| [file ntcrash.py] |
| from typing import NamedTuple |
| class C: |
| def f(self) -> None: |
| A = NamedTuple('A', [('x', int), ('y', int)]) |
| [out1] |
| main:1: error: Module 'ntcrash' has no attribute 'nope' |
| [out2] |
| main:1: error: Module 'ntcrash' has no attribute 'nope' |
| |
| [case testIncrementalNamedTupleInMethod2] |
| from ntcrash import nope |
| [file ntcrash.py] |
| from typing import NamedTuple |
| class C: |
| class D: |
| def f(self) -> None: |
| A = NamedTuple('A', [('x', int), ('y', int)]) |
| [out1] |
| main:1: error: Module 'ntcrash' has no attribute 'nope' |
| [out2] |
| main:1: error: Module 'ntcrash' has no attribute 'nope' |
| |
| [case testIncrementalNamedTupleInMethod3] |
| from ntcrash import nope |
| [file ntcrash.py] |
| from typing import NamedTuple |
| class C: |
| def a(self): |
| class D: |
| def f(self) -> None: |
| A = NamedTuple('A', [('x', int), ('y', int)]) |
| [out1] |
| main:1: error: Module 'ntcrash' has no attribute 'nope' |
| [out2] |
| main:1: error: Module 'ntcrash' has no attribute 'nope' |
| |
| [case testIncrementalNamedTupleInMethod4] |
| from ntcrash import C |
| reveal_type(C().a) |
| reveal_type(C().b) |
| reveal_type(C().c) |
| [file ntcrash.py] |
| from typing import NamedTuple |
| class C: |
| def __init__(self) -> None: |
| A = NamedTuple('A', [('x', int)]) |
| self.a = A(0) |
| self.b = A(0) # type: A |
| self.c = A |
| [out1] |
| main:2: error: Revealed type is 'Tuple[builtins.int, fallback=ntcrash.C.A@4]' |
| main:3: error: Revealed type is 'Tuple[builtins.int, fallback=ntcrash.C.A@4]' |
| main:4: error: Revealed type is 'def (x: builtins.int) -> Tuple[builtins.int, fallback=ntcrash.C.A@4]' |
| [out2] |
| main:2: error: Revealed type is 'Tuple[builtins.int, fallback=ntcrash.C.A@4]' |
| main:3: error: Revealed type is 'Tuple[builtins.int, fallback=ntcrash.C.A@4]' |
| main:4: error: Revealed type is 'def (x: builtins.int) -> Tuple[builtins.int, fallback=ntcrash.C.A@4]' |
| |
| [case testIncrementalTypedDictInMethod] |
| from tdcrash import nope |
| [file tdcrash.py] |
| from mypy_extensions import TypedDict |
| class C: |
| def f(self) -> None: |
| A = TypedDict('A', {'x': int, 'y': int}) |
| [builtins fixtures/dict.pyi] |
| [out1] |
| main:1: error: Module 'tdcrash' has no attribute 'nope' |
| [out2] |
| main:1: error: Module 'tdcrash' has no attribute 'nope' |
| |
| [case testIncrementalTypedDictInMethod2] |
| from tdcrash import nope |
| [file tdcrash.py] |
| from mypy_extensions import TypedDict |
| class C: |
| class D: |
| def f(self) -> None: |
| A = TypedDict('A', {'x': int, 'y': int}) |
| [builtins fixtures/dict.pyi] |
| [out1] |
| main:1: error: Module 'tdcrash' has no attribute 'nope' |
| [out2] |
| main:1: error: Module 'tdcrash' has no attribute 'nope' |
| |
| [case testIncrementalTypedDictInMethod3] |
| from tdcrash import nope |
| [file tdcrash.py] |
| from mypy_extensions import TypedDict |
| class C: |
| def a(self): |
| class D: |
| def f(self) -> None: |
| A = TypedDict('A', {'x': int, 'y': int}) |
| [builtins fixtures/dict.pyi] |
| [out1] |
| main:1: error: Module 'tdcrash' has no attribute 'nope' |
| [out2] |
| main:1: error: Module 'tdcrash' has no attribute 'nope' |
| |
| [case testIncrementalTypedDictInMethod4] |
| from ntcrash import C |
| reveal_type(C().a) |
| reveal_type(C().b) |
| reveal_type(C().c) |
| [file ntcrash.py] |
| from mypy_extensions import TypedDict |
| class C: |
| def __init__(self) -> None: |
| A = TypedDict('A', {'x': int}) |
| self.a = A(x=0) |
| self.b = A(x=0) # type: A |
| self.c = A |
| [builtins fixtures/dict.pyi] |
| [out1] |
| main:2: error: Revealed type is 'TypedDict(x=builtins.int, _fallback=typing.Mapping[builtins.str, builtins.int])' |
| main:3: error: Revealed type is 'TypedDict(x=builtins.int, _fallback=ntcrash.C.A@4)' |
| main:4: error: Revealed type is 'def () -> ntcrash.C.A@4' |
| [out2] |
| main:2: error: Revealed type is 'TypedDict(x=builtins.int, _fallback=typing.Mapping[builtins.str, builtins.int])' |
| main:3: error: Revealed type is 'TypedDict(x=builtins.int, _fallback=ntcrash.C.A@4)' |
| main:4: error: Revealed type is 'def () -> ntcrash.C.A@4' |
| |
| [case testIncrementalPerFileFlags] |
| # flags: --config-file tmp/mypy.ini |
| import a |
| [file a.py] |
| pass |
| [file mypy.ini] |
| [[mypy] |
| warn_no_return = False |
| [[mypy-a] |
| warn_no_return = True |
| [rechecked] |