Skip to content

Commit a59b02e

Browse files
authored
Merge branch 'python:main' into patch-1
2 parents 8c5a948 + efadc58 commit a59b02e

File tree

12 files changed

+65
-215
lines changed

12 files changed

+65
-215
lines changed

Doc/library/concurrent.futures.rst

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -415,30 +415,6 @@ to a :class:`ProcessPoolExecutor` will result in deadlock.
415415
require the *fork* start method for :class:`ProcessPoolExecutor` you must
416416
explicitly pass ``mp_context=multiprocessing.get_context("fork")``.
417417

418-
.. method:: terminate_workers()
419-
420-
Attempt to terminate all living worker processes immediately by calling
421-
:meth:`Process.terminate <multiprocessing.Process.terminate>` on each of them.
422-
Internally, it will also call :meth:`Executor.shutdown` to ensure that all
423-
other resources associated with the executor are freed.
424-
425-
After calling this method the caller should no longer submit tasks to the
426-
executor.
427-
428-
.. versionadded:: next
429-
430-
.. method:: kill_workers()
431-
432-
Attempt to kill all living worker processes immediately by calling
433-
:meth:`Process.kill <multiprocessing.Process.kill>` on each of them.
434-
Internally, it will also call :meth:`Executor.shutdown` to ensure that all
435-
other resources associated with the executor are freed.
436-
437-
After calling this method the caller should no longer submit tasks to the
438-
executor.
439-
440-
.. versionadded:: next
441-
442418
.. _processpoolexecutor-example:
443419

444420
ProcessPoolExecutor Example

Doc/library/typing.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2551,15 +2551,20 @@ types.
25512551

25522552
This functional syntax allows defining keys which are not valid
25532553
:ref:`identifiers <identifiers>`, for example because they are
2554-
keywords or contain hyphens::
2554+
keywords or contain hyphens, or when key names must not be
2555+
:ref:`mangled <private-name-mangling>` like regular private names::
25552556

25562557
# raises SyntaxError
25572558
class Point2D(TypedDict):
25582559
in: int # 'in' is a keyword
25592560
x-y: int # name with hyphens
25602561

2562+
class Definition(TypedDict):
2563+
__schema: str # mangled to `_Definition__schema`
2564+
25612565
# OK, functional syntax
25622566
Point2D = TypedDict('Point2D', {'in': int, 'x-y': int})
2567+
Definition = TypedDict('Definition', {'__schema': str}) # not mangled
25632568

25642569
By default, all keys must be present in a ``TypedDict``. It is possible to
25652570
mark individual keys as non-required using :data:`NotRequired`::

Doc/whatsnew/3.14.rst

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -444,11 +444,6 @@ contextvars
444444
* Support context manager protocol by :class:`contextvars.Token`.
445445
(Contributed by Andrew Svetlov in :gh:`129889`.)
446446

447-
* Add :meth:`concurrent.futures.ProcessPoolExecutor.terminate_workers` and
448-
:meth:`concurrent.futures.ProcessPoolExecutor.kill_workers` as
449-
ways to terminate or kill all living worker processes in the given pool.
450-
(Contributed by Charles Machalow in :gh:`128043`.)
451-
452447

453448
ctypes
454449
------

Lib/concurrent/futures/process.py

Lines changed: 0 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -626,14 +626,6 @@ class BrokenProcessPool(_base.BrokenExecutor):
626626
while a future was in the running state.
627627
"""
628628

629-
_TERMINATE = "terminate"
630-
_KILL = "kill"
631-
632-
_SHUTDOWN_CALLBACK_OPERATION = {
633-
_TERMINATE,
634-
_KILL
635-
}
636-
637629

638630
class ProcessPoolExecutor(_base.Executor):
639631
def __init__(self, max_workers=None, mp_context=None,
@@ -863,66 +855,3 @@ def shutdown(self, wait=True, *, cancel_futures=False):
863855
self._executor_manager_thread_wakeup = None
864856

865857
shutdown.__doc__ = _base.Executor.shutdown.__doc__
866-
867-
def _force_shutdown(self, operation):
868-
"""Attempts to terminate or kill the executor's workers based off the
869-
given operation. Iterates through all of the current processes and
870-
performs the relevant task if the process is still alive.
871-
872-
After terminating workers, the pool will be in a broken state
873-
and no longer usable (for instance, new tasks should not be
874-
submitted).
875-
"""
876-
if operation not in _SHUTDOWN_CALLBACK_OPERATION:
877-
raise ValueError(f"Unsupported operation: {operation!r}")
878-
879-
processes = {}
880-
if self._processes:
881-
processes = self._processes.copy()
882-
883-
# shutdown will invalidate ._processes, so we copy it right before
884-
# calling. If we waited here, we would deadlock if a process decides not
885-
# to exit.
886-
self.shutdown(wait=False, cancel_futures=True)
887-
888-
if not processes:
889-
return
890-
891-
for proc in processes.values():
892-
try:
893-
if not proc.is_alive():
894-
continue
895-
except ValueError:
896-
# The process is already exited/closed out.
897-
continue
898-
899-
try:
900-
if operation == _TERMINATE:
901-
proc.terminate()
902-
elif operation == _KILL:
903-
proc.kill()
904-
except ProcessLookupError:
905-
# The process just ended before our signal
906-
continue
907-
908-
def terminate_workers(self):
909-
"""Attempts to terminate the executor's workers.
910-
Iterates through all of the current worker processes and terminates
911-
each one that is still alive.
912-
913-
After terminating workers, the pool will be in a broken state
914-
and no longer usable (for instance, new tasks should not be
915-
submitted).
916-
"""
917-
return self._force_shutdown(operation=_TERMINATE)
918-
919-
def kill_workers(self):
920-
"""Attempts to kill the executor's workers.
921-
Iterates through all of the current worker processes and kills
922-
each one that is still alive.
923-
924-
After killing workers, the pool will be in a broken state
925-
and no longer usable (for instance, new tasks should not be
926-
submitted).
927-
"""
928-
return self._force_shutdown(operation=_KILL)

Lib/dataclasses.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,10 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen,
11631163
try:
11641164
# In some cases fetching a signature is not possible.
11651165
# But, we surely should not fail in this case.
1166-
text_sig = str(inspect.signature(cls)).replace(' -> None', '')
1166+
text_sig = str(inspect.signature(
1167+
cls,
1168+
annotation_format=annotationlib.Format.FORWARDREF,
1169+
)).replace(' -> None', '')
11671170
except (TypeError, ValueError):
11681171
text_sig = ''
11691172
cls.__doc__ = (cls.__name__ + text_sig)

Lib/inspect.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@
143143

144144

145145
import abc
146-
from annotationlib import Format
146+
from annotationlib import Format, ForwardRef
147147
from annotationlib import get_annotations # re-exported
148148
import ast
149149
import dis
@@ -1342,6 +1342,8 @@ def repl(match):
13421342
if annotation.__module__ in ('builtins', base_module):
13431343
return annotation.__qualname__
13441344
return annotation.__module__+'.'+annotation.__qualname__
1345+
if isinstance(annotation, ForwardRef):
1346+
return annotation.__forward_arg__
13451347
return repr(annotation)
13461348

13471349
def formatannotationrelativeto(object):

Lib/test/test_concurrent_futures/test_process_pool.py

Lines changed: 0 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import os
2-
import queue
3-
import signal
42
import sys
53
import threading
64
import time
75
import unittest
8-
import unittest.mock
96
from concurrent import futures
107
from concurrent.futures.process import BrokenProcessPool
118

129
from test import support
1310
from test.support import hashlib_helper
14-
from test.test_importlib.metadata.fixtures import parameterize
1511

1612
from .executor import ExecutorTest, mul
1713
from .util import (
@@ -26,19 +22,6 @@ def __init__(self, mgr):
2622
def __del__(self):
2723
self.event.set()
2824

29-
TERMINATE_WORKERS = futures.ProcessPoolExecutor.terminate_workers.__name__
30-
KILL_WORKERS = futures.ProcessPoolExecutor.kill_workers.__name__
31-
FORCE_SHUTDOWN_PARAMS = [
32-
dict(function_name=TERMINATE_WORKERS),
33-
dict(function_name=KILL_WORKERS),
34-
]
35-
36-
def _put_sleep_put(queue):
37-
""" Used as part of test_terminate_workers """
38-
queue.put('started')
39-
time.sleep(2)
40-
queue.put('finished')
41-
4225

4326
class ProcessPoolExecutorTest(ExecutorTest):
4427

@@ -235,86 +218,6 @@ def mock_start_new_thread(func, *args, **kwargs):
235218
list(executor.map(mul, [(2, 3)] * 10))
236219
executor.shutdown()
237220

238-
def test_terminate_workers(self):
239-
mock_fn = unittest.mock.Mock()
240-
with self.executor_type(max_workers=1) as executor:
241-
executor._force_shutdown = mock_fn
242-
executor.terminate_workers()
243-
244-
mock_fn.assert_called_once_with(operation=futures.process._TERMINATE)
245-
246-
def test_kill_workers(self):
247-
mock_fn = unittest.mock.Mock()
248-
with self.executor_type(max_workers=1) as executor:
249-
executor._force_shutdown = mock_fn
250-
executor.kill_workers()
251-
252-
mock_fn.assert_called_once_with(operation=futures.process._KILL)
253-
254-
def test_force_shutdown_workers_invalid_op(self):
255-
with self.executor_type(max_workers=1) as executor:
256-
self.assertRaises(ValueError,
257-
executor._force_shutdown,
258-
operation='invalid operation'),
259-
260-
@parameterize(*FORCE_SHUTDOWN_PARAMS)
261-
def test_force_shutdown_workers(self, function_name):
262-
manager = self.get_context().Manager()
263-
q = manager.Queue()
264-
265-
with self.executor_type(max_workers=1) as executor:
266-
executor.submit(_put_sleep_put, q)
267-
268-
# We should get started, but not finished since we'll terminate the
269-
# workers just after
270-
self.assertEqual(q.get(timeout=5), 'started')
271-
272-
worker_process = list(executor._processes.values())[0]
273-
getattr(executor, function_name)()
274-
worker_process.join()
275-
276-
if function_name == TERMINATE_WORKERS or \
277-
sys.platform == 'win32':
278-
# On windows, kill and terminate both send SIGTERM
279-
self.assertEqual(worker_process.exitcode, -signal.SIGTERM)
280-
elif function_name == KILL_WORKERS:
281-
self.assertEqual(worker_process.exitcode, -signal.SIGKILL)
282-
else:
283-
self.fail(f"Unknown operation: {function_name}")
284-
285-
self.assertRaises(queue.Empty, q.get, timeout=1)
286-
287-
@parameterize(*FORCE_SHUTDOWN_PARAMS)
288-
def test_force_shutdown_workers_dead_workers(self, function_name):
289-
with self.executor_type(max_workers=1) as executor:
290-
future = executor.submit(os._exit, 1)
291-
self.assertRaises(BrokenProcessPool, future.result)
292-
293-
# even though the pool is broken, this shouldn't raise
294-
getattr(executor, function_name)()
295-
296-
@parameterize(*FORCE_SHUTDOWN_PARAMS)
297-
def test_force_shutdown_workers_not_started_yet(self, function_name):
298-
ctx = self.get_context()
299-
with unittest.mock.patch.object(ctx, 'Process') as mock_process:
300-
with self.executor_type(max_workers=1, mp_context=ctx) as executor:
301-
# The worker has not been started yet, terminate/kill_workers
302-
# should basically no-op
303-
getattr(executor, function_name)()
304-
305-
mock_process.return_value.kill.assert_not_called()
306-
mock_process.return_value.terminate.assert_not_called()
307-
308-
@parameterize(*FORCE_SHUTDOWN_PARAMS)
309-
def test_force_shutdown_workers_stops_pool(self, function_name):
310-
with self.executor_type(max_workers=1) as executor:
311-
task = executor.submit(time.sleep, 0)
312-
self.assertIsNone(task.result())
313-
314-
getattr(executor, function_name)()
315-
316-
self.assertRaises(RuntimeError, executor.submit, time.sleep, 0)
317-
318221

319222
create_executor_tests(globals(), ProcessPoolExecutorTest,
320223
executor_mixins=(ProcessPoolForkMixin,

Lib/test/test_dataclasses/__init__.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import types
1313
import weakref
1414
import traceback
15+
import textwrap
1516
import unittest
1617
from unittest.mock import Mock
1718
from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional, Protocol, DefaultDict
@@ -2343,6 +2344,31 @@ class C:
23432344

23442345
self.assertDocStrEqual(C.__doc__, "C(x:collections.deque=<factory>)")
23452346

2347+
def test_docstring_undefined_name(self):
2348+
@dataclass
2349+
class C:
2350+
x: undef
2351+
2352+
self.assertDocStrEqual(C.__doc__, "C(x:undef)")
2353+
2354+
def test_docstring_with_unsolvable_forward_ref_in_init(self):
2355+
# See: https://github.com/python/cpython/issues/128184
2356+
ns = {}
2357+
exec(
2358+
textwrap.dedent(
2359+
"""
2360+
from dataclasses import dataclass
2361+
2362+
@dataclass
2363+
class C:
2364+
def __init__(self, x: X, num: int) -> None: ...
2365+
""",
2366+
),
2367+
ns,
2368+
)
2369+
2370+
self.assertDocStrEqual(ns['C'].__doc__, "C(x:X,num:int)")
2371+
23462372
def test_docstring_with_no_signature(self):
23472373
# See https://github.com/python/cpython/issues/103449
23482374
class Meta(type):

Lib/test/test_inspect/test_inspect.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1753,6 +1753,10 @@ def test_typing_replacement(self):
17531753
self.assertEqual(inspect.formatannotation(ann), 'Union[List[str], int]')
17541754
self.assertEqual(inspect.formatannotation(ann1), 'Union[List[testModule.typing.A], int]')
17551755

1756+
def test_forwardref(self):
1757+
fwdref = ForwardRef('fwdref')
1758+
self.assertEqual(inspect.formatannotation(fwdref), 'fwdref')
1759+
17561760

17571761
class TestIsMethodDescriptor(unittest.TestCase):
17581762

@@ -4587,6 +4591,11 @@ def foo(a: list[str]) -> Tuple[str, float]:
45874591
self.assertEqual(str(inspect.signature(foo)),
45884592
inspect.signature(foo).format())
45894593

4594+
def foo(x: undef):
4595+
pass
4596+
sig = inspect.signature(foo, annotation_format=Format.FORWARDREF)
4597+
self.assertEqual(str(sig), '(x: undef)')
4598+
45904599
def test_signature_str_positional_only(self):
45914600
P = inspect.Parameter
45924601
S = inspect.Signature

0 commit comments

Comments
 (0)