Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,14 +652,14 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
# To prevent assignment of 'builtins.type' inferred as 'builtins.object'
# See https://github.com/python/mypy/issues/9476 for more information
return None
type_str = "Type[...]" if fullname == "typing.Type" else "type[...]"
if len(t.args) != 1:
type_str = "Type[...]" if fullname == "typing.Type" else "type[...]"
self.fail(
type_str + " must have exactly one type argument", t, code=codes.VALID_TYPE
f"{type_str} must have exactly one type argument", t, code=codes.VALID_TYPE
)
item = self.anal_type(t.args[0])
if is_bad_type_type_item(item):
self.fail("Type[...] can't contain another Type[...]", t, code=codes.VALID_TYPE)
self.fail(f'{type_str} can\'t contain "{item}"', t, code=codes.VALID_TYPE)
item = AnyType(TypeOfAny.from_error)
return TypeType.make_normalized(item, line=t.line, column=t.column)
elif fullname == "typing.ClassVar":
Expand Down
9 changes: 5 additions & 4 deletions mypy/types_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
AnyType,
CallableType,
Instance,
LiteralType,
NoneType,
Overloaded,
ParamSpecType,
Expand Down Expand Up @@ -81,14 +82,14 @@ def is_bad_type_type_item(item: Type) -> bool:
Such types are explicitly prohibited by PEP 484. Also, they cause problems
with recursive types like T = Type[T], because internal representation of
TypeType item is normalized (i.e. always a proper type).

Also forbids `Type[Literal[...]]`, because typing spec does not allow it.
Copy link
Member

Choose a reason for hiding this comment

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

I think it might be better to frame this in terms of what is allowed. Here are some other things mypy allows that I don't think make sense: https://mypy-play.net/?mypy=latest&python=3.12&gist=aa900cb6c86fd55901b21ee182aa35ec

Copy link
Member Author

@sobolevn sobolevn Dec 10, 2024

Choose a reason for hiding this comment

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

I suspected that this will be brought up :)
I would prefer to keep this PR focused on just one thing. Because, for example, I only agree with the first example: type[TypeGuard[int]], but it is a completely different thing to fix.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think that we can clarify this in https://github.com/python/typing/tree/main/docs/spec

Copy link
Member

Choose a reason for hiding this comment

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

This is already specified in the grammar at https://typing.readthedocs.io/en/latest/spec/annotations.html#type-and-annotation-expressions . (Though I noticed we're missing unions there, which should be supported.)

Copy link
Member

Choose a reason for hiding this comment

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

I'm fine with deferring other similar changes to other PRs, though.

"""
item = get_proper_type(item)
if isinstance(item, TypeType):
if isinstance(item, (TypeType, LiteralType)):
return True
if isinstance(item, UnionType):
return any(
isinstance(get_proper_type(i), TypeType) for i in flatten_nested_unions(item.items)
)
return any(is_bad_type_type_item(typ) for typ in flatten_nested_unions(item.items))
return False


Expand Down
9 changes: 9 additions & 0 deletions test-data/unit/check-literal.test
Original file line number Diff line number Diff line change
Expand Up @@ -2984,3 +2984,12 @@ class C(Base):
reveal_type(sep) # N: Revealed type is "Union[Literal['a'], Literal['b']]"
return super().feed_data(sep)
[builtins fixtures/tuple.pyi]

[case testLiteralInsideAType]
from typing_extensions import Literal
from typing import Type, Union

x: Type[Literal[1]] # E: Type[...] can't contain "Literal[1]"
y: Type[Union[Literal[1], Literal[2]]] # E: Type[...] can't contain "Union[Literal[1], Literal[2]]"
z: Type[Literal[1, 2]] # E: Type[...] can't contain "Union[Literal[1], Literal[2]]"
[builtins fixtures/tuple.pyi]
6 changes: 4 additions & 2 deletions test-data/unit/check-recursive-types.test
Original file line number Diff line number Diff line change
Expand Up @@ -409,8 +409,10 @@ def local() -> None:
x: L
reveal_type(x) # N: Revealed type is "builtins.list[Union[builtins.int, Any]]"

S = Type[S] # E: Type[...] can't contain another Type[...]
U = Type[Union[int, U]] # E: Type[...] can't contain another Type[...]
S = Type[S] # E: Type[...] can't contain "type[<placeholder __main__.S>]" \
# E: Type[...] can't contain "type[Any]"
U = Type[Union[int, U]] # E: Type[...] can't contain "Union[builtins.int, Union[type[builtins.int], type[<placeholder __main__.U>]]]" \
# E: Type[...] can't contain "Union[builtins.int, type[Any]]"
x: U
reveal_type(x) # N: Revealed type is "Type[Any]"

Expand Down
Loading