Skip to content

Commit

Permalink
Exclude the same special attributes from Protocol as CPython (#15490)
Browse files Browse the repository at this point in the history
  • Loading branch information
HexDecimal authored Jun 26, 2023
1 parent 9511daa commit 7ce3568
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 0 deletions.
21 changes: 21 additions & 0 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2802,6 +2802,25 @@ def accept(self, visitor: ExpressionVisitor[T]) -> T:
return visitor.visit_temp_node(self)


# Special attributes not collected as protocol members by Python 3.12
# See typing._SPECIAL_NAMES
EXCLUDED_PROTOCOL_ATTRIBUTES: Final = frozenset(
{
"__abstractmethods__",
"__annotations__",
"__dict__",
"__doc__",
"__init__",
"__module__",
"__new__",
"__slots__",
"__subclasshook__",
"__weakref__",
"__class_getitem__", # Since Python 3.9
}
)


class TypeInfo(SymbolNode):
"""The type structure of a single class.
Expand Down Expand Up @@ -3116,6 +3135,8 @@ def protocol_members(self) -> list[str]:
if isinstance(node.node, (TypeAlias, TypeVarExpr, MypyFile)):
# These are auxiliary definitions (and type aliases are prohibited).
continue
if name in EXCLUDED_PROTOCOL_ATTRIBUTES:
continue
members.add(name)
return sorted(list(members))

Expand Down
64 changes: 64 additions & 0 deletions test-data/unit/check-protocols.test
Original file line number Diff line number Diff line change
Expand Up @@ -2789,6 +2789,70 @@ class A(Protocol):

[builtins fixtures/tuple.pyi]

[case testProtocolSlotsIsNotProtocolMember]
# https://github.com/python/mypy/issues/11884
from typing import Protocol

class Foo(Protocol):
__slots__ = ()
class NoSlots:
pass
class EmptySlots:
__slots__ = ()
class TupleSlots:
__slots__ = ('x', 'y')
class StringSlots:
__slots__ = 'x y'
class InitSlots:
__slots__ = ('x',)
def __init__(self) -> None:
self.x = None
def foo(f: Foo):
pass

# All should pass:
foo(NoSlots())
foo(EmptySlots())
foo(TupleSlots())
foo(StringSlots())
foo(InitSlots())
[builtins fixtures/tuple.pyi]

[case testProtocolSlotsAndRuntimeCheckable]
from typing import Protocol, runtime_checkable

@runtime_checkable
class Foo(Protocol):
__slots__ = ()
class Bar:
pass
issubclass(Bar, Foo) # Used to be an error, when `__slots__` counted as a protocol member
[builtins fixtures/isinstance.pyi]
[typing fixtures/typing-full.pyi]


[case testProtocolWithClassGetItem]
# https://github.com/python/mypy/issues/11886
from typing import Any, Iterable, Protocol, Union

class B:
...

class C:
def __class_getitem__(cls, __item: Any) -> Any:
...

class SupportsClassGetItem(Protocol):
__slots__: Union[str, Iterable[str]] = ()
def __class_getitem__(cls, __item: Any) -> Any:
...

b1: SupportsClassGetItem = B()
c1: SupportsClassGetItem = C()
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]


[case testNoneVsProtocol]
# mypy: strict-optional
from typing_extensions import Protocol
Expand Down

0 comments on commit 7ce3568

Please sign in to comment.