Bug Report
@runtime_checkable is only accepted on classes whose direct base is typing.Protocol. A subclass of a protocol class — an ordinary protocol in every other respect — is rejected, which is inconsistent with the runtime behavior and with other type checkers.
To Reproduce
from typing import Protocol, runtime_checkable
class Mid(Protocol): ...
@runtime_checkable
class NewProto(Mid): ...
Result (mypy 2.3.1):
main.py:7: error: @runtime_checkable can only be used with protocol classes [misc]
- Pyright: no errors on the same file.
- CPython runtime:
typing.runtime_checkable performs a duck-typed check (the protocol metaclass sets _is_protocol), so decorating NewProto works fine at runtime.
Expected Behavior
NewProto is a protocol class: it inherits the protocol metaclass and the protocol machinery from Mid. @runtime_checkable should be accepted, consistent with the runtime and with Pyright.
Actual Results
mypy rejects it with misc: @runtime_checkable can only be used with protocol classes.
Additional context
In mypy/semanal.py, is_protocol is only set when a direct base expression's fully-qualified name is in PROTOCOL_NAMES (the "also remove bare 'Protocol' bases" branch in analyze_class), and defn.info.is_protocol = is_protocol does not propagate the flag from protocol base classes.
This came up in the discussion on #21940, where the hardcoding of PROTOCOL_NAMES was raised. Unlike removing that hardcoding (which would conflict with mypy's name-based identification of typing features), propagating is_protocol from protocol base classes looks like a well-scoped improvement. Please let me know if the current behavior is intentional.
Bug Report
@runtime_checkableis only accepted on classes whose direct base istyping.Protocol. A subclass of a protocol class — an ordinary protocol in every other respect — is rejected, which is inconsistent with the runtime behavior and with other type checkers.To Reproduce
Result (mypy 2.3.1):
typing.runtime_checkableperforms a duck-typed check (the protocol metaclass sets_is_protocol), so decoratingNewProtoworks fine at runtime.Expected Behavior
NewProtois a protocol class: it inherits the protocol metaclass and the protocol machinery fromMid.@runtime_checkableshould be accepted, consistent with the runtime and with Pyright.Actual Results
mypy rejects it with
misc: @runtime_checkable can only be used with protocol classes.Additional context
In
mypy/semanal.py,is_protocolis only set when a direct base expression's fully-qualified name is inPROTOCOL_NAMES(the "also remove bare 'Protocol' bases" branch inanalyze_class), anddefn.info.is_protocol = is_protocoldoes not propagate the flag from protocol base classes.This came up in the discussion on #21940, where the hardcoding of
PROTOCOL_NAMESwas raised. Unlike removing that hardcoding (which would conflict with mypy's name-based identification of typing features), propagatingis_protocolfrom protocol base classes looks like a well-scoped improvement. Please let me know if the current behavior is intentional.