Skip to content

Commit d82c468

Browse files
committed
Merge remote-tracking branch 'upstream/main' into binary-op-extend
2 parents 444c8d8 + 3c9c3d3 commit d82c468

39 files changed

Lines changed: 616 additions & 176 deletions

.github/CODEOWNERS

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@
6363
.azure-pipelines/ @AA-Turner
6464

6565
# GitHub & related scripts
66-
.github/ @ezio-melotti @hugovk @AA-Turner
67-
Tools/build/compute-changes.py @AA-Turner
66+
.github/ @ezio-melotti @hugovk @AA-Turner @webknjaz
67+
Tools/build/compute-changes.py @AA-Turner @hugovk @webknjaz
6868
Tools/build/verify_ensurepip_wheels.py @AA-Turner @pfmoore @pradyunsg
6969

7070
# Pre-commit

Doc/c-api/lifecycle.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ To allocate and free memory, see :ref:`allocating-objects`.
256256
collection (i.e., the :c:macro:`Py_TPFLAGS_HAVE_GC` flag is set); this may
257257
change in the future.
258258
259+
.. versionadded:: 3.4
260+
259261
260262
.. c:function:: int PyObject_CallFinalizerFromDealloc(PyObject *op)
261263
@@ -266,6 +268,8 @@ To allocate and free memory, see :ref:`allocating-objects`.
266268
should happen. Otherwise, this function returns 0 and destruction can
267269
continue normally.
268270
271+
.. versionadded:: 3.4
272+
269273
.. seealso::
270274
271275
:c:member:`~PyTypeObject.tp_dealloc` for example code.

Doc/library/multiprocessing.rst

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,22 +1234,32 @@ Miscellaneous
12341234
.. versionchanged:: 3.11
12351235
Accepts a :term:`path-like object`.
12361236

1237-
.. function:: set_forkserver_preload(module_names)
1237+
.. function:: set_forkserver_preload(module_names, *, on_error='ignore')
12381238

12391239
Set a list of module names for the forkserver main process to attempt to
12401240
import so that their already imported state is inherited by forked
1241-
processes. Any :exc:`ImportError` when doing so is silently ignored.
1242-
This can be used as a performance enhancement to avoid repeated work
1243-
in every process.
1241+
processes. This can be used as a performance enhancement to avoid repeated
1242+
work in every process.
12441243

12451244
For this to work, it must be called before the forkserver process has been
12461245
launched (before creating a :class:`Pool` or starting a :class:`Process`).
12471246

1247+
The *on_error* parameter controls how :exc:`ImportError` exceptions during
1248+
module preloading are handled: ``"ignore"`` (default) silently ignores
1249+
failures, ``"warn"`` causes the forkserver subprocess to emit an
1250+
:exc:`ImportWarning` to stderr, and ``"fail"`` causes the forkserver
1251+
subprocess to exit with the exception traceback on stderr, making
1252+
subsequent process creation fail with :exc:`EOFError` or
1253+
:exc:`ConnectionError`.
1254+
12481255
Only meaningful when using the ``'forkserver'`` start method.
12491256
See :ref:`multiprocessing-start-methods`.
12501257

12511258
.. versionadded:: 3.4
12521259

1260+
.. versionchanged:: next
1261+
Added the *on_error* parameter.
1262+
12531263
.. function:: set_start_method(method, force=False)
12541264

12551265
Set the method which should be used to start child processes.

Doc/library/stdtypes.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2562,6 +2562,19 @@ expression support in the :mod:`re` module).
25622562
after the separator. If the separator is not found, return a 3-tuple containing
25632563
two empty strings, followed by the string itself.
25642564

2565+
For example:
2566+
2567+
.. doctest::
2568+
2569+
>>> 'Monty Python'.rpartition(' ')
2570+
('Monty', ' ', 'Python')
2571+
>>> "Monty Python's Flying Circus".rpartition(' ')
2572+
("Monty Python's Flying", ' ', 'Circus')
2573+
>>> 'Monty Python'.rpartition('-')
2574+
('', '', 'Monty Python')
2575+
2576+
See also :meth:`partition`.
2577+
25652578

25662579
.. method:: str.rsplit(sep=None, maxsplit=-1)
25672580

Doc/library/tkinter.rst

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,12 @@ the modern themed widget set and API::
177177
.. attribute:: master
178178

179179
The widget object that contains this widget. For :class:`Tk`, the
180-
*master* is :const:`None` because it is the main window. The terms
180+
:attr:`!master` is :const:`None` because it is the main window. The terms
181181
*master* and *parent* are similar and sometimes used interchangeably
182182
as argument names; however, calling :meth:`winfo_parent` returns a
183-
string of the widget name whereas :attr:`master` returns the object.
183+
string of the widget name whereas :attr:`!master` returns the object.
184184
*parent*/*child* reflects the tree-like relationship while
185-
*master*/*slave* reflects the container structure.
185+
*master* (or *container*)/*content* reflects the container structure.
186186

187187
.. attribute:: children
188188

@@ -638,15 +638,15 @@ The Packer
638638
.. index:: single: packing (widgets)
639639

640640
The packer is one of Tk's geometry-management mechanisms. Geometry managers
641-
are used to specify the relative positioning of widgets within their container -
642-
their mutual *master*. In contrast to the more cumbersome *placer* (which is
641+
are used to specify the relative positioning of widgets within their container.
642+
In contrast to the more cumbersome *placer* (which is
643643
used less commonly, and we do not cover here), the packer takes qualitative
644644
relationship specification - *above*, *to the left of*, *filling*, etc - and
645645
works everything out to determine the exact placement coordinates for you.
646646

647-
The size of any *master* widget is determined by the size of the "slave widgets"
648-
inside. The packer is used to control where slave widgets appear inside the
649-
master into which they are packed. You can pack widgets into frames, and frames
647+
The size of any container widget is determined by the size of the "content widgets"
648+
inside. The packer is used to control where content widgets appear inside the
649+
container into which they are packed. You can pack widgets into frames, and frames
650650
into other frames, in order to achieve the kind of layout you desire.
651651
Additionally, the arrangement is dynamically adjusted to accommodate incremental
652652
changes to the configuration, once it is packed.
@@ -673,7 +673,7 @@ For more extensive information on the packer and the options that it can take,
673673
see the man pages and page 183 of John Ousterhout's book.
674674

675675
anchor
676-
Anchor type. Denotes where the packer is to place each slave in its parcel.
676+
Anchor type. Denotes where the packer is to place each content in its parcel.
677677

678678
expand
679679
Boolean, ``0`` or ``1``.
@@ -682,10 +682,10 @@ fill
682682
Legal values: ``'x'``, ``'y'``, ``'both'``, ``'none'``.
683683

684684
ipadx and ipady
685-
A distance - designating internal padding on each side of the slave widget.
685+
A distance - designating internal padding on each side of the content.
686686

687687
padx and pady
688-
A distance - designating external padding on each side of the slave widget.
688+
A distance - designating external padding on each side of the content.
689689

690690
side
691691
Legal values are: ``'left'``, ``'right'``, ``'top'``, ``'bottom'``.
@@ -758,8 +758,8 @@ subclassed from the :class:`Wm` class, and so can call the :class:`Wm` methods
758758
directly.
759759

760760
To get at the toplevel window that contains a given widget, you can often just
761-
refer to the widget's master. Of course if the widget has been packed inside of
762-
a frame, the master won't represent a toplevel window. To get at the toplevel
761+
refer to the widget's :attr:`master`. Of course if the widget has been packed inside of
762+
a frame, the :attr:`!master` won't represent a toplevel window. To get at the toplevel
763763
window that contains an arbitrary widget, you can call the :meth:`_root` method.
764764
This method begins with an underscore to denote the fact that this function is
765765
part of the implementation, and not an interface to Tk functionality.

Lib/annotationlib.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,13 @@ def __eq__(self, other):
279279
# because dictionaries are not hashable.
280280
and self.__globals__ is other.__globals__
281281
and self.__forward_is_class__ == other.__forward_is_class__
282-
and self.__cell__ == other.__cell__
282+
# Two separate cells are always considered unequal in forward refs.
283+
and (
284+
{name: id(cell) for name, cell in self.__cell__.items()}
285+
== {name: id(cell) for name, cell in other.__cell__.items()}
286+
if isinstance(self.__cell__, dict) and isinstance(other.__cell__, dict)
287+
else self.__cell__ is other.__cell__
288+
)
283289
and self.__owner__ == other.__owner__
284290
and (
285291
(tuple(sorted(self.__extra_names__.items())) if self.__extra_names__ else None) ==
@@ -293,7 +299,10 @@ def __hash__(self):
293299
self.__forward_module__,
294300
id(self.__globals__), # dictionaries are not hashable, so hash by identity
295301
self.__forward_is_class__,
296-
tuple(sorted(self.__cell__.items())) if isinstance(self.__cell__, dict) else self.__cell__,
302+
( # cells are not hashable as well
303+
tuple(sorted([(name, id(cell)) for name, cell in self.__cell__.items()]))
304+
if isinstance(self.__cell__, dict) else id(self.__cell__),
305+
),
297306
self.__owner__,
298307
tuple(sorted(self.__extra_names__.items())) if self.__extra_names__ else None,
299308
))

Lib/email/_header_value_parser.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,12 @@ def make_quoted_pairs(value):
101101
return str(value).replace('\\', '\\\\').replace('"', '\\"')
102102

103103

104+
def make_parenthesis_pairs(value):
105+
"""Escape parenthesis and backslash for use within a comment."""
106+
return str(value).replace('\\', '\\\\') \
107+
.replace('(', '\\(').replace(')', '\\)')
108+
109+
104110
def quote_string(value):
105111
escaped = make_quoted_pairs(value)
106112
return f'"{escaped}"'
@@ -943,7 +949,7 @@ def value(self):
943949
return ' '
944950

945951
def startswith_fws(self):
946-
return True
952+
return self and self[0] in WSP
947953

948954

949955
class ValueTerminal(Terminal):
@@ -2963,6 +2969,13 @@ def _refold_parse_tree(parse_tree, *, policy):
29632969
[ValueTerminal(make_quoted_pairs(p), 'ptext')
29642970
for p in newparts] +
29652971
[ValueTerminal('"', 'ptext')])
2972+
if part.token_type == 'comment':
2973+
newparts = (
2974+
[ValueTerminal('(', 'ptext')] +
2975+
[ValueTerminal(make_parenthesis_pairs(p), 'ptext')
2976+
if p.token_type == 'ptext' else p
2977+
for p in newparts] +
2978+
[ValueTerminal(')', 'ptext')])
29662979
if not part.as_ew_allowed:
29672980
wrap_as_ew_blocked += 1
29682981
newparts.append(end_ew_not_allowed)

Lib/multiprocessing/context.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,12 +177,15 @@ def set_executable(self, executable):
177177
from .spawn import set_executable
178178
set_executable(executable)
179179

180-
def set_forkserver_preload(self, module_names):
180+
def set_forkserver_preload(self, module_names, *, on_error='ignore'):
181181
'''Set list of module names to try to load in forkserver process.
182-
This is really just a hint.
182+
183+
The on_error parameter controls how import failures are handled:
184+
"ignore" (default) silently ignores failures, "warn" emits warnings,
185+
and "fail" raises exceptions breaking the forkserver context.
183186
'''
184187
from .forkserver import set_forkserver_preload
185-
set_forkserver_preload(module_names)
188+
set_forkserver_preload(module_names, on_error=on_error)
186189

187190
def get_context(self, method=None):
188191
if method is None:

Lib/multiprocessing/forkserver.py

Lines changed: 87 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ def __init__(self):
4242
self._inherited_fds = None
4343
self._lock = threading.Lock()
4444
self._preload_modules = ['__main__']
45+
self._preload_on_error = 'ignore'
4546

4647
def _stop(self):
4748
# Method used by unit tests to stop the server
@@ -64,11 +65,22 @@ def _stop_unlocked(self):
6465
self._forkserver_address = None
6566
self._forkserver_authkey = None
6667

67-
def set_forkserver_preload(self, modules_names):
68-
'''Set list of module names to try to load in forkserver process.'''
68+
def set_forkserver_preload(self, modules_names, *, on_error='ignore'):
69+
'''Set list of module names to try to load in forkserver process.
70+
71+
The on_error parameter controls how import failures are handled:
72+
"ignore" (default) silently ignores failures, "warn" emits warnings,
73+
and "fail" raises exceptions breaking the forkserver context.
74+
'''
6975
if not all(type(mod) is str for mod in modules_names):
7076
raise TypeError('module_names must be a list of strings')
77+
if on_error not in ('ignore', 'warn', 'fail'):
78+
raise ValueError(
79+
f"on_error must be 'ignore', 'warn', or 'fail', "
80+
f"not {on_error!r}"
81+
)
7182
self._preload_modules = modules_names
83+
self._preload_on_error = on_error
7284

7385
def get_inherited_fds(self):
7486
'''Return list of fds inherited from parent process.
@@ -107,6 +119,14 @@ def connect_to_new_process(self, fds):
107119
wrapped_client, self._forkserver_authkey)
108120
connection.deliver_challenge(
109121
wrapped_client, self._forkserver_authkey)
122+
except (EOFError, ConnectionError, BrokenPipeError) as exc:
123+
if (self._preload_modules and
124+
self._preload_on_error == 'fail'):
125+
exc.add_note(
126+
"Forkserver process may have crashed during module "
127+
"preloading. Check stderr."
128+
)
129+
raise
110130
finally:
111131
wrapped_client._detach()
112132
del wrapped_client
@@ -154,6 +174,8 @@ def ensure_running(self):
154174
main_kws['main_path'] = data['init_main_from_path']
155175
if 'sys_argv' in data:
156176
main_kws['sys_argv'] = data['sys_argv']
177+
if self._preload_on_error != 'ignore':
178+
main_kws['on_error'] = self._preload_on_error
157179

158180
with socket.socket(socket.AF_UNIX) as listener:
159181
address = connection.arbitrary_address('AF_UNIX')
@@ -198,8 +220,69 @@ def ensure_running(self):
198220
#
199221
#
200222

223+
def _handle_import_error(on_error, modinfo, exc, *, warn_stacklevel):
224+
"""Handle an import error according to the on_error policy."""
225+
match on_error:
226+
case 'fail':
227+
raise
228+
case 'warn':
229+
warnings.warn(
230+
f"Failed to preload {modinfo}: {exc}",
231+
ImportWarning,
232+
stacklevel=warn_stacklevel + 1
233+
)
234+
case 'ignore':
235+
pass
236+
237+
238+
def _handle_preload(preload, main_path=None, sys_path=None, sys_argv=None,
239+
on_error='ignore'):
240+
"""Handle module preloading with configurable error handling.
241+
242+
Args:
243+
preload: List of module names to preload.
244+
main_path: Path to __main__ module if '__main__' is in preload.
245+
sys_path: sys.path to use for imports (None means use current).
246+
sys_argv: sys.argv to use (None means use current).
247+
on_error: How to handle import errors ("ignore", "warn", or "fail").
248+
"""
249+
if not preload:
250+
return
251+
252+
if sys_argv is not None:
253+
sys.argv[:] = sys_argv
254+
if sys_path is not None:
255+
sys.path[:] = sys_path
256+
257+
if '__main__' in preload and main_path is not None:
258+
process.current_process()._inheriting = True
259+
try:
260+
spawn.import_main_path(main_path)
261+
except Exception as e:
262+
# Catch broad Exception because import_main_path() uses
263+
# runpy.run_path() which executes the script and can raise
264+
# any exception, not just ImportError
265+
_handle_import_error(
266+
on_error, f"__main__ from {main_path!r}", e, warn_stacklevel=2
267+
)
268+
finally:
269+
del process.current_process()._inheriting
270+
271+
for modname in preload:
272+
try:
273+
__import__(modname)
274+
except ImportError as e:
275+
_handle_import_error(
276+
on_error, f"module {modname!r}", e, warn_stacklevel=2
277+
)
278+
279+
# gh-135335: flush stdout/stderr in case any of the preloaded modules
280+
# wrote to them, otherwise children might inherit buffered data
281+
util._flush_std_streams()
282+
283+
201284
def main(listener_fd, alive_r, preload, main_path=None, sys_path=None,
202-
*, sys_argv=None, authkey_r=None):
285+
*, sys_argv=None, authkey_r=None, on_error='ignore'):
203286
"""Run forkserver."""
204287
if authkey_r is not None:
205288
try:
@@ -210,26 +293,7 @@ def main(listener_fd, alive_r, preload, main_path=None, sys_path=None,
210293
else:
211294
authkey = b''
212295

213-
if preload:
214-
if sys_argv is not None:
215-
sys.argv[:] = sys_argv
216-
if sys_path is not None:
217-
sys.path[:] = sys_path
218-
if '__main__' in preload and main_path is not None:
219-
process.current_process()._inheriting = True
220-
try:
221-
spawn.import_main_path(main_path)
222-
finally:
223-
del process.current_process()._inheriting
224-
for modname in preload:
225-
try:
226-
__import__(modname)
227-
except ImportError:
228-
pass
229-
230-
# gh-135335: flush stdout/stderr in case any of the preloaded modules
231-
# wrote to them, otherwise children might inherit buffered data
232-
util._flush_std_streams()
296+
_handle_preload(preload, main_path, sys_path, sys_argv, on_error)
233297

234298
util._close_stdin()
235299

0 commit comments

Comments
 (0)