You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
False positive: Protocol with EXCLUDED_ATTRIBUTES cannot be used with issubclass() #5614
When an "runtime_checkable" Protocol defines attributes defined in EXCLUDED_ATTRIBUTES (e.g., __slots__), Pyrefly incorrectly treats them as non-callable / data members of the protocol. As a result, using this protocol with issubclass() triggers a false-positive error.
Pyrefly should exclude attribute names included in EXCLUDED_ATTRIBUTES when checking whether a protocol contains non-method members. issubclass(bytes, Buffer) should be accepted without diagnostic errors.
Current Behavior
Pyrefly flags issubclass(bytes, Buffer) with:
Protocol `Buffer` has non-method members and cannot be used with issubclass()
Additional Context & Behavior
CPython Runtime Behavior
In CPython's typing.py, __slots__ is explicitly listed in EXCLUDED_ATTRIBUTES and filtered out by _get_protocol_attrs(). Consequently, Buffer.__non_callable_proto_members__ evaluates to set(), and issubclass(bytes, Buffer) executes cleanly at runtime, returning True.
Standard Library Precedent
Many @runtime_checkable protocols in the standard library (such as typing.SupportsInt, typing.SupportsFloat, and collections.abc.Buffer) define __slots__ = () by convention, and checking issubclass(..., SupportsInt) is valid both at runtime and in typeshed.
Typing Community Consensus
As noted by Guido van Rossum on Discuss Python "Should Protocols with empty __slots__ count as Data Protocols?":
"Empty `__slots__` no more makes it a data protocol as `__doc__`, `__module__` and others."
Control Cases and Comparisons
Case
Declaration
CPython 3.12+ runtime
A
__slots__ = () plus a method
issubclass() succeeds
B
typing.SupportsInt (__slots__ = () in typeshed)
issubclass() succeeds
C
__slots__ = ("value",) plus a method
TypeError: Protocols with non-method members don't support issubclass()
D
Plain data member value: int
TypeError: Protocols with non-method members don't support issubclass()
E
__slots__ = () plus value: int
TypeError: Protocols with non-method members don't support issubclass()
A correct checker should accept A and B, and report an error for C, D, and E.
The following results were produced with each checker's default settings, except where noted. TypeError below refers to the CPython runtime result, not to the checker. A checker never raises TypeError in this comparison; it either emits a diagnostic or does not.
none: the checker emits no diagnostic; CPython succeeds.
FP: the checker emits a diagnostic; CPython succeeds, so the diagnostic is a false positive.
diagnostic: the checker emits a diagnostic; CPython raises TypeError, so the diagnostic is correct.
missed: the checker emits no diagnostic; CPython raises TypeError, so the checker missed an error.
Activity
amas127 commentedon Nov 11, 2026
Description
When an "
runtime_checkable"Protocoldefines attributes defined inEXCLUDED_ATTRIBUTES(e.g.,__slots__), Pyrefly incorrectly treats them as non-callable / data members of the protocol. As a result, using this protocol withissubclass()triggers a false-positive error.Steps to Reproduce
Shown as in Sandbox Link.
Related
cpython/Lib/typing.py
python/typing #1833
microsoft/pyright #11834
"Should Protocols with empty
__slots__count as Data Protocols?"python/cpython #113320
python/cpython #106363
Expected Behavior
Pyrefly should exclude attribute names included in
EXCLUDED_ATTRIBUTESwhen checking whether a protocol contains non-method members.issubclass(bytes, Buffer)should be accepted without diagnostic errors.Current Behavior
Pyrefly flags
issubclass(bytes, Buffer)with:Additional Context & Behavior
In CPython's
typing.py,__slots__is explicitly listed inEXCLUDED_ATTRIBUTESand filtered out by_get_protocol_attrs(). Consequently,Buffer.__non_callable_proto_members__evaluates toset(), andissubclass(bytes, Buffer)executes cleanly at runtime, returningTrue.Many
@runtime_checkableprotocols in the standard library (such astyping.SupportsInt,typing.SupportsFloat, andcollections.abc.Buffer) define__slots__ = ()by convention, and checkingissubclass(..., SupportsInt)is valid both at runtime and in typeshed.As noted by Guido van Rossum on Discuss Python "Should Protocols with empty
__slots__count as Data Protocols?":Control Cases and Comparisons
__slots__ = ()plus a methodissubclass()succeedstyping.SupportsInt(__slots__ = ()in typeshed)issubclass()succeeds__slots__ = ("value",)plus a methodTypeError: Protocols with non-method members don't support issubclass()value: intTypeError: Protocols with non-method members don't support issubclass()__slots__ = ()plusvalue: intTypeError: Protocols with non-method members don't support issubclass()A correct checker should accept A and B, and report an error for C, D, and E.
The following results were produced with each checker's default settings, except where noted.
TypeErrorbelow refers to the CPython runtime result, not to the checker. A checker never raisesTypeErrorin this comparison; it either emits a diagnostic or does not.none: the checker emits no diagnostic; CPython succeeds.FP: the checker emits a diagnostic; CPython succeeds, so the diagnostic is a false positive.diagnostic: the checker emits a diagnostic; CPython raisesTypeError, so the diagnostic is correct.missed: the checker emits no diagnostic; CPython raisesTypeError, so the checker missed an error.mypy --strict --python-version 3.13 case.py pyright --pythonversion 3.13 --level error case.py basedpyright --pythonversion 3.13 --level error case.py pytype -V 3.12 case.py zuban check --strict case.py ty check case.py pyre check # with source_directories = ["."] pyrefly check case.py --preset strictSupportsIntTypeErrorTypeErrorTypeError--strict)-V 3.12)--strict)--preset strict)amas127 commentedon Nov 11, 2026
#claim
github-actions commentedon Nov 11, 2026
@amas127 you've claimed this issue — it's now assigned to you. Thanks for picking it up! 🎉