diff --git a/docs/source/common_issues.rst b/docs/source/common_issues.rst index ed122b097005..6891b3262547 100644 --- a/docs/source/common_issues.rst +++ b/docs/source/common_issues.rst @@ -710,3 +710,44 @@ You can install the latest development version of mypy from source. Clone the git clone --recurse-submodules https://github.com/python/mypy.git cd mypy sudo python3 -m pip install --upgrade . + +Variables vs type aliases +----------------------------------- + +Mypy has both type aliases and variables with types like ``Type[...]`` and it is important to know their difference. + +1. Variables with type ``Type[...]`` should be created by assignments with an explicit type annotations: + +.. code-block:: python + + class A: ... + tp: Type[A] = A + +2. Aliases are created by assignments without an explicit type: + +.. code-block:: python + + class A: ... + Alias = A + +3. The difference is that aliases are completely known statically and can be used in type context (annotations): + +.. code-block:: python + + class A: ... + class B: ... + + if random() > 0.5: + Alias = A + else: + Alias = B # error: Cannot assign multiple types to name "Alias" without an explicit "Type[...]" annotation \ + # error: Incompatible types in assignment (expression has type "Type[B]", variable has type "Type[A]") + + tp: Type[object] # tp is a type variable + if random() > 0.5: + tp = A + else: + tp = B # This is OK + + def fun1(x: Alias) -> None: ... # This is OK + def fun2(x: tp) -> None: ... # error: Variable "__main__.tp" is not valid as a type \ No newline at end of file diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 82b4585cfafb..ed5d0e0474e4 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -423,7 +423,8 @@ def analyze_unbound_type_without_type_info(self, t: UnboundType, sym: SymbolTabl # TODO: Move this message building logic to messages.py. notes = [] # type: List[str] if isinstance(sym.node, Var): - # TODO: add a link to alias docs, see #3494. + notes.append('See https://mypy.readthedocs.io/en/' + 'latest/common_issues.html#variables-vs-type-aliases') message = 'Variable "{}" is not valid as a type' elif isinstance(sym.node, (SYMBOL_FUNCBASE_TYPES, Decorator)): message = 'Function "{}" is not valid as a type' diff --git a/test-data/unit/check-columns.test b/test-data/unit/check-columns.test index 4bc70457ac29..206ff15a9d91 100644 --- a/test-data/unit/check-columns.test +++ b/test-data/unit/check-columns.test @@ -153,16 +153,22 @@ from typing import Iterable bad = 0 -def f(x: bad): # E:10: Variable "__main__.bad" is not valid as a type - y: bad # E:8: Variable "__main__.bad" is not valid as a type +def f(x: bad): # E:10: Variable "__main__.bad" is not valid as a type \ + # N:10: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases + y: bad # E:8: Variable "__main__.bad" is not valid as a type \ + # N:8: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases if int(): - def g(x): # E:5: Variable "__main__.bad" is not valid as a type + def g(x): # E:5: Variable "__main__.bad" is not valid as a type \ + # N:5: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases # type: (bad) -> None - y = 0 # type: bad # E:9: Variable "__main__.bad" is not valid as a type + y = 0 # type: bad # E:9: Variable "__main__.bad" is not valid as a type \ + # N:9: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases -z: Iterable[bad] # E:13: Variable "__main__.bad" is not valid as a type -h: bad[int] # E:4: Variable "__main__.bad" is not valid as a type +z: Iterable[bad] # E:13: Variable "__main__.bad" is not valid as a type \ + # N:13: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases +h: bad[int] # E:4: Variable "__main__.bad" is not valid as a type \ + # N:4: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testColumnInvalidType_python2] @@ -171,11 +177,14 @@ from typing import Iterable bad = 0 if int(): - def g(x): # E:5: Variable "__main__.bad" is not valid as a type + def g(x): # E:5: Variable "__main__.bad" is not valid as a type \ + # N:5: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases # type: (bad) -> None - y = 0 # type: bad # E:9: Variable "__main__.bad" is not valid as a type + y = 0 # type: bad # E:9: Variable "__main__.bad" is not valid as a type \ + # N:9: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases - z = () # type: Iterable[bad] # E:5: Variable "__main__.bad" is not valid as a type + z = () # type: Iterable[bad] # E:5: Variable "__main__.bad" is not valid as a type \ + # N:5: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testColumnFunctionMissingTypeAnnotation] # flags: --disallow-untyped-defs diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 16651e16efc1..77225b7df9ba 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -498,10 +498,13 @@ Bad1 = non_declarative_base() Bad2 = Bad3 = declarative_base() class C1(Bad1): ... # E: Variable "__main__.Bad1" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases \ # E: Invalid base class "Bad1" class C2(Bad2): ... # E: Variable "__main__.Bad2" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases \ # E: Invalid base class "Bad2" class C3(Bad3): ... # E: Variable "__main__.Bad3" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases \ # E: Invalid base class "Bad3" [file mod.py] from typing import Generic, TypeVar diff --git a/test-data/unit/check-errorcodes.test b/test-data/unit/check-errorcodes.test index f6886261570f..c0e3a9782b5e 100644 --- a/test-data/unit/check-errorcodes.test +++ b/test-data/unit/check-errorcodes.test @@ -250,7 +250,8 @@ x: f # E: Function "__main__.f" is not valid as a type [valid-type] \ import sys y: sys # E: Module "sys" is not valid as a type [valid-type] -z: y # E: Variable "__main__.y" is not valid as a type [valid-type] +z: y # E: Variable "__main__.y" is not valid as a type [valid-type] \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [builtins fixtures/tuple.pyi] [case testErrorCodeNeedTypeAnnotation] diff --git a/test-data/unit/check-generics.test b/test-data/unit/check-generics.test index 93714a97ddde..9b1af9a47628 100644 --- a/test-data/unit/check-generics.test +++ b/test-data/unit/check-generics.test @@ -985,9 +985,11 @@ class C: b = int # E: Cannot assign multiple types to name "b" without an explicit "Type[...]" annotation if int(): c = int - def f(self, x: a) -> None: pass # E: Variable "__main__.C.a" is not valid as a type + def f(self, x: a) -> None: pass # E: Variable "__main__.C.a" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases def g(self, x: b) -> None: pass - def h(self, x: c) -> None: pass # E: Variable "__main__.C.c" is not valid as a type + def h(self, x: c) -> None: pass # E: Variable "__main__.C.c" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases x: b reveal_type(x) # N: Revealed type is 'Union[builtins.int, builtins.str]' [out] diff --git a/test-data/unit/check-literal.test b/test-data/unit/check-literal.test index 773a2e36f6a0..1d401986e8e6 100644 --- a/test-data/unit/check-literal.test +++ b/test-data/unit/check-literal.test @@ -707,6 +707,7 @@ y: Foo[Foo] # E: Literal[...] must have at least one parameter NotAType = 3 def f() -> NotAType['also' + 'not' + 'a' + 'type']: ... # E: Variable "__main__.NotAType" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases \ # E: Invalid type comment or annotation # Note: this makes us re-inspect the type (e.g. via '_patch_indirect_dependencies' @@ -907,10 +908,12 @@ d2t = 3j a2: a2t reveal_type(a2) # N: Revealed type is 'Any' -b2: b2t # E: Variable "__main__.b2t" is not valid as a type +b2: b2t # E: Variable "__main__.b2t" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases c2: c2t reveal_type(c2) # N: Revealed type is 'Any' -d2: d2t # E: Variable "__main__.d2t" is not valid as a type +d2: d2t # E: Variable "__main__.d2t" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [builtins fixtures/complex_tuple.pyi] [out] @@ -949,8 +952,10 @@ c: [1, 2, 3] # E: Bracketed expression "[...]" is not valid a from typing_extensions import Literal at = Literal[{"a": 1, "b": 2}] # E: Invalid type alias: expression is not a valid type bt = {"a": 1, "b": 2} -a: at # E: Variable "__main__.at" is not valid as a type -b: bt # E: Variable "__main__.bt" is not valid as a type +a: at # E: Variable "__main__.at" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases +b: bt # E: Variable "__main__.bt" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [builtins fixtures/dict.pyi] [out] @@ -959,8 +964,10 @@ b: bt # E: Variable "__main__.bt" is not valid as a ty from typing_extensions import Literal at = Literal[{1, 2, 3}] # E: Invalid type alias: expression is not a valid type bt = {1, 2, 3} -a: at # E: Variable "__main__.at" is not valid as a type -b: bt # E: Variable "__main__.bt" is not valid as a type +a: at # E: Variable "__main__.at" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases +b: bt # E: Variable "__main__.bt" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [builtins fixtures/set.pyi] [out] @@ -2868,13 +2875,17 @@ d: Literal[3] # "3" wherever it's used and get the same behavior -- so maybe we do need to support # at least case "b" for consistency? a_wrap: Literal[4, a] # E: Parameter 2 of Literal[...] is invalid \ - # E: Variable "__main__.a" is not valid as a type + # E: Variable "__main__.a" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases b_wrap: Literal[4, b] # E: Parameter 2 of Literal[...] is invalid \ - # E: Variable "__main__.b" is not valid as a type + # E: Variable "__main__.b" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases c_wrap: Literal[4, c] # E: Parameter 2 of Literal[...] is invalid \ - # E: Variable "__main__.c" is not valid as a type + # E: Variable "__main__.c" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases d_wrap: Literal[4, d] # E: Parameter 2 of Literal[...] is invalid \ - # E: Variable "__main__.d" is not valid as a type + # E: Variable "__main__.d" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [builtins fixtures/tuple.pyi] [out] diff --git a/test-data/unit/check-python38.test b/test-data/unit/check-python38.test index f0a346a40c4c..98eda306c731 100644 --- a/test-data/unit/check-python38.test +++ b/test-data/unit/check-python38.test @@ -238,10 +238,12 @@ def f(x: int = (c := 4)) -> int: # Just make sure we don't crash on this sort of thing. if NT := NamedTuple("NT", [("x", int)]): # E: "int" not callable - z2: NT # E: Variable "NT" is not valid as a type + z2: NT # E: Variable "NT" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases if Alias := int: - z3: Alias # E: Variable "Alias" is not valid as a type + z3: Alias # E: Variable "Alias" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases if (reveal_type(y9 := 3) and # N: Revealed type is 'Literal[3]?' reveal_type(y9)): # N: Revealed type is 'builtins.int' diff --git a/test-data/unit/check-redefine.test b/test-data/unit/check-redefine.test index 8e6368ab16be..d5f453c4e84d 100644 --- a/test-data/unit/check-redefine.test +++ b/test-data/unit/check-redefine.test @@ -276,7 +276,8 @@ def f() -> None: # NOTE: '"int" not callable' is due to test stubs y = TypeVar('y') # E: Cannot redefine 'y' as a type variable \ # E: "int" not callable - def h(a: y) -> y: return a # E: Variable "y" is not valid as a type + def h(a: y) -> y: return a # E: Variable "y" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testCannotRedefineVarAsModule] # flags: --allow-redefinition diff --git a/test-data/unit/check-semanal-error.test b/test-data/unit/check-semanal-error.test index d47674a13475..ac8f72b4cd36 100644 --- a/test-data/unit/check-semanal-error.test +++ b/test-data/unit/check-semanal-error.test @@ -57,6 +57,7 @@ A().foo(1) A().x = '' # E [out] main:3: error: Variable "__main__.X" is not valid as a type +main:3: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases main:3: error: Invalid base class "X" main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int") diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index 1e3c6f10a37b..cab61d7dcffb 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -99,8 +99,10 @@ T = TypeVar('T') A: Type[float] = int if int(): A = float # OK -x: A # E: Variable "__main__.A" is not valid as a type -def bad(tp: A) -> None: # E: Variable "__main__.A" is not valid as a type +x: A # E: Variable "__main__.A" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases +def bad(tp: A) -> None: # E: Variable "__main__.A" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases pass Alias = int diff --git a/test-data/unit/fine-grained.test b/test-data/unit/fine-grained.test index d09aaad614e1..ad8357f3d4e9 100644 --- a/test-data/unit/fine-grained.test +++ b/test-data/unit/fine-grained.test @@ -7951,14 +7951,18 @@ x = 1 a.py:1: error: Name 'TypeVar' is not defined a.py:1: note: Did you forget to import it from "typing"? (Suggestion: "from typing import TypeVar") a.py:7: error: Variable "a.T" is not valid as a type +a.py:7: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases a.py:10: error: Name 'bar' already defined on line 6 a.py:11: error: Variable "a.T" is not valid as a type +a.py:11: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases == a.py:1: error: Name 'TypeVar' is not defined a.py:1: note: Did you forget to import it from "typing"? (Suggestion: "from typing import TypeVar") a.py:7: error: Variable "a.T" is not valid as a type +a.py:7: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases a.py:10: error: Name 'bar' already defined on line 6 a.py:11: error: Variable "a.T" is not valid as a type +a.py:11: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testRefreshForWithTypeComment1] [file a.py] @@ -8423,6 +8427,7 @@ B = func [out] == main:5: error: Variable "b.B" is not valid as a type +main:5: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testNamedTupleForwardFunctionIndirect] # flags: --ignore-missing-imports @@ -8440,6 +8445,7 @@ B = func [out] == main:5: error: Variable "a.A" is not valid as a type +main:5: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testNamedTupleForwardFunctionIndirectReveal] # flags: --ignore-missing-imports @@ -8467,8 +8473,10 @@ B = func [out] == m.py:4: error: Variable "a.A" is not valid as a type +m.py:4: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases == m.py:4: error: Variable "a.A" is not valid as a type +m.py:4: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases m.py:5: note: Revealed type is 'A?' m.py:7: note: Revealed type is 'A?' @@ -8484,6 +8492,7 @@ B = int() [out] == main:5: error: Variable "b.B" is not valid as a type +main:5: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testAliasForwardFunctionIndirect] # flags: --ignore-missing-imports @@ -8500,6 +8509,7 @@ B = func [out] == main:5: error: Variable "a.A" is not valid as a type +main:5: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testLiteralFineGrainedVarConversion] import mod diff --git a/test-data/unit/merge.test b/test-data/unit/merge.test index 407262a99262..aafcbc2427a6 100644 --- a/test-data/unit/merge.test +++ b/test-data/unit/merge.test @@ -779,6 +779,7 @@ foo: int x: foo[A] [out] tmp/target.py:4: error: Variable "target.foo" is not valid as a type +tmp/target.py:4: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases ## target NameExpr:3: builtins.int<0> NameExpr:4: foo?[target.A<1>] diff --git a/test-data/unit/pythoneval-asyncio.test b/test-data/unit/pythoneval-asyncio.test index cd95c6d66f94..48b9bd3a0bb7 100644 --- a/test-data/unit/pythoneval-asyncio.test +++ b/test-data/unit/pythoneval-asyncio.test @@ -502,3 +502,4 @@ def bad(arg: P) -> T: [out] _program.py:8: note: Revealed type is 'def [T] (arg: P?) -> T`-1' _program.py:12: error: Variable "_testForwardRefToBadAsyncShouldNotCrash_newsemanal.P" is not valid as a type +_program.py:12: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases diff --git a/test-data/unit/semanal-errors.test b/test-data/unit/semanal-errors.test index 144218df6f58..f92a1a5e338f 100644 --- a/test-data/unit/semanal-errors.test +++ b/test-data/unit/semanal-errors.test @@ -137,6 +137,7 @@ z = 0 # type: x main:5: error: Function "__main__.f" is not valid as a type main:5: note: Perhaps you need "Callable[...]" or a callback protocol? main:6: error: Variable "__main__.x" is not valid as a type +main:6: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testGlobalVarRedefinition] import typing @@ -802,7 +803,8 @@ cast([int, str], None) # E: Bracketed expression "[...]" is not valid as a typ from typing import cast x = 0 -cast(x, None) # E: Variable "__main__.x" is not valid as a type +cast(x, None) # E: Variable "__main__.x" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases cast(t, None) # E: Name 't' is not defined cast(__builtins__.x, None) # E: Name '__builtins__.x' is not defined [out] @@ -897,7 +899,8 @@ main:4: error: Type cannot be declared in assignment to non-self attribute from typing import TypeVar, Generic t = TypeVar('t') class A(Generic[t]): pass -A[TypeVar] # E: Variable "typing.TypeVar" is not valid as a type +A[TypeVar] # E: Variable "typing.TypeVar" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [out] [case testInvalidTypeInTypeApplication2] diff --git a/test-data/unit/semanal-typealiases.test b/test-data/unit/semanal-typealiases.test index 7230580c40a6..46af11674717 100644 --- a/test-data/unit/semanal-typealiases.test +++ b/test-data/unit/semanal-typealiases.test @@ -404,13 +404,15 @@ MypyFile:1( import typing A = [int, str] -a = 1 # type: A # E: Variable "__main__.A" is not valid as a type +a = 1 # type: A # E: Variable "__main__.A" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testCantUseStringLiteralAsTypeAlias] from typing import Union A = 'Union[int, str]' -a = 1 # type: A # E: Variable "__main__.A" is not valid as a type +a = 1 # type: A # E: Variable "__main__.A" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases [case testStringLiteralTypeAsAliasComponent] from typing import Union