Skip to content

Commit 7837be5

Browse files
committed
Merge remote-tracking branch 'origin/main' into gh-159136-func-attrs
2 parents 74d032d + 64018ac commit 7837be5

19 files changed

Lines changed: 366 additions & 88 deletions

File tree

‎.github/dependabot.yml‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,3 @@ updates:
117117
patterns: ["*"]
118118
cooldown:
119119
default-days: 14
120-
121-
- package-ecosystem: "pip"
122-
target-branch: "3.13"
123-
directory: "/Tools/"
124-
schedule:
125-
interval: "quarterly"
126-
labels: ["skip issue", "skip news"]
127-
groups:
128-
pip:
129-
patterns: ["*"]
130-
cooldown:
131-
default-days: 14

‎Doc/library/concurrent.interpreters.rst‎

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,15 @@ This module defines the following functions:
224224
Initialize a new (idle) Python interpreter
225225
and return a :class:`Interpreter` object for it.
226226

227-
.. function:: create_queue()
227+
.. function:: create_queue(maxsize=0)
228228

229229
Initialize a new cross-interpreter queue and return a :class:`Queue`
230230
object for it.
231231

232+
*maxsize* is an integer that sets the upperbound limit on the number of
233+
items that can be placed in the queue. Insertion will be blocked once this
234+
size has been reached, until queue items are consumed. If *maxsize* is
235+
less than or equal to zero, the queue size is infinite.
232236

233237
Interpreter objects
234238
^^^^^^^^^^^^^^^^^^^
@@ -268,7 +272,7 @@ Interpreter objects
268272
Some objects are actually shared and some are copied efficiently,
269273
but most are copied via :mod:`pickle`. See :ref:`interp-object-sharing`.
270274

271-
.. method:: exec(code, /, dedent=True)
275+
.. method:: exec(code, /)
272276

273277
Run the given source code in the interpreter (in the current thread).
274278

@@ -319,7 +323,9 @@ Communicating Between Interpreters
319323
.. class:: Queue(id)
320324

321325
A wrapper around a low-level, cross-interpreter queue, which
322-
implements the :class:`queue.Queue` interface. The underlying queue
326+
implements all the methods of :class:`queue.Queue` except for
327+
:meth:`~queue.Queue.task_done`, :meth:`~queue.Queue.join`
328+
and :meth:`~queue.Queue.shutdown`. The underlying queue
323329
can only be created through :func:`create_queue`.
324330

325331
Some objects are actually shared and some are copied efficiently,
@@ -331,17 +337,59 @@ Communicating Between Interpreters
331337

332338
The queue's ID.
333339

340+
.. method:: qsize()
334341

335-
.. exception:: QueueEmptyError
342+
Return the number of items in the queue.
343+
344+
.. method:: empty()
345+
346+
Return ``True`` if the queue is empty, ``False`` otherwise.
347+
348+
.. method:: full()
349+
350+
Return ``True`` if the queue is full, ``False`` otherwise.
351+
352+
.. method:: put(obj, block=True, timeout=None)
353+
354+
Put *obj* into the queue. If optional args *block* is true and
355+
*timeout* is ``None`` (the default), block if necessary until a free
356+
slot is available. If *timeout* is a positive number, it blocks at
357+
most *timeout* seconds and raises the :exc:`QueueFull` exception if
358+
no free slot was available within that time. Otherwise (*block* is
359+
false), put an object on the queue if a free slot is immediately
360+
available, else raise the :exc:`QueueFull` exception (*timeout* is
361+
ignored in that case).
362+
363+
.. method:: put_nowait(obj)
364+
365+
Equivalent to ``put(obj, block=False)``.
366+
367+
.. method:: get(block=True, timeout=None)
368+
369+
Remove and return an object from the queue. If optional args *block*
370+
is true and *timeout* is ``None`` (the default), block if necessary
371+
until an object is available. If *timeout* is a positive number, it
372+
blocks at most *timeout* seconds and raises the :exc:`QueueEmpty`
373+
exception if no object was available within that time. Otherwise
374+
(*block* is false), return an object if one is immediately available,
375+
else raise the :exc:`QueueEmpty` exception (*timeout* is ignored in
376+
that case).
377+
378+
.. method:: get_nowait()
379+
380+
Equivalent to ``get(block=False)``.
381+
382+
383+
.. exception:: QueueEmpty
336384

337385
This exception, a subclass of :exc:`queue.Empty`, is raised from
338-
:meth:`!Queue.get` and :meth:`!Queue.get_nowait` when the queue
386+
:meth:`Queue.get` and :meth:`Queue.get_nowait` when the queue
339387
is empty.
340388

341-
.. exception:: QueueFullError
389+
.. exception:: QueueFull
342390

343391
This exception, a subclass of :exc:`queue.Full`, is raised from
344-
:meth:`!Queue.put` and :meth:`!Queue.put_nowait` when the queue
392+
:meth:`Queue.put` and :meth:`Queue.put_nowait` when the queue
345393
is full.
346394

347395

@@ -382,3 +430,17 @@ Creating an interpreter and running code in it::
382430

383431
t = interp.call_in_thread(run)
384432
t.join()
433+
434+
Communicating between interpreters with a queue::
435+
436+
from concurrent import interpreters
437+
438+
interp = interpreters.create()
439+
queue = interpreters.create_queue()
440+
441+
def worker(queue):
442+
queue.put('spam!')
443+
444+
t = interp.call_in_thread(worker, queue)
445+
print(queue.get())
446+
t.join()

‎Doc/library/multiprocessing.rst‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,21 @@ to start a process. These *start methods* are
160160
No unnecessary resources are inherited.
161161

162162
Available on POSIX platforms which support passing file descriptors over
163-
Unix pipes such as Linux. The default on those.
163+
Unix pipes such as Linux. The default on those, unless there is no
164+
writable temporary directory (see :func:`tempfile.gettempdir`), which
165+
the fork server requires; *spawn* is the default in that case.
164166

165167
.. versionchanged:: 3.14
166168
This became the default start method on POSIX platforms.
167169

170+
.. versionchanged:: next
171+
*spawn* is now the default start method when there is no writable
172+
temporary directory, for example on a read-only file system.
173+
Previously *forkserver* remained the default there and starting a
174+
process failed with an :exc:`OSError`. The default is determined
175+
when it is first needed rather than when :mod:`!multiprocessing`
176+
is imported. See :gh:`155717`.
177+
168178
.. versionchanged:: 3.4
169179
*spawn* added on all POSIX platforms, and *forkserver* added for
170180
some POSIX platforms.

‎Doc/library/tarfile.rst‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1067,7 +1067,8 @@ can be:
10671067

10681068
* the string ``'tar'``: Honor most *tar*-specific features (i.e. features of
10691069
UNIX-like filesystems), but block features that are very likely to be
1070-
surprising or malicious. See :func:`tar_filter` for details.
1070+
surprising or malicious. Note that this filter does not limit the targets of
1071+
symbolic links. See :func:`tar_filter` for details.
10711072

10721073
* the string ``'data'``: Ignore or block most features specific to UNIX-like
10731074
filesystems. Intended for extracting cross-platform data archives.
@@ -1135,6 +1136,17 @@ reused in custom filters:
11351136

11361137
Return the modified ``TarInfo`` member.
11371138

1139+
.. warning::
1140+
This filter does not check :attr:`TarInfo.linkname`. This means it does
1141+
not limit where a symbolic link member points. An absolute symbolic-link
1142+
target will remain absolute, and a relative target may resolve outside the
1143+
extraction destination. The destination path protection described above
1144+
concerns only where the archive member itself is written, not where a
1145+
symbolic link resolves. The stricter :func:`data_filter` rejects these
1146+
unsafe link targets, raising :class:`~tarfile.AbsoluteLinkError` for
1147+
absolute link targets or :class:`~tarfile.LinkOutsideDestinationError`
1148+
for link targets outside the destination.
1149+
11381150
.. versionchanged:: next
11391151

11401152
Filenames containing ``..`` components are now normalized.

‎Doc/tools/removed-ids.txt‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,9 @@ using/windows.html: the-full-installer-deprecated
9393
using/windows.html: virtual-environments
9494
using/windows.html: windows-full
9595

96+
# Duplicated label from reference/datamodel:
97+
using/windows.html: customization
98+
9699
# Moved to library/stdtypes:
97100
reference/expressions.html: agen.__anext__
98101
reference/expressions.html: agen.aclose
@@ -114,3 +117,7 @@ library/turtle.html: changes-since-python-3-0
114117
# 'turtle' documentation reorganisation (gh-157847)
115118
library/turtle.html: note
116119
library/turtle.html: introduction
120+
121+
# Wrong exception names in 'concurrent.interpreters' docs (gh-136563)
122+
library/concurrent.interpreters.html: concurrent.interpreters.QueueEmptyError
123+
library/concurrent.interpreters.html: concurrent.interpreters.QueueFullError

‎Doc/using/windows.rst‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,6 @@ again. To completely clean up all Python runtimes, run with ``--purge`` before
382382
uninstalling the Python install manager.
383383

384384
.. _pymanager-config:
385-
.. _customization:
386385
.. _customizing-default-python-versions:
387386
.. _customization-via-ini-files:
388387
.. _launcher-ini:

‎Lib/annotationlib.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,9 +1105,17 @@ def type_repr(value):
11051105
11061106
"""
11071107
if isinstance(value, (type, types.FunctionType, types.BuiltinFunctionType)):
1108-
if value.__module__ == "builtins":
1108+
module = value.__module__
1109+
if module is None:
1110+
# Built-in methods have __module__ set to None; recover the module
1111+
# from the bound object (the class itself for class-bound methods).
1112+
obj = getattr(value, "__self__", None)
1113+
if obj is not None:
1114+
cls = obj if isinstance(obj, type) else type(obj)
1115+
module = cls.__module__
1116+
if module is None or module == "builtins":
11091117
return value.__qualname__
1110-
return f"{value.__module__}.{value.__qualname__}"
1118+
return f"{module}.{value.__qualname__}"
11111119
elif isinstance(value, _Template):
11121120
tree = _template_to_ast(value)
11131121
return ast.unparse(tree)

‎Lib/multiprocessing/context.py‎

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1+
import functools
12
import os
23
import sys
34
import threading
45

56
from . import process
67
from . import reduction
7-
from . import util
88

99
__all__ = ()
1010

@@ -240,10 +240,19 @@ def _after_fork():
240240
class DefaultContext(BaseContext):
241241
Process = Process
242242

243-
def __init__(self, context):
244-
self._default_context = context
243+
def __init__(self, context=None):
244+
if context is not None:
245+
self._default_context = context
245246
self._actual_context = None
246247

248+
# The platform default is only determined when first needed, as doing
249+
# so can be costly; importing multiprocessing must stay cheap.
250+
# A cached_property rather than a property so that the attribute can
251+
# still be assigned, deleted and patched like a plain one.
252+
@functools.cached_property
253+
def _default_context(self):
254+
return _get_platform_default_context()
255+
247256
def get_context(self, method=None):
248257
if method is None:
249258
if self._actual_context is None:
@@ -331,18 +340,21 @@ def _check_available(self):
331340
if reduction.HAVE_SEND_HANDLE:
332341
_concrete_contexts['forkserver'] = ForkServerContext()
333342

334-
# bpo-33725: running arbitrary code after fork() is no longer reliable
335-
# on macOS since macOS 10.14 (Mojave). Use spawn by default instead.
336-
# gh-84559: We changed everyones default to a thread safeish one in 3.14.
337-
if (
338-
reduction.HAVE_SEND_HANDLE
339-
and sys.platform != 'darwin'
340-
# gh-155717: forkserver requires to write temporary files
341-
and util._has_writeable_tempdir()
342-
):
343-
_default_context = DefaultContext(_concrete_contexts['forkserver'])
344-
else:
345-
_default_context = DefaultContext(_concrete_contexts['spawn'])
343+
def _get_platform_default_context():
344+
# bpo-33725: running arbitrary code after fork() is no longer reliable
345+
# on macOS since macOS 10.14 (Mojave). Use spawn by default instead.
346+
# gh-84559: We changed everyones default to a thread safeish one in 3.14.
347+
if reduction.HAVE_SEND_HANDLE and sys.platform != 'darwin':
348+
# gh-155717: forkserver requires to write temporary files.
349+
#
350+
# Imported here rather than at the top level as util is costly
351+
# to import and is not otherwise needed to import this module.
352+
from . import util
353+
if util._has_writeable_tempdir():
354+
return _concrete_contexts['forkserver']
355+
return _concrete_contexts['spawn']
356+
357+
_default_context = DefaultContext()
346358

347359
else: # Windows
348360

@@ -365,7 +377,10 @@ class SpawnContext(BaseContext):
365377
_concrete_contexts = {
366378
'spawn': SpawnContext(),
367379
}
368-
_default_context = DefaultContext(_concrete_contexts['spawn'])
380+
def _get_platform_default_context():
381+
return _concrete_contexts['spawn']
382+
383+
_default_context = DefaultContext()
369384

370385
#
371386
# Force the start method

‎Lib/multiprocessing/util.py‎

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import os
1111
import itertools
1212
import sys
13-
import tempfile
1413
import weakref
1514
import atexit
1615
import threading # we want threading to install it's
@@ -155,7 +154,7 @@ def _remove_temp_dir(rmtree, tempdir):
155154
current_process._config['tempdir'] = None
156155

157156

158-
def _get_base_temp_dir():
157+
def _get_base_temp_dir(tempfile):
159158
"""Get a temporary directory where socket files will be created.
160159
161160
To prevent additional imports, pass a pre-imported 'tempfile' module.
@@ -216,8 +215,9 @@ def get_temp_dir():
216215
# get name of a temp directory which will be automatically cleaned up
217216
tempdir = process.current_process()._config.get('tempdir')
218217
if tempdir is None:
219-
import shutil
220-
base_tempdir = _get_base_temp_dir()
218+
# Deferred until first use to keep importing this module cheap.
219+
import shutil, tempfile
220+
base_tempdir = _get_base_temp_dir(tempfile)
221221
tempdir = tempfile.mkdtemp(prefix='pymp-', dir=base_tempdir)
222222
info('created temp directory %s', tempdir)
223223
# keep a strong reference to shutil.rmtree(), since the finalizer
@@ -234,7 +234,14 @@ def _has_writeable_tempdir():
234234
#
235235
# See: https://github.com/python/cpython/issues/155717.
236236

237-
path = _get_base_temp_dir()
237+
# Deferred until first use to keep importing this module cheap.
238+
import tempfile
239+
240+
try:
241+
path = _get_base_temp_dir(tempfile)
242+
except OSError:
243+
# No usable temporary directory at all.
244+
return False
238245
if path is None:
239246
return False
240247

0 commit comments

Comments
 (0)