| -- Tests for command line parsing |
| -- ------------------------------ |
| -- |
| -- The initial line specifies the command line, in the format |
| -- |
| -- # cmd: mypy <options> |
| -- |
| -- Note that # flags: --some-flag IS NOT SUPPORTED. |
| -- Use # cmd: mypy --some-flag ... |
| -- |
| -- '== Return code: <value>' is added to the output when the process return code |
| -- is "nonobvious" -- that is, when it is something other than 0 if there are no |
| -- messages and 1 if there are. |
| |
| -- Directories/packages on the command line |
| -- ---------------------------------------- |
| |
| [case testCmdlinePackage] |
| # cmd: mypy pkg |
| [file pkg/__init__.py] |
| [file pkg/a.py] |
| undef |
| [file pkg/subpkg/__init__.py] |
| [file pkg/subpkg/a.py] |
| undef |
| import pkg.subpkg.a |
| [out] |
| pkg/a.py:1: error: Name "undef" is not defined |
| pkg/subpkg/a.py:1: error: Name "undef" is not defined |
| |
| [case testCmdlinePackageSlash] |
| # cmd: mypy pkg/ |
| [file pkg/__init__.py] |
| [file pkg/a.py] |
| undef |
| [file pkg/subpkg/__init__.py] |
| [file pkg/subpkg/a.py] |
| undef |
| import pkg.subpkg.a |
| [out] |
| pkg/a.py:1: error: Name "undef" is not defined |
| pkg/subpkg/a.py:1: error: Name "undef" is not defined |
| |
| [case testCmdlineNonPackage] |
| # cmd: mypy dir |
| [file dir/a.py] |
| undef |
| [file dir/subdir/b.py] |
| undef |
| [out] |
| dir/a.py:1: error: Name "undef" is not defined |
| dir/subdir/b.py:1: error: Name "undef" is not defined |
| |
| [case testCmdlineNonPackageDuplicate] |
| # cmd: mypy dir |
| [file dir/a.py] |
| undef |
| [file dir/subdir/a.py] |
| undef |
| [out] |
| dir/a.py: error: Duplicate module named "a" (also at "dir/subdir/a.py") |
| dir/a.py: note: Are you missing an __init__.py? Alternatively, consider using --exclude to avoid checking one of them. |
| == Return code: 2 |
| |
| [case testCmdlineNonPackageSlash] |
| # cmd: mypy dir/ |
| [file dir/a.py] |
| undef |
| import b |
| [file dir/subdir/b.py] |
| undef |
| import a |
| [out] |
| dir/a.py:1: error: Name "undef" is not defined |
| dir/subdir/b.py:1: error: Name "undef" is not defined |
| |
| [case testCmdlinePackageContainingSubdir] |
| # cmd: mypy pkg |
| [file pkg/__init__.py] |
| [file pkg/a.py] |
| undef |
| import pkg.a |
| [file pkg/subdir/a.py] |
| undef |
| import pkg.a |
| [out] |
| pkg/a.py:1: error: Name "undef" is not defined |
| pkg/subdir/a.py:1: error: Name "undef" is not defined |
| |
| [case testCmdlineNonPackageContainingPackage] |
| # cmd: mypy dir |
| [file dir/a.py] |
| undef |
| import subpkg.a |
| [file dir/subpkg/__init__.py] |
| [file dir/subpkg/a.py] |
| undef |
| [out] |
| dir/subpkg/a.py:1: error: Name "undef" is not defined |
| dir/a.py:1: error: Name "undef" is not defined |
| |
| [case testCmdlineInvalidPackageName] |
| # cmd: mypy dir/sub.pkg/a.py |
| [file dir/sub.pkg/__init__.py] |
| [file dir/sub.pkg/a.py] |
| undef |
| [out] |
| sub.pkg is not a valid Python package name |
| == Return code: 2 |
| |
| [case testBadFileEncoding] |
| # cmd: mypy a.py |
| [file a.py] |
| # coding: uft-8 |
| [out] |
| mypy: can't decode file 'a.py': unknown encoding: uft-8 |
| == Return code: 2 |
| |
| -- ' |
| [case testCannotIgnoreDuplicateModule] |
| # cmd: mypy one/mod/__init__.py two/mod/__init__.py |
| [file one/mod/__init__.py] |
| # type: ignore |
| [file two/mod/__init__.py] |
| # type: ignore |
| [out] |
| two/mod/__init__.py: error: Duplicate module named "mod" (also at "one/mod/__init__.py") |
| two/mod/__init__.py: note: Are you missing an __init__.py? Alternatively, consider using --exclude to avoid checking one of them. |
| == Return code: 2 |
| |
| [case testFlagsFile] |
| # cmd: mypy @flagsfile |
| [file flagsfile] |
| -2 |
| main.py |
| [file main.py] |
| def f(): |
| try: |
| 1/0 |
| except ZeroDivisionError, err: |
| print err |
| |
| [case testConfigFile] |
| # cmd: mypy main.py |
| [file mypy.ini] |
| \[mypy] |
| python_version = 2.7 |
| [file main.py] |
| def f(): |
| try: |
| 1/0 |
| except ZeroDivisionError, err: |
| print err |
| |
| [case testErrorContextConfig] |
| # cmd: mypy main.py |
| [file mypy.ini] |
| \[mypy] |
| show_error_context=True |
| [file main.py] |
| def f() -> None: |
| 0 + "" |
| [out] |
| main.py: note: In function "f": |
| main.py:2: error: Unsupported operand types for + ("int" and "str") |
| |
| [case testAltConfigFile] |
| # cmd: mypy --config-file config.ini main.py |
| [file config.ini] |
| \[mypy] |
| python_version = 2.7 |
| [file main.py] |
| def f(): |
| try: |
| 1/0 |
| except ZeroDivisionError, err: |
| print err |
| |
| [case testNoConfigFile] |
| # cmd: mypy main.py --config-file= |
| [file mypy.ini] |
| \[mypy] |
| warn_unused_ignores = True |
| [file main.py] |
| # type: ignore |
| |
| [case testPerFileConfigSection] |
| # cmd: mypy x.py y.py z.py |
| [file mypy.ini] |
| \[mypy] |
| disallow_untyped_defs = True |
| \[mypy-y] |
| disallow_untyped_defs = False |
| \[mypy-z] |
| disallow_untyped_calls = True |
| [file x.py] |
| def f(a): |
| pass |
| def g(a: int) -> int: |
| return f(a) |
| [file y.py] |
| def f(a): |
| pass |
| def g(a: int) -> int: |
| return f(a) |
| [file z.py] |
| def f(a): |
| pass |
| def g(a: int) -> int: |
| return f(a) |
| [out] |
| z.py:1: error: Function is missing a type annotation |
| z.py:4: error: Call to untyped function "f" in typed context |
| x.py:1: error: Function is missing a type annotation |
| |
| [case testPerFileConfigSectionMultipleMatchesDisallowed] |
| # cmd: mypy xx.py xy.py yx.py yy.py |
| [file mypy.ini] |
| \[mypy] |
| \[mypy-*x*] |
| disallow_untyped_defs = True |
| \[mypy-*y*] |
| disallow_untyped_calls = True |
| [file xx.py] |
| def f(a): pass |
| def g(a: int) -> int: return f(a) |
| [file xy.py] |
| def f(a): pass |
| def g(a: int) -> int: return f(a) |
| [file yx.py] |
| def f(a): pass |
| def g(a: int) -> int: return f(a) |
| [file yy.py] |
| def f(a): pass |
| def g(a: int) -> int: return f(a) |
| [out] |
| mypy.ini: [mypy-*x*]: Patterns must be fully-qualified module names, optionally with '*' in some components (e.g spam.*.eggs.*) |
| mypy.ini: [mypy-*y*]: Patterns must be fully-qualified module names, optionally with '*' in some components (e.g spam.*.eggs.*) |
| == Return code: 0 |
| |
| [case testMultipleGlobConfigSection] |
| # cmd: mypy x.py y.py z.py |
| [file mypy.ini] |
| \[mypy] |
| \[mypy-x.*,z.*] |
| disallow_untyped_defs = True |
| [file x.py] |
| def f(a): pass |
| [file y.py] |
| def f(a): pass |
| [file z.py] |
| def f(a): pass |
| [out] |
| z.py:1: error: Function is missing a type annotation |
| x.py:1: error: Function is missing a type annotation |
| |
| [case testConfigErrorNoSection] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| [out] |
| mypy.ini: No [mypy] section in config file |
| == Return code: 0 |
| |
| [case testConfigErrorUnknownFlag] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| bad = 0 |
| [out] |
| mypy.ini: [mypy]: Unrecognized option: bad = 0 |
| == Return code: 0 |
| |
| [case testConfigErrorBadFlag] |
| # cmd: mypy a.py |
| [file mypy.ini] |
| \[mypy] |
| disallow-untyped-defs = True |
| [file a.py] |
| def f(): |
| pass |
| [out] |
| mypy.ini: [mypy]: Unrecognized option: disallow-untyped-defs = True |
| == Return code: 0 |
| |
| [case testConfigErrorBadBoolean] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| ignore_missing_imports = nah |
| [out] |
| mypy.ini: [mypy]: ignore_missing_imports: Not a boolean: nah |
| == Return code: 0 |
| |
| [case testConfigErrorNotPerFile] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| \[mypy-*] |
| python_version = 3.4 |
| [out] |
| mypy.ini: [mypy-*]: Per-module sections should only specify per-module flags (python_version) |
| == Return code: 0 |
| |
| [case testConfigMypyPath] |
| # cmd: mypy file.py |
| [file mypy.ini] |
| \[mypy] |
| mypy_path = |
| foo_dir:bar_dir |
| , baz_dir |
| [file foo_dir/foo.pyi] |
| def foo(x: int) -> str: ... |
| [file bar_dir/bar.pyi] |
| def bar(x: str) -> list: ... |
| [file baz_dir/baz.pyi] |
| def baz(x: list) -> dict: ... |
| [file file.py] |
| import no_stubs |
| from foo import foo |
| from bar import bar |
| from baz import baz |
| baz(bar(foo(42))) |
| baz(bar(foo('oof'))) |
| [out] |
| file.py:1: error: Cannot find implementation or library stub for module named "no_stubs" |
| file.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports |
| file.py:6: error: Argument 1 to "foo" has incompatible type "str"; expected "int" |
| |
| [case testIgnoreErrorsConfig] |
| # cmd: mypy x.py y.py |
| [file mypy.ini] |
| \[mypy] |
| \[mypy-x] |
| ignore_errors = True |
| [file x.py] |
| "" + 0 |
| [file y.py] |
| "" + 0 |
| [out] |
| y.py:1: error: Unsupported operand types for + ("str" and "int") |
| |
| [case testConfigFollowImportsNormal] |
| # cmd: mypy main.py |
| [file main.py] |
| from a import x |
| x + 0 |
| x + '' # E |
| import a |
| a.x + 0 |
| a.x + '' # E |
| a.y # E |
| a + 0 # E |
| [file mypy.ini] |
| \[mypy] |
| follow_imports = normal |
| [file a.py] |
| x = 0 |
| x += '' # Error reported here |
| [out] |
| a.py:2: error: Unsupported operand types for + ("int" and "str") |
| main.py:3: error: Unsupported operand types for + ("int" and "str") |
| main.py:6: error: Unsupported operand types for + ("int" and "str") |
| main.py:7: error: Module has no attribute "y" |
| main.py:8: error: Unsupported operand types for + (Module and "int") |
| |
| [case testConfigFollowImportsSilent] |
| # cmd: mypy main.py |
| [file main.py] |
| from a import x |
| x + '' |
| import a |
| a.x + '' |
| a.y |
| a + 0 |
| [file mypy.ini] |
| \[mypy] |
| follow_imports = silent |
| [file a.py] |
| x = 0 |
| x += '' # No error reported |
| [out] |
| main.py:2: error: Unsupported operand types for + ("int" and "str") |
| main.py:4: error: Unsupported operand types for + ("int" and "str") |
| main.py:5: error: Module has no attribute "y" |
| main.py:6: error: Unsupported operand types for + (Module and "int") |
| |
| [case testConfigFollowImportsSkip] |
| # cmd: mypy main.py |
| [file main.py] |
| from a import x |
| reveal_type(x) # Expect Any |
| import a |
| reveal_type(a.x) # Expect Any |
| [file mypy.ini] |
| \[mypy] |
| follow_imports = skip |
| [file a.py] |
| / # No error reported |
| [out] |
| main.py:2: note: Revealed type is "Any" |
| main.py:4: note: Revealed type is "Any" |
| |
| [case testConfigFollowImportsError] |
| # cmd: mypy main.py |
| [file main.py] |
| from a import x |
| reveal_type(x) # Expect Any |
| import a # Error reported here |
| reveal_type(a.x) # Expect Any |
| [file mypy.ini] |
| \[mypy] |
| follow_imports = error |
| [file a.py] |
| / # No error reported |
| [out] |
| main.py:1: error: Import of "a" ignored |
| main.py:1: note: (Using --follow-imports=error, module not passed on command line) |
| main.py:2: note: Revealed type is "Any" |
| main.py:4: note: Revealed type is "Any" |
| |
| [case testConfigFollowImportsSelective] |
| # cmd: mypy main.py |
| [file mypy.ini] |
| \[mypy] |
| \[mypy-normal] |
| follow_imports = normal |
| \[mypy-silent] |
| follow_imports = silent |
| \[mypy-skip] |
| follow_imports = skip |
| \[mypy-error] |
| follow_imports = error |
| [file main.py] |
| import normal |
| import silent |
| import skip |
| import error |
| reveal_type(normal.x) |
| reveal_type(silent.x) |
| reveal_type(skip) |
| reveal_type(error) |
| [file normal.py] |
| x = 0 |
| x += '' |
| [file silent.py] |
| x = 0 |
| x += '' |
| [file skip.py] |
| bla bla |
| [file error.py] |
| bla bla |
| [out] |
| normal.py:2: error: Unsupported operand types for + ("int" and "str") |
| main.py:4: error: Import of "error" ignored |
| main.py:4: note: (Using --follow-imports=error, module not passed on command line) |
| main.py:5: note: Revealed type is "builtins.int" |
| main.py:6: note: Revealed type is "builtins.int" |
| main.py:7: note: Revealed type is "Any" |
| main.py:8: note: Revealed type is "Any" |
| |
| [case testConfigFollowImportsInvalid] |
| # cmd: mypy main.py |
| [file mypy.ini] |
| \[mypy] |
| follow_imports =True |
| [file main.py] |
| [out] |
| mypy.ini: [mypy]: follow_imports: invalid choice 'True' (choose from 'normal', 'silent', 'skip', 'error') |
| == Return code: 0 |
| |
| [case testConfigSilentMissingImportsOff] |
| # cmd: mypy main.py |
| [file main.py] |
| import missing # Expect error here |
| reveal_type(missing.x) # Expect Any |
| [file mypy.ini] |
| \[mypy] |
| ignore_missing_imports = False |
| [out] |
| main.py:1: error: Cannot find implementation or library stub for module named "missing" |
| main.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports |
| main.py:2: note: Revealed type is "Any" |
| |
| [case testConfigSilentMissingImportsOn] |
| # cmd: mypy main.py |
| [file main.py] |
| import missing # No error here |
| reveal_type(missing.x) # Expect Any |
| [file mypy.ini] |
| \[mypy] |
| ignore_missing_imports = True |
| [out] |
| main.py:2: note: Revealed type is "Any" |
| |
| |
| [case testFailedImportOnWrongCWD] |
| # cmd: mypy main.py |
| # cwd: main/subdir1/subdir2 |
| [file main/subdir1/subdir2/main.py] |
| import parent |
| import grandparent |
| import missing |
| [file main/subdir1/subdir2/__init__.py] |
| [file main/subdir1/parent.py] |
| [file main/subdir1/__init__.py] |
| [file main/grandparent.py] |
| [file main/__init__.py] |
| [out] |
| main.py:1: error: Cannot find implementation or library stub for module named "parent" |
| main.py:1: note: You may be running mypy in a subpackage, mypy should be run on the package root |
| main.py:2: error: Cannot find implementation or library stub for module named "grandparent" |
| main.py:3: error: Cannot find implementation or library stub for module named "missing" |
| main.py:3: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports |
| |
| [case testImportInParentButNoInit] |
| # cmd: mypy main.py |
| # cwd: main/not_a_package |
| [file main/not_a_package/main.py] |
| import needs_init |
| [file main/needs_init.py] |
| [file main/__init__.py] |
| [out] |
| main.py:1: error: Cannot find implementation or library stub for module named "needs_init" |
| main.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports |
| |
| [case testConfigNoErrorForUnknownXFlagInSubsection] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| \[mypy-foo] |
| x_bad = 0 |
| [out] |
| |
| [case testDotInFilenameOKScript] |
| # cmd: mypy a.b.py c.d.pyi |
| [file a.b.py] |
| undef |
| [file c.d.pyi] |
| whatever |
| [out] |
| c.d.pyi:1: error: Name "whatever" is not defined |
| a.b.py:1: error: Name "undef" is not defined |
| |
| [case testDotInFilenameOKFolder] |
| # cmd: mypy my.folder |
| [file my.folder/tst.py] |
| undef |
| [out] |
| my.folder/tst.py:1: error: Name "undef" is not defined |
| |
| [case testDotInFilenameNoImport] |
| # cmd: mypy main.py |
| [file main.py] |
| import a.b |
| [file a.b.py] |
| whatever |
| [out] |
| main.py:1: error: Cannot find implementation or library stub for module named "a.b" |
| main.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports |
| main.py:1: error: Cannot find implementation or library stub for module named "a" |
| |
| [case testPythonVersionTooOld10] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| python_version = 1.0 |
| [out] |
| mypy.ini: [mypy]: python_version: Python major version '1' out of range (must be 2 or 3) |
| == Return code: 0 |
| |
| [case testPythonVersionTooOld26] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| python_version = 2.6 |
| [out] |
| mypy.ini: [mypy]: python_version: Python 2.6 is not supported (must be 2.7) |
| == Return code: 0 |
| |
| [case testPythonVersionTooOld33] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| python_version = 3.3 |
| [out] |
| mypy.ini: [mypy]: python_version: Python 3.3 is not supported (must be 3.4 or higher) |
| == Return code: 0 |
| |
| [case testPythonVersionTooNew28] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| python_version = 2.8 |
| [out] |
| mypy.ini: [mypy]: python_version: Python 2.8 is not supported (must be 2.7) |
| == Return code: 0 |
| |
| [case testPythonVersionTooNew40] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| python_version = 4.0 |
| [out] |
| mypy.ini: [mypy]: python_version: Python major version '4' out of range (must be 2 or 3) |
| == Return code: 0 |
| |
| [case testPythonVersionAccepted27] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| python_version = 2.7 |
| [out] |
| |
| [case testPythonVersionAccepted34] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| python_version = 3.4 |
| [out] |
| |
| [case testPythonVersionAccepted36] |
| # cmd: mypy -c pass |
| [file mypy.ini] |
| \[mypy] |
| python_version = 3.6 |
| [out] |
| |
| -- This should be a dumping ground for tests of plugins that are sensitive to |
| -- typeshed changes. |
| [case testTypeshedSensitivePlugins] |
| # cmd: mypy int_pow.py |
| |
| [file int_pow.py] |
| a = 1 |
| b = a + 2 |
| reveal_type(a**0) # N: Revealed type is "builtins.int" |
| reveal_type(a**1) # N: Revealed type is "builtins.int" |
| reveal_type(a**2) # N: Revealed type is "builtins.int" |
| reveal_type(a**-0) # N: Revealed type is "builtins.int" |
| reveal_type(a**-1) # N: Revealed type is "builtins.float" |
| reveal_type(a**(-2)) # N: Revealed type is "builtins.float" |
| reveal_type(a**b) # N: Revealed type is "Any" |
| reveal_type(a.__pow__(2)) # N: Revealed type is "builtins.int" |
| reveal_type(a.__pow__(a)) # N: Revealed type is "Any" |
| a.__pow__() # E: All overload variants of "__pow__" of "int" require at least one argument \ |
| # N: Possible overload variants: \ |
| # N: def __pow__(self, Literal[2], Optional[int] = ...) -> int \ |
| # N: def __pow__(self, int, Optional[int] = ...) -> Any |
| |
| [case testDisallowAnyGenericsBuiltinCollections] |
| # cmd: mypy m.py |
| [file mypy.ini] |
| \[mypy] |
| python_version=3.6 |
| \[mypy-m] |
| disallow_any_generics = True |
| |
| [file m.py] |
| s = tuple([1, 2, 3]) # no error |
| |
| def f(t: tuple) -> None: pass |
| def g() -> list: pass |
| def h(s: dict) -> None: pass |
| def i(s: set) -> None: pass |
| def j(s: frozenset) -> None: pass |
| [out] |
| m.py:3: error: Implicit generic "Any". Use "typing.Tuple" and specify generic parameters |
| m.py:4: error: Implicit generic "Any". Use "typing.List" and specify generic parameters |
| m.py:5: error: Implicit generic "Any". Use "typing.Dict" and specify generic parameters |
| m.py:6: error: Implicit generic "Any". Use "typing.Set" and specify generic parameters |
| m.py:7: error: Implicit generic "Any". Use "typing.FrozenSet" and specify generic parameters |
| |
| [case testDisallowAnyGenericsTypingCollections] |
| # cmd: mypy m.py |
| [file mypy.ini] |
| \[mypy] |
| \[mypy-m] |
| disallow_any_generics = True |
| |
| [file m.py] |
| from typing import Tuple, List, Dict, Set, FrozenSet |
| |
| def f(t: Tuple) -> None: pass |
| def g() -> List: pass |
| def h(s: Dict) -> None: pass |
| def i(s: Set) -> None: pass |
| def j(s: FrozenSet) -> None: pass |
| [out] |
| m.py:3: error: Missing type parameters for generic type "Tuple" |
| m.py:4: error: Missing type parameters for generic type "List" |
| m.py:5: error: Missing type parameters for generic type "Dict" |
| m.py:6: error: Missing type parameters for generic type "Set" |
| m.py:7: error: Missing type parameters for generic type "FrozenSet" |
| |
| [case testSectionInheritance] |
| # cmd: mypy a |
| [file a/__init__.py] |
| 0() |
| [file a/foo.py] |
| 0() |
| [file a/b/__init__.py] |
| [file a/b/c/__init__.py] |
| 0() |
| [file a/b/c/d/__init__.py] |
| [file a/b/c/d/e/__init__.py] |
| from typing import List |
| def g(x: List) -> None: pass |
| g(None) |
| [file mypy.ini] |
| \[mypy] |
| allow_any_generics = True |
| \[mypy-a.*] |
| ignore_errors = True |
| \[mypy-a.b.*] |
| disallow_any_generics = True |
| ignore_errors = True |
| \[mypy-a.b.c.*] |
| ignore_errors = True |
| \[mypy-a.b.c.d.*] |
| ignore_errors = True |
| \[mypy-a.b.c.d.e.*] |
| ignore_errors = True |
| strict_optional = True |
| \[mypy-a.b.c.d.e] |
| ignore_errors = False |
| [out] |
| a/b/c/d/e/__init__.py:2: error: Missing type parameters for generic type "List" |
| a/b/c/d/e/__init__.py:3: error: Argument 1 to "g" has incompatible type "None"; expected "List[Any]" |
| |
| [case testDisallowUntypedDefsAndGenerics] |
| # cmd: mypy a.py |
| [file mypy.ini] |
| \[mypy] |
| disallow_untyped_defs = True |
| disallow_any_generics = True |
| [file a.py] |
| def get_tasks(self): |
| return 'whatever' |
| [out] |
| a.py:1: error: Function is missing a return type annotation |
| |
| [case testMissingFile] |
| # cmd: mypy nope.py |
| [out] |
| mypy: can't read file 'nope.py': No such file or directory |
| == Return code: 2 |
| --' |
| |
| [case testParseError] |
| # cmd: mypy a.py |
| [file a.py] |
| def foo( |
| [out] |
| a.py:1: error: unexpected EOF while parsing |
| == Return code: 2 |
| [out version>=3.10] |
| a.py:1: error: '(' was never closed |
| == Return code: 2 |
| |
| [case testParseErrorAnnots] |
| # cmd: mypy a.py |
| [file a.py] |
| def foo(x): |
| # type: (str, int) -> None |
| return |
| [out] |
| a.py:1: error: Type signature has too many arguments |
| |
| [case testModulesAndPackages] |
| # cmd: mypy --package p.a --package p.b --module c |
| [file p/__init__.py] |
| [file p/a.py] |
| def foo(x): |
| # type: (int) -> str |
| return "x" |
| foo("wrong") |
| [file p/b/__init__.py] |
| from ..a import foo |
| def bar(a): |
| # type: (int) -> str |
| return foo(a) |
| bar("wrong") |
| [file c.py] |
| import p.b |
| p.b.bar("wrong") |
| [out] |
| p/a.py:4: error: Argument 1 to "foo" has incompatible type "str"; expected "int" |
| p/b/__init__.py:5: error: Argument 1 to "bar" has incompatible type "str"; expected "int" |
| c.py:2: error: Argument 1 to "bar" has incompatible type "str"; expected "int" |
| |
| [case testSrcPEP420Packages] |
| # cmd: mypy -p anamespace --namespace-packages |
| [file mypy.ini] |
| \[mypy] |
| mypy_path = src |
| [file src/setup.cfg] |
| [file src/anamespace/foo/__init__.py] |
| [file src/anamespace/foo/bar.py] |
| def bar(a: int, b: int) -> str: |
| return a + b |
| [out] |
| src/anamespace/foo/bar.py:2: error: Incompatible return value type (got "int", expected "str") |
| |
| [case testNestedPEP420Packages] |
| # cmd: mypy -p pkg --namespace-packages |
| [file pkg/a1/b/c/d/e.py] |
| x = 0 # type: str |
| [file pkg/a1/b/f.py] |
| from pkg.a1.b.c.d.e import x |
| x + 1 |
| |
| [file pkg/a2/__init__.py] |
| [file pkg/a2/b/c/d/e.py] |
| x = 0 # type: str |
| [file pkg/a2/b/f.py] |
| from pkg.a2.b.c.d.e import x |
| x + 1 |
| [out] |
| pkg/a2/b/c/d/e.py:1: error: Incompatible types in assignment (expression has type "int", variable has type "str") |
| pkg/a1/b/c/d/e.py:1: error: Incompatible types in assignment (expression has type "int", variable has type "str") |
| pkg/a2/b/f.py:2: error: Unsupported operand types for + ("str" and "int") |
| pkg/a1/b/f.py:2: error: Unsupported operand types for + ("str" and "int") |
| |
| [case testFollowImportStubs1] |
| # cmd: mypy main.py |
| [file mypy.ini] |
| \[mypy] |
| \[mypy-math.*] |
| follow_imports = error |
| follow_imports_for_stubs = True |
| [file main.py] |
| import math |
| math.frobnicate() |
| [out] |
| main.py:1: error: Import of "math" ignored |
| main.py:1: note: (Using --follow-imports=error, module not passed on command line) |
| |
| [case testFollowImportStubs2] |
| # cmd: mypy main.py |
| [file mypy.ini] |
| \[mypy] |
| \[mypy-math.*] |
| follow_imports = skip |
| follow_imports_for_stubs = True |
| [file main.py] |
| import math |
| math.frobnicate() |
| |
| [case testShadowFile1] |
| # cmd: mypy --shadow-file source.py shadow.py source.py |
| [file source.py] |
| def foo() -> str: |
| return "bar" |
| [file shadow.py] |
| def bar() -> str: |
| return 14 |
| [out] |
| source.py:2: error: Incompatible return value type (got "int", expected "str") |
| |
| [case testShadowFile2] |
| # cmd: mypy --shadow-file s1.py shad1.py --shadow-file s2.py shad2.py --shadow-file s3.py shad3.py s1.py s2.py s3.py s4.py |
| [file s1.py] |
| def foo() -> str: |
| return "bar" |
| [file shad1.py] |
| def bar() -> str: |
| return 14 |
| [file s2.py] |
| def baz() -> str: |
| return 14 |
| [file shad2.py] |
| def baz() -> int: |
| return 14 |
| [file s3.py] |
| def qux() -> str: |
| return "bar" |
| [file shad3.py] |
| def foo() -> int: |
| return [42] |
| [file s4.py] |
| def foo() -> str: |
| return 9 |
| [out] |
| s4.py:2: error: Incompatible return value type (got "int", expected "str") |
| s3.py:2: error: Incompatible return value type (got "List[int]", expected "int") |
| s1.py:2: error: Incompatible return value type (got "int", expected "str") |
| |
| [case testConfigWarnUnusedSection1] |
| # cmd: mypy foo.py quux.py spam/eggs.py |
| [file mypy.ini] |
| \[mypy] |
| warn_unused_configs = True |
| incremental = False |
| \[mypy-bar] |
| \[mypy-foo] |
| \[mypy-baz.*] |
| \[mypy-quux.*] |
| \[mypy-spam.*] |
| \[mypy-spam.eggs] |
| \[mypy-emarg.*] |
| \[mypy-emarg.hatch] |
| -- Currently we don't treat an unstructured pattern like a.*.b as unused |
| -- if it matches another section (like a.x.b). This would be reasonable |
| -- to change. ' |
| \[mypy-a.*.b] |
| \[mypy-a.*.c] |
| \[mypy-a.x.b] |
| [file foo.py] |
| [file quux.py] |
| [file spam/__init__.py] |
| [file spam/eggs.py] |
| [out] |
| Warning: unused section(s) in mypy.ini: [mypy-bar], [mypy-baz.*], [mypy-emarg.*], [mypy-emarg.hatch], [mypy-a.*.c], [mypy-a.x.b] |
| == Return code: 0 |
| |
| [case testConfigUnstructuredGlob] |
| # cmd: mypy emarg foo |
| [file mypy.ini] |
| \[mypy] |
| ignore_errors = true |
| \[mypy-*.lol] |
| ignore_errors = false |
| \[mypy-emarg.*] |
| ignore_errors = false |
| \[mypy-emarg.*.villip.*] |
| ignore_errors = true |
| \[mypy-emarg.hatch.villip.mankangulisk] |
| ignore_errors = false |
| [file emarg/__init__.py] |
| [file emarg/foo.py] |
| fail |
| [file emarg/villip.py] |
| fail |
| [file emarg/hatch/__init__.py] |
| [file emarg/hatch/villip/__init__.py] |
| [file emarg/hatch/villip/nus.py] |
| fail |
| [file emarg/hatch/villip/mankangulisk.py] |
| fail |
| [file foo/__init__.py] |
| [file foo/lol.py] |
| fail |
| [out] |
| foo/lol.py:1: error: Name "fail" is not defined |
| emarg/foo.py:1: error: Name "fail" is not defined |
| emarg/hatch/villip/mankangulisk.py:1: error: Name "fail" is not defined |
| |
| [case testPackageRootEmpty] |
| # cmd: mypy --no-namespace-packages --package-root= a/b/c.py main.py |
| [file a/b/c.py] |
| [file main.py] |
| import a.b.c |
| |
| [case testPackageRootEmptyNamespacePackage] |
| # cmd: mypy --namespace-packages --package-root= a/b/c.py main.py |
| [file a/b/c.py] |
| [file main.py] |
| import a.b.c |
| |
| [case testPackageRootNonEmpty] |
| # cmd: mypy --package-root=a/ a/b/c.py main.py |
| [file a/b/c.py] |
| [file main.py] |
| import b.c |
| |
| [case testPackageRootMultiple1] |
| # cmd: mypy --package-root=. --package-root=a a/b/c.py d.py main.py |
| [file a/b/c.py] |
| [file d.py] |
| [file main.py] |
| import b.c |
| import d |
| |
| [case testPackageRootMultiple2] |
| # cmd: mypy --package-root=a/ --package-root=./ a/b/c.py d.py main.py |
| [file a/b/c.py] |
| [file d.py] |
| [file main.py] |
| import b.c |
| import d |
| |
| [case testCacheMap] |
| -- This just checks that a valid --cache-map triple is accepted. |
| -- (Errors are too verbose to check.) |
| # cmd: mypy a.py --no-sqlite-cache --cache-map a.py a.meta.json a.data.json |
| [file a.py] |
| [out] |
| |
| [case testIniFiles] |
| # cmd: mypy |
| [file mypy.ini] |
| \[mypy] |
| files = a.py, b.py |
| [file a.py] |
| fail |
| [file b.py] |
| fail |
| [out] |
| b.py:1: error: Name "fail" is not defined |
| a.py:1: error: Name "fail" is not defined |
| |
| [case testIniFilesGlobbing] |
| # cmd: mypy |
| [file mypy.ini] |
| \[mypy] |
| files = **/*.py |
| [file a/b.py] |
| fail |
| [file c.py] |
| fail |
| [out] |
| a/b.py:1: error: Name "fail" is not defined |
| c.py:1: error: Name "fail" is not defined |
| |
| [case testIniFilesCmdlineOverridesConfig] |
| # cmd: mypy override.py |
| [file mypy.ini] |
| \[mypy] |
| files = config.py |
| [out] |
| mypy: can't read file 'override.py': No such file or directory |
| == Return code: 2 |
| |
| [case testErrorSummaryOnSuccess] |
| # cmd: mypy --error-summary good.py |
| [file good.py] |
| x = 2 + 2 |
| [out] |
| Success: no issues found in 1 source file |
| == Return code: 0 |
| |
| [case testErrorSummaryOnFail] |
| # cmd: mypy --error-summary bad.py |
| [file bad.py] |
| 42 + 'no' |
| [out] |
| bad.py:1: error: Unsupported operand types for + ("int" and "str") |
| Found 1 error in 1 file (checked 1 source file) |
| |
| [case testErrorSummaryOnFailNotes] |
| # cmd: mypy --error-summary bad.py |
| [file bad.py] |
| from typing import List |
| x = [] # type: List[float] |
| y = [] # type: List[int] |
| x = y |
| [out] |
| bad.py:4: error: Incompatible types in assignment (expression has type "List[int]", variable has type "List[float]") |
| bad.py:4: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance |
| bad.py:4: note: Consider using "Sequence" instead, which is covariant |
| Found 1 error in 1 file (checked 1 source file) |
| |
| [case testErrorSummaryOnFailTwoErrors] |
| # cmd: mypy --error-summary bad.py foo.py |
| [file bad.py] |
| 42 + 'no' |
| 42 + 'no' |
| [file foo.py] |
| [out] |
| bad.py:1: error: Unsupported operand types for + ("int" and "str") |
| bad.py:2: error: Unsupported operand types for + ("int" and "str") |
| Found 2 errors in 1 file (checked 2 source files) |
| |
| [case testErrorSummaryOnFailTwoFiles] |
| # cmd: mypy --error-summary bad.py bad2.py |
| [file bad.py] |
| 42 + 'no' |
| [file bad2.py] |
| 42 + 'no' |
| [out] |
| bad2.py:1: error: Unsupported operand types for + ("int" and "str") |
| bad.py:1: error: Unsupported operand types for + ("int" and "str") |
| Found 2 errors in 2 files (checked 2 source files) |
| |
| [case testErrorSummaryOnBadUsage] |
| # cmd: mypy --error-summary missing.py |
| [out] |
| mypy: can't read file 'missing.py': No such file or directory |
| == Return code: 2 |
| |
| [case testShowSourceCodeSnippetsWrappedFormatting] |
| # cmd: mypy --pretty --python-version=3.6 some_file.py |
| [file some_file.py] |
| from typing import Union |
| |
| 42 + 'no way' |
| |
| class OneCustomClassName: |
| def some_interesting_method(self, arg: AnotherCustomClassDefinedBelow) -> AnotherCustomClassDefinedBelow: |
| ... |
| |
| class AnotherCustomClassDefinedBelow: |
| def another_even_more_interesting_method(self, arg: Union[int, str, float]) -> None: |
| self.very_important_attribute_with_long_name: OneCustomClassName = OneCustomClassName().some_interesting_method(arg) |
| [out] |
| some_file.py:3: error: Unsupported operand types for + ("int" and "str") |
| 42 + 'no way' |
| ^ |
| some_file.py:11: error: Incompatible types in assignment (expression has type |
| "AnotherCustomClassDefinedBelow", variable has type "OneCustomClassName") |
| ...t_attribute_with_long_name: OneCustomClassName = OneCustomClassName().... |
| ^ |
| some_file.py:11: error: Argument 1 to "some_interesting_method" of |
| "OneCustomClassName" has incompatible type "Union[int, str, float]"; expected |
| "AnotherCustomClassDefinedBelow" |
| ...OneCustomClassName = OneCustomClassName().some_interesting_method(arg) |
| ^ |
| |
| [case testShowSourceCodeSnippetsBlockingError] |
| # cmd: mypy --pretty --show-error-codes some_file.py |
| [file some_file.py] |
| it_looks_like_we_started_typing_something_but_then. = did_not_notice(an_extra_dot) |
| [out] |
| some_file.py:1: error: invalid syntax [syntax] |
| ...ooks_like_we_started_typing_something_but_then. = did_not_notice(an_ex... |
| ^ |
| == Return code: 2 |
| |
| [case testTabRenderingUponError] |
| # cmd: mypy --pretty tabs.py |
| [file tabs.py] |
| def test_tabs() -> str: |
| return None |
| [out] |
| tabs.py:2: error: Incompatible return value type (got "None", expected "str") |
| return None |
| ^ |
| |
| [case testErrorMessageWhenOpenPydFile] |
| # cmd: mypy a.pyd |
| [file a.pyd] |
| # coding: uft-8 |
| [out] |
| mypy: stubgen does not support .pyd files: 'a.pyd' |
| == Return code: 2 |
| |
| [case testDuplicateModules] |
| # cmd: mypy src |
| [file mypy.ini] |
| \[mypy] |
| mypy_path = src |
| [file src/__init__.py] |
| [file src/a.py] |
| import foo.bar |
| [file src/foo/__init__.py] |
| [file src/foo/bar.py] |
| 1+'x' |
| [out] |
| src/foo/bar.py: error: Source file found twice under different module names: "src.foo.bar" and "foo.bar" |
| src/foo/bar.py: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#mapping-file-paths-to-modules for more info |
| == Return code: 2 |
| |
| [case testEnableInvalidErrorCode] |
| # cmd: mypy --enable-error-code YOLO test.py |
| [file test.py] |
| x = 1 |
| [out] |
| usage: mypy [-h] [-v] [-V] [more options; see below] |
| [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] |
| mypy: error: Invalid error code(s): YOLO |
| == Return code: 2 |
| |
| [case testDisableInvalidErrorCode] |
| # cmd: mypy --disable-error-code YOLO test.py |
| [file test.py] |
| x = 1 |
| [out] |
| usage: mypy [-h] [-v] [-V] [more options; see below] |
| [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] |
| mypy: error: Invalid error code(s): YOLO |
| == Return code: 2 |
| |
| [case testEnableAndDisableInvalidErrorCode] |
| # cmd: mypy --disable-error-code YOLO --enable-error-code YOLO2 test.py |
| [file test.py] |
| x = 1 |
| [out] |
| usage: mypy [-h] [-v] [-V] [more options; see below] |
| [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] |
| mypy: error: Invalid error code(s): YOLO, YOLO2 |
| == Return code: 2 |
| |
| [case testEnableValidAndInvalidErrorCode] |
| # cmd: mypy --enable-error-code attr-defined --enable-error-code YOLO test.py |
| [file test.py] |
| x = 1 |
| [out] |
| usage: mypy [-h] [-v] [-V] [more options; see below] |
| [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] |
| mypy: error: Invalid error code(s): YOLO |
| == Return code: 2 |
| |
| [case testDisableValidAndInvalidErrorCode] |
| # cmd: mypy --disable-error-code attr-defined --disable-error-code YOLO test.py |
| [file test.py] |
| x = 1 |
| [out] |
| usage: mypy [-h] [-v] [-V] [more options; see below] |
| [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] |
| mypy: error: Invalid error code(s): YOLO |
| == Return code: 2 |
| |
| [case testStubsDirectory] |
| # cmd: mypy --error-summary pkg-stubs |
| [file pkg-stubs/__init__.pyi] |
| [file pkg-stubs/thing.pyi] |
| class Thing: ... |
| [out] |
| Success: no issues found in 2 source files |
| == Return code: 0 |
| |
| [case testStubsDirectoryFile] |
| # cmd: mypy --error-summary pkg-stubs/thing.pyi |
| [file pkg-stubs/__init__.pyi] |
| [file pkg-stubs/thing.pyi] |
| class Thing: ... |
| [out] |
| Success: no issues found in 1 source file |
| == Return code: 0 |
| |
| [case testStubsSubDirectory] |
| # cmd: mypy --error-summary src/pkg-stubs |
| [file src/pkg-stubs/__init__.pyi] |
| [file src/pkg-stubs/thing.pyi] |
| class Thing: ... |
| [out] |
| Success: no issues found in 2 source files |
| == Return code: 0 |
| |
| [case testStubsSubDirectoryFile] |
| # cmd: mypy --error-summary src/pkg-stubs/thing.pyi |
| [file src/pkg-stubs/__init__.pyi] |
| [file src/pkg-stubs/thing.pyi] |
| class Thing: ... |
| [out] |
| Success: no issues found in 1 source file |
| == Return code: 0 |
| |
| [case testBlocker] |
| # cmd: mypy pkg --error-summary --disable-error-code syntax |
| [file pkg/x.py] |
| public static void main(String[] args) |
| [file pkg/y.py] |
| x: str = 0 |
| [out] |
| pkg/x.py:1: error: invalid syntax |
| Found 1 error in 1 file (errors prevented further checking) |
| == Return code: 2 |
| [out version>=3.10] |
| pkg/x.py:1: error: invalid syntax. Perhaps you forgot a comma? |
| Found 1 error in 1 file (errors prevented further checking) |
| == Return code: 2 |
| |
| [case testCmdlinePackageAndFile] |
| # cmd: mypy -p pkg file |
| [out] |
| usage: mypy [-h] [-v] [-V] [more options; see below] |
| [-m MODULE] [-p PACKAGE] [-c PROGRAM_TEXT] [files ...] |
| mypy: error: May only specify one of: module/package, files, or command. |
| == Return code: 2 |
| |
| [case testCmdlinePackageAndIniFiles] |
| # cmd: mypy -p pkg |
| [file mypy.ini] |
| \[mypy] |
| files=file |
| [file pkg.py] |
| x = 0 # type: str |
| [file file.py] |
| y = 0 # type: str |
| [out] |
| pkg.py:1: error: Incompatible types in assignment (expression has type "int", variable has type "str") |
| |
| |
| [case testCmdlineModuleAndIniFiles] |
| # cmd: mypy -m pkg |
| [file mypy.ini] |
| \[mypy] |
| files=file |
| [file pkg.py] |
| x = 0 # type: str |
| [file file.py] |
| y = 0 # type: str |
| [out] |
| pkg.py:1: error: Incompatible types in assignment (expression has type "int", variable has type "str") |
| |
| [case testCmdlineNonInteractiveWithoutInstallTypes] |
| # cmd: mypy --non-interactive -m pkg |
| [out] |
| error: --non-interactive is only supported with --install-types |
| == Return code: 2 |
| |
| [case testCmdlineNonInteractiveInstallTypesNothingToDo] |
| # cmd: mypy --install-types --non-interactive -m pkg |
| [file pkg.py] |
| 1() |
| [out] |
| pkg.py:1: error: "int" not callable |
| |
| [case testCmdlineNonInteractiveInstallTypesNothingToDoNoError] |
| # cmd: mypy --install-types --non-interactive -m pkg |
| [file pkg.py] |
| 1 + 2 |
| [out] |
| |
| [case testCmdlineInteractiveInstallTypesNothingToDo] |
| # cmd: mypy --install-types -m pkg |
| [file pkg.py] |
| 1() |
| [out] |
| pkg.py:1: error: "int" not callable |