Skip to content

Commit d0afb11

Browse files
committed
Merge branch 'main' into buffer-protocol-thread-safety
2 parents 35bd2fd + 00a2585 commit d0afb11

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1379
-680
lines changed

Doc/c-api/dict.rst

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ Dictionary objects
4242
enforces read-only behavior. This is normally used to create a view to
4343
prevent modification of the dictionary for non-dynamic class types.
4444
45+
The first argument can be a :class:`dict`, a :class:`frozendict`, or a
46+
mapping.
47+
48+
.. versionchanged:: next
49+
Also accept :class:`frozendict`.
50+
4551
4652
.. c:var:: PyTypeObject PyDictProxy_Type
4753
@@ -68,15 +74,25 @@ Dictionary objects
6874
*key*, return ``1``, otherwise return ``0``. On error, return ``-1``.
6975
This is equivalent to the Python expression ``key in p``.
7076
77+
The first argument can be a :class:`dict` or a :class:`frozendict`.
78+
79+
.. versionchanged:: next
80+
Also accept :class:`frozendict`.
81+
7182
7283
.. c:function:: int PyDict_ContainsString(PyObject *p, const char *key)
7384
7485
This is the same as :c:func:`PyDict_Contains`, but *key* is specified as a
7586
:c:expr:`const char*` UTF-8 encoded bytes string, rather than a
7687
:c:expr:`PyObject*`.
7788
89+
The first argument can be a :class:`dict` or a :class:`frozendict`.
90+
7891
.. versionadded:: 3.13
7992
93+
.. versionchanged:: next
94+
Also accept :class:`frozendict`.
95+
8096
8197
.. c:function:: PyObject* PyDict_Copy(PyObject *p)
8298
@@ -122,8 +138,13 @@ Dictionary objects
122138
* If the key is missing, set *\*result* to ``NULL`` and return ``0``.
123139
* On error, raise an exception and return ``-1``.
124140
141+
The first argument can be a :class:`dict` or a :class:`frozendict`.
142+
125143
.. versionadded:: 3.13
126144
145+
.. versionchanged:: next
146+
Also accept :class:`frozendict`.
147+
127148
See also the :c:func:`PyObject_GetItem` function.
128149
129150
@@ -133,6 +154,8 @@ Dictionary objects
133154
has a key *key*. Return ``NULL`` if the key *key* is missing *without*
134155
setting an exception.
135156
157+
The first argument can be a :class:`dict` or a :class:`frozendict`.
158+
136159
.. note::
137160
138161
Exceptions that occur while this calls :meth:`~object.__hash__` and
@@ -143,6 +166,9 @@ Dictionary objects
143166
Calling this API without an :term:`attached thread state` had been allowed for historical
144167
reason. It is no longer allowed.
145168
169+
.. versionchanged:: next
170+
Also accept :class:`frozendict`.
171+
146172
147173
.. c:function:: PyObject* PyDict_GetItemWithError(PyObject *p, PyObject *key)
148174
@@ -151,6 +177,9 @@ Dictionary objects
151177
occurred. Return ``NULL`` **without** an exception set if the key
152178
wasn't present.
153179
180+
.. versionchanged:: next
181+
Also accept :class:`frozendict`.
182+
154183
155184
.. c:function:: PyObject* PyDict_GetItemString(PyObject *p, const char *key)
156185
@@ -166,6 +195,9 @@ Dictionary objects
166195
Prefer using the :c:func:`PyDict_GetItemWithError` function with your own
167196
:c:func:`PyUnicode_FromString` *key* instead.
168197
198+
.. versionchanged:: next
199+
Also accept :class:`frozendict`.
200+
169201
170202
.. c:function:: int PyDict_GetItemStringRef(PyObject *p, const char *key, PyObject **result)
171203
@@ -175,6 +207,9 @@ Dictionary objects
175207
176208
.. versionadded:: 3.13
177209
210+
.. versionchanged:: next
211+
Also accept :class:`frozendict`.
212+
178213
179214
.. c:function:: PyObject* PyDict_SetDefault(PyObject *p, PyObject *key, PyObject *defaultobj)
180215
@@ -238,17 +273,32 @@ Dictionary objects
238273
239274
Return a :c:type:`PyListObject` containing all the items from the dictionary.
240275
276+
The first argument can be a :class:`dict` or a :class:`frozendict`.
277+
278+
.. versionchanged:: next
279+
Also accept :class:`frozendict`.
280+
241281
242282
.. c:function:: PyObject* PyDict_Keys(PyObject *p)
243283
244284
Return a :c:type:`PyListObject` containing all the keys from the dictionary.
245285
286+
The first argument can be a :class:`dict` or a :class:`frozendict`.
287+
288+
.. versionchanged:: next
289+
Also accept :class:`frozendict`.
290+
246291
247292
.. c:function:: PyObject* PyDict_Values(PyObject *p)
248293
249294
Return a :c:type:`PyListObject` containing all the values from the dictionary
250295
*p*.
251296
297+
The first argument can be a :class:`dict` or a :class:`frozendict`.
298+
299+
.. versionchanged:: next
300+
Also accept :class:`frozendict`.
301+
252302
253303
.. c:function:: Py_ssize_t PyDict_Size(PyObject *p)
254304
@@ -257,11 +307,19 @@ Dictionary objects
257307
Return the number of items in the dictionary. This is equivalent to
258308
``len(p)`` on a dictionary.
259309
310+
The argument can be a :class:`dict` or a :class:`frozendict`.
311+
312+
.. versionchanged:: next
313+
Also accept :class:`frozendict`.
314+
260315
261316
.. c:function:: Py_ssize_t PyDict_GET_SIZE(PyObject *p)
262317
263318
Similar to :c:func:`PyDict_Size`, but without error checking.
264319
320+
.. versionchanged:: next
321+
Also accept :class:`frozendict`.
322+
265323
266324
.. c:function:: int PyDict_Next(PyObject *p, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue)
267325
@@ -276,6 +334,8 @@ Dictionary objects
276334
value represents offsets within the internal dictionary structure, and
277335
since the structure is sparse, the offsets are not consecutive.
278336
337+
The first argument can be a :class:`dict` or a :class:`frozendict`.
338+
279339
For example::
280340
281341
PyObject *key, *value;
@@ -309,7 +369,7 @@ Dictionary objects
309369
}
310370
311371
The function is not thread-safe in the :term:`free-threaded <free threading>`
312-
build without external synchronization. You can use
372+
build without external synchronization for a mutable :class:`dict`. You can use
313373
:c:macro:`Py_BEGIN_CRITICAL_SECTION` to lock the dictionary while iterating
314374
over it::
315375
@@ -319,6 +379,8 @@ Dictionary objects
319379
}
320380
Py_END_CRITICAL_SECTION();
321381
382+
The function is thread-safe on a :class:`frozendict`.
383+
322384
.. note::
323385
324386
On the free-threaded build, this function can be used safely inside a
@@ -329,6 +391,9 @@ Dictionary objects
329391
:term:`strong reference <strong reference>` (for example, using
330392
:c:func:`Py_NewRef`).
331393
394+
.. versionchanged:: next
395+
Also accept :class:`frozendict`.
396+
332397
.. c:function:: int PyDict_Merge(PyObject *a, PyObject *b, int override)
333398
334399
Iterate over mapping object *b* adding key-value pairs to dictionary *a*.

Doc/library/datetime.rst

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2611,8 +2611,10 @@ requires, and these work on all supported platforms.
26112611
| ``%M`` | Minute as a zero-padded | 00, 01, ..., 59 | \(9) |
26122612
| | decimal number. | | |
26132613
+-----------+--------------------------------+------------------------+-------+
2614-
| ``%n`` | The newline character | ``\n`` | \(0) |
2615-
| | (``'\n'``). | | |
2614+
| ``%n`` | The newline character | ``\n`` | |
2615+
| | (``'\n'``). For | | |
2616+
| | :meth:`!strptime`, zero or | | |
2617+
| | more whitespace. | | |
26162618
+-----------+--------------------------------+------------------------+-------+
26172619
| ``%p`` | Locale's equivalent of either || AM, PM (en_US); | \(1), |
26182620
| | AM or PM. || am, pm (de_DE) | \(3) |
@@ -2625,8 +2627,9 @@ requires, and these work on all supported platforms.
26252627
| ``%S`` | Second as a zero-padded | 00, 01, ..., 59 | \(4), |
26262628
| | decimal number. | | \(9) |
26272629
+-----------+--------------------------------+------------------------+-------+
2628-
| ``%t`` | The tab character | ``\t`` | \(0) |
2629-
| | (``'\t'``). | | |
2630+
| ``%t`` | The tab character (``'\t'``). | ``\t`` | |
2631+
| | For :meth:`!strptime`, | | |
2632+
| | zero or more whitespace. | | |
26302633
+-----------+--------------------------------+------------------------+-------+
26312634
| ``%T`` | ISO 8601 time format, | 10:01:59 | |
26322635
| | equivalent to ``%H:%M:%S``. | | |
@@ -2717,7 +2720,8 @@ differences between platforms in handling of unsupported format specifiers.
27172720
``%:z`` was added for :meth:`~.datetime.strftime`.
27182721

27192722
.. versionadded:: 3.15
2720-
``%:z``, ``%F``, and ``%D`` were added for :meth:`~.datetime.strptime`.
2723+
``%D``, ``%F``, ``%n``, ``%t``, and ``%:z`` were added for
2724+
:meth:`~.datetime.strptime`.
27212725

27222726

27232727
Technical detail

Doc/library/stdtypes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5254,6 +5254,11 @@ Note, the *elem* argument to the :meth:`~object.__contains__`,
52545254
:meth:`~set.discard` methods may be a set. To support searching for an equivalent
52555255
frozenset, a temporary one is created from *elem*.
52565256

5257+
.. seealso::
5258+
5259+
For detailed information on thread-safety guarantees for :class:`set`
5260+
objects, see :ref:`thread-safety-set`.
5261+
52575262

52585263
.. _typesmapping:
52595264

Doc/library/threadsafety.rst

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,111 @@ Consider external synchronization when sharing :class:`dict` instances
344344
across threads.
345345

346346

347+
.. _thread-safety-set:
348+
349+
Thread safety for set objects
350+
==============================
351+
352+
The :func:`len` function is lock-free and :term:`atomic <atomic operation>`.
353+
354+
The following read operation is lock-free. It does not block concurrent
355+
modifications and may observe intermediate states from operations that
356+
hold the per-object lock:
357+
358+
.. code-block::
359+
:class: good
360+
361+
elem in s # set.__contains__
362+
363+
This operation may compare elements using :meth:`~object.__eq__`, which can
364+
execute arbitrary Python code. During such comparisons, the set may be
365+
modified by another thread. For built-in types like :class:`str`,
366+
:class:`int`, and :class:`float`, :meth:`!__eq__` does not release the
367+
underlying lock during comparisons and this is not a concern.
368+
369+
All other operations from here on hold the per-object lock.
370+
371+
Adding or removing a single element is safe to call from multiple threads
372+
and will not corrupt the set:
373+
374+
.. code-block::
375+
:class: good
376+
377+
s.add(elem) # add element
378+
s.remove(elem) # remove element, raise if missing
379+
s.discard(elem) # remove element if present
380+
s.pop() # remove and return arbitrary element
381+
382+
These operations also compare elements, so the same :meth:`~object.__eq__`
383+
considerations as above apply.
384+
385+
The :meth:`~set.copy` method returns a new object and holds the per-object lock
386+
for the duration so that it is always atomic.
387+
388+
The :meth:`~set.clear` method holds the lock for its duration. Other
389+
threads cannot observe elements being removed.
390+
391+
The following operations only accept :class:`set` or :class:`frozenset`
392+
as operands and always lock both objects:
393+
394+
.. code-block::
395+
:class: good
396+
397+
s |= other # other must be set/frozenset
398+
s &= other # other must be set/frozenset
399+
s -= other # other must be set/frozenset
400+
s ^= other # other must be set/frozenset
401+
s & other # other must be set/frozenset
402+
s | other # other must be set/frozenset
403+
s - other # other must be set/frozenset
404+
s ^ other # other must be set/frozenset
405+
406+
:meth:`set.update`, :meth:`set.union`, :meth:`set.intersection` and
407+
:meth:`set.difference` can take multiple iterables as arguments. They all
408+
iterate through all the passed iterables and do the following:
409+
410+
* :meth:`set.update` and :meth:`set.union` lock both objects only when
411+
the other operand is a :class:`set`, :class:`frozenset`, or :class:`dict`.
412+
* :meth:`set.intersection` and :meth:`set.difference` always try to lock
413+
all objects.
414+
415+
:meth:`set.symmetric_difference` tries to lock both objects.
416+
417+
The update variants of the above methods also have some differences between
418+
them:
419+
420+
* :meth:`set.difference_update` and :meth:`set.intersection_update` try
421+
to lock all objects one-by-one.
422+
* :meth:`set.symmetric_difference_update` only locks the arguments if it is
423+
of type :class:`set`, :class:`frozenset`, or :class:`dict`.
424+
425+
The following methods always try to lock both objects:
426+
427+
.. code-block::
428+
:class: good
429+
430+
s.isdisjoint(other) # both locked
431+
s.issubset(other) # both locked
432+
s.issuperset(other) # both locked
433+
434+
Operations that involve multiple accesses, as well as iteration, are never
435+
atomic:
436+
437+
.. code-block::
438+
:class: bad
439+
440+
# NOT atomic: check-then-act
441+
if elem in s:
442+
s.remove(elem)
443+
444+
# NOT thread-safe: iteration while modifying
445+
for elem in s:
446+
process(elem) # another thread may modify s
447+
448+
Consider external synchronization when sharing :class:`set` instances
449+
across threads. See :ref:`freethreading-python-howto` for more information.
450+
451+
347452
.. _thread-safety-memoryview:
348453

349454
Thread safety for memoryview objects

0 commit comments

Comments
 (0)