Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update docs about variables and aliases #8200

Merged
merged 6 commits into from Jan 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
41 changes: 41 additions & 0 deletions docs/source/common_issues.rst
Expand Up @@ -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
3 changes: 2 additions & 1 deletion mypy/typeanal.py
Expand Up @@ -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'
Expand Down
27 changes: 18 additions & 9 deletions test-data/unit/check-columns.test
Expand Up @@ -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]

Expand All @@ -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
Expand Down
3 changes: 3 additions & 0 deletions test-data/unit/check-custom-plugin.test
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-errorcodes.test
Expand Up @@ -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]
Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/check-generics.test
Expand Up @@ -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]
Expand Down
31 changes: 21 additions & 10 deletions test-data/unit/check-literal.test
Expand Up @@ -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'
Expand Down Expand Up @@ -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]

Expand Down Expand Up @@ -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]

Expand All @@ -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]

Expand Down Expand Up @@ -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]

Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/check-python38.test
Expand Up @@ -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'
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-redefine.test
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions test-data/unit/check-semanal-error.test
Expand Up @@ -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")

Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/check-type-aliases.test
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions test-data/unit/fine-grained.test
Expand Up @@ -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]
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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?'

Expand All @@ -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
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions test-data/unit/merge.test
Expand Up @@ -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>]
Expand Down
1 change: 1 addition & 0 deletions test-data/unit/pythoneval-asyncio.test
Expand Up @@ -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
7 changes: 5 additions & 2 deletions test-data/unit/semanal-errors.test
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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]
Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/semanal-typealiases.test
Expand Up @@ -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
Expand Down