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

Fix new style union syntax in type aliases #14008

Merged
merged 14 commits into from
Nov 7, 2022
8 changes: 4 additions & 4 deletions mypy/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -6019,11 +6019,11 @@ def lookup_qualified(self, name: str) -> SymbolTableNode:
last = parts[-1]
if last in n.names:
return n.names[last]
elif len(parts) == 2 and parts[0] == "builtins":
fullname = "builtins." + last
elif len(parts) == 2 and parts[0] in ("builtins", "typing"):
fullname = ".".join(parts)
if fullname in SUGGESTED_TEST_FIXTURES:
suggestion = ", e.g. add '[builtins fixtures/{}]' to your test".format(
SUGGESTED_TEST_FIXTURES[fullname]
suggestion = ", e.g. add '[{} fixtures/{}]' to your test".format(
parts[0], SUGGESTED_TEST_FIXTURES[fullname]
)
else:
suggestion = ""
Expand Down
7 changes: 3 additions & 4 deletions mypy/checkexpr.py
Original file line number Diff line number Diff line change
Expand Up @@ -3869,9 +3869,8 @@ class LongName(Generic[T]): ...
else:
if alias_definition:
return AnyType(TypeOfAny.special_form)
# This type is invalid in most runtime contexts, give it an 'object' type.
# TODO: Use typing._SpecialForm instead?
return self.named_type("builtins.object")
# The _SpecialForm type can be used in some runtime contexts (e.g. it may have __or__).
return self.named_type("typing._SpecialForm")

def apply_type_arguments_to_callable(
self, tp: Type, args: Sequence[Type], ctx: Context
Expand Down Expand Up @@ -4741,7 +4740,7 @@ def has_member(self, typ: Type, member: str) -> bool:
typ = typ.fallback
if isinstance(typ, Instance):
return typ.type.has_readable_member(member)
if isinstance(typ, CallableType) and typ.is_type_obj():
if isinstance(typ, FunctionLike) and typ.is_type_obj():
return typ.fallback.type.has_readable_member(member)
elif isinstance(typ, AnyType):
return True
Expand Down
4 changes: 4 additions & 0 deletions mypy/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
"builtins.isinstance": "isinstancelist.pyi",
"builtins.property": "property.pyi",
"builtins.classmethod": "classmethod.pyi",
"typing._SpecialForm": "typing-medium.pyi",
}


Expand Down Expand Up @@ -2253,6 +2254,9 @@ def format_literal_value(typ: LiteralType) -> str:
if itype.extra_attrs and itype.extra_attrs.mod_name and module_names:
return f"{base_str} {itype.extra_attrs.mod_name}"
return base_str
if itype.type.fullname == "typing._SpecialForm":
# This is not a real type but used for some typing-related constructs.
return "<typing special form>"
if verbosity >= 2 or (fullnames and itype.type.fullname in fullnames):
base_str = itype.type.fullname
else:
Expand Down
3 changes: 3 additions & 0 deletions mypyc/test-data/run-async.test
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ async def g() -> int:
async def f() -> int:
return await g()

[file asyncio/__init__.pyi]
async def sleep(t: float) -> None: ...

[typing fixtures/typing-full.pyi]

[file driver.py]
Expand Down
13 changes: 13 additions & 0 deletions test-data/unit/check-ctypes.test
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ a[3] = b"bytes" # E: No overload variant of "__setitem__" of "Array" matches ar
for x in a:
reveal_type(x) # N: Revealed type is "builtins.int"
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesArrayCustomElementType]
import ctypes
Expand Down Expand Up @@ -52,6 +53,7 @@ myu: Union[ctypes.Array[ctypes.c_int], List[str]]
for myi in myu:
reveal_type(myi) # N: Revealed type is "Union[builtins.int, builtins.str]"
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesArrayUnionElementType]
import ctypes
Expand All @@ -76,6 +78,7 @@ mya[3] = b"bytes" # E: No overload variant of "__setitem__" of "Array" matches
for myx in mya:
reveal_type(myx) # N: Revealed type is "Union[__main__.MyCInt, builtins.int]"
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesCharArrayAttrs]
import ctypes
Expand All @@ -84,13 +87,15 @@ ca = (ctypes.c_char * 4)(b'a', b'b', b'c', b'\x00')
reveal_type(ca.value) # N: Revealed type is "builtins.bytes"
reveal_type(ca.raw) # N: Revealed type is "builtins.bytes"
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesCharPArrayDoesNotCrash]
import ctypes

# The following line used to crash with "Could not find builtin symbol 'NoneType'"
ca = (ctypes.c_char_p * 0)()
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesWcharArrayAttrs]
import ctypes
Expand All @@ -99,6 +104,7 @@ wca = (ctypes.c_wchar * 4)('a', 'b', 'c', '\x00')
reveal_type(wca.value) # N: Revealed type is "builtins.str"
wca.raw # E: Array attribute "raw" is only available with element type "c_char", not "c_wchar"
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesCharUnionArrayAttrs]
import ctypes
Expand All @@ -108,6 +114,7 @@ cua: ctypes.Array[Union[ctypes.c_char, ctypes.c_wchar]]
reveal_type(cua.value) # N: Revealed type is "Union[builtins.bytes, builtins.str]"
cua.raw # E: Array attribute "raw" is only available with element type "c_char", not "Union[c_char, c_wchar]"
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesAnyUnionArrayAttrs]
import ctypes
Expand All @@ -117,6 +124,7 @@ caa: ctypes.Array[Union[ctypes.c_char, Any]]
reveal_type(caa.value) # N: Revealed type is "Union[builtins.bytes, Any]"
reveal_type(caa.raw) # N: Revealed type is "builtins.bytes"
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesOtherUnionArrayAttrs]
import ctypes
Expand All @@ -126,6 +134,7 @@ cua: ctypes.Array[Union[ctypes.c_char, ctypes.c_int]]
cua.value # E: Array attribute "value" is only available with element type "c_char" or "c_wchar", not "Union[c_char, c_int]"
cua.raw # E: Array attribute "raw" is only available with element type "c_char", not "Union[c_char, c_int]"
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesAnyArrayAttrs]
import ctypes
Expand All @@ -134,6 +143,7 @@ aa: ctypes.Array[Any]
reveal_type(aa.value) # N: Revealed type is "Any"
reveal_type(aa.raw) # N: Revealed type is "builtins.bytes"
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesOtherArrayAttrs]
import ctypes
Expand All @@ -142,6 +152,7 @@ oa = (ctypes.c_int * 4)(1, 2, 3, 4)
oa.value # E: Array attribute "value" is only available with element type "c_char" or "c_wchar", not "c_int"
oa.raw # E: Array attribute "raw" is only available with element type "c_char", not "c_int"
[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]

[case testCtypesArrayConstructorStarargs]
import ctypes
Expand All @@ -154,6 +165,7 @@ reveal_type(intarr4(*int_values)) # N: Revealed type is "ctypes.Array[ctypes.c_
reveal_type(intarr4(*c_int_values)) # N: Revealed type is "ctypes.Array[ctypes.c_int]"
reveal_type(intarr6(1, ctypes.c_int(2), *int_values)) # N: Revealed type is "ctypes.Array[ctypes.c_int]"
reveal_type(intarr6(1, ctypes.c_int(2), *c_int_values)) # N: Revealed type is "ctypes.Array[ctypes.c_int]"
[typing fixtures/typing-medium.pyi]

float_values = [1.0, 2.0, 3.0, 4.0]
intarr4(*float_values) # E: Array constructor argument 1 of type "List[float]" is not convertible to the array element type "Iterable[c_int]"
Expand All @@ -167,3 +179,4 @@ x = {"a": 1, "b": 2}
intarr4(**x)

[builtins fixtures/floatdict.pyi]
[typing fixtures/typing-medium.pyi]
3 changes: 2 additions & 1 deletion test-data/unit/check-dataclasses.test
Original file line number Diff line number Diff line change
Expand Up @@ -633,8 +633,9 @@ class Two:

c = Two()
x = c.S
reveal_type(x) # N: Revealed type is "builtins.object"
reveal_type(x) # N: Revealed type is "typing._SpecialForm"
[builtins fixtures/dataclasses.pyi]
[typing fixtures/typing-medium.pyi]

[case testDataclassOrdering]
# flags: --python-version 3.7
Expand Down
8 changes: 4 additions & 4 deletions test-data/unit/check-generics.test
Original file line number Diff line number Diff line change
Expand Up @@ -1049,20 +1049,20 @@ CA = Callable[[T], int]
TA = Tuple[T, int]
UA = Union[T, int]

cs = CA + 1 # E: Unsupported left operand type for + ("object")
cs = CA + 1 # E: Unsupported left operand type for + ("<typing special form>")
reveal_type(cs) # N: Revealed type is "Any"

ts = TA() # E: "object" not callable
ts = TA() # E: "<typing special form>" not callable
reveal_type(ts) # N: Revealed type is "Any"

us = UA.x # E: "object" has no attribute "x"
us = UA.x # E: "<typing special form>" has no attribute "x"
reveal_type(us) # N: Revealed type is "Any"

xx = CA[str] + 1 # E: Type application is only supported for generic classes
yy = TA[str]() # E: Type application is only supported for generic classes
zz = UA[str].x # E: Type application is only supported for generic classes
[builtins fixtures/tuple.pyi]

[typing fixtures/typing-medium.pyi]
[out]

[case testGenericTypeAliasesTypeVarBinding]
Expand Down
5 changes: 3 additions & 2 deletions test-data/unit/check-literal.test
Original file line number Diff line number Diff line change
Expand Up @@ -1484,16 +1484,17 @@ Alias = Literal[3]

isinstance(3, Literal[3]) # E: Cannot use isinstance() with Literal type
isinstance(3, Alias) # E: Cannot use isinstance() with Literal type \
# E: Argument 2 to "isinstance" has incompatible type "object"; expected "Union[type, Tuple[Any, ...]]"
# E: Argument 2 to "isinstance" has incompatible type "<typing special form>"; expected "Union[type, Tuple[Any, ...]]"
isinstance(3, Renamed[3]) # E: Cannot use isinstance() with Literal type
isinstance(3, indirect.Literal[3]) # E: Cannot use isinstance() with Literal type

issubclass(int, Literal[3]) # E: Cannot use issubclass() with Literal type
issubclass(int, Alias) # E: Cannot use issubclass() with Literal type \
# E: Argument 2 to "issubclass" has incompatible type "object"; expected "Union[type, Tuple[Any, ...]]"
# E: Argument 2 to "issubclass" has incompatible type "<typing special form>"; expected "Union[type, Tuple[Any, ...]]"
issubclass(int, Renamed[3]) # E: Cannot use issubclass() with Literal type
issubclass(int, indirect.Literal[3]) # E: Cannot use issubclass() with Literal type
[builtins fixtures/isinstancelist.pyi]
[typing fixtures/typing-medium.pyi]
[out]

[case testLiteralErrorsWhenSubclassed]
Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/check-python310.test
Original file line number Diff line number Diff line change
Expand Up @@ -1788,3 +1788,19 @@ def f6(a: object) -> None:
case _ if y is not None: # E: Name "y" may be undefined
pass
[builtins fixtures/tuple.pyi]

[case testTypeAliasWithNewUnionSyntaxAndNoneLeftOperand]
from typing import overload
class C:
@overload
def __init__(self) -> None: pass
@overload
def __init__(self, x: int) -> None: pass
def __init__(self, x=0):
pass

class D: pass

X = None | C
Y = None | D
[builtins fixtures/type.pyi]
26 changes: 14 additions & 12 deletions test-data/unit/check-type-aliases.test
Original file line number Diff line number Diff line change
Expand Up @@ -821,28 +821,28 @@ c = Child()

reveal_type(NormalImplicit) # N: Revealed type is "def () -> __main__.Foo"
reveal_type(NormalExplicit) # N: Revealed type is "def () -> __main__.Foo"
reveal_type(SpecialImplicit) # N: Revealed type is "builtins.object"
reveal_type(SpecialExplicit) # N: Revealed type is "builtins.object"
reveal_type(SpecialImplicit) # N: Revealed type is "typing._SpecialForm"
reveal_type(SpecialExplicit) # N: Revealed type is "typing._SpecialForm"

reveal_type(Parent.NormalImplicit) # N: Revealed type is "def () -> __main__.Foo"
reveal_type(Parent.NormalExplicit) # N: Revealed type is "def () -> __main__.Foo"
reveal_type(Parent.SpecialImplicit) # N: Revealed type is "builtins.object"
reveal_type(Parent.SpecialExplicit) # N: Revealed type is "builtins.object"
reveal_type(Parent.SpecialImplicit) # N: Revealed type is "typing._SpecialForm"
reveal_type(Parent.SpecialExplicit) # N: Revealed type is "typing._SpecialForm"

reveal_type(Child.NormalImplicit) # N: Revealed type is "def () -> __main__.Foo"
reveal_type(Child.NormalExplicit) # N: Revealed type is "def () -> __main__.Foo"
reveal_type(Child.SpecialImplicit) # N: Revealed type is "builtins.object"
reveal_type(Child.SpecialExplicit) # N: Revealed type is "builtins.object"
reveal_type(Child.SpecialImplicit) # N: Revealed type is "typing._SpecialForm"
reveal_type(Child.SpecialExplicit) # N: Revealed type is "typing._SpecialForm"

reveal_type(p.NormalImplicit) # N: Revealed type is "def () -> __main__.Foo"
reveal_type(p.NormalExplicit) # N: Revealed type is "def () -> __main__.Foo"
reveal_type(p.SpecialImplicit) # N: Revealed type is "builtins.object"
reveal_type(p.SpecialExplicit) # N: Revealed type is "builtins.object"
reveal_type(p.SpecialImplicit) # N: Revealed type is "typing._SpecialForm"
reveal_type(p.SpecialExplicit) # N: Revealed type is "typing._SpecialForm"

reveal_type(c.NormalImplicit) # N: Revealed type is "def () -> __main__.Foo"
reveal_type(p.NormalExplicit) # N: Revealed type is "def () -> __main__.Foo"
reveal_type(c.SpecialImplicit) # N: Revealed type is "builtins.object"
reveal_type(c.SpecialExplicit) # N: Revealed type is "builtins.object"
reveal_type(c.SpecialImplicit) # N: Revealed type is "typing._SpecialForm"
reveal_type(c.SpecialExplicit) # N: Revealed type is "typing._SpecialForm"

# Use type aliases in a type alias context in a plausible way

Expand Down Expand Up @@ -895,6 +895,7 @@ reveal_type(weird_child_2) # N: Revealed type is "def () -> Any"
reveal_type(weird_child_3) # N: Revealed type is "def () -> Any"
reveal_type(weird_child_4) # N: Revealed type is "def () -> Any"
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-medium.pyi]

[case testMalformedTypeAliasRuntimeReassignments]
from typing import Union
Expand Down Expand Up @@ -927,8 +928,8 @@ SpecialExplicit = 4 # E: Cannot assign multiple types to name "SpecialExplicit"

Parent.NormalImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
Parent.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
Parent.SpecialImplicit = 4
Parent.SpecialExplicit = 4
Parent.SpecialImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "<typing special form>")
Parent.SpecialExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "<typing special form>")

Child.NormalImplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
Child.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type "int", variable has type "Type[Foo]")
Expand All @@ -945,3 +946,4 @@ c.NormalExplicit = 4 # E: Incompatible types in assignment (expression has type
c.SpecialImplicit = 4
c.SpecialExplicit = 4
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-medium.pyi]
1 change: 1 addition & 0 deletions test-data/unit/fixtures/args.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class list(Sequence[T], Generic[T]): pass

class int:
def __eq__(self, o: object) -> bool: pass
class float: pass
class str: pass
class bytes: pass
class bool: pass
Expand Down
1 change: 1 addition & 0 deletions test-data/unit/fixtures/type.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class list(Generic[T]): pass
class type(Generic[T]):
__name__: str
def __or__(self, other: Union[type, None]) -> type: pass
def __ror__(self, other: Union[type, None]) -> type: pass
def mro(self) -> List['type']: pass

class tuple(Generic[T]): pass
Expand Down
2 changes: 2 additions & 0 deletions test-data/unit/fixtures/typing-full.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,5 @@ class _TypedDict(Mapping[str, object]):
def pop(self, k: NoReturn, default: T = ...) -> object: ...
def update(self: T, __m: T) -> None: ...
def __delitem__(self, k: NoReturn) -> None: ...

class _SpecialForm: pass
2 changes: 2 additions & 0 deletions test-data/unit/fixtures/typing-medium.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,6 @@ class ContextManager(Generic[T]):
# Use Any because not all the precise types are in the fixtures.
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Any: pass

class _SpecialForm: pass

TYPE_CHECKING = 1
4 changes: 4 additions & 0 deletions test-data/unit/lib-stub/_decimal.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Very simplified decimal stubs for use in tests

class Decimal:
def __new__(cls, value: str = ...) -> Decimal: ...
16 changes: 16 additions & 0 deletions test-data/unit/lib-stub/datetime.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Very simplified datetime stubs for use in tests

class datetime:
def __new__(
cls,
year: int,
month: int,
day: int,
hour: int = ...,
minute: int = ...,
second: int = ...,
microsecond: int = ...,
*,
fold: int = ...,
) -> datetime: ...
def __format__(self, __fmt: str) -> str: ...
3 changes: 3 additions & 0 deletions test-data/unit/lib-stub/decimal.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Very simplified decimal stubs for use in tests

from _decimal import *
35 changes: 35 additions & 0 deletions test-data/unit/lib-stub/functools.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import Generic, TypeVar, Callable, Any, Mapping
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some tests were using the typeshed stub for functools, and it requires typing._SpecialForm. Instead of adding a bunch of typing-medium.pyi fixtures, I decided to create a simplified stub to use in tests. This also speeds up the test cases a bit.


_T = TypeVar("_T")

class _SingleDispatchCallable(Generic[_T]):
registry: Mapping[Any, Callable[..., _T]]
def dispatch(self, cls: Any) -> Callable[..., _T]: ...
# @fun.register(complex)
# def _(arg, verbose=False): ...
@overload
def register(self, cls: type[Any], func: None = ...) -> Callable[[Callable[..., _T]], Callable[..., _T]]: ...
# @fun.register
# def _(arg: int, verbose=False):
@overload
def register(self, cls: Callable[..., _T], func: None = ...) -> Callable[..., _T]: ...
# fun.register(int, lambda x: x)
@overload
def register(self, cls: type[Any], func: Callable[..., _T]) -> Callable[..., _T]: ...
def _clear_cache(self) -> None: ...
def __call__(__self, *args: Any, **kwargs: Any) -> _T: ...

def singledispatch(func: Callable[..., _T]) -> _SingleDispatchCallable[_T]: ...

def total_ordering(cls: type[_T]) -> type[_T]: ...

class cached_property(Generic[_T]):
func: Callable[[Any], _T]
attrname: str | None
def __init__(self, func: Callable[[Any], _T]) -> None: ...
@overload
def __get__(self, instance: None, owner: type[Any] | None = ...) -> cached_property[_T]: ...
@overload
def __get__(self, instance: object, owner: type[Any] | None = ...) -> _T: ...
def __set_name__(self, owner: type[Any], name: str) -> None: ...
def __class_getitem__(cls, item: Any) -> Any: ...
3 changes: 3 additions & 0 deletions test-data/unit/lib-stub/traceback.pyi
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Very simplified traceback stubs for use in tests

def print_tb(*args, **kwargs) -> None: ...
Loading