Skip to content

Commit ea3c65c

Browse files
authored
Update docs about variables and aliases (#8200)
Resolves #3494 (Since module types are tracked in #3500) Following #8187 (comment) and #3494, if I understand correctly and the semantics in #3494's example code has been fixed (report error on re-assign Alias = B, the remaining work of #3494 is to update the docs, which is the main focus of this PR. Newly added docs are in common issues and solutions section, with the content mostly adapted from Ivan's example in #3494. And a note point to the docs is also added.
1 parent bb5649e commit ea3c65c

16 files changed

+123
-32
lines changed

docs/source/common_issues.rst

+41
Original file line numberDiff line numberDiff line change
@@ -710,3 +710,44 @@ You can install the latest development version of mypy from source. Clone the
710710
git clone --recurse-submodules https://github.com/python/mypy.git
711711
cd mypy
712712
sudo python3 -m pip install --upgrade .
713+
714+
Variables vs type aliases
715+
-----------------------------------
716+
717+
Mypy has both type aliases and variables with types like ``Type[...]`` and it is important to know their difference.
718+
719+
1. Variables with type ``Type[...]`` should be created by assignments with an explicit type annotations:
720+
721+
.. code-block:: python
722+
723+
class A: ...
724+
tp: Type[A] = A
725+
726+
2. Aliases are created by assignments without an explicit type:
727+
728+
.. code-block:: python
729+
730+
class A: ...
731+
Alias = A
732+
733+
3. The difference is that aliases are completely known statically and can be used in type context (annotations):
734+
735+
.. code-block:: python
736+
737+
class A: ...
738+
class B: ...
739+
740+
if random() > 0.5:
741+
Alias = A
742+
else:
743+
Alias = B # error: Cannot assign multiple types to name "Alias" without an explicit "Type[...]" annotation \
744+
# error: Incompatible types in assignment (expression has type "Type[B]", variable has type "Type[A]")
745+
746+
tp: Type[object] # tp is a type variable
747+
if random() > 0.5:
748+
tp = A
749+
else:
750+
tp = B # This is OK
751+
752+
def fun1(x: Alias) -> None: ... # This is OK
753+
def fun2(x: tp) -> None: ... # error: Variable "__main__.tp" is not valid as a type

mypy/typeanal.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,8 @@ def analyze_unbound_type_without_type_info(self, t: UnboundType, sym: SymbolTabl
423423
# TODO: Move this message building logic to messages.py.
424424
notes = [] # type: List[str]
425425
if isinstance(sym.node, Var):
426-
# TODO: add a link to alias docs, see #3494.
426+
notes.append('See https://mypy.readthedocs.io/en/'
427+
'latest/common_issues.html#variables-vs-type-aliases')
427428
message = 'Variable "{}" is not valid as a type'
428429
elif isinstance(sym.node, (SYMBOL_FUNCBASE_TYPES, Decorator)):
429430
message = 'Function "{}" is not valid as a type'

test-data/unit/check-columns.test

+18-9
Original file line numberDiff line numberDiff line change
@@ -153,16 +153,22 @@ from typing import Iterable
153153

154154
bad = 0
155155

156-
def f(x: bad): # E:10: Variable "__main__.bad" is not valid as a type
157-
y: bad # E:8: Variable "__main__.bad" is not valid as a type
156+
def f(x: bad): # E:10: Variable "__main__.bad" is not valid as a type \
157+
# N:10: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
158+
y: bad # E:8: Variable "__main__.bad" is not valid as a type \
159+
# N:8: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
158160

159161
if int():
160-
def g(x): # E:5: Variable "__main__.bad" is not valid as a type
162+
def g(x): # E:5: Variable "__main__.bad" is not valid as a type \
163+
# N:5: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
161164
# type: (bad) -> None
162-
y = 0 # type: bad # E:9: Variable "__main__.bad" is not valid as a type
165+
y = 0 # type: bad # E:9: Variable "__main__.bad" is not valid as a type \
166+
# N:9: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
163167

164-
z: Iterable[bad] # E:13: Variable "__main__.bad" is not valid as a type
165-
h: bad[int] # E:4: Variable "__main__.bad" is not valid as a type
168+
z: Iterable[bad] # E:13: Variable "__main__.bad" is not valid as a type \
169+
# N:13: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
170+
h: bad[int] # E:4: Variable "__main__.bad" is not valid as a type \
171+
# N:4: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
166172

167173
[case testColumnInvalidType_python2]
168174

@@ -171,11 +177,14 @@ from typing import Iterable
171177
bad = 0
172178

173179
if int():
174-
def g(x): # E:5: Variable "__main__.bad" is not valid as a type
180+
def g(x): # E:5: Variable "__main__.bad" is not valid as a type \
181+
# N:5: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
175182
# type: (bad) -> None
176-
y = 0 # type: bad # E:9: Variable "__main__.bad" is not valid as a type
183+
y = 0 # type: bad # E:9: Variable "__main__.bad" is not valid as a type \
184+
# N:9: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
177185

178-
z = () # type: Iterable[bad] # E:5: Variable "__main__.bad" is not valid as a type
186+
z = () # type: Iterable[bad] # E:5: Variable "__main__.bad" is not valid as a type \
187+
# N:5: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
179188

180189
[case testColumnFunctionMissingTypeAnnotation]
181190
# flags: --disallow-untyped-defs

test-data/unit/check-custom-plugin.test

+3
Original file line numberDiff line numberDiff line change
@@ -498,10 +498,13 @@ Bad1 = non_declarative_base()
498498
Bad2 = Bad3 = declarative_base()
499499

500500
class C1(Bad1): ... # E: Variable "__main__.Bad1" is not valid as a type \
501+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases \
501502
# E: Invalid base class "Bad1"
502503
class C2(Bad2): ... # E: Variable "__main__.Bad2" is not valid as a type \
504+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases \
503505
# E: Invalid base class "Bad2"
504506
class C3(Bad3): ... # E: Variable "__main__.Bad3" is not valid as a type \
507+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases \
505508
# E: Invalid base class "Bad3"
506509
[file mod.py]
507510
from typing import Generic, TypeVar

test-data/unit/check-errorcodes.test

+2-1
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,8 @@ x: f # E: Function "__main__.f" is not valid as a type [valid-type] \
250250

251251
import sys
252252
y: sys # E: Module "sys" is not valid as a type [valid-type]
253-
z: y # E: Variable "__main__.y" is not valid as a type [valid-type]
253+
z: y # E: Variable "__main__.y" is not valid as a type [valid-type] \
254+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
254255
[builtins fixtures/tuple.pyi]
255256

256257
[case testErrorCodeNeedTypeAnnotation]

test-data/unit/check-generics.test

+4-2
Original file line numberDiff line numberDiff line change
@@ -985,9 +985,11 @@ class C:
985985
b = int # E: Cannot assign multiple types to name "b" without an explicit "Type[...]" annotation
986986
if int():
987987
c = int
988-
def f(self, x: a) -> None: pass # E: Variable "__main__.C.a" is not valid as a type
988+
def f(self, x: a) -> None: pass # E: Variable "__main__.C.a" is not valid as a type \
989+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
989990
def g(self, x: b) -> None: pass
990-
def h(self, x: c) -> None: pass # E: Variable "__main__.C.c" is not valid as a type
991+
def h(self, x: c) -> None: pass # E: Variable "__main__.C.c" is not valid as a type \
992+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
991993
x: b
992994
reveal_type(x) # N: Revealed type is 'Union[builtins.int, builtins.str]'
993995
[out]

test-data/unit/check-literal.test

+21-10
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,7 @@ y: Foo[Foo] # E: Literal[...] must have at least one parameter
707707

708708
NotAType = 3
709709
def f() -> NotAType['also' + 'not' + 'a' + 'type']: ... # E: Variable "__main__.NotAType" is not valid as a type \
710+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases \
710711
# E: Invalid type comment or annotation
711712

712713
# Note: this makes us re-inspect the type (e.g. via '_patch_indirect_dependencies'
@@ -907,10 +908,12 @@ d2t = 3j
907908

908909
a2: a2t
909910
reveal_type(a2) # N: Revealed type is 'Any'
910-
b2: b2t # E: Variable "__main__.b2t" is not valid as a type
911+
b2: b2t # E: Variable "__main__.b2t" is not valid as a type \
912+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
911913
c2: c2t
912914
reveal_type(c2) # N: Revealed type is 'Any'
913-
d2: d2t # E: Variable "__main__.d2t" is not valid as a type
915+
d2: d2t # E: Variable "__main__.d2t" is not valid as a type \
916+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
914917
[builtins fixtures/complex_tuple.pyi]
915918
[out]
916919

@@ -949,8 +952,10 @@ c: [1, 2, 3] # E: Bracketed expression "[...]" is not valid a
949952
from typing_extensions import Literal
950953
at = Literal[{"a": 1, "b": 2}] # E: Invalid type alias: expression is not a valid type
951954
bt = {"a": 1, "b": 2}
952-
a: at # E: Variable "__main__.at" is not valid as a type
953-
b: bt # E: Variable "__main__.bt" is not valid as a type
955+
a: at # E: Variable "__main__.at" is not valid as a type \
956+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
957+
b: bt # E: Variable "__main__.bt" is not valid as a type \
958+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
954959
[builtins fixtures/dict.pyi]
955960
[out]
956961

@@ -959,8 +964,10 @@ b: bt # E: Variable "__main__.bt" is not valid as a ty
959964
from typing_extensions import Literal
960965
at = Literal[{1, 2, 3}] # E: Invalid type alias: expression is not a valid type
961966
bt = {1, 2, 3}
962-
a: at # E: Variable "__main__.at" is not valid as a type
963-
b: bt # E: Variable "__main__.bt" is not valid as a type
967+
a: at # E: Variable "__main__.at" is not valid as a type \
968+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
969+
b: bt # E: Variable "__main__.bt" is not valid as a type \
970+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
964971
[builtins fixtures/set.pyi]
965972
[out]
966973

@@ -2868,13 +2875,17 @@ d: Literal[3]
28682875
# "3" wherever it's used and get the same behavior -- so maybe we do need to support
28692876
# at least case "b" for consistency?
28702877
a_wrap: Literal[4, a] # E: Parameter 2 of Literal[...] is invalid \
2871-
# E: Variable "__main__.a" is not valid as a type
2878+
# E: Variable "__main__.a" is not valid as a type \
2879+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
28722880
b_wrap: Literal[4, b] # E: Parameter 2 of Literal[...] is invalid \
2873-
# E: Variable "__main__.b" is not valid as a type
2881+
# E: Variable "__main__.b" is not valid as a type \
2882+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
28742883
c_wrap: Literal[4, c] # E: Parameter 2 of Literal[...] is invalid \
2875-
# E: Variable "__main__.c" is not valid as a type
2884+
# E: Variable "__main__.c" is not valid as a type \
2885+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
28762886
d_wrap: Literal[4, d] # E: Parameter 2 of Literal[...] is invalid \
2877-
# E: Variable "__main__.d" is not valid as a type
2887+
# E: Variable "__main__.d" is not valid as a type \
2888+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
28782889
[builtins fixtures/tuple.pyi]
28792890
[out]
28802891

test-data/unit/check-python38.test

+4-2
Original file line numberDiff line numberDiff line change
@@ -238,10 +238,12 @@ def f(x: int = (c := 4)) -> int:
238238

239239
# Just make sure we don't crash on this sort of thing.
240240
if NT := NamedTuple("NT", [("x", int)]): # E: "int" not callable
241-
z2: NT # E: Variable "NT" is not valid as a type
241+
z2: NT # E: Variable "NT" is not valid as a type \
242+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
242243

243244
if Alias := int:
244-
z3: Alias # E: Variable "Alias" is not valid as a type
245+
z3: Alias # E: Variable "Alias" is not valid as a type \
246+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
245247

246248
if (reveal_type(y9 := 3) and # N: Revealed type is 'Literal[3]?'
247249
reveal_type(y9)): # N: Revealed type is 'builtins.int'

test-data/unit/check-redefine.test

+2-1
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,8 @@ def f() -> None:
276276
# NOTE: '"int" not callable' is due to test stubs
277277
y = TypeVar('y') # E: Cannot redefine 'y' as a type variable \
278278
# E: "int" not callable
279-
def h(a: y) -> y: return a # E: Variable "y" is not valid as a type
279+
def h(a: y) -> y: return a # E: Variable "y" is not valid as a type \
280+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
280281

281282
[case testCannotRedefineVarAsModule]
282283
# flags: --allow-redefinition

test-data/unit/check-semanal-error.test

+1
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ A().foo(1)
5757
A().x = '' # E
5858
[out]
5959
main:3: error: Variable "__main__.X" is not valid as a type
60+
main:3: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
6061
main:3: error: Invalid base class "X"
6162
main:6: error: Incompatible types in assignment (expression has type "str", variable has type "int")
6263

test-data/unit/check-type-aliases.test

+4-2
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,10 @@ T = TypeVar('T')
9999
A: Type[float] = int
100100
if int():
101101
A = float # OK
102-
x: A # E: Variable "__main__.A" is not valid as a type
103-
def bad(tp: A) -> None: # E: Variable "__main__.A" is not valid as a type
102+
x: A # E: Variable "__main__.A" is not valid as a type \
103+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
104+
def bad(tp: A) -> None: # E: Variable "__main__.A" is not valid as a type \
105+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
104106
pass
105107

106108
Alias = int

test-data/unit/fine-grained.test

+10
Original file line numberDiff line numberDiff line change
@@ -7951,14 +7951,18 @@ x = 1
79517951
a.py:1: error: Name 'TypeVar' is not defined
79527952
a.py:1: note: Did you forget to import it from "typing"? (Suggestion: "from typing import TypeVar")
79537953
a.py:7: error: Variable "a.T" is not valid as a type
7954+
a.py:7: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
79547955
a.py:10: error: Name 'bar' already defined on line 6
79557956
a.py:11: error: Variable "a.T" is not valid as a type
7957+
a.py:11: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
79567958
==
79577959
a.py:1: error: Name 'TypeVar' is not defined
79587960
a.py:1: note: Did you forget to import it from "typing"? (Suggestion: "from typing import TypeVar")
79597961
a.py:7: error: Variable "a.T" is not valid as a type
7962+
a.py:7: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
79607963
a.py:10: error: Name 'bar' already defined on line 6
79617964
a.py:11: error: Variable "a.T" is not valid as a type
7965+
a.py:11: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
79627966

79637967
[case testRefreshForWithTypeComment1]
79647968
[file a.py]
@@ -8423,6 +8427,7 @@ B = func
84238427
[out]
84248428
==
84258429
main:5: error: Variable "b.B" is not valid as a type
8430+
main:5: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
84268431

84278432
[case testNamedTupleForwardFunctionIndirect]
84288433
# flags: --ignore-missing-imports
@@ -8440,6 +8445,7 @@ B = func
84408445
[out]
84418446
==
84428447
main:5: error: Variable "a.A" is not valid as a type
8448+
main:5: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
84438449

84448450
[case testNamedTupleForwardFunctionIndirectReveal]
84458451
# flags: --ignore-missing-imports
@@ -8467,8 +8473,10 @@ B = func
84678473
[out]
84688474
==
84698475
m.py:4: error: Variable "a.A" is not valid as a type
8476+
m.py:4: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
84708477
==
84718478
m.py:4: error: Variable "a.A" is not valid as a type
8479+
m.py:4: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
84728480
m.py:5: note: Revealed type is 'A?'
84738481
m.py:7: note: Revealed type is 'A?'
84748482

@@ -8484,6 +8492,7 @@ B = int()
84848492
[out]
84858493
==
84868494
main:5: error: Variable "b.B" is not valid as a type
8495+
main:5: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
84878496

84888497
[case testAliasForwardFunctionIndirect]
84898498
# flags: --ignore-missing-imports
@@ -8500,6 +8509,7 @@ B = func
85008509
[out]
85018510
==
85028511
main:5: error: Variable "a.A" is not valid as a type
8512+
main:5: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
85038513

85048514
[case testLiteralFineGrainedVarConversion]
85058515
import mod

test-data/unit/merge.test

+1
Original file line numberDiff line numberDiff line change
@@ -779,6 +779,7 @@ foo: int
779779
x: foo[A]
780780
[out]
781781
tmp/target.py:4: error: Variable "target.foo" is not valid as a type
782+
tmp/target.py:4: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
782783
## target
783784
NameExpr:3: builtins.int<0>
784785
NameExpr:4: foo?[target.A<1>]

test-data/unit/pythoneval-asyncio.test

+1
Original file line numberDiff line numberDiff line change
@@ -502,3 +502,4 @@ def bad(arg: P) -> T:
502502
[out]
503503
_program.py:8: note: Revealed type is 'def [T] (arg: P?) -> T`-1'
504504
_program.py:12: error: Variable "_testForwardRefToBadAsyncShouldNotCrash_newsemanal.P" is not valid as a type
505+
_program.py:12: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases

test-data/unit/semanal-errors.test

+5-2
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ z = 0 # type: x
137137
main:5: error: Function "__main__.f" is not valid as a type
138138
main:5: note: Perhaps you need "Callable[...]" or a callback protocol?
139139
main:6: error: Variable "__main__.x" is not valid as a type
140+
main:6: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
140141

141142
[case testGlobalVarRedefinition]
142143
import typing
@@ -802,7 +803,8 @@ cast([int, str], None) # E: Bracketed expression "[...]" is not valid as a typ
802803

803804
from typing import cast
804805
x = 0
805-
cast(x, None) # E: Variable "__main__.x" is not valid as a type
806+
cast(x, None) # E: Variable "__main__.x" is not valid as a type \
807+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
806808
cast(t, None) # E: Name 't' is not defined
807809
cast(__builtins__.x, None) # E: Name '__builtins__.x' is not defined
808810
[out]
@@ -897,7 +899,8 @@ main:4: error: Type cannot be declared in assignment to non-self attribute
897899
from typing import TypeVar, Generic
898900
t = TypeVar('t')
899901
class A(Generic[t]): pass
900-
A[TypeVar] # E: Variable "typing.TypeVar" is not valid as a type
902+
A[TypeVar] # E: Variable "typing.TypeVar" is not valid as a type \
903+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
901904
[out]
902905

903906
[case testInvalidTypeInTypeApplication2]

test-data/unit/semanal-typealiases.test

+4-2
Original file line numberDiff line numberDiff line change
@@ -404,13 +404,15 @@ MypyFile:1(
404404

405405
import typing
406406
A = [int, str]
407-
a = 1 # type: A # E: Variable "__main__.A" is not valid as a type
407+
a = 1 # type: A # E: Variable "__main__.A" is not valid as a type \
408+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
408409

409410
[case testCantUseStringLiteralAsTypeAlias]
410411

411412
from typing import Union
412413
A = 'Union[int, str]'
413-
a = 1 # type: A # E: Variable "__main__.A" is not valid as a type
414+
a = 1 # type: A # E: Variable "__main__.A" is not valid as a type \
415+
# N: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
414416

415417
[case testStringLiteralTypeAsAliasComponent]
416418
from typing import Union

0 commit comments

Comments
 (0)