Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
24 changes: 24 additions & 0 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
LITERAL_TYPE_NAMES,
MYPYC_NATIVE_INT_NAMES,
NEVER_NAMES,
PROTOCOL_NAMES,
TUPLE_NAMES,
TYPE_ALIAS_NAMES,
TYPE_NAMES,
Expand Down Expand Up @@ -702,6 +703,29 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ
self.fail(f'{type_str} can\'t contain "{bad_item_name}"', t, code=codes.VALID_TYPE)
item = AnyType(TypeOfAny.from_error)
return TypeType.make_normalized(item, line=t.line, column=t.column)
elif fullname in PROTOCOL_NAMES or fullname == "typing.Generic":
# A bare reference to Protocol or Generic in type expression position.
# Stubs declare both as X: type[_X] (mirroring the runtime, where they
# are classes on 3.12+), so e.g. 'type[Protocol]' must accept the
# special form itself.
# https://github.com/python/mypy/issues/21940
if t.args or t.empty_tuple_index:
# 'Protocol[...]'/ 'Generic[...]' are class base syntax, not a type.
return None
private = "_Protocol" if fullname in PROTOCOL_NAMES else "_Generic"
module = "typing_extensions" if fullname.startswith("typing_extensions.") else "typing"
sym = self.api.lookup_fully_qualified_or_none(f"{module}.{private}")
if sym is not None and isinstance(sym.node, PlaceholderNode):
if self.api.is_incomplete_namespace(module):
# The private class is not defined yet, defer until it is.
self.api.record_incomplete_ref()
return AnyType(TypeOfAny.special_form)
return None
if sym is None or not isinstance(sym.node, TypeInfo):
# Stub sets that do not declare the private class yet: keep the
# regular 'not valid as a type' error instead of resolving to Any.
return None
return Instance(sym.node, [], line=t.line, column=t.column)
elif fullname in ("typing_extensions.TypeForm", "typing.TypeForm"):
if len(t.args) == 0:
any_type = self.get_omitted_any(t)
Expand Down
8 changes: 8 additions & 0 deletions test-data/unit/check-generics.test
Original file line number Diff line number Diff line change
Expand Up @@ -3737,3 +3737,11 @@ def test(tp: type[T]) -> T: ...

class C(Generic[T]): ...
reveal_type(test(C)) # N: Revealed type is "__main__.C[Any]"

[case testGenericSpecialFormAsType]
from typing import Generic

g: type[Generic] = Generic

def f(t: type[Generic]) -> None: ...
f(Generic)
20 changes: 20 additions & 0 deletions test-data/unit/check-protocols.test
Original file line number Diff line number Diff line change
Expand Up @@ -4809,3 +4809,23 @@ bad_rep(t) # E: Argument 1 to "bad_rep" has incompatible type "C"; expected "P[
# N: Got: \
# N: def rep(self) -> C
[builtins fixtures/tuple.pyi]

[case testProtocolSpecialFormAsType]
from typing import Protocol

x: type[Protocol] = Protocol

def f(t: type[Protocol]) -> None: ...
f(Protocol)

ts: tuple[type[Protocol], ...] = (Protocol,)
[builtins fixtures/bool.pyi]

[case testTypingExtensionsProtocolSpecialFormAsType]
from typing_extensions import Protocol

x: type[Protocol] = Protocol

def f(t: type[Protocol]) -> None: ...
f(Protocol)
[builtins fixtures/dict.pyi]
6 changes: 4 additions & 2 deletions test-data/unit/fixtures/typing-async.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ Any = object()
Union = 0
Optional = 0
TypeVar = 0
Generic = 0
Protocol = 0
class _Generic: ...
Generic: type[_Generic]
class _Protocol: ...
Protocol: type[_Protocol]
Tuple = 0
Callable = 0
NamedTuple = 0
Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/fixtures/typing-full.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ def assert_type(o, t): ...
overload = 0
Any = object()
Optional = 0
Generic = 0
Protocol = 0
class _Generic: ...
Generic: type[_Generic]
class _Protocol: ...
Protocol: type[_Protocol]
Tuple = 0
_promote = 0
Type = 0
Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/fixtures/typing-medium.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ Any = object()
Union = 0
Optional = 0
TypeVar = 0
Generic = 0
Protocol = 0
class _Generic: ...
Generic: type[_Generic]
class _Protocol: ...
Protocol: type[_Protocol]
Tuple = 0
Callable = 0
_promote = 0
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/fixtures/typing-namedtuple.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
TypeVar = 0
Generic = 0
class _Generic: ...
Generic: type[_Generic]
Any = object()
overload = 0
Type = 0
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/fixtures/typing-override.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
TypeVar = 0
Generic = 0
class _Generic: ...
Generic: type[_Generic]
Any = object()
overload = 0
Type = 0
Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/fixtures/typing-typeddict-iror.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ Any = object()
Union = 0
Optional = 0
TypeVar = 0
Generic = 0
Protocol = 0
class _Generic: ...
Generic: type[_Generic]
class _Protocol: ...
Protocol: type[_Protocol]
Tuple = 0
Callable = 0
NamedTuple = 0
Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/fixtures/typing-typeddict.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ Any = object()
Union = 0
Optional = 0
TypeVar = 0
Generic = 0
Protocol = 0
class _Generic: ...
Generic: type[_Generic]
class _Protocol: ...
Protocol: type[_Protocol]
Tuple = 0
Callable = 0
NamedTuple = 0
Expand Down
6 changes: 4 additions & 2 deletions test-data/unit/lib-stub/typing.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@ Any = object()
Union = 0
Optional = 0
TypeVar = 0
Generic = 0
Protocol = 0
class _Generic: ...
Generic: type[_Generic]
class _Protocol: ...
Protocol: type[_Protocol]
Tuple = 0
Callable = 0
NamedTuple = 0
Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/lib-stub/typing_extensions.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ class _SpecialForm:
pass

NamedTuple = 0
Protocol: _SpecialForm = ...
class _Protocol: ...
Protocol: type[_Protocol]
def runtime_checkable(x: _T) -> _T: pass
runtime = runtime_checkable

Expand Down
Loading