| -- Tests for strict Optional behavior |
| |
| [case testImplicitNoneType] |
| x = None |
| x() # E: "None" not callable |
| |
| [case testImplicitNoneTypeInNestedFunction] |
| def f() -> None: |
| def g() -> None: |
| x = None |
| x() # E: "None" not callable |
| |
| [case testExplicitNoneType] |
| x = None # type: None |
| x() # E: "None" not callable |
| |
| [case testNoneMemberOfOptional] |
| from typing import Optional |
| x = None # type: Optional[int] |
| |
| [case testTypeMemberOfOptional] |
| from typing import Optional |
| x = 0 # type: Optional[int] |
| |
| [case testNoneNotMemberOfType] |
| x = None # type: int |
| [out] |
| main:1: error: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| [case testTypeNotMemberOfNone] |
| x = 0 # type: None |
| [out] |
| main:1: error: Incompatible types in assignment (expression has type "int", variable has type "None") |
| |
| [case testOptionalNotMemberOfType] |
| from typing import Optional |
| def f(a: int) -> None: pass |
| x = None # type: Optional[int] |
| f(x) # E: Argument 1 to "f" has incompatible type "Optional[int]"; expected "int" |
| |
| [case testIsinstanceCases] |
| from typing import Optional |
| x = None # type: Optional[int] |
| if isinstance(x, int): |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # N: Revealed type is "None" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testIfCases] |
| from typing import Optional |
| x = None # type: Optional[int] |
| if x: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" |
| [builtins fixtures/bool.pyi] |
| |
| [case testIfNotCases] |
| from typing import Optional |
| x = None # type: Optional[int] |
| if not x: |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| [builtins fixtures/bool.pyi] |
| |
| [case testIsNotNoneCases] |
| from typing import Optional |
| x = None # type: Optional[int] |
| if x is not None: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # N: Revealed type is "None" |
| [builtins fixtures/bool.pyi] |
| |
| [case testIsNoneCases] |
| from typing import Optional |
| x = None # type: Optional[int] |
| if x is None: |
| reveal_type(x) # N: Revealed type is "None" |
| else: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" |
| [builtins fixtures/bool.pyi] |
| |
| [case testAnyCanBeNone] |
| from typing import Optional, Any |
| x = None # type: Any |
| if x is None: |
| reveal_type(x) # N: Revealed type is "None" |
| else: |
| reveal_type(x) # N: Revealed type is "Any" |
| [builtins fixtures/bool.pyi] |
| |
| [case testOrCases] |
| from typing import Optional |
| x = None # type: Optional[str] |
| y1 = x or 'a' |
| reveal_type(y1) # N: Revealed type is "builtins.str" |
| y2 = x or 1 |
| reveal_type(y2) # N: Revealed type is "Union[builtins.str, builtins.int]" |
| z1 = 'a' or x |
| reveal_type(z1) # N: Revealed type is "Union[builtins.str, None]" |
| z2 = int() or x |
| reveal_type(z2) # N: Revealed type is "Union[builtins.int, builtins.str, None]" |
| |
| [case testAndCases] |
| from typing import Optional |
| x = None # type: Optional[str] |
| y1 = x and 'b' |
| reveal_type(y1) # N: Revealed type is "Union[builtins.str, None]" |
| y2 = x and 1 # x could be '', so... |
| reveal_type(y2) # N: Revealed type is "Union[builtins.str, None, builtins.int]" |
| z1 = 'b' and x |
| reveal_type(z1) # N: Revealed type is "Union[builtins.str, None]" |
| z2 = int() and x |
| reveal_type(z2) # N: Revealed type is "Union[builtins.int, builtins.str, None]" |
| |
| [case testLambdaReturningNone] |
| f = lambda: None |
| x = f() |
| reveal_type(x) # N: Revealed type is "None" |
| |
| [case testNoneArgumentType] |
| def f(x: None) -> None: pass |
| f(None) |
| |
| [case testInferOptionalFromDefaultNone] |
| # flags: --implicit-optional |
| def f(x: int = None) -> None: |
| x + 1 # E: Unsupported left operand type for + ("None") \ |
| # N: Left operand is of type "Optional[int]" |
| f(None) |
| [out] |
| |
| [case testNoInferOptionalFromDefaultNone] |
| # flags: --no-implicit-optional |
| def f(x: int = None) -> None: # E: Incompatible default for argument "x" (default has type "None", argument has type "int") \ |
| # N: PEP 484 prohibits implicit Optional. Accordingly, mypy has changed its default to no_implicit_optional=True \ |
| # N: Use https://github.com/hauntsaninja/no_implicit_optional to automatically upgrade your codebase |
| pass |
| [out] |
| |
| [case testInferOptionalFromDefaultNoneComment] |
| # flags: --implicit-optional |
| def f(x=None): |
| # type: (int) -> None |
| x + 1 # E: Unsupported left operand type for + ("None") \ |
| # N: Left operand is of type "Optional[int]" |
| f(None) |
| [out] |
| |
| [case testNoInferOptionalFromDefaultNoneComment] |
| # flags: --no-implicit-optional |
| def f(x=None): # E: Incompatible default for argument "x" (default has type "None", argument has type "int") \ |
| # N: PEP 484 prohibits implicit Optional. Accordingly, mypy has changed its default to no_implicit_optional=True \ |
| # N: Use https://github.com/hauntsaninja/no_implicit_optional to automatically upgrade your codebase |
| # type: (int) -> None |
| pass |
| [out] |
| |
| [case testInferOptionalType] |
| x = None |
| if bool(): |
| # scope limit assignment |
| x = 1 |
| # in scope of the assignment, x is an int |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| # out of scope of the assignment, it's an Optional[int] |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" |
| [builtins fixtures/bool.pyi] |
| |
| [case testInferOptionalTypeLocallyBound] |
| x = None |
| x = 1 |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| |
| [case testInferOptionalAnyType] |
| from typing import Any |
| x = None |
| a = None # type: Any |
| if bool(): |
| x = a |
| reveal_type(x) # N: Revealed type is "Any" |
| reveal_type(x) # N: Revealed type is "Union[Any, None]" |
| [builtins fixtures/bool.pyi] |
| |
| [case testInferOptionalTypeFromOptional] |
| from typing import Optional |
| y = None # type: Optional[int] |
| x = None |
| x = y |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" |
| |
| [case testInferOptionalListType] |
| x = [None] |
| x.append(1) # E: Argument 1 to "append" of "list" has incompatible type "int"; expected "None" |
| [builtins fixtures/list.pyi] |
| |
| [case testInferNonOptionalListType] |
| x = [] |
| x.append(1) |
| x() # E: "List[int]" not callable |
| [builtins fixtures/list.pyi] |
| |
| [case testInferOptionalDictKeyValueTypes] |
| x = {None: None} |
| x["bar"] = 1 |
| [builtins fixtures/dict.pyi] |
| [out] |
| main:2: error: Invalid index type "str" for "Dict[None, None]"; expected type "None" |
| main:2: error: Incompatible types in assignment (expression has type "int", target has type "None") |
| |
| [case testInferNonOptionalDictType] |
| x = {} |
| x["bar"] = 1 |
| x() # E: "Dict[str, int]" not callable |
| [builtins fixtures/dict.pyi] |
| |
| [case testNoneClassVariable] |
| from typing import Optional |
| class C: |
| x = None # type: int |
| def __init__(self) -> None: |
| self.x = 0 |
| |
| [case testNoneClassVariableInInit] |
| from typing import Optional |
| class C: |
| x = None # type: int |
| def __init__(self) -> None: |
| self.x = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| [out] |
| |
| [case testMultipleAssignmentNoneClassVariableInInit] |
| from typing import Optional |
| class C: |
| x, y = None, None # type: int, str |
| def __init__(self) -> None: |
| self.x = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| self.y = None # E: Incompatible types in assignment (expression has type "None", variable has type "str") |
| [builtins fixtures/tuple.pyi] |
| [out] |
| |
| [case testOverloadWithNone] |
| from foo import * |
| [file foo.pyi] |
| from typing import overload |
| @overload |
| def f(x: None) -> str: pass |
| @overload |
| def f(x: int) -> int: pass |
| reveal_type(f(None)) # N: Revealed type is "builtins.str" |
| reveal_type(f(0)) # N: Revealed type is "builtins.int" |
| |
| [case testOptionalTypeOrTypePlain] |
| from typing import Optional |
| def f(a: Optional[int]) -> int: |
| return a or 0 |
| [out] |
| |
| [case testOptionalTypeOrTypeTypeVar] |
| from typing import Optional, TypeVar |
| T = TypeVar('T') |
| def f(a: Optional[T], b: T) -> T: |
| return a or b |
| [out] |
| |
| [case testOptionalTypeOrTypeBothOptional] |
| from typing import Optional |
| def f(a: Optional[int], b: Optional[int]) -> None: |
| reveal_type(a or b) |
| def g(a: int, b: Optional[int]) -> None: |
| reveal_type(a or b) |
| [out] |
| main:3: note: Revealed type is "Union[builtins.int, None]" |
| main:5: note: Revealed type is "Union[builtins.int, None]" |
| |
| [case testOptionalTypeOrTypeComplexUnion] |
| from typing import Union |
| def f(a: Union[int, str, None]) -> None: |
| reveal_type(a or 'default') |
| [out] |
| main:3: note: Revealed type is "Union[builtins.int, builtins.str]" |
| |
| [case testOptionalTypeOrTypeNoTriggerPlain] |
| from typing import Optional |
| def f(a: Optional[int], b: int) -> int: |
| return b or a |
| [out] |
| main:3: error: Incompatible return value type (got "Optional[int]", expected "int") |
| |
| [case testOptionalTypeOrTypeNoTriggerTypeVar] |
| from typing import Optional, TypeVar |
| T = TypeVar('T') |
| def f(a: Optional[T], b: T) -> T: |
| return b or a |
| [out] |
| main:4: error: Incompatible return value type (got "Optional[T]", expected "T") |
| |
| [case testNoneOrStringIsString] |
| def f() -> str: |
| a = None |
| b = '' |
| return a or b |
| [out] |
| |
| [case testNoneOrTypeVarIsTypeVar] |
| from typing import TypeVar |
| T = TypeVar('T') |
| def f(b: T) -> T: |
| a = None |
| return a or b |
| [out] |
| |
| [case testYieldNothingInFunctionReturningGenerator] |
| from typing import Generator |
| def f() -> Generator[None, None, None]: |
| yield |
| [out] |
| |
| [case testNoneAndStringIsNone] |
| a = None |
| b = "foo" |
| reveal_type(a and b) # N: Revealed type is "None" |
| |
| [case testNoneMatchesObjectInOverload] |
| import a |
| a.f(None) |
| |
| [file a.pyi] |
| from typing import overload |
| @overload |
| def f() -> None: ... |
| @overload |
| def f(o: object) -> None: ... |
| |
| [case testGenericSubclassReturningNone] |
| from typing import Generic, TypeVar |
| |
| T = TypeVar('T') |
| |
| class Base(Generic[T]): |
| def f(self) -> T: |
| pass |
| |
| class SubNone(Base[None]): |
| def f(self) -> None: |
| pass |
| |
| class SubInt(Base[int]): |
| def f(self) -> int: |
| return 1 |
| |
| [case testUseOfNoneReturningFunction] |
| from typing import Optional |
| def f() -> None: |
| pass |
| |
| def g(x: Optional[int]) -> int: |
| pass |
| |
| x = f() # E: "f" does not return a value |
| f() + 1 # E: "f" does not return a value |
| g(f()) # E: "f" does not return a value |
| |
| [case testEmptyReturn] |
| def f() -> None: |
| return |
| |
| [case testReturnNone] |
| def f() -> None: |
| return None |
| |
| [case testNoneCallable] |
| from typing import Callable |
| def f() -> None: pass |
| x = f # type: Callable[[], None] |
| |
| [case testOptionalCallable] |
| from typing import Callable, Optional |
| T = Optional[Callable[..., None]] |
| |
| [case testAnyTypeInPartialTypeList] |
| # flags: --check-untyped-defs |
| def f(): ... |
| |
| def lookup_field(name, obj): |
| try: |
| pass |
| except: |
| attr = f() |
| else: |
| attr = None |
| |
| [case testTernaryWithNone] |
| reveal_type(None if bool() else 0) # N: Revealed type is "Union[Literal[0]?, None]" |
| [builtins fixtures/bool.pyi] |
| |
| [case testListWithNone] |
| reveal_type([0, None, 0]) # N: Revealed type is "builtins.list[Union[builtins.int, None]]" |
| [builtins fixtures/list.pyi] |
| |
| [case testNoneContextInference] |
| from typing import Dict, List |
| def f() -> List[None]: |
| return [] |
| def g() -> Dict[None, None]: |
| return {} |
| [builtins fixtures/dict.pyi] |
| |
| [case testRaiseFromNone] |
| raise BaseException from None |
| [builtins fixtures/exception.pyi] |
| |
| [case testOptionalNonPartialTypeWithNone] |
| from typing import Generator |
| def f() -> Generator[str, None, None]: pass |
| x = f() |
| reveal_type(x) # N: Revealed type is "typing.Generator[builtins.str, None, None]" |
| l = [f()] |
| reveal_type(l) # N: Revealed type is "builtins.list[typing.Generator[builtins.str, None, None]]" |
| [builtins fixtures/list.pyi] |
| |
| [case testNoneListTernary] |
| x = [None] if "" else [1] # E: List item 0 has incompatible type "int"; expected "None" |
| [builtins fixtures/list.pyi] |
| |
| [case testListIncompatibleErrorMessage] |
| from typing import List, Callable |
| |
| def foo(l: List[Callable[[], str]]) -> None: pass |
| def f() -> int: |
| return 42 |
| |
| foo([f]) # E: List item 0 has incompatible type "Callable[[], int]"; expected "Callable[[], str]" |
| [builtins fixtures/list.pyi] |
| |
| [case testInferEqualsNotOptional] |
| from typing import Optional |
| x = '' # type: Optional[str] |
| if x == '<string>': |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, None]" |
| if x is '<string>': |
| reveal_type(x) # N: Revealed type is "builtins.str" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, None]" |
| [builtins fixtures/ops.pyi] |
| |
| [case testInferEqualsNotOptionalWithUnion] |
| from typing import Union |
| x = '' # type: Union[str, int, None] |
| if x == '<string>': |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int, None]" |
| if x is '<string>': |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int, None]" |
| [builtins fixtures/ops.pyi] |
| |
| [case testInferEqualsNotOptionalWithOverlap] |
| from typing import Union |
| x = '' # type: Union[str, int, None] |
| if x == object(): |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int, None]" |
| if x is object(): |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int]" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int, None]" |
| [builtins fixtures/ops.pyi] |
| |
| [case testInferEqualsStillOptionalWithNoOverlap] |
| from typing import Optional |
| x = '' # type: Optional[str] |
| if x == 0: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, None]" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, None]" |
| if x is 0: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, None]" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, None]" |
| [builtins fixtures/ops.pyi] |
| |
| [case testInferEqualsStillOptionalWithBothOptional] |
| from typing import Union |
| x = '' # type: Union[str, int, None] |
| y = '' # type: Union[str, None] |
| if x == y: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int, None]" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int, None]" |
| if x is y: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int, None]" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.str, builtins.int, None]" |
| [builtins fixtures/ops.pyi] |
| |
| [case testInferEqualsNotOptionalWithMultipleArgs] |
| from typing import Optional |
| x: Optional[int] |
| y: Optional[int] |
| if x == y == 1: |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| reveal_type(y) # N: Revealed type is "builtins.int" |
| else: |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" |
| reveal_type(y) # N: Revealed type is "Union[builtins.int, None]" |
| |
| class A: pass |
| a: Optional[A] |
| b: Optional[A] |
| if a == b == object(): |
| reveal_type(a) # N: Revealed type is "__main__.A" |
| reveal_type(b) # N: Revealed type is "__main__.A" |
| else: |
| reveal_type(a) # N: Revealed type is "Union[__main__.A, None]" |
| reveal_type(b) # N: Revealed type is "Union[__main__.A, None]" |
| [builtins fixtures/ops.pyi] |
| |
| [case testInferInWithErasedTypes] |
| from typing import TypeVar, Callable |
| |
| T = TypeVar('T') |
| def foo(f: Callable[[T], bool], it: T) -> None: ... |
| |
| foo(lambda x: x in [1, 2] and bool(), 3) |
| [builtins fixtures/list.pyi] |
| |
| [case testWarnNoReturnWorksWithStrictOptional] |
| # flags: --warn-no-return |
| def f() -> None: |
| 1 + 1 # no error |
| |
| def g() -> int: |
| 1 + 1 # |
| [out] |
| main:5: error: Missing return statement |
| |
| [case testGenericTypeAliasesOptional] |
| from typing import TypeVar, Generic, Optional |
| T = TypeVar('T') |
| class Node(Generic[T]): |
| def __init__(self, x: T) -> None: |
| self.x = x |
| |
| ONode = Optional[Node[T]] |
| def f(x: T) -> ONode[T]: |
| if 1 > 0: |
| return Node(x) |
| else: |
| return None |
| |
| x = None # type: ONode[int] |
| if int(): |
| x = f(1) |
| if int(): |
| x = f('x') # E: Argument 1 to "f" has incompatible type "str"; expected "int" |
| |
| x.x = 1 # E: Item "None" of "Optional[Node[int]]" has no attribute "x" |
| if x is not None: |
| x.x = 1 # OK here |
| |
| [builtins fixtures/ops.pyi] |
| |
| [case testOptionalTypeNarrowedInBooleanStatement] |
| from typing import Optional |
| |
| x: Optional[int] = None |
| x is not None and x + 42 |
| x is not None and x + '42' # E: Unsupported operand types for + ("int" and "str") |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testInvalidBooleanBranchIgnored] |
| from typing import Optional |
| |
| x = None |
| x is not None and x + 42 |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testOptionalLambdaInference] |
| from typing import Optional, Callable |
| f = None # type: Optional[Callable[[int], None]] |
| f = lambda x: None |
| f(0) |
| [builtins fixtures/function.pyi] |
| |
| [case testDontSimplifyNoneUnionsWithStrictOptional] |
| from typing import Any, TypeVar, Union |
| A = None # type: Any |
| class C(A): pass |
| T = TypeVar('T') |
| S = TypeVar('S') |
| def u(x: T, y: S) -> Union[S, T]: pass |
| a = None # type: Any |
| |
| # Test both orders |
| reveal_type(u(C(), None)) # N: Revealed type is "Union[None, __main__.C]" |
| reveal_type(u(None, C())) # N: Revealed type is "Union[__main__.C, None]" |
| |
| reveal_type(u(a, None)) # N: Revealed type is "Union[None, Any]" |
| reveal_type(u(None, a)) # N: Revealed type is "Union[Any, None]" |
| |
| reveal_type(u(1, None)) # N: Revealed type is "Union[None, builtins.int]" |
| reveal_type(u(None, 1)) # N: Revealed type is "Union[builtins.int, None]" |
| |
| [case testOptionalAndAnyBaseClass] |
| from typing import Any, Optional |
| A = None # type: Any |
| class C(A): |
| pass |
| x = None # type: Optional[C] |
| x.foo() # E: Item "None" of "Optional[C]" has no attribute "foo" |
| |
| [case testIsinstanceAndOptionalAndAnyBase] |
| from typing import Any, Optional |
| |
| B = None # type: Any |
| class A(B): pass |
| |
| def f(a: Optional[A]): |
| reveal_type(a) # N: Revealed type is "Union[__main__.A, None]" |
| if a is not None: |
| reveal_type(a) # N: Revealed type is "__main__.A" |
| else: |
| reveal_type(a) # N: Revealed type is "None" |
| reveal_type(a) # N: Revealed type is "Union[__main__.A, None]" |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testFlattenOptionalUnion] |
| from typing import Optional, Union |
| |
| x: Optional[Union[int, str]] |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, builtins.str, None]" |
| y: Optional[Union[int, None]] |
| reveal_type(y) # N: Revealed type is "Union[builtins.int, None]" |
| |
| [case testOverloadWithNoneAndOptional] |
| from typing import overload, Optional |
| |
| @overload |
| def f(x: int) -> str: ... |
| @overload |
| def f(x: Optional[int]) -> Optional[str]: ... |
| def f(x): return x |
| |
| reveal_type(f(1)) # N: Revealed type is "builtins.str" |
| reveal_type(f(None)) # N: Revealed type is "Union[builtins.str, None]" |
| x: Optional[int] |
| reveal_type(f(x)) # N: Revealed type is "Union[builtins.str, None]" |
| |
| [case testUnionTruthinessTracking] |
| from typing import Optional, Any |
| def test_or_shortcut(value: Optional[Any]) -> None: |
| if not value: |
| pass |
| if not value or value.get('foo') == 'hello': |
| pass |
| [builtins fixtures/bool.pyi] |
| |
| [case testNarrowingFromObjectToOptional] |
| from typing import Optional |
| x: object |
| y: Optional[int] |
| x = y |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" |
| [out] |
| |
| [case testNarrowOptionalOutsideLambda] |
| from typing import Optional |
| |
| class A: |
| a: int |
| |
| def f(x: Optional[A]) -> None: |
| assert x |
| lambda: x.a |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testNarrowOptionalOutsideLambdaWithDeferred] |
| from typing import Optional |
| |
| class A: |
| a: int |
| |
| def f(self, x: Optional['A']) -> None: |
| assert x |
| lambda: (self.y, x.a) # E: Cannot determine type of "y" |
| self.y = int() |
| [builtins fixtures/isinstancelist.pyi] |
| |
| [case testDeferredAndOptionalInferenceSpecialCase] |
| def f() -> str: |
| y |
| x = None |
| if int(): |
| x = '' |
| if x is None: |
| x = '' |
| g(x) |
| return x |
| |
| def g(x: str) -> None: pass |
| |
| y = int() |
| [builtins fixtures/bool.pyi] |
| |
| [case testOptionalAssignAny1] |
| from typing import Optional |
| def f(): |
| return 0 |
| |
| def g(x: Optional[int]) -> int: |
| if x is None: |
| reveal_type(x) # N: Revealed type is "None" |
| # As a special case for Unions containing None, during |
| x = f() |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" |
| return x |
| |
| [builtins fixtures/bool.pyi] |
| |
| [case testOptionalAssignAny2] |
| from typing import Optional |
| def f(): |
| return 0 |
| |
| def g(x: Optional[int]) -> int: |
| if x is None: |
| reveal_type(x) # N: Revealed type is "None" |
| x = 1 |
| reveal_type(x) # N: Revealed type is "builtins.int" |
| # Since we've assigned to x, the special case None behavior shouldn't happen |
| x = f() |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, None]" |
| return x # E: Incompatible return value type (got "Optional[int]", expected "int") |
| |
| [builtins fixtures/bool.pyi] |
| |
| [case testOptionalAssignAny3] |
| from typing import Optional |
| def f(): |
| return 0 |
| |
| def g(x: Optional[int]) -> int: |
| if x is not None: |
| return x |
| reveal_type(x) # N: Revealed type is "None" |
| if 1: |
| x = f() |
| reveal_type(x) # N: Revealed type is "Union[builtins.int, Any]" |
| return x |
| |
| [builtins fixtures/bool.pyi] |
| |
| [case testStrictOptionalCovarianceCrossModule] |
| # flags: --config-file tmp/mypy.ini |
| from a import asdf |
| x = ["lol"] |
| asdf(x) |
| |
| [file a.py] |
| from typing import List, Optional |
| |
| def asdf(x: List[Optional[str]]) -> None: |
| pass |
| |
| x = ["lol"] |
| asdf(x) |
| |
| [file mypy.ini] |
| \[mypy] |
| \[mypy-a] |
| strict_optional = False |
| [out] |
| main:4: error: Argument 1 to "asdf" has incompatible type "List[str]"; expected "List[Optional[str]]" |
| main:4: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance |
| main:4: note: Consider using "Sequence" instead, which is covariant |
| [builtins fixtures/list.pyi] |
| |
| [case testOptionalBackwards1] |
| from typing import Any, Optional |
| |
| def f1(b: bool) -> Optional[int]: |
| if b: |
| z = 10 |
| reveal_type(z) # N: Revealed type is "builtins.int" |
| else: |
| z = None |
| reveal_type(z) # N: Revealed type is "None" |
| reveal_type(z) # N: Revealed type is "Union[builtins.int, None]" |
| return z |
| |
| def f2(b: bool) -> int: |
| if b: |
| z = 10 |
| else: |
| z = None |
| return z # E: Incompatible return value type (got "Optional[int]", expected "int") |
| |
| def f3(b: bool) -> int: |
| # XXX: This one is a little questionable! Maybe we *do* want to allow this? |
| z = 10 |
| if b: |
| z = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| return z |
| |
| def f4() -> Optional[int]: |
| z = 10 |
| z = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| return z |
| |
| def f5() -> None: |
| z = 10 |
| |
| def f() -> None: |
| nonlocal z |
| z = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| def f6(b: bool) -> None: |
| if b: |
| z = 10 |
| else: |
| z = 11 |
| |
| def f() -> None: |
| nonlocal z |
| z = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| def f7(b: bool) -> None: |
| if b: |
| z = 10 |
| else: |
| z = 11 |
| |
| z = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| def f8(b: bool, c: bool) -> Optional[int]: |
| if b: |
| if c: |
| z = 10 |
| else: |
| z = 11 |
| else: |
| z = None |
| return z |
| |
| def f9(b: bool) -> None: |
| if b: |
| z: int = 10 |
| else: |
| z = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| def f10(b: bool) -> None: |
| z: int |
| if b: |
| z = 10 |
| else: |
| z = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| def f11(b: bool, c: bool) -> None: |
| if b: |
| z = 10 |
| elif c: |
| z = 30 |
| else: |
| z = None |
| |
| def f12(b: bool, a: Any) -> None: |
| if b: |
| z = a |
| else: |
| z = None |
| reveal_type(z) # N: Revealed type is "Any" |
| |
| def f13(b: bool, a: Any) -> None: |
| if b: |
| try: |
| z = f2(True) |
| except Exception: |
| raise RuntimeError |
| else: |
| z = None |
| |
| def f14(b: bool, a: Any) -> None: |
| if b: |
| with a: |
| z = 10 |
| else: |
| z = None |
| |
| def f15() -> None: |
| try: |
| z = f2(True) |
| except Exception: |
| z = None |
| reveal_type(z) # N: Revealed type is "Union[builtins.int, None]" |
| |
| def f16(z: Any) -> None: |
| for x in z: |
| if x == 0: |
| y = 50 |
| break |
| else: |
| y = None |
| reveal_type(y) # N: Revealed type is "Union[builtins.int, None]" |
| |
| def f17(b: bool, c: bool, d: bool) -> None: |
| if b: |
| z = 2 |
| elif c: |
| z = None |
| elif d: |
| z = 3 |
| reveal_type(z) # N: Revealed type is "Union[builtins.int, None]" |
| |
| def f18(b: bool, c: bool, d: bool) -> None: |
| if b: |
| z = 4 |
| else: |
| if c: |
| z = 5 |
| else: |
| z = None |
| reveal_type(z) # N: Revealed type is "Union[builtins.int, None]" |
| |
| def f19(b: bool, c: bool, d: bool) -> None: |
| if b: |
| z = 5 |
| else: |
| z = None |
| if c: |
| z = 6 |
| reveal_type(z) # N: Revealed type is "Union[builtins.int, None]" |
| |
| def f20(b: bool) -> None: |
| if b: |
| x: Any = 5 |
| else: |
| x = None |
| reveal_type(x) # N: Revealed type is "Any" |
| |
| def f_unannot(): pass |
| |
| def f21(b: bool) -> None: |
| if b: |
| x = f_unannot() |
| else: |
| x = None |
| reveal_type(x) # N: Revealed type is "Any" |
| |
| def f22(b: bool) -> None: |
| if b: |
| z = 10 |
| if not b: |
| z = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| def f23(b: bool) -> None: |
| if b: |
| z = 10 |
| if b: |
| z = 11 |
| else: |
| z = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| [builtins fixtures/exception.pyi] |
| |
| [case testOptionalBackwards2] |
| |
| def f1(b: bool) -> None: |
| if b: |
| x = [] # E: Need type annotation for "x" (hint: "x: List[<type>] = ...") |
| else: |
| x = None |
| |
| def f2(b: bool) -> None: |
| if b: |
| x = [] |
| x.append(1) |
| else: |
| x = None |
| reveal_type(x) # N: Revealed type is "Union[builtins.list[builtins.int], None]" |
| |
| |
| [builtins fixtures/list.pyi] |
| |
| [case testOptionalBackwards3] |
| |
| # We don't allow this sort of updating for globals or attributes currently. |
| gb: bool |
| if gb: |
| Z = 10 |
| else: |
| Z = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| class Foo: |
| def __init__(self, b: bool) -> None: |
| if b: |
| self.x = 5 |
| else: |
| self.x = None # E: Incompatible types in assignment (expression has type "None", variable has type "int") |
| |
| def foo(self) -> None: |
| reveal_type(self.x) # N: Revealed type is "builtins.int" |
| |
| [case testOptionalBackwards4] |
| from typing import Any, Optional |
| |
| def f1(b: bool) -> Optional[int]: |
| if b: |
| z = 10 |
| reveal_type(z) # N: Revealed type is "builtins.int" |
| else: |
| # Force the node to get deferred between the two assignments |
| Defer().defer |
| z = None |
| reveal_type(z) # N: Revealed type is "None" |
| reveal_type(z) # N: Revealed type is "Union[builtins.int, None]" |
| return z |
| |
| class Defer: |
| def __init__(self) -> None: |
| self.defer = 10 |
| |
| [case testOptionalIterator] |
| # mypy: no-strict-optional |
| from typing import Optional, List |
| |
| x: Optional[List[int]] |
| if 3 in x: |
| pass |
| |
| [case testNarrowedVariableInNestedFunctionBasic] |
| from typing import Optional |
| |
| def can_narrow(x: Optional[str]) -> None: |
| if x is None: |
| x = "a" |
| def nested() -> str: |
| return reveal_type(x) # N: Revealed type is "builtins.str" |
| nested() |
| |
| def foo(a): pass |
| |
| class C: |
| def can_narrow_in_method(self, x: Optional[str]) -> None: |
| if x is None: |
| x = "a" |
| def nested() -> str: |
| return reveal_type(x) # N: Revealed type is "builtins.str" |
| # Reading the variable is fine |
| y = x |
| with foo(x): |
| foo(x) |
| for a in foo(x): |
| foo(x) |
| nested() |
| |
| def can_narrow_lambda(x: Optional[str]) -> None: |
| if x is None: |
| x = "a" |
| nested = lambda: x |
| reveal_type(nested()) # N: Revealed type is "builtins.str" |
| |
| def cannot_narrow_if_reassigned(x: Optional[str]) -> None: |
| if x is None: |
| x = "a" |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| if int(): |
| x = None |
| nested() |
| |
| x: Optional[str] = "x" |
| |
| def narrow_global_in_func() -> None: |
| global x |
| if x is None: |
| x = "a" |
| def nested() -> str: |
| # This should perhaps not be narrowed, since the nested function could outlive |
| # the outer function, and since other functions could also assign to x, but |
| # this seems like a minor issue. |
| return x |
| nested() |
| |
| x = "y" |
| |
| def narrowing_global_at_top_level_not_propagated() -> str: |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| |
| [case testNarrowedVariableInNestedFunctionMore1] |
| from typing import Optional, overload |
| |
| class C: |
| a: Optional[str] |
| |
| def attribute_narrowing(c: C) -> None: |
| # This case is not supported, since we can't keep track of assignments to attributes. |
| c.a = "x" |
| def nested() -> str: |
| return c.a # E: Incompatible return value type (got "Optional[str]", expected "str") |
| nested() |
| |
| def assignment_in_for(x: Optional[str]) -> None: |
| if x is None: |
| x = "e" |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| for x in ["x"]: |
| pass |
| |
| def foo(): pass |
| |
| def assignment_in_with(x: Optional[str]) -> None: |
| if x is None: |
| x = "e" |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| with foo() as x: |
| pass |
| |
| g: Optional[str] |
| |
| def assign_to_global() -> None: |
| global g |
| g = "x" |
| # This is unsafe, but we don't generate an error, for convenience. Besides, |
| # this is probably a very rare case. |
| def nested() -> str: |
| return g |
| |
| def assign_to_nonlocal(x: Optional[str]) -> None: |
| def nested() -> str: |
| nonlocal x |
| |
| if x is None: |
| x = "a" |
| |
| def nested2() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| |
| return nested2() |
| nested() |
| x = None |
| |
| def dec(f): |
| return f |
| |
| @dec |
| def decorated_outer(x: Optional[str]) -> None: |
| if x is None: |
| x = "a" |
| def nested() -> str: |
| return x |
| nested() |
| |
| @dec |
| def decorated_outer_bad(x: Optional[str]) -> None: |
| if x is None: |
| x = "a" |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| x = None |
| nested() |
| |
| def decorated_inner(x: Optional[str]) -> None: |
| if x is None: |
| x = "a" |
| @dec |
| def nested() -> str: |
| return x |
| nested() |
| |
| def decorated_inner_bad(x: Optional[str]) -> None: |
| if x is None: |
| x = "a" |
| @dec |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| x = None |
| nested() |
| |
| @overload |
| def overloaded_outer(x: None) -> None: ... |
| @overload |
| def overloaded_outer(x: str) -> None: ... |
| def overloaded_outer(x: Optional[str]) -> None: |
| if x is None: |
| x = "a" |
| def nested() -> str: |
| return x |
| nested() |
| |
| @overload |
| def overloaded_outer_bad(x: None) -> None: ... |
| @overload |
| def overloaded_outer_bad(x: str) -> None: ... |
| def overloaded_outer_bad(x: Optional[str]) -> None: |
| if x is None: |
| x = "a" |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| x = None |
| nested() |
| |
| [case testNarrowedVariableInNestedFunctionMore2] |
| from typing import Optional |
| |
| def narrow_multiple(x: Optional[str], y: Optional[int]) -> None: |
| z: Optional[str] = x |
| if x is None: |
| x = "" |
| if y is None: |
| y = 1 |
| if int(): |
| if z is None: |
| z = "" |
| def nested() -> None: |
| a: str = x |
| b: int = y |
| c: str = z |
| nested() |
| |
| def narrow_multiple_partial(x: Optional[str], y: Optional[int]) -> None: |
| z: Optional[str] = x |
| if x is None: |
| x = "" |
| if isinstance(y, int): |
| if z is None: |
| z = "" |
| def nested() -> None: |
| a: str = x |
| b: int = y |
| c: str = z # E: Incompatible types in assignment (expression has type "Optional[str]", variable has type "str") |
| z = None |
| nested() |
| |
| def multiple_nested_functions(x: Optional[str], y: Optional[str]) -> None: |
| if x is None: |
| x = "" |
| def nested1() -> str: |
| return x |
| if y is None: |
| y = "" |
| def nested2() -> str: |
| a: str = y |
| return x |
| |
| class C: |
| a: str |
| def __setitem__(self, key, value): pass |
| |
| def narrowed_variable_used_in_lvalue_but_not_assigned(c: Optional[C]) -> None: |
| if c is None: |
| c = C() |
| def nested() -> C: |
| return c |
| c.a = "x" |
| c[1] = 2 |
| cc = C() |
| cc[c] = 3 |
| nested() |
| |
| def narrow_with_multi_lvalues_1(x: Optional[str]) -> None: |
| if x is None: |
| x = "" |
| |
| def nested() -> str: |
| return x |
| |
| y = z = None |
| |
| def narrow_with_multi_lvalue_2(x: Optional[str]) -> None: |
| if x is None: |
| x = "" |
| |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| |
| x = y = None |
| |
| def narrow_with_multi_lvalue_3(x: Optional[str]) -> None: |
| if x is None: |
| x = "" |
| |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| |
| y = x = None |
| |
| def narrow_with_multi_assign_1(x: Optional[str]) -> None: |
| if x is None: |
| x = "" |
| |
| def nested() -> str: |
| return x |
| |
| y, z = None, None |
| |
| def narrow_with_multi_assign_2(x: Optional[str]) -> None: |
| if x is None: |
| x = "" |
| |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| |
| x, y = None, None |
| |
| def narrow_with_multi_assign_3(x: Optional[str]) -> None: |
| if x is None: |
| x = "" |
| |
| def nested() -> str: |
| return x # E: Incompatible return value type (got "Optional[str]", expected "str") |
| |
| y, x = None, None |
| |
| [builtins fixtures/isinstance.pyi] |
| |
| [case testNestedFunctionSpecialCase] |
| class C: |
| def __enter__(self, *args): ... |
| def __exit__(self, *args) -> bool: ... |
| |
| def f(x: object) -> None: |
| if x is not None: |
| pass |
| |
| def nested() -> None: |
| with C(): |
| pass |
| [builtins fixtures/tuple.pyi] |