Skip to content

Commit

Permalink
pythongh-104873: Add typing.get_protocol_members
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra committed May 24, 2023
1 parent c90a862 commit 25eacef
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 1 deletion.
17 changes: 17 additions & 0 deletions Doc/library/typing.rst
Expand Up @@ -2905,6 +2905,23 @@ Introspection helpers

.. versionadded:: 3.8

.. function:: get_protocol_members(tp)

Return the set of members defined in a :class:`Protocol`.

::

>>> from typing import Protocol, get_protocol_members
>>> class P(Protocol):
... def a(self) -> str: ...
... b: int
>>> get_protocol_members(P)
{'a', 'b'}

Return None for arguments that are not Protocols.

.. versionadded:: 3.13

.. function:: is_typeddict(tp)

Check if a type is a :class:`TypedDict`.
Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.13.rst
Expand Up @@ -87,6 +87,13 @@ New Modules
Improved Modules
================

typing
------

* Added :func:`typing.get_protocol_members` to return the set of members
defining a :class:`typing.Protocol`. (Contributed by Jelle Zijlstra in
:gh:`104873`.)


Optimizations
=============
Expand Down
40 changes: 39 additions & 1 deletion Lib/test/test_typing.py
Expand Up @@ -22,7 +22,7 @@
from typing import Generic, ClassVar, Final, final, Protocol
from typing import assert_type, cast, runtime_checkable
from typing import get_type_hints
from typing import get_origin, get_args
from typing import get_origin, get_args, get_protocol_members
from typing import override
from typing import is_typeddict
from typing import reveal_type
Expand Down Expand Up @@ -3172,6 +3172,11 @@ def meth(self): pass
self.assertNotIn("__callable_proto_members_only__", vars(NonP))
self.assertNotIn("__callable_proto_members_only__", vars(NonPR))

self.assertIs(get_protocol_members(NonP), None)
self.assertIs(get_protocol_members(NonPR), None)
self.assertEqual(get_protocol_members(P), {"x"})
self.assertEqual(get_protocol_members(PR), {"meth"})

acceptable_extra_attrs = {
'_is_protocol', '_is_runtime_protocol', '__parameters__',
'__init__', '__annotations__', '__subclasshook__',
Expand Down Expand Up @@ -3587,6 +3592,39 @@ def __init__(self):

Foo() # Previously triggered RecursionError

def test_get_protocol_members(self):
self.assertIs(get_protocol_members(object), None)
self.assertIs(get_protocol_members(object()), None)
self.assertIs(get_protocol_members(Protocol), None)
self.assertIs(get_protocol_members(Generic), None)

class P(Protocol):
a: int
def b(self) -> str: ...
@property
def c(self) -> int: ...

self.assertEqual(get_protocol_members(P), {'a', 'b', 'c'})

class Concrete:
a: int
def b(self) -> str: return "capybara"
@property
def c(self) -> int: return 5

self.assertIs(get_protocol_members(Concrete), None)
self.assertIs(get_protocol_members(Concrete()), None)

class ConcreteInherit(P):
a: int = 42
def b(self) -> str: return "capybara"
@property
def c(self) -> int: return 5

# not a protocol
self.assertEqual(get_protocol_members(ConcreteInherit), None)
self.assertIs(get_protocol_members(ConcreteInherit()), None)


class GenericTests(BaseTestCase):

Expand Down
21 changes: 21 additions & 0 deletions Lib/typing.py
Expand Up @@ -132,6 +132,7 @@
'get_args',
'get_origin',
'get_overloads',
'get_protocol_members',
'get_type_hints',
'is_typeddict',
'LiteralString',
Expand Down Expand Up @@ -3296,3 +3297,23 @@ def method(self) -> None:
# read-only property, TypeError if it's a builtin class.
pass
return method


def get_protocol_members(tp: type, /) -> set[str]:
"""Return the set of members defined in a Protocol.
Example::
>>> from typing import Protocol, get_protocol_members
>>> class P(Protocol):
... def a(self) -> str: ...
... b: int
>>> get_protocol_members(P)
{'a', 'b'}
Return None for arguments that are not Protocols.
"""
if not getattr(tp, '_is_protocol', False) or tp is Protocol:
return None
return tp.__protocol_attrs__
@@ -0,0 +1,2 @@
Add :func:`typing.get_protocol_members` to return the set of members
defining a :class:`typing.Protocol`. Patch by Jelle Zijlstra.

0 comments on commit 25eacef

Please sign in to comment.