Skip to content

Commit ce6c245

Browse files
AlexWaygoodJelleZijlstra
authored andcommitted
pythongh-74690: Document changes made to runtime-checkable protocols in 3.12 (python#103348)
Co-authored-by: Jelle Zijlstra <[email protected]>
1 parent a82048a commit ce6c245

File tree

4 files changed

+64
-9
lines changed

4 files changed

+64
-9
lines changed

Doc/library/typing.rst

+18-9
Original file line numberDiff line numberDiff line change
@@ -1598,15 +1598,6 @@ These are not used in annotations. They are building blocks for creating generic
15981598
import threading
15991599
assert isinstance(threading.Thread(name='Bob'), Named)
16001600

1601-
.. versionchanged:: 3.12
1602-
The internal implementation of :func:`isinstance` checks against
1603-
runtime-checkable protocols now uses :func:`inspect.getattr_static`
1604-
to look up attributes (previously, :func:`hasattr` was used).
1605-
As a result, some objects which used to be considered instances
1606-
of a runtime-checkable protocol may no longer be considered instances
1607-
of that protocol on Python 3.12+, and vice versa.
1608-
Most users are unlikely to be affected by this change.
1609-
16101601
.. note::
16111602

16121603
:func:`!runtime_checkable` will check only the presence of the required
@@ -1628,6 +1619,24 @@ These are not used in annotations. They are building blocks for creating generic
16281619

16291620
.. versionadded:: 3.8
16301621

1622+
.. versionchanged:: 3.12
1623+
The internal implementation of :func:`isinstance` checks against
1624+
runtime-checkable protocols now uses :func:`inspect.getattr_static`
1625+
to look up attributes (previously, :func:`hasattr` was used).
1626+
As a result, some objects which used to be considered instances
1627+
of a runtime-checkable protocol may no longer be considered instances
1628+
of that protocol on Python 3.12+, and vice versa.
1629+
Most users are unlikely to be affected by this change.
1630+
1631+
.. versionchanged:: 3.12
1632+
The members of a runtime-checkable protocol are now considered "frozen"
1633+
at runtime as soon as the class has been created. Monkey-patching
1634+
attributes onto a runtime-checkable protocol will still work, but will
1635+
have no impact on :func:`isinstance` checks comparing objects to the
1636+
protocol. See :ref:`"What's new in Python 3.12" <whatsnew-typing-py312>`
1637+
for more details.
1638+
1639+
16311640
Other special directives
16321641
""""""""""""""""""""""""
16331642

Doc/whatsnew/3.12.rst

+35
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,8 @@ tempfile
422422
The :class:`tempfile.NamedTemporaryFile` function has a new optional parameter
423423
*delete_on_close* (Contributed by Evgeny Zorin in :gh:`58451`.)
424424

425+
.. _whatsnew-typing-py312:
426+
425427
typing
426428
------
427429

@@ -441,6 +443,39 @@ typing
441443
vice versa. Most users are unlikely to be affected by this change.
442444
(Contributed by Alex Waygood in :gh:`102433`.)
443445

446+
* The members of a runtime-checkable protocol are now considered "frozen" at
447+
runtime as soon as the class has been created. Monkey-patching attributes
448+
onto a runtime-checkable protocol will still work, but will have no impact on
449+
:func:`isinstance` checks comparing objects to the protocol. For example::
450+
451+
>>> from typing import Protocol, runtime_checkable
452+
>>> @runtime_checkable
453+
... class HasX(Protocol):
454+
... x = 1
455+
...
456+
>>> class Foo: ...
457+
...
458+
>>> f = Foo()
459+
>>> isinstance(f, HasX)
460+
False
461+
>>> f.x = 1
462+
>>> isinstance(f, HasX)
463+
True
464+
>>> HasX.y = 2
465+
>>> isinstance(f, HasX) # unchanged, even though HasX now also has a "y" attribute
466+
True
467+
468+
This change was made in order to speed up ``isinstance()`` checks against
469+
runtime-checkable protocols.
470+
471+
* The performance profile of :func:`isinstance` checks against
472+
:func:`runtime-checkable protocols <typing.runtime_checkable>` has changed
473+
significantly. Most ``isinstance()`` checks against protocols with only a few
474+
members should be at least 2x faster than in 3.11, and some may be 20x
475+
faster or more. However, ``isinstance()`` checks against protocols with seven
476+
or more members may be slower than in Python 3.11. (Contributed by Alex
477+
Waygood in :gh:`74690` and :gh:`103193`.)
478+
444479
sys
445480
---
446481

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The members of a runtime-checkable protocol are now considered "frozen" at
2+
runtime as soon as the class has been created. See
3+
:ref:`"What's new in Python 3.12" <whatsnew-typing-py312>` for more details.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
The performance of :func:`isinstance` checks against
2+
:func:`runtime-checkable protocols <typing.runtime_checkable>` has been
3+
considerably improved for protocols that only have a few members. To achieve
4+
this improvement, several internal implementation details of the
5+
:mod:`typing` module have been refactored, including
6+
``typing._ProtocolMeta.__instancecheck__``,
7+
``typing._is_callable_members_only``, and ``typing._get_protocol_attrs``.
8+
Patches by Alex Waygood.

0 commit comments

Comments
 (0)