diff --git a/.gitignore b/.gitignore index 7aa6272cf8e382..392b6ec93cc173 100644 --- a/.gitignore +++ b/.gitignore @@ -179,3 +179,8 @@ CLAUDE.local.md #### main branch only stuff below this line, things to backport go above. #### # main branch only: ABI files are not checked/maintained. Doc/data/python*.abi + +# Mypy symlinks (generated by Misc/mypy/make_symlinks.py) +Misc/mypy/_colorize.py +Misc/mypy/_pyrepl +Misc/mypy/token.py \ No newline at end of file diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py index a1259ff1d63d18..3724e147709310 100644 --- a/Lib/test/_test_multiprocessing.py +++ b/Lib/test/_test_multiprocessing.py @@ -39,7 +39,7 @@ from test.support import socket_helper from test.support import threading_helper from test.support import warnings_helper - +from test.support.script_helper import assert_python_failure, assert_python_ok # Skip tests if _multiprocessing wasn't built. _multiprocessing = import_helper.import_module('_multiprocessing') @@ -325,6 +325,7 @@ def test_current(self): self.assertEqual(current.ident, os.getpid()) self.assertEqual(current.exitcode, None) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_set_executable(self): if self.TYPE == 'threads': self.skipTest(f'test not appropriate for {self.TYPE}') @@ -341,6 +342,7 @@ def test_set_executable(self): p.join() self.assertEqual(p.exitcode, 0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_resource('cpu') def test_args_argument(self): # bpo-45735: Using list or tuple as *args* in constructor could @@ -388,6 +390,7 @@ def _test(cls, q, *args, **kwds): q.put(bytes(current.authkey)) q.put(current.pid) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_parent_process_attributes(self): if self.TYPE == "threads": self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -408,6 +411,7 @@ def _test_send_parent_process(cls, wconn): from multiprocessing.process import parent_process wconn.send([parent_process().pid, parent_process().name]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_parent_process(self): if self.TYPE == "threads": self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -446,6 +450,7 @@ def _test_report_parent_status(cls, wconn): parent_process().join(timeout=support.SHORT_TIMEOUT) wconn.send("alive" if parent_process().is_alive() else "not alive") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_process(self): q = self.Queue(1) e = self.Event() @@ -486,6 +491,7 @@ def test_process(self): self.assertNotIn(p, self.active_children()) close_queue(q) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(threading._HAVE_THREAD_NATIVE_ID, "needs native_id") def test_process_mainthread_native_id(self): if self.TYPE == 'threads': @@ -526,6 +532,7 @@ def _sleep_no_int_handler(cls, event): def _test_sleep(cls, delay): time.sleep(delay) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def _kill_process(self, meth, target=None): if self.TYPE == 'threads': self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -583,6 +590,7 @@ def handler(*args): return p.exitcode + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipIf(os.name == 'nt', "POSIX only") def test_interrupt(self): exitcode = self._kill_process(multiprocessing.Process.interrupt) @@ -591,15 +599,18 @@ def test_interrupt(self): # (KeyboardInterrupt in this case) # in multiprocessing.BaseProcess._bootstrap + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipIf(os.name == 'nt', "POSIX only") def test_interrupt_no_handler(self): exitcode = self._kill_process(multiprocessing.Process.interrupt, target=self._sleep_no_int_handler) self.assertEqual(exitcode, -signal.SIGINT) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_terminate(self): exitcode = self._kill_process(multiprocessing.Process.terminate) self.assertEqual(exitcode, -signal.SIGTERM) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_kill(self): exitcode = self._kill_process(multiprocessing.Process.kill) if os.name != 'nt': @@ -615,6 +626,7 @@ def test_cpu_count(self): self.assertIsInstance(cpus, int) self.assertGreaterEqual(cpus, 1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_active_children(self): self.assertEqual(type(self.active_children()), list) @@ -643,6 +655,7 @@ def _test_recursion(cls, wconn, id): p.start() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_recursion(self): rconn, wconn = self.Pipe(duplex=False) self._test_recursion(wconn, []) @@ -667,6 +680,7 @@ def test_recursion(self): def _test_sentinel(cls, event): event.wait(10.0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_sentinel(self): if self.TYPE == "threads": self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -689,6 +703,7 @@ def _test_close(cls, rc=0, q=None): q.get() sys.exit(rc) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_close(self): if self.TYPE == "threads": self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -721,6 +736,7 @@ def test_close(self): close_queue(q) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_resource('walltime') def test_many_processes(self): if self.TYPE == 'threads': @@ -757,6 +773,7 @@ def test_many_processes(self): for p in procs: self.assertIn(p.exitcode, exitcodes) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_lose_target_ref(self): c = DummyCallable() wr = weakref.ref(c) @@ -819,6 +836,7 @@ def func2(): threading.Thread(target=func1).start() threading.Thread(target=func2, daemon=True).start() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wait_for_threads(self): # A child process should wait for non-daemonic threads to end # before exiting @@ -843,6 +861,7 @@ def _test_error_on_stdio_flush(self, evt, break_std_streams={}): setattr(sys, stream_name, None) evt.set() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_error_on_stdio_flush_1(self): # Check that Process works with broken standard streams streams = [io.StringIO(), None] @@ -862,6 +881,7 @@ def test_error_on_stdio_flush_1(self): finally: setattr(sys, stream_name, old_stream) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_error_on_stdio_flush_2(self): # Same as test_error_on_stdio_flush_1(), but standard streams are # broken by the child process @@ -1012,6 +1032,7 @@ class _TestSubclassingProcess(BaseTestCase): ALLOWED_TYPES = ('processes',) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_subclassing(self): uppercaser = _UpperCaser() uppercaser.daemon = True @@ -1021,6 +1042,7 @@ def test_subclassing(self): uppercaser.stop() uppercaser.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_stderr_flush(self): # sys.stderr is flushed at process shutdown (issue #13812) if self.TYPE == "threads": @@ -1051,6 +1073,7 @@ def _test_sys_exit(cls, reason, testfn): sys.stderr = open(fd, 'w', encoding="utf-8", closefd=False) sys.exit(reason) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_sys_exit(self): # See Issue 13854 if self.TYPE == 'threads': @@ -1118,6 +1141,7 @@ def _test_put(cls, queue, child_can_start, parent_can_continue): queue.get() parent_can_continue.set() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_put(self): MAXSIZE = 6 queue = self.Queue(maxsize=MAXSIZE) @@ -1187,6 +1211,7 @@ def _test_get(cls, queue, child_can_start, parent_can_continue): queue.put(5) parent_can_continue.set() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_get(self): queue = self.Queue() child_can_start = self.Event() @@ -1248,6 +1273,7 @@ def _test_fork(cls, queue): # process cannot shutdown until the feeder thread has finished # pushing items onto the pipe. + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_fork(self): # Old versions of Queue would fail to create a new feeder # thread for a forked process if the original process had its @@ -1298,6 +1324,7 @@ def _test_task_done(cls, q): time.sleep(DELTA) q.task_done() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_task_done(self): queue = self.JoinableQueue() @@ -1341,6 +1368,7 @@ def test_no_import_lock_contention(self): self.fail("Probable regression on import lock contention;" " see Issue #22853") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_timeout(self): q = multiprocessing.Queue() start = time.monotonic() @@ -1464,6 +1492,7 @@ def _acquire_event(lock, event): event.set() time.sleep(1.0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_repr_lock(self): if self.TYPE != 'processes': self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -1527,6 +1556,7 @@ def _test_lock_locked_2processes(cls, lock, event, res): res.value = lock.locked() event.set() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes') def test_lock_locked_2processes(self): if self.TYPE != 'processes': @@ -1553,6 +1583,7 @@ def _acquire_release(lock, timeout, l=None, n=1): for _ in range(n): lock.release() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_repr_rlock(self): if self.TYPE != 'processes': self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -1612,6 +1643,7 @@ def test_rlock(self): self.assertFalse(lock.locked()) self.assertRaises((AssertionError, RuntimeError), lock.release) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes') def test_rlock_locked_2processes(self): if self.TYPE != 'processes': @@ -1723,6 +1755,7 @@ def check_invariant(self, cond): except NotImplementedError: pass + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_notify(self): cond = self.Condition() sleeping = self.Semaphore(0) @@ -1765,6 +1798,7 @@ def test_notify(self): threading_helper.join_thread(t) join_process(p) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_notify_all(self): cond = self.Condition() sleeping = self.Semaphore(0) @@ -1834,6 +1868,7 @@ def test_notify_all(self): # NOTE: join_process and join_thread are the same threading_helper.join_thread(w) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_notify_n(self): cond = self.Condition() sleeping = self.Semaphore(0) @@ -1907,6 +1942,7 @@ def _test_waitfor_f(cls, cond, state): if not result or state.value != 4: sys.exit(1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes') def test_waitfor(self): # based on test in test/lock_tests.py @@ -1942,6 +1978,7 @@ def _test_waitfor_timeout_f(cls, cond, state, success, sem): if not result and (expected - CLOCK_RES) <= dt: success.value = True + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_SHAREDCTYPES, 'needs sharedctypes') def test_waitfor_timeout(self): # based on test in test/lock_tests.py @@ -1974,6 +2011,7 @@ def _test_wait_result(cls, c, pid): if pid is not None: os.kill(pid, signal.SIGINT) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wait_result(self): if isinstance(self, ProcessesMixin) and sys.platform != 'win32': pid = os.getpid() @@ -2002,6 +2040,7 @@ def _test_event(cls, event): time.sleep(TIMEOUT2) event.set() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_event(self): event = self.Event() wait = TimingWrapper(event.wait) @@ -2198,6 +2237,7 @@ def multipass(cls, barrier, results, n): pass assert not barrier.broken + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_barrier(self, passes=1): """ Test that a barrier is passed in lockstep @@ -2205,6 +2245,7 @@ def test_barrier(self, passes=1): results = [self.DummyList(), self.DummyList()] self.run_threads(self.multipass, (self.barrier, results, passes)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_barrier_10(self): """ Test that a barrier works for 10 consecutive runs @@ -2216,6 +2257,7 @@ def _test_wait_return_f(cls, barrier, queue): res = barrier.wait() queue.put(res) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wait_return(self): """ test the return value from barrier.wait @@ -2232,6 +2274,7 @@ def _test_action_f(cls, barrier, results): if len(results) != 1: raise RuntimeError + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_action(self): """ Test the 'action' callback @@ -2254,6 +2297,7 @@ def _test_abort_f(cls, barrier, results1, results2): except RuntimeError: barrier.abort() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_abort(self): """ Test that an abort will put the barrier in a broken state @@ -2284,6 +2328,7 @@ def _test_reset_f(cls, barrier, results1, results2, results3): barrier.wait() results3.append(True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_reset(self): """ Test that a 'reset' on a barrier frees the waiting threads @@ -2319,6 +2364,7 @@ def _test_abort_and_reset_f(cls, barrier, barrier2, barrier.wait() results3.append(True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_abort_and_reset(self): """ Test that a barrier can be reset after being broken. @@ -2345,6 +2391,7 @@ def _test_timeout_f(cls, barrier, results): except threading.BrokenBarrierError: results.append(True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_timeout(self): """ Test wait(timeout) @@ -2364,6 +2411,7 @@ def _test_default_timeout_f(cls, barrier, results): except threading.BrokenBarrierError: results.append(True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_default_timeout(self): """ Test the barrier's default timeout @@ -2385,6 +2433,7 @@ def _test_thousand_f(cls, barrier, passes, conn, lock): with lock: conn.send(i) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_thousand(self): if self.TYPE == 'manager': self.skipTest('test not appropriate for {}'.format(self.TYPE)) @@ -2426,7 +2475,7 @@ def _test(cls, values): for sv, cv in zip(values, cls.codes_values): sv.value = cv[2] - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_value(self, raw=False): if raw: values = [self.RawValue(code, value) @@ -2490,6 +2539,7 @@ def f(cls, seq): for i in range(1, len(seq)): seq[i] += seq[i-1] + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipIf(c_int is None, "requires _ctypes") def test_array(self, raw=False): seq = [680, 626, 934, 821, 150, 233, 548, 982, 714, 831] @@ -2801,8 +2851,16 @@ class _TestPool(BaseTestCase): @classmethod def setUpClass(cls): - super().setUpClass() - cls.pool = cls.Pool(4) + # gh-135427 + # In some of the tests, a forked child forks another child of itself. In that case, using + # warnings_helper.ignore_warnings decorator does not actually ignore the warning from that + # child of child, and a warnings_helper.ignore_warnings exception is raised. + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', + message=".*fork.*may lead to deadlocks in the child.*", + category=DeprecationWarning) + super().setUpClass() + cls.pool = cls.Pool(4) @classmethod def tearDownClass(cls): @@ -2901,6 +2959,7 @@ def test_async(self): self.assertEqual(get(), 49) self.assertTimingAlmostEqual(get.elapsed, TIMEOUT1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_async_timeout(self): p = self.Pool(3) try: @@ -2998,6 +3057,7 @@ def test_imap_unordered_handle_iterable_exception(self): self.assertIn(value, expected_values) expected_values.remove(value) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_make_pool(self): expected_error = (RemoteError if self.TYPE == 'manager' else ValueError) @@ -3013,6 +3073,7 @@ def test_make_pool(self): p.close() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_terminate(self): # Simulate slow tasks which take "forever" to complete sleep_time = support.LONG_TIMEOUT @@ -3030,6 +3091,7 @@ def test_terminate(self): p.terminate() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_empty_iterable(self): # See Issue 12157 p = self.Pool(1) @@ -3042,6 +3104,7 @@ def test_empty_iterable(self): p.close() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_context(self): if self.TYPE == 'processes': L = list(range(10)) @@ -3056,6 +3119,7 @@ def test_context(self): def _test_traceback(cls): raise RuntimeError(123) # some comment + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_traceback(self): # We want ensure that the traceback from the child process is # contained in the traceback raised in the main process. @@ -3095,9 +3159,11 @@ def test_traceback(self): p.join() @classmethod + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def _test_wrapped_exception(cls): raise RuntimeError('foo') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wrapped_exception(self): # Issue #20980: Should not wrap exception when using thread pool with self.Pool(1) as p: @@ -3105,6 +3171,7 @@ def test_wrapped_exception(self): p.apply(self._test_wrapped_exception) p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_no_failfast(self): # Issue #23992: the fail-fast behaviour when an exception is raised # during map() would make Pool.join() deadlock, because a worker @@ -3140,6 +3207,7 @@ def test_release_task_refs(self): # they were released too. self.assertEqual(CountedObject.n_instances, 0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_enter(self): if self.TYPE == 'manager': self.skipTest("test not applicable to manager") @@ -3156,6 +3224,7 @@ def test_enter(self): pass pool.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_resource_warning(self): if self.TYPE == 'manager': self.skipTest("test not applicable to manager") @@ -3181,6 +3250,7 @@ def unpickleable_result(): class _TestPoolWorkerErrors(BaseTestCase): ALLOWED_TYPES = ('processes', ) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_async_error_callback(self): p = multiprocessing.Pool(2) @@ -3196,6 +3266,7 @@ def errback(exc): p.close() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_unpickleable_result(self): from multiprocessing.pool import MaybeEncodingError p = multiprocessing.Pool(2) @@ -3221,6 +3292,7 @@ def errback(exc): class _TestPoolWorkerLifetime(BaseTestCase): ALLOWED_TYPES = ('processes', ) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pool_worker_lifetime(self): p = multiprocessing.Pool(3, maxtasksperchild=10) self.assertEqual(3, len(p._pool)) @@ -3250,6 +3322,7 @@ def test_pool_worker_lifetime(self): p.close() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pool_worker_lifetime_early_close(self): # Issue #10332: closing a pool whose workers have limited lifetimes # before all the tasks completed would make join() hang. @@ -3323,6 +3396,7 @@ class _TestMyManager(BaseTestCase): ALLOWED_TYPES = ('manager',) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_mymanager(self): manager = MyManager(shutdown_timeout=SHUTDOWN_TIMEOUT) manager.start() @@ -3334,6 +3408,7 @@ def test_mymanager(self): # which happens on slow buildbots. self.assertIn(manager._process.exitcode, (0, -signal.SIGTERM)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_mymanager_context(self): manager = MyManager(shutdown_timeout=SHUTDOWN_TIMEOUT) with manager: @@ -3343,6 +3418,7 @@ def test_mymanager_context(self): # which happens on slow buildbots. self.assertIn(manager._process.exitcode, (0, -signal.SIGTERM)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_mymanager_context_prestarted(self): manager = MyManager(shutdown_timeout=SHUTDOWN_TIMEOUT) manager.start() @@ -3413,6 +3489,7 @@ def _putter(cls, address, authkey): # Note that xmlrpclib will deserialize object as a list not a tuple queue.put(tuple(cls.values)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_remote(self): authkey = os.urandom(32) @@ -3454,6 +3531,7 @@ def _putter(cls, address, authkey): queue = manager.get_queue() queue.put('hello world') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_rapid_restart(self): authkey = os.urandom(32) manager = QueueManager( @@ -3506,12 +3584,21 @@ def recv(self): class TestManagerExceptions(unittest.TestCase): # Issue 106558: Manager exceptions avoids creating cyclic references. def setUp(self): - self.mgr = multiprocessing.Manager() + # gh-135427 + # In some of the tests, a forked child forks another child of itself. In that case, using + # warnings_helper.ignore_warnings decorator does not actually ignore the warning from that + # child of child, and a warnings_helper.ignore_warnings exception is raised. + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', + message=".*fork.*may lead to deadlocks in the child.*", + category=DeprecationWarning) + self.mgr = multiprocessing.Manager() def tearDown(self): self.mgr.shutdown() self.mgr.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_queue_get(self): queue = self.mgr.Queue() if gc.isenabled(): @@ -3523,6 +3610,7 @@ def test_queue_get(self): wr = weakref.ref(e) self.assertEqual(wr(), None) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_dispatch(self): if gc.isenabled(): gc.disable() @@ -3549,6 +3637,7 @@ def _echo(cls, conn): conn.send_bytes(msg) conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_connection(self): conn, child_conn = self.Pipe() @@ -3641,6 +3730,7 @@ def test_duplex_false(self): self.assertRaises(OSError, writer.recv) self.assertRaises(OSError, writer.poll) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_spawn_close(self): # We test that a pipe connection can be closed by parent # process immediately after child is spawned. On Windows this @@ -3717,6 +3807,7 @@ def _writefd(cls, conn, data, create_dummy_fds=False): os.write(fd, data) os.close(fd) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_REDUCTION, "test needs multiprocessing.reduction") def test_fd_transfer(self): if self.TYPE != 'processes': @@ -3736,6 +3827,7 @@ def test_fd_transfer(self): with open(os_helper.TESTFN, "rb") as f: self.assertEqual(f.read(), b"foo") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_REDUCTION, "test needs multiprocessing.reduction") @unittest.skipIf(sys.platform == "win32", "test semantics don't make sense on Windows") @@ -3773,6 +3865,7 @@ def test_large_fd_transfer(self): def _send_data_without_fd(self, conn): os.write(conn.fileno(), b"\0") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(HAS_REDUCTION, "test needs multiprocessing.reduction") @unittest.skipIf(sys.platform == "win32", "doesn't make sense on Windows") def test_missing_fd_transfer(self): @@ -3872,6 +3965,7 @@ def _test(cls, address): conn.send('hello') conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_listener_client(self): for family in self.connection.families: l = self.connection.Listener(family=family) @@ -3883,6 +3977,7 @@ def test_listener_client(self): p.join() l.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_issue14725(self): l = self.connection.Listener() p = self.Process(target=self._test, args=(l.address,)) @@ -3928,6 +4023,7 @@ def _child_strings(cls, conn, strings): conn.send_bytes(s) conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_strings(self): strings = (b'hello', b'', b'a', b'b', b'', b'bye', b'', b'lop') a, b = self.Pipe() @@ -3951,6 +4047,7 @@ def _child_boundaries(cls, r): # read from it. r.poll(5) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_boundaries(self): r, w = self.Pipe(False) p = self.Process(target=self._child_boundaries, args=(r,)) @@ -3969,6 +4066,7 @@ def _child_dont_merge(cls, b): b.send_bytes(b'b') b.send_bytes(b'cd') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_dont_merge(self): a, b = self.Pipe() self.assertEqual(a.poll(0.0), False) @@ -4037,6 +4135,7 @@ def _remote(cls, conn): conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pickling(self): families = self.connection.families @@ -4095,6 +4194,7 @@ def child_access(cls, conn): conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_access(self): # On Windows, if we do not specify a destination pid when # using DupHandle then we need to be careful to use the @@ -4258,6 +4358,7 @@ def _double(cls, x, y, z, foo, arr, string): for i in range(len(arr)): arr[i] *= 2 + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_sharedctypes(self, lock=False): x = Value('i', 7, lock=lock) y = Value(c_double, 1.0/3.0, lock=lock) @@ -4519,6 +4620,7 @@ def test_shared_memory_pickle_unpickle_dead_object(self): with self.assertRaises(FileNotFoundError): pickle.loads(pickled_sms) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shared_memory_across_processes(self): # bpo-40135: don't define shared memory block's name in case of # the failure when we run multiprocessing tests in parallel. @@ -4547,6 +4649,7 @@ def test_shared_memory_across_processes(self): sms.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipIf(os.name != "posix", "not feasible in non-posix platforms") def test_shared_memory_SharedMemoryServer_ignores_sigint(self): # bpo-36368: protect SharedMemoryManager server process from @@ -4592,6 +4695,7 @@ def test_shared_memory_SharedMemoryManager_reuses_resource_tracker(self): # properly released sl. self.assertFalse(err) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shared_memory_SharedMemoryManager_basics(self): smm1 = multiprocessing.managers.SharedMemoryManager() with self.assertRaises(ValueError): @@ -4931,6 +5035,7 @@ class Foo(object): conn.close() os._exit(0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_finalize(self): conn, child_conn = self.Pipe() @@ -5058,6 +5163,7 @@ def _test_level(cls, conn): logger = multiprocessing.get_logger() conn.send(logger.getEffectiveLevel()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_level(self): LEVEL1 = 32 LEVEL2 = 37 @@ -5142,6 +5248,7 @@ def _killer(cls, pid): time.sleep(0.1) os.kill(pid, signal.SIGUSR1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1') def test_poll_eintr(self): got_signal = [False] @@ -5264,14 +5371,23 @@ def initializer(ns): @hashlib_helper.requires_hashdigest('sha256') class TestInitializers(unittest.TestCase): def setUp(self): - self.mgr = multiprocessing.Manager() - self.ns = self.mgr.Namespace() - self.ns.test = 0 + # gh-135427 + # In some of the tests, a forked child forks another child of itself. In that case, using + # warnings_helper.ignore_warnings decorator does not actually ignore the warning from that + # child of child, and a warnings_helper.ignore_warnings exception is raised. + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', + message=".*fork.*may lead to deadlocks in the child.*", + category=DeprecationWarning) + self.mgr = multiprocessing.Manager() + self.ns = self.mgr.Namespace() + self.ns.test = 0 def tearDown(self): self.mgr.shutdown() self.mgr.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_manager_initializer(self): m = multiprocessing.managers.SyncManager() self.assertRaises(TypeError, m.start, 1) @@ -5280,6 +5396,7 @@ def test_manager_initializer(self): m.shutdown() m.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pool_initializer(self): self.assertRaises(TypeError, multiprocessing.Pool, initializer=1) p = multiprocessing.Pool(1, initializer, (self.ns,)) @@ -5337,16 +5454,19 @@ def flush(self): class TestStdinBadfiledescriptor(unittest.TestCase): + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_queue_in_process(self): proc = multiprocessing.Process(target=_test_process) proc.start() proc.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pool_in_process(self): p = multiprocessing.Process(target=pool_in_process) p.start() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_flushing(self): sio = io.StringIO() flike = _file_like(sio) @@ -5366,6 +5486,7 @@ def _child_test_wait(cls, w, slow): w.send((i, os.getpid())) w.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wait(self, slow=False): from multiprocessing.connection import wait readers = [] @@ -5406,6 +5527,7 @@ def _child_test_wait_socket(cls, address, slow): s.sendall(('%s\n' % i).encode('ascii')) s.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_wait_socket(self, slow=False): from multiprocessing.connection import wait l = socket.create_server((socket_helper.HOST, 0)) @@ -5470,6 +5592,7 @@ def signal_and_sleep(cls, sem, period): sem.release() time.sleep(period) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_resource('walltime') def test_wait_integer(self): from multiprocessing.connection import wait @@ -5514,6 +5637,7 @@ def test_wait_integer(self): p.terminate() p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_neg_timeout(self): from multiprocessing.connection import wait a, b = multiprocessing.Pipe() @@ -5591,6 +5715,7 @@ def _test_timeout(cls, child, address): conn.send(456) conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_timeout(self): old_timeout = socket.getdefaulttimeout() try: @@ -5648,6 +5773,7 @@ def child(cls, n, conn): conn.send(len(util._afterfork_registry)) conn.close() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_lock(self): r, w = multiprocessing.Pipe(False) l = util.ForkAwareThreadLock() @@ -5699,6 +5825,7 @@ def _test_closefds(cls, conn, fd): s.close() conn.send(None) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_closefd(self): if not HAS_REDUCTION: raise unittest.SkipTest('requires fd pickling') @@ -5744,6 +5871,7 @@ def handler(signum, frame): conn.send(x) conn.send_bytes(b'x' * cls.CONN_MAX_SIZE) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1') def test_ignore(self): conn, child_conn = multiprocessing.Pipe() @@ -5777,6 +5905,7 @@ def handler(signum, frame): a = l.accept() a.send('welcome') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(hasattr(signal, 'SIGUSR1'), 'requires SIGUSR1') def test_ignore_listener(self): conn, child_conn = multiprocessing.Pipe() @@ -5811,6 +5940,7 @@ def check_context(self, ctx): p.join() self.assertEqual(child_method, ctx.get_start_method()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_context(self): for method in ('fork', 'spawn', 'forkserver'): try: @@ -5831,6 +5961,7 @@ def test_context_check_module_types(self): with self.assertRaisesRegex(TypeError, 'module_names must be a list of strings'): ctx.set_forkserver_preload([1, 2, 3]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_set_get(self): multiprocessing.set_forkserver_preload(PRELOAD) count = 0 @@ -5888,6 +6019,7 @@ def test_preload_resources(self): print(err) self.fail("failed spawning forkserver or grandchild") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipIf(sys.platform == "win32", "Only Spawn on windows so no risk of mixing") @only_run_in_spawn_testsuite("avoids redundant testing.") @@ -5921,6 +6053,7 @@ def _put_two_and_nest_once(cls, queue): process.start() process.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_nested_startmethod(self): # gh-108520: Regression test to ensure that child process can send its # arguments to another process @@ -6076,6 +6209,7 @@ def _is_resource_tracker_reused(conn, pid): reused &= _resource_tracker._check_alive() conn.send(reused) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_resource_tracker_reused(self): from multiprocessing.resource_tracker import _resource_tracker _resource_tracker.ensure_running() @@ -6177,6 +6311,7 @@ def test_empty_exceptions(self): with self.assertRaisesRegex(OSError, 'is closed'): q.empty() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_empty(self): queue = multiprocessing.SimpleQueue() child_can_start = multiprocessing.Event() @@ -6284,7 +6419,15 @@ def test_list(self): def setUp(self): self.manager = self.manager_class() - self.manager.start() + # gh-135427 + # In some of the tests, a forked child forks another child of itself. In that case, using + # warnings_helper.ignore_warnings decorator does not actually ignore the warning from that + # child of child, and a warnings_helper.ignore_warnings exception is raised. + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', + message=".*fork.*may lead to deadlocks in the child.*", + category=DeprecationWarning) + self.manager.start() self.proc = None def tearDown(self): @@ -6333,6 +6476,7 @@ def _test_event(cls, obj): obj.clear() obj.wait(0.001) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_event(self): o = self.manager.Event() o.set() @@ -6345,6 +6489,7 @@ def _test_lock(cls, obj): obj.acquire() obj.locked() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_lock(self, lname="Lock"): o = getattr(self.manager, lname)() self.run_worker(self._test_lock, o) @@ -6357,6 +6502,7 @@ def _test_rlock(cls, obj): obj.release() obj.locked() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_rlock(self, lname="RLock"): o = getattr(self.manager, lname)() self.run_worker(self._test_rlock, o) @@ -6365,6 +6511,7 @@ def test_rlock(self, lname="RLock"): def _test_semaphore(cls, obj): obj.acquire() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_semaphore(self, sname="Semaphore"): o = getattr(self.manager, sname)() self.run_worker(self._test_semaphore, o) @@ -6378,6 +6525,7 @@ def _test_condition(cls, obj): obj.acquire() obj.release() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_condition(self): o = self.manager.Condition() self.run_worker(self._test_condition, o) @@ -6387,6 +6535,7 @@ def _test_barrier(cls, obj): assert obj.parties == 5 obj.reset() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_barrier(self): o = self.manager.Barrier(5) self.run_worker(self._test_barrier, o) @@ -6397,6 +6546,7 @@ def _test_pool(cls, obj): with obj: pass + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_pool(self): o = self.manager.Pool(processes=4) self.run_worker(self._test_pool, o) @@ -6411,6 +6561,7 @@ def _test_queue(cls, obj): assert obj.get() == 6 assert obj.empty() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_queue(self, qname="Queue"): o = getattr(self.manager, qname)(2) o.put(5) @@ -6419,6 +6570,7 @@ def test_queue(self, qname="Queue"): assert o.empty() assert not o.full() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_joinable_queue(self): self.test_queue("JoinableQueue") @@ -6453,6 +6605,7 @@ def _test_list(cls, obj): obj.clear() case.assertEqual(len(obj), 0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_list(self): o = self.manager.list() o.append(5) @@ -6494,6 +6647,7 @@ def _test_dict(cls, obj): obj.clear() case.assertEqual(len(obj), 0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_dict(self): o = self.manager.dict() o['foo'] = 5 @@ -6508,6 +6662,7 @@ def _test_value(cls, obj): case.assertEqual(obj.get(), 1) obj.set(2) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_value(self): o = self.manager.Value('i', 1) self.run_worker(self._test_value, o) @@ -6522,6 +6677,7 @@ def _test_array(cls, obj): case.assertEqual(len(obj), 2) case.assertListEqual(list(obj), [0, 1]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_array(self): o = self.manager.Array('i', [0, 1]) self.run_worker(self._test_array, o) @@ -6532,6 +6688,7 @@ def _test_namespace(cls, obj): case.assertEqual(obj.x, 0) case.assertEqual(obj.y, 1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_namespace(self): o = self.manager.Namespace() o.x = 0 @@ -6651,6 +6808,7 @@ def _test_set_comparisons(cls, obj): case.assertGreater(obj, {'a'}) case.assertGreaterEqual(obj, {'a', 'b'}) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_set(self): o = self.manager.set() self.run_worker(self._test_set_operator_symbols, o) @@ -6668,6 +6826,7 @@ def test_set_init(self): self.assertSetEqual(o, {"a", "b", "c"}) self.assertRaises(RemoteError, self.manager.set, 1234) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_set_contain_all_method(self): o = self.manager.set() set_methods = { @@ -6721,6 +6880,7 @@ def exit_handler(): f.write("deadbeef") atexit.register(exit_handler) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_atexit(self): # gh-83856 with os_helper.temp_dir() as temp_dir: @@ -6873,6 +7033,7 @@ def f(x): return x*x self.assertEqual("332833500", out.decode('utf-8').strip()) self.assertFalse(err, msg=err.decode('utf-8')) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_forked_thread_not_started(self): # gh-134381: Ensure that a thread that has not been started yet in # the parent process can be started within a forked child process. @@ -6974,8 +7135,16 @@ def Pool(cls, *args, **kwds): @classmethod def setUpClass(cls): - super().setUpClass() - cls.manager = multiprocessing.Manager() + # gh-135427 + # In some of the tests, a forked child forks another child of itself. In that case, using + # warnings_helper.ignore_warnings decorator does not actually ignore the warning from that + # child of child, and a warnings_helper.ignore_warnings exception is raised. + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', + message=".*fork.*may lead to deadlocks in the child.*", + category=DeprecationWarning) + super().setUpClass() + cls.manager = multiprocessing.Manager() @classmethod def tearDownClass(cls): @@ -7142,3 +7311,62 @@ class SemLock(_multiprocessing.SemLock): name = f'test_semlock_subclass-{os.getpid()}' s = SemLock(1, 0, 10, name, False) _multiprocessing.sem_unlink(name) + + +@unittest.skipIf(sys.platform != "linux", "Linux only") +class ForkInThreads(unittest.TestCase): + + def test_fork(self): + code = """ + import os, sys, threading, time + + t = threading.Thread(target=time.sleep, args=(1,), daemon=True) + t.start() + + assert threading.active_count() == 2 + + pid = os.fork() + if pid < 0: + print("Fork failed") + elif pid == 0: + print("In child") + sys.exit(0) + print("In parent") + """ + + res = assert_python_ok("-c", code, PYTHONWARNINGS='always') + self.assertIn(b'In child', res.out) + self.assertIn(b'In parent', res.out) + self.assertIn(b'DeprecationWarning', res.err) + self.assertIn(b'is multi-threaded, use of fork() may lead to deadlocks in the child', res.err) + + res = assert_python_failure("-c", code, PYTHONWARNINGS='error') + self.assertIn(b'DeprecationWarning', res.err) + self.assertIn(b'is multi-threaded, use of fork() may lead to deadlocks in the child', res.err) + + def test_forkpty(self): + code = """ + import os, sys, threading, time + + t = threading.Thread(target=time.sleep, args=(1,), daemon=True) + t.start() + + assert threading.active_count() == 2 + + pid, _ = os.forkpty() + if pid < 0: + print(f"forkpty failed") + elif pid == 0: + print(f"In child") + sys.exit(0) + print(f"In parent") + """ + + res = assert_python_ok("-c", code, PYTHONWARNINGS='always') + self.assertIn(b'In parent', res.out) + self.assertIn(b'DeprecationWarning', res.err) + self.assertIn(b'is multi-threaded, use of forkpty() may lead to deadlocks in the child', res.err) + + res = assert_python_failure("-c", code, PYTHONWARNINGS='error') + self.assertIn(b'DeprecationWarning', res.err) + self.assertIn(b'is multi-threaded, use of forkpty() may lead to deadlocks in the child', res.err) diff --git a/Lib/test/support/warnings_helper.py b/Lib/test/support/warnings_helper.py index 5f6f14afd74a6e..c046f39fbff511 100644 --- a/Lib/test/support/warnings_helper.py +++ b/Lib/test/support/warnings_helper.py @@ -1,11 +1,11 @@ import contextlib -import functools import importlib import re import sys import warnings + def import_deprecated(name): """Import *name* while suppressing DeprecationWarning.""" with warnings.catch_warnings(): @@ -42,20 +42,32 @@ def check_syntax_warning(testcase, statement, errtext='', testcase.assertEqual(warns, []) -def ignore_warnings(*, category): +@contextlib.contextmanager +def ignore_warnings(*, category, message=''): """Decorator to suppress warnings. - Use of context managers to hide warnings make diffs - more noisy and tools like 'git blame' less useful. + Can also be used as a context manager. This is not preferred, + because it makes diffs more noisy and tools like 'git blame' less useful. + But, it's useful for async functions. """ - def decorator(test): - @functools.wraps(test) - def wrapper(self, *args, **kwargs): - with warnings.catch_warnings(): - warnings.simplefilter('ignore', category=category) - return test(self, *args, **kwargs) - return wrapper - return decorator + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', category=category, message=message) + yield + + +@contextlib.contextmanager +def ignore_fork_in_thread_deprecation_warnings(): + """Suppress deprecation warnings related to forking in multi-threaded code. + + See gh-135427 + + Can be used as decorator (preferred) or context manager. + """ + with ignore_warnings( + message=".*fork.*may lead to deadlocks in the child.*", + category=DeprecationWarning, + ): + yield class WarningsRecorder(object): diff --git a/Lib/test/test_asyncio/test_unix_events.py b/Lib/test/test_asyncio/test_unix_events.py index 22982dc9d8aefe..a69a5e32b1b2bd 100644 --- a/Lib/test/test_asyncio/test_unix_events.py +++ b/Lib/test/test_asyncio/test_unix_events.py @@ -15,7 +15,7 @@ from unittest import mock from test import support -from test.support import os_helper +from test.support import os_helper, warnings_helper from test.support import socket_helper from test.support import wait_process from test.support import hashlib_helper @@ -1183,29 +1183,31 @@ async def runner(): class TestFork(unittest.IsolatedAsyncioTestCase): async def test_fork_not_share_event_loop(self): - # The forked process should not share the event loop with the parent - loop = asyncio.get_running_loop() - r, w = os.pipe() - self.addCleanup(os.close, r) - self.addCleanup(os.close, w) - pid = os.fork() - if pid == 0: - # child - try: - loop = asyncio.get_event_loop() - os.write(w, b'LOOP:' + str(id(loop)).encode()) - except RuntimeError: - os.write(w, b'NO LOOP') - except BaseException as e: - os.write(w, b'ERROR:' + ascii(e).encode()) - finally: - os._exit(0) - else: - # parent - result = os.read(r, 100) - self.assertEqual(result, b'NO LOOP') - wait_process(pid, exitcode=0) - + with warnings_helper.ignore_fork_in_thread_deprecation_warnings(): + # The forked process should not share the event loop with the parent + loop = asyncio.get_running_loop() + r, w = os.pipe() + self.addCleanup(os.close, r) + self.addCleanup(os.close, w) + pid = os.fork() + if pid == 0: + # child + try: + loop = asyncio.get_event_loop() + os.write(w, b'LOOP:' + str(id(loop)).encode()) + except RuntimeError: + os.write(w, b'NO LOOP') + except BaseException as e: + os.write(w, b'ERROR:' + ascii(e).encode()) + finally: + os._exit(0) + else: + # parent + result = os.read(r, 100) + self.assertEqual(result, b'NO LOOP') + wait_process(pid, exitcode=0) + + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @hashlib_helper.requires_hashdigest('md5') @support.skip_if_sanitizer("TSAN doesn't support threads after fork", thread=True) def test_fork_signal_handling(self): @@ -1253,6 +1255,7 @@ async def func(): self.assertFalse(parent_handled.is_set()) self.assertTrue(child_handled.is_set()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @hashlib_helper.requires_hashdigest('md5') @support.skip_if_sanitizer("TSAN doesn't support threads after fork", thread=True) def test_fork_asyncio_run(self): @@ -1273,6 +1276,7 @@ async def child_main(): self.assertEqual(result.value, 42) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @hashlib_helper.requires_hashdigest('md5') @support.skip_if_sanitizer("TSAN doesn't support threads after fork", thread=True) def test_fork_asyncio_subprocess(self): diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py index 8830641f0abdc7..85cfe5c90f48af 100644 --- a/Lib/test/test_builtin.py +++ b/Lib/test/test_builtin.py @@ -31,6 +31,7 @@ from test import support from test.support import cpython_only, swap_attr from test.support import async_yield, run_yielding_async_fn +from test.support import warnings_helper from test.support.import_helper import import_module from test.support.os_helper import (EnvironmentVarGuard, TESTFN, unlink) from test.support.script_helper import assert_python_ok @@ -2545,6 +2546,7 @@ def run_child(self, child, terminal_input): finally: signal.signal(signal.SIGHUP, old_sighup) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def _run_child(self, child, terminal_input): r, w = os.pipe() # Pipe test results from child back to parent try: diff --git a/Lib/test/test_concurrent_futures/executor.py b/Lib/test/test_concurrent_futures/executor.py index 95bf8fcd25bf54..a37c4d45f07b17 100644 --- a/Lib/test/test_concurrent_futures/executor.py +++ b/Lib/test/test_concurrent_futures/executor.py @@ -5,7 +5,7 @@ from concurrent import futures from operator import add from test import support -from test.support import Py_GIL_DISABLED +from test.support import Py_GIL_DISABLED, warnings_helper def mul(x, y): @@ -43,10 +43,12 @@ class ExecutorTest: # Executor.shutdown() and context manager usage is tested by # ExecutorShutdownTest. + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_submit(self): future = self.executor.submit(pow, 2, 8) self.assertEqual(256, future.result()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_submit_keyword(self): future = self.executor.submit(mul, 2, y=8) self.assertEqual(16, future.result()) @@ -57,6 +59,7 @@ def test_submit_keyword(self): with self.assertRaises(TypeError): self.executor.submit(arg=1) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map(self): self.assertEqual( list(self.executor.map(pow, range(10), range(10))), @@ -66,6 +69,7 @@ def test_map(self): list(self.executor.map(pow, range(10), range(10), chunksize=3)), list(map(pow, range(10), range(10)))) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_exception(self): i = self.executor.map(divmod, [1, 1, 1, 1], [2, 3, 0, 5]) self.assertEqual(i.__next__(), (0, 1)) @@ -73,6 +77,7 @@ def test_map_exception(self): with self.assertRaises(ZeroDivisionError): i.__next__() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_resource('walltime') def test_map_timeout(self): results = [] @@ -108,6 +113,7 @@ def test_map_buffersize_value_validation(self): ): self.executor.map(str, range(4), buffersize=buffersize) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_buffersize(self): ints = range(4) for buffersize in (1, 2, len(ints), len(ints) * 2): @@ -115,6 +121,7 @@ def test_map_buffersize(self): res = self.executor.map(str, ints, buffersize=buffersize) self.assertListEqual(list(res), ["0", "1", "2", "3"]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_buffersize_on_multiple_iterables(self): ints = range(4) for buffersize in (1, 2, len(ints), len(ints) * 2): @@ -122,12 +129,14 @@ def test_map_buffersize_on_multiple_iterables(self): res = self.executor.map(add, ints, ints, buffersize=buffersize) self.assertListEqual(list(res), [0, 2, 4, 6]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_buffersize_on_infinite_iterable(self): res = self.executor.map(str, itertools.count(), buffersize=2) self.assertEqual(next(res, None), "0") self.assertEqual(next(res, None), "1") self.assertEqual(next(res, None), "2") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_buffersize_on_multiple_infinite_iterables(self): res = self.executor.map( add, @@ -147,6 +156,7 @@ def test_map_buffersize_without_iterable(self): res = self.executor.map(str, buffersize=2) self.assertIsNone(next(res, None)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_buffersize_when_buffer_is_full(self): ints = iter(range(4)) buffersize = 2 @@ -158,6 +168,7 @@ def test_map_buffersize_when_buffer_is_full(self): msg="should have fetched only `buffersize` elements from `ints`.", ) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shutdown_race_issue12456(self): # Issue #12456: race condition at shutdown where trying to post a # sentinel in the call queue blocks (the queue is full while processes @@ -165,6 +176,7 @@ def test_shutdown_race_issue12456(self): self.executor.map(str, [2] * (self.worker_count + 1)) self.executor.shutdown() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.cpython_only def test_no_stale_references(self): # Issue #16284: check that the executors don't unnecessarily hang onto @@ -209,6 +221,7 @@ def test_max_workers_negative(self): "than 0"): self.executor_type(max_workers=number) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_free_reference(self): # Issue #14406: Result iterator should not keep an internal # reference to result objects. @@ -221,6 +234,7 @@ def test_free_reference(self): if wr() is None: break + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_swallows_falsey_exceptions(self): # see gh-132063: Prevent exceptions that evaluate as falsey # from being ignored. diff --git a/Lib/test/test_concurrent_futures/test_as_completed.py b/Lib/test/test_concurrent_futures/test_as_completed.py index c90b0021d85fc7..31c7bb3ebd872c 100644 --- a/Lib/test/test_concurrent_futures/test_as_completed.py +++ b/Lib/test/test_concurrent_futures/test_as_completed.py @@ -7,6 +7,7 @@ CANCELLED_AND_NOTIFIED, FINISHED, Future) from test import support +from test.support import warnings_helper from .util import ( PENDING_FUTURE, RUNNING_FUTURE, @@ -19,6 +20,7 @@ def mul(x, y): class AsCompletedTests: + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_no_timeout(self): future1 = self.executor.submit(mul, 2, 21) future2 = self.executor.submit(mul, 7, 6) @@ -35,6 +37,7 @@ def test_no_timeout(self): future1, future2]), completed) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_future_times_out(self): """Test ``futures.as_completed`` timing out before completing it's final future.""" @@ -62,6 +65,7 @@ def test_future_times_out(self): # Check that ``future`` wasn't completed. self.assertEqual(completed_futures, already_completed) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_duplicate_futures(self): # Issue 20367. Duplicate futures should not raise exceptions or give # duplicate responses. diff --git a/Lib/test/test_concurrent_futures/test_deadlock.py b/Lib/test/test_concurrent_futures/test_deadlock.py index a465400509d411..5cd84f7e43043c 100644 --- a/Lib/test/test_concurrent_futures/test_deadlock.py +++ b/Lib/test/test_concurrent_futures/test_deadlock.py @@ -10,6 +10,7 @@ from concurrent.futures.process import BrokenProcessPool, _ThreadWakeup from test import support +from test.support import warnings_helper from .util import ( create_executor_tests, setup_module, @@ -111,6 +112,7 @@ def _fail_on_deadlock(self, executor): print(f"\nTraceback:\n {tb}", file=sys.__stderr__) self.fail(f"Executor deadlock:\n\n{tb}") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def _check_error(self, error, func, *args, ignore_stderr=False): # test for deadlock caused by crashes or exiting in a pool self.executor.shutdown(wait=True) @@ -199,6 +201,7 @@ def test_exit_during_result_unpickle_in_result_handler(self): # the result_handler thread self._check_error(BrokenProcessPool, _return_instance, ExitAtUnpickle) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.skip_if_sanitizer("UBSan: explicit SIGSEV not allowed", ub=True) def test_shutdown_deadlock(self): # Test that the pool calling shutdown do not cause deadlock @@ -212,6 +215,7 @@ def test_shutdown_deadlock(self): with self.assertRaises(BrokenProcessPool): f.result() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shutdown_deadlock_pickle(self): # Test that the pool calling shutdown with wait=False does not cause # a deadlock if a task fails at pickle after the shutdown call. @@ -238,6 +242,7 @@ def test_shutdown_deadlock_pickle(self): # dangling threads executor_manager.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.skip_if_sanitizer("UBSan: explicit SIGSEV not allowed", ub=True) def test_crash_big_data(self): # Test that there is a clean exception instead of a deadlock when a @@ -254,6 +259,7 @@ def test_crash_big_data(self): executor.shutdown(wait=True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_gh105829_should_not_deadlock_if_wakeup_pipe_full(self): # Issue #105829: The _ExecutorManagerThread wakeup pipe could # fill up and block. See: https://github.com/python/cpython/issues/105829 diff --git a/Lib/test/test_concurrent_futures/test_init.py b/Lib/test/test_concurrent_futures/test_init.py index 6b8484c0d5f197..5ea543bf748982 100644 --- a/Lib/test/test_concurrent_futures/test_init.py +++ b/Lib/test/test_concurrent_futures/test_init.py @@ -11,6 +11,7 @@ from logging.handlers import QueueHandler from test import support +from test.support import warnings_helper from .util import ExecutorMixin, create_executor_tests, setup_module @@ -48,6 +49,7 @@ def setUp(self): initargs=('initialized',)) super().setUp() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_initializer(self): futures = [self.executor.submit(get_init_status) for _ in range(self.worker_count)] @@ -74,6 +76,7 @@ def setUp(self): self.executor_kwargs = dict(initializer=init_fail) super().setUp() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_initializer(self): with self._assert_logged('ValueError: error in initializer'): try: diff --git a/Lib/test/test_concurrent_futures/test_process_pool.py b/Lib/test/test_concurrent_futures/test_process_pool.py index 3f13a1900a4ca4..9685f980119a0e 100644 --- a/Lib/test/test_concurrent_futures/test_process_pool.py +++ b/Lib/test/test_concurrent_futures/test_process_pool.py @@ -9,7 +9,7 @@ from concurrent.futures.process import BrokenProcessPool from test import support -from test.support import hashlib_helper +from test.support import hashlib_helper, warnings_helper from test.test_importlib.metadata.fixtures import parameterize from .executor import ExecutorTest, mul @@ -49,6 +49,7 @@ def test_max_workers_too_large(self): "max_workers must be <= 61"): futures.ProcessPoolExecutor(max_workers=62) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_killed_child(self): # When a child process is abruptly terminated, the whole pool gets # "broken". @@ -61,6 +62,7 @@ def test_killed_child(self): # Submitting other jobs fails as well. self.assertRaises(BrokenProcessPool, self.executor.submit, pow, 2, 8) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_map_chunksize(self): def bad_map(): list(self.executor.map(pow, range(40), range(40), chunksize=-1)) @@ -81,6 +83,7 @@ def bad_map(): def _test_traceback(cls): raise RuntimeError(123) # some comment + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_traceback(self): # We want ensure that the traceback from the child process is # contained in the traceback raised in the main process. @@ -103,6 +106,7 @@ def test_traceback(self): self.assertIn('raise RuntimeError(123) # some comment', f1.getvalue()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @hashlib_helper.requires_hashdigest('md5') def test_ressources_gced_in_workers(self): # Ensure that argument for a job are correctly gc-ed after the job @@ -123,6 +127,7 @@ def test_ressources_gced_in_workers(self): mgr.shutdown() mgr.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_saturation(self): executor = self.executor mp_context = self.get_context() @@ -208,6 +213,7 @@ def test_max_tasks_early_shutdown(self): for i, future in enumerate(futures): self.assertEqual(future.result(), mul(i, i)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_python_finalization_error(self): # gh-109047: Catch RuntimeError on thread creation # during Python finalization. @@ -258,6 +264,7 @@ def test_force_shutdown_workers_invalid_op(self): executor._force_shutdown, operation='invalid operation'), + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @parameterize(*FORCE_SHUTDOWN_PARAMS) def test_force_shutdown_workers(self, function_name): manager = self.get_context().Manager() diff --git a/Lib/test/test_concurrent_futures/test_shutdown.py b/Lib/test/test_concurrent_futures/test_shutdown.py index 99b315b47e2530..a05b2703aa147d 100644 --- a/Lib/test/test_concurrent_futures/test_shutdown.py +++ b/Lib/test/test_concurrent_futures/test_shutdown.py @@ -6,6 +6,7 @@ from concurrent import futures from test import support +from test.support import warnings_helper from test.support.script_helper import assert_python_ok from .util import ( @@ -77,12 +78,14 @@ def run_last(): self.assertIn("RuntimeError: cannot schedule new futures", err.decode()) self.assertEqual(out.strip(), b"runtime-error") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_hang_issue12364(self): fs = [self.executor.submit(time.sleep, 0.1) for _ in range(50)] self.executor.shutdown() for f in fs: f.result() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_cancel_futures(self): assert self.worker_count <= 5, "test needs few workers" fs = [self.executor.submit(time.sleep, .1) for _ in range(50)] @@ -128,6 +131,7 @@ def test_hang_gh83386(self): self.assertFalse(err) self.assertEqual(out.strip(), b"apple") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_hang_gh94440(self): """shutdown(wait=True) doesn't hang when a future was submitted and quickly canceled right before shutdown. @@ -171,6 +175,7 @@ def acquire_lock(lock): for t in self.executor._threads: t.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_context_manager_shutdown(self): with futures.ThreadPoolExecutor(max_workers=5) as e: executor = e @@ -180,6 +185,7 @@ def test_context_manager_shutdown(self): for t in executor._threads: t.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_del_shutdown(self): executor = futures.ThreadPoolExecutor(max_workers=5) res = executor.map(abs, range(-5, 5)) @@ -193,6 +199,7 @@ def test_del_shutdown(self): # executor got shutdown. assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shutdown_no_wait(self): # Ensure that the executor cleans up the threads when calling # shutdown with wait=False @@ -207,7 +214,7 @@ def test_shutdown_no_wait(self): # executor got shutdown. assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_thread_names_assigned(self): executor = futures.ThreadPoolExecutor( max_workers=5, thread_name_prefix='SpecialPool') @@ -220,6 +227,7 @@ def test_thread_names_assigned(self): self.assertRegex(t.name, r'^SpecialPool_[0-4]$') t.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_thread_names_default(self): executor = futures.ThreadPoolExecutor(max_workers=5) executor.map(abs, range(-5, 5)) @@ -253,6 +261,7 @@ def test_cancel_futures_wait_false(self): class ProcessPoolShutdownTest(ExecutorShutdownTest): + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_processes_terminate(self): def acquire_lock(lock): lock.acquire() @@ -276,6 +285,7 @@ def acquire_lock(lock): for p in processes.values(): p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_context_manager_shutdown(self): with futures.ProcessPoolExecutor( max_workers=5, mp_context=self.get_context()) as e: @@ -286,6 +296,7 @@ def test_context_manager_shutdown(self): for p in processes.values(): p.join() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_del_shutdown(self): executor = futures.ProcessPoolExecutor( max_workers=5, mp_context=self.get_context()) @@ -308,6 +319,7 @@ def test_del_shutdown(self): # executor got shutdown. assert all([r == abs(v) for r, v in zip(res, range(-5, 5))]) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_shutdown_no_wait(self): # Ensure that the executor cleans up the processes when calling # shutdown with wait=False diff --git a/Lib/test/test_concurrent_futures/test_wait.py b/Lib/test/test_concurrent_futures/test_wait.py index cc387883141b0e..b8250cec7ab3e7 100644 --- a/Lib/test/test_concurrent_futures/test_wait.py +++ b/Lib/test/test_concurrent_futures/test_wait.py @@ -3,7 +3,7 @@ import unittest from concurrent import futures from test import support -from test.support import threading_helper +from test.support import threading_helper, warnings_helper from .util import ( CANCELLED_FUTURE, CANCELLED_AND_NOTIFIED_FUTURE, EXCEPTION_FUTURE, @@ -22,6 +22,7 @@ def wait_and_raise(e): class WaitTests: + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_20369(self): # See https://bugs.python.org/issue20369 future = self.executor.submit(mul, 1, 2) @@ -30,7 +31,7 @@ def test_20369(self): self.assertEqual({future}, done) self.assertEqual(set(), not_done) - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_first_completed(self): event = self.create_event() future1 = self.executor.submit(mul, 21, 2) @@ -47,6 +48,7 @@ def test_first_completed(self): event.set() future2.result() # wait for job to finish + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_first_completed_some_already_completed(self): event = self.create_event() future1 = self.executor.submit(event.wait) @@ -64,6 +66,7 @@ def test_first_completed_some_already_completed(self): event.set() future1.result() # wait for job to finish + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_first_exception(self): event1 = self.create_event() event2 = self.create_event() @@ -93,6 +96,7 @@ def wait_for_future1(): event2.set() future3.result() # wait for job to finish + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_first_exception_some_already_complete(self): event = self.create_event() future1 = self.executor.submit(divmod, 21, 0) @@ -114,6 +118,7 @@ def test_first_exception_some_already_complete(self): event.set() future2.result() # wait for job to finish + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_first_exception_one_already_failed(self): event = self.create_event() future1 = self.executor.submit(event.wait) @@ -129,6 +134,7 @@ def test_first_exception_one_already_failed(self): event.set() future1.result() # wait for job to finish + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_all_completed(self): future1 = self.executor.submit(divmod, 2, 0) future2 = self.executor.submit(mul, 2, 21) @@ -148,6 +154,7 @@ def test_all_completed(self): future2]), finished) self.assertEqual(set(), pending) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_timeout(self): short_timeout = 0.050 diff --git a/Lib/test/test_concurrent_futures/util.py b/Lib/test/test_concurrent_futures/util.py index b12940414d9142..6e63aa1307cdc8 100644 --- a/Lib/test/test_concurrent_futures/util.py +++ b/Lib/test/test_concurrent_futures/util.py @@ -11,6 +11,7 @@ from test import support from test.support import threading_helper +import warnings def create_future(state=PENDING, exception=None, result=None): @@ -51,7 +52,15 @@ def setUp(self): max_workers=self.worker_count, mp_context=self.get_context(), **self.executor_kwargs) - self.manager = self.get_context().Manager() + # gh-135427 + # In some of the tests, a forked child forks another child of itself. In that case, using + # warnings_helper.ignore_warnings decorator does not actually ignore the warning from that + # child of child, and a warnings_helper.ignore_warnings exception is raised. + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', + message=".*fork.*may lead to deadlocks in the child.*", + category=DeprecationWarning) + self.manager = self.get_context().Manager() else: self.executor = self.executor_type( max_workers=self.worker_count, diff --git a/Lib/test/test_fork1.py b/Lib/test/test_fork1.py index a6523bbc518176..550faa8a174ec6 100644 --- a/Lib/test/test_fork1.py +++ b/Lib/test/test_fork1.py @@ -11,6 +11,7 @@ from test.fork_wait import ForkWait from test import support +from test.support import warnings_helper # Skip test if fork does not exist. @@ -19,6 +20,7 @@ class ForkTest(ForkWait): + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_threaded_import_lock_fork(self): """Check fork() in main thread works while a subthread is doing an import""" import_started = threading.Event() @@ -61,7 +63,7 @@ def importer(): except OSError: pass - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_nested_import_lock_fork(self): """Check fork() in main thread works while the main thread is doing an import""" exitcode = 42 diff --git a/Lib/test/test_kqueue.py b/Lib/test/test_kqueue.py index e94edcbc107ba9..d2ab45c4a5b1ea 100644 --- a/Lib/test/test_kqueue.py +++ b/Lib/test/test_kqueue.py @@ -9,6 +9,8 @@ import time import unittest +from test.support import warnings_helper + if not hasattr(select, "kqueue"): raise unittest.SkipTest("test works only on BSD") @@ -257,6 +259,7 @@ def test_fd_non_inheritable(self): self.addCleanup(kqueue.close) self.assertEqual(os.get_inheritable(kqueue.fileno()), False) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() def test_fork(self): # gh-110395: kqueue objects must be closed after fork diff --git a/Lib/test/test_logging.py b/Lib/test/test_logging.py index 275f7ce47d09b5..7a08f9ec9de9fb 100644 --- a/Lib/test/test_logging.py +++ b/Lib/test/test_logging.py @@ -730,6 +730,7 @@ def remove_loop(fname, tries): # based on os.fork existing because that is what users and this test use. # This helps ensure that when fork exists (the important concept) that the # register_at_fork mechanism is also present and used. + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() @threading_helper.requires_working_threading() @skip_if_asan_fork @@ -4045,6 +4046,7 @@ def test_config_queue_handler_invalid_config_does_not_create_multiprocessing_man self._apply_simple_queue_listener_configuration(qspec) manager.assert_not_called() + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @skip_if_tsan_fork @support.requires_subprocess() @unittest.skipUnless(support.Py_DEBUG, "requires a debug build for testing" @@ -4067,6 +4069,7 @@ def test_config_reject_simple_queue_handler_multiprocessing_context(self): with self.assertRaises(ValueError): self._apply_simple_queue_listener_configuration(qspec) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @skip_if_tsan_fork @support.requires_subprocess() @unittest.skipUnless(support.Py_DEBUG, "requires a debug build for testing" @@ -4107,6 +4110,7 @@ def _mpinit_issue121723(qspec, message_to_log): # log a message (this creates a record put in the queue) logging.getLogger().info(message_to_log) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @skip_if_tsan_fork @support.requires_subprocess() def test_multiprocessing_queues(self): @@ -5337,6 +5341,7 @@ def _extract_logrecord_process_name(key, logMultiprocessing, conn=None): else: return results + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @skip_if_tsan_fork def test_multiprocessing(self): support.skip_if_broken_multiprocessing_synchronize() diff --git a/Lib/test/test_mailbox.py b/Lib/test/test_mailbox.py index 0169948e453438..288b2c4496faa1 100644 --- a/Lib/test/test_mailbox.py +++ b/Lib/test/test_mailbox.py @@ -8,7 +8,7 @@ import io import tempfile from test import support -from test.support import import_helper +from test.support import import_helper, warnings_helper from test.support import os_helper from test.support import refleak_helper from test.support import socket_helper @@ -1212,6 +1212,7 @@ def test_add_and_close(self): self.assertEqual(contents, f.read()) self._box = self._factory(self._path) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() @unittest.skipUnless(hasattr(socket, 'socketpair'), "Test needs socketpair().") def test_lock_conflict(self): diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py index de3a17fe893170..9827a7f12ea21d 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -3518,6 +3518,7 @@ def test_getppid(self): self.assertEqual(error, b'') self.assertEqual(int(stdout), os.getpid()) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def check_waitpid(self, code, exitcode, callback=None): if sys.platform == 'win32': # On Windows, os.spawnv() simply joins arguments with spaces: @@ -3620,30 +3621,35 @@ def create_args(self, *, with_env=False, use_bytes=False): return program, args + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnl') def test_spawnl(self): program, args = self.create_args() exitcode = os.spawnl(os.P_WAIT, program, *args) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnle') def test_spawnle(self): program, args = self.create_args(with_env=True) exitcode = os.spawnle(os.P_WAIT, program, *args, self.env) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnlp') def test_spawnlp(self): program, args = self.create_args() exitcode = os.spawnlp(os.P_WAIT, program, *args) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnlpe') def test_spawnlpe(self): program, args = self.create_args(with_env=True) exitcode = os.spawnlpe(os.P_WAIT, program, *args, self.env) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnv') def test_spawnv(self): program, args = self.create_args() @@ -3654,30 +3660,35 @@ def test_spawnv(self): exitcode = os.spawnv(os.P_WAIT, FakePath(program), args) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnve') def test_spawnve(self): program, args = self.create_args(with_env=True) exitcode = os.spawnve(os.P_WAIT, program, args, self.env) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnvp') def test_spawnvp(self): program, args = self.create_args() exitcode = os.spawnvp(os.P_WAIT, program, args) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnvpe') def test_spawnvpe(self): program, args = self.create_args(with_env=True) exitcode = os.spawnvpe(os.P_WAIT, program, args, self.env) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnv') def test_nowait(self): program, args = self.create_args() pid = os.spawnv(os.P_NOWAIT, program, args) support.wait_process(pid, exitcode=self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnve') def test_spawnve_bytes(self): # Test bytes handling in parse_arglist and parse_envlist (#28114) @@ -3685,18 +3696,21 @@ def test_spawnve_bytes(self): exitcode = os.spawnve(os.P_WAIT, program, args, self.env) self.assertEqual(exitcode, self.exitcode) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnl') def test_spawnl_noargs(self): program, __ = self.create_args() self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, program) self.assertRaises(ValueError, os.spawnl, os.P_NOWAIT, program, '') + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnle') def test_spawnle_noargs(self): program, __ = self.create_args() self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, program, {}) self.assertRaises(ValueError, os.spawnle, os.P_NOWAIT, program, '', {}) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnv') def test_spawnv_noargs(self): program, __ = self.create_args() @@ -3705,6 +3719,7 @@ def test_spawnv_noargs(self): self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, ('',)) self.assertRaises(ValueError, os.spawnv, os.P_NOWAIT, program, ['']) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnve') def test_spawnve_noargs(self): program, __ = self.create_args() @@ -3761,10 +3776,12 @@ def _test_invalid_env(self, spawn): exitcode = spawn(os.P_WAIT, program, args, newenv) self.assertEqual(exitcode, 0) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnve') def test_spawnve_invalid_env(self): self._test_invalid_env(os.spawnve) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_os_func('spawnvpe') def test_spawnvpe_invalid_env(self): self._test_invalid_env(os.spawnvpe) @@ -4881,6 +4898,7 @@ def test_posix_pty_functions(self): self.addCleanup(os.close, son_fd) self.assertEqual(os.ptsname(mother_fd), os.ttyname(son_fd)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(hasattr(os, 'spawnl'), "need os.spawnl()") @support.requires_subprocess() def test_pipe_spawnl(self): diff --git a/Lib/test/test_platform.py b/Lib/test/test_platform.py index 479649053abc01..2e26134bae8323 100644 --- a/Lib/test/test_platform.py +++ b/Lib/test/test_platform.py @@ -11,7 +11,7 @@ from unittest import mock from test import support -from test.support import os_helper +from test.support import os_helper, warnings_helper try: # Some of the iOS tests need ctypes to operate. @@ -465,7 +465,7 @@ def test_mac_ver(self): else: self.assertEqual(res[2], 'PowerPC') - + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(sys.platform == 'darwin', "OSX only test") def test_mac_ver_with_fork(self): # Issue7895: platform.mac_ver() crashes when using fork without exec diff --git a/Lib/test/test_pty.py b/Lib/test/test_pty.py index ed4fe8a140879d..fbba7025ac4abf 100644 --- a/Lib/test/test_pty.py +++ b/Lib/test/test_pty.py @@ -1,6 +1,6 @@ import unittest from test.support import ( - is_android, is_apple_mobile, is_wasm32, reap_children, verbose + is_android, is_apple_mobile, is_wasm32, reap_children, verbose, warnings_helper ) from test.support.import_helper import import_module from test.support.os_helper import TESTFN, unlink @@ -194,6 +194,7 @@ def test_openpty(self): s2 = _readline(master_fd) self.assertEqual(b'For my pet fish, Eric.\n', normalize_output(s2)) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_fork(self): debug("calling pty.fork()") pid, master_fd = pty.fork() @@ -295,6 +296,7 @@ def test_master_read(self): self.assertEqual(data, b"") + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() def test_spawn_doesnt_hang(self): self.addCleanup(unlink, TESTFN) with open(TESTFN, 'wb') as f: diff --git a/Lib/test/test_random.py b/Lib/test/test_random.py index 0217ebd132b110..1e57b9244b4fd5 100644 --- a/Lib/test/test_random.py +++ b/Lib/test/test_random.py @@ -14,6 +14,8 @@ from fractions import Fraction from collections import abc, Counter +from test.support import warnings_helper + class MyIndex: def __init__(self, value): @@ -1399,6 +1401,7 @@ def test__all__(self): # tests validity but not completeness of the __all__ list self.assertTrue(set(random.__all__) <= set(dir(random))) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @test.support.requires_fork() def test_after_fork(self): # Test the global Random instance gets reseeded in child diff --git a/Lib/test/test_socketserver.py b/Lib/test/test_socketserver.py index 0f62f9eb200e42..893372cbbd0be9 100644 --- a/Lib/test/test_socketserver.py +++ b/Lib/test/test_socketserver.py @@ -17,6 +17,7 @@ from test.support import os_helper from test.support import socket_helper from test.support import threading_helper +from test.support import warnings_helper test.support.requires("network") @@ -43,6 +44,7 @@ def receive(sock, n, timeout=test.support.SHORT_TIMEOUT): raise RuntimeError("timed out on %r" % (sock,)) +@warnings_helper.ignore_fork_in_thread_deprecation_warnings() @test.support.requires_fork() @contextlib.contextmanager def simple_subprocess(testcase): @@ -173,6 +175,7 @@ def test_ThreadingTCPServer(self): socketserver.StreamRequestHandler, self.stream_examine) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_forking def test_ForkingTCPServer(self): with simple_subprocess(self): @@ -192,6 +195,7 @@ def test_ThreadingUnixStreamServer(self): socketserver.StreamRequestHandler, self.stream_examine) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_unix_sockets @requires_forking def test_ForkingUnixStreamServer(self): @@ -210,6 +214,7 @@ def test_ThreadingUDPServer(self): socketserver.DatagramRequestHandler, self.dgram_examine) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_forking def test_ForkingUDPServer(self): with simple_subprocess(self): @@ -229,6 +234,7 @@ def test_ThreadingUnixDatagramServer(self): socketserver.DatagramRequestHandler, self.dgram_examine) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_unix_sockets @requires_forking def test_ForkingUnixDatagramServer(self): @@ -314,11 +320,13 @@ def test_threading_not_handled(self): self.assertIs(cm.exc_type, SystemExit) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_forking def test_forking_handled(self): ForkingErrorTestServer(ValueError) self.check_result(handled=True) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @requires_forking def test_forking_not_handled(self): ForkingErrorTestServer(SystemExit) diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 9ec382afb65fe4..12361aa4e518a6 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -485,6 +485,7 @@ def test_check__all__(self): self.assertRaises(AssertionError, support.check__all__, self, unittest) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @unittest.skipUnless(hasattr(os, 'waitpid') and hasattr(os, 'WNOHANG'), 'need os.waitpid() and os.WNOHANG') @support.requires_fork() diff --git a/Lib/test/test_tracemalloc.py b/Lib/test/test_tracemalloc.py index 6dc6880ff8c356..9d3ff8a620b6f2 100644 --- a/Lib/test/test_tracemalloc.py +++ b/Lib/test/test_tracemalloc.py @@ -8,7 +8,7 @@ from test.support.script_helper import (assert_python_ok, assert_python_failure, interpreter_requires_environment) from test import support -from test.support import force_not_colorized +from test.support import force_not_colorized, warnings_helper from test.support import os_helper from test.support import threading_helper @@ -354,6 +354,7 @@ def fork_child(self): # everything is fine return 0 + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() def test_fork(self): # check that tracemalloc is still working after fork diff --git a/Lib/test/test_uuid.py b/Lib/test/test_uuid.py index 0e1a723ce3a151..edce504fc4ba65 100755 --- a/Lib/test/test_uuid.py +++ b/Lib/test/test_uuid.py @@ -13,7 +13,7 @@ from unittest import mock from test import support -from test.support import import_helper +from test.support import import_helper, warnings_helper from test.support.script_helper import assert_python_ok py_uuid = import_helper.import_fresh_module('uuid', blocked=['_uuid']) @@ -1112,6 +1112,7 @@ def test_uuid8_uniqueness(self): versions = {u.version for u in uuids} self.assertSetEqual(versions, {8}) + @warnings_helper.ignore_fork_in_thread_deprecation_warnings() @support.requires_fork() def testIssue8621(self): # On at least some versions of OSX self.uuid.uuid4 generates diff --git a/Misc/NEWS.d/next/Library/2025-07-19-11-53-19.gh-issue-135427.iJM_X2.rst b/Misc/NEWS.d/next/Library/2025-07-19-11-53-19.gh-issue-135427.iJM_X2.rst new file mode 100644 index 00000000000000..a14aa844c011e9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-07-19-11-53-19.gh-issue-135427.iJM_X2.rst @@ -0,0 +1,4 @@ +With :option:`-Werror <-W>`, the DeprecationWarning emitted by :py:func:`os.fork` +and :py:func:`os.forkpty` in mutli-threaded processes is now raised as an exception. +Previously it was silently ignored. +Patch by Rani Pinchuk. diff --git a/Misc/mypy/_colorize.py b/Misc/mypy/_colorize.py deleted file mode 120000 index 9b7304769ec30b..00000000000000 --- a/Misc/mypy/_colorize.py +++ /dev/null @@ -1 +0,0 @@ -../../Lib/_colorize.py \ No newline at end of file diff --git a/Misc/mypy/_pyrepl b/Misc/mypy/_pyrepl deleted file mode 120000 index bd7b69909663b6..00000000000000 --- a/Misc/mypy/_pyrepl +++ /dev/null @@ -1 +0,0 @@ -../../Lib/_pyrepl \ No newline at end of file diff --git a/Misc/mypy/token.py b/Misc/mypy/token.py deleted file mode 120000 index 0a39f726dda1aa..00000000000000 --- a/Misc/mypy/token.py +++ /dev/null @@ -1 +0,0 @@ -../../Lib/token.py \ No newline at end of file diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 77622fbc4e8065..de1c5d5b60ef35 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -7992,7 +7992,7 @@ os_register_at_fork_impl(PyObject *module, PyObject *before, // // This should only be called from the parent process after // PyOS_AfterFork_Parent(). -static void +static int warn_about_fork_with_threads(const char* name) { // It's not safe to issue the warning while the world is stopped, because @@ -8043,14 +8043,14 @@ warn_about_fork_with_threads(const char* name) PyObject *threading = PyImport_GetModule(&_Py_ID(threading)); if (!threading) { PyErr_Clear(); - return; + return 0; } PyObject *threading_active = PyObject_GetAttr(threading, &_Py_ID(_active)); if (!threading_active) { PyErr_Clear(); Py_DECREF(threading); - return; + return 0; } PyObject *threading_limbo = PyObject_GetAttr(threading, &_Py_ID(_limbo)); @@ -8058,7 +8058,7 @@ warn_about_fork_with_threads(const char* name) PyErr_Clear(); Py_DECREF(threading); Py_DECREF(threading_active); - return; + return 0; } Py_DECREF(threading); // Duplicating what threading.active_count() does but without holding @@ -8074,7 +8074,7 @@ warn_about_fork_with_threads(const char* name) Py_DECREF(threading_limbo); } if (num_python_threads > 1) { - PyErr_WarnFormat( + return PyErr_WarnFormat( PyExc_DeprecationWarning, 1, #ifdef HAVE_GETPID "This process (pid=%d) is multi-threaded, " @@ -8086,8 +8086,8 @@ warn_about_fork_with_threads(const char* name) getpid(), #endif name); - PyErr_Clear(); } + return 0; } #endif // HAVE_FORK1 || HAVE_FORKPTY || HAVE_FORK @@ -8126,7 +8126,9 @@ os_fork1_impl(PyObject *module) /* parent: release the import lock. */ PyOS_AfterFork_Parent(); // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. - warn_about_fork_with_threads("fork1"); + if (warn_about_fork_with_threads("fork1") < 0) { + return NULL; + } } if (pid == -1) { errno = saved_errno; @@ -8175,7 +8177,8 @@ os_fork_impl(PyObject *module) /* parent: release the import lock. */ PyOS_AfterFork_Parent(); // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. - warn_about_fork_with_threads("fork"); + if (warn_about_fork_with_threads("fork") < 0) + return NULL; } if (pid == -1) { errno = saved_errno; @@ -9030,7 +9033,8 @@ os_forkpty_impl(PyObject *module) /* parent: release the import lock. */ PyOS_AfterFork_Parent(); // After PyOS_AfterFork_Parent() starts the world to avoid deadlock. - warn_about_fork_with_threads("forkpty"); + if (warn_about_fork_with_threads("forkpty") < 0) + return NULL; } if (pid == -1) { return posix_error(); diff --git a/changed, 7314 insertions(+), 3271 deletions(-) b/changed, 7314 insertions(+), 3271 deletions(-) new file mode 100644 index 00000000000000..7412e0a5a2b01b --- /dev/null +++ b/changed, 7314 insertions(+), 3271 deletions(-) @@ -0,0 +1,227 @@ + .github/workflows/build.yml | 30 [32m+[m[31m-[m + .github/workflows/reusable-san.yml | 124 [32m+++++[m + .github/workflows/reusable-tsan.yml | 94 [31m----[m + .github/workflows/reusable-ubsan.yml | 74 [31m---[m + Android/android.py | 73 [32m+[m[31m--[m + Doc/Makefile | 1 [32m+[m + Doc/c-api/arg.rst | 15 [31m-[m + Doc/c-api/init.rst | 42 [32m+[m[31m-[m + Doc/c-api/memory.rst | 4 [32m+[m + Doc/c-api/perfmaps.rst | 9 [32m+[m[31m-[m + Doc/deprecations/pending-removal-in-3.15.rst | 2 [32m+[m[31m-[m + Doc/glossary.rst | 7 [32m+[m[31m-[m + Doc/howto/logging.rst | 8 [32m+[m[31m-[m + Doc/howto/perf_profiling.rst | 56 [32m++[m[31m-[m + Doc/library/annotationlib.rst | 2 [32m+[m[31m-[m + Doc/library/argparse.rst | 32 [32m+[m[31m-[m + Doc/library/ast.rst | 54 [32m++[m[31m-[m + Doc/library/dis.rst | 42 [32m++[m + Doc/library/enum.rst | 23 [32m+[m[31m-[m + Doc/library/fractions.rst | 4 [32m+[m[31m-[m + Doc/library/gc.rst | 8 [32m+[m[31m-[m + Doc/library/gzip.rst | 20 [32m+[m[31m-[m + Doc/library/http.client.rst | 22 [32m+[m[31m-[m + Doc/library/ipaddress.rst | 12 [32m+[m[31m-[m + Doc/library/multiprocessing.rst | 9 [32m+[m[31m-[m + Doc/library/os.path.rst | 36 [32m+[m[31m-[m + Doc/library/pathlib.rst | 14 [31m-[m + Doc/library/stdtypes.rst | 6 [32m+[m[31m-[m + Doc/library/string.rst | 25 [32m+[m[31m-[m + Doc/library/string.templatelib.rst | 313 [32m++++++++++++[m + Doc/library/sys.rst | 7 [32m+[m[31m-[m + Doc/library/tarfile.rst | 46 [32m+[m[31m-[m + Doc/library/text.rst | 1 [32m+[m + Doc/library/types.rst | 10 [32m+[m + Doc/library/urllib.request.rst | 14 [32m+[m[31m-[m + Doc/library/zipfile.rst | 8 [31m-[m + Doc/reference/compound_stmts.rst | 4 [32m+[m[31m-[m + Doc/reference/lexical_analysis.rst | 50 [32m+[m[31m-[m + Doc/tutorial/inputoutput.rst | 9 [32m+[m[31m-[m + Doc/tutorial/modules.rst | 4 [32m+[m[31m-[m + Doc/using/configure.rst | 3 [32m+[m + Doc/using/windows.rst | 362 [32m++++++++[m[31m------[m + Doc/whatsnew/3.13.rst | 2 [32m+[m[31m-[m + Doc/whatsnew/3.14.rst | 90 [32m+++[m[31m-[m + Doc/whatsnew/3.15.rst | 32 [32m+[m[31m-[m + Grammar/python.gram | 11 [32m+[m[31m-[m + Include/cpython/critical_section.h | 20 [32m+[m + Include/internal/pycore_critical_section.h | 14 [32m+[m[31m-[m + Include/internal/pycore_pylifecycle.h | 1 [32m+[m + Lib/_ast_unparse.py | 26 [32m+[m[31m-[m + Lib/_pyrepl/_minimal_curses.py | 68 [31m---[m + Lib/_pyrepl/curses.py | 33 [31m--[m + Lib/_pyrepl/terminfo.py | 488 [32m+++++++++++++++++++[m + Lib/_pyrepl/trace.py | 27 [32m+[m[31m-[m + Lib/_pyrepl/unix_console.py | 16 [32m+[m[31m-[m + Lib/_pyrepl/unix_eventqueue.py | 11 [32m+[m[31m-[m + Lib/_pyrepl/utils.py | 4 [32m+[m[31m-[m + Lib/argparse.py | 6 [32m+[m[31m-[m + Lib/concurrent/futures/interpreter.py | 2 [32m+[m + Lib/dataclasses.py | 17 [32m+[m[31m-[m + Lib/enum.py | 2 [32m+[m[31m-[m + Lib/gzip.py | 6 [32m+[m[31m-[m + Lib/hashlib.py | 32 [32m+[m[31m-[m + Lib/hmac.py | 14 [32m+[m + Lib/html/parser.py | 24 [32m+[m[31m-[m + Lib/http/client.py | 40 [32m+[m[31m-[m + Lib/multiprocessing/resource_tracker.py | 8 [32m+[m[31m-[m + Lib/pathlib/__init__.py | 12 [31m-[m + Lib/tarfile.py | 4 [32m+[m[31m-[m + Lib/test/_test_multiprocessing.py | 250 [32m+++++++++[m[31m-[m + Lib/test/datetimetester.py | 28 [32m++[m + Lib/test/pythoninfo.py | 1 [32m+[m + Lib/test/support/__init__.py | 5 [32m+[m[31m-[m + Lib/test/support/hashlib_helper.py | 711 [32m+++++++++++++++++[m[31m----------[m + Lib/test/support/warnings_helper.py | 37 [32m+[m[31m-[m + Lib/test/test_argparse.py | 23 [32m+[m[31m-[m + Lib/test/test_ast/test_ast.py | 7 [31m-[m + Lib/test/test_asyncio/test_unix_events.py | 6 [32m+[m[31m-[m + Lib/test/test_build_details.py | 13 [32m+[m[31m-[m + Lib/test/test_builtin.py | 2 [32m+[m + Lib/test/test_capi/test_emscripten.py | 25 [32m+[m + Lib/test/test_capi/test_getargs.py | 117 [31m-----[m + Lib/test/test_clinic.py | 1 [32m+[m + Lib/test/test_concurrent_futures/executor.py | 16 [32m+[m[31m-[m + Lib/test/test_concurrent_futures/test_as_completed.py | 4 [32m+[m + Lib/test/test_concurrent_futures/test_deadlock.py | 6 [32m+[m + Lib/test/test_concurrent_futures/test_init.py | 3 [32m+[m + Lib/test/test_concurrent_futures/test_interpreter_pool.py | 15 [32m+[m + Lib/test/test_concurrent_futures/test_process_pool.py | 9 [32m+[m[31m-[m + Lib/test/test_concurrent_futures/test_shutdown.py | 14 [32m+[m[31m-[m + Lib/test/test_concurrent_futures/test_wait.py | 11 [32m+[m[31m-[m + Lib/test/test_concurrent_futures/util.py | 11 [32m+[m[31m-[m + Lib/test/test_dataclasses/__init__.py | 35 [32m++[m + Lib/test/test_fcntl.py | 5 [32m+[m[31m-[m + Lib/test/test_fork1.py | 4 [32m+[m[31m-[m + Lib/test/test_free_threading/test_syslog.py | 44 [32m++[m + Lib/test/test_generated_cases.py | 2 [32m+[m[31m-[m + Lib/test/test_gettext.py | 7 [32m+[m + Lib/test/test_gzip.py | 2 [32m+[m[31m-[m + Lib/test/test_hashlib.py | 26 [32m+[m[31m-[m + Lib/test/test_hmac.py | 2 [32m+[m[31m-[m + Lib/test/test_htmlparser.py | 124 [32m++++[m[31m-[m + Lib/test/test_httplib.py | 46 [32m++[m + Lib/test/test_import/__init__.py | 4 [32m+[m[31m-[m + Lib/test/test_inspect/test_inspect.py | 1 [32m+[m + Lib/test/test_kqueue.py | 3 [32m+[m + Lib/test/test_logging.py | 5 [32m+[m + Lib/test/test_mailbox.py | 3 [32m+[m[31m-[m + Lib/test/test_mmap.py | 2 [32m+[m[31m-[m + Lib/test/test_os.py | 18 [32m+[m + Lib/test/test_pathlib/test_pathlib.py | 12 [32m+[m[31m-[m + Lib/test/test_perfmaps.py | 12 [32m+[m[31m-[m + Lib/test/test_platform.py | 4 [32m+[m[31m-[m + Lib/test/test_pty.py | 6 [32m+[m[31m-[m + Lib/test/test_pydoc/test_pydoc.py | 4 [32m+[m[31m-[m + Lib/test/test_pyrepl/__init__.py | 20 [32m+[m[31m-[m + Lib/test/test_pyrepl/test_eventqueue.py | 11 [32m+[m[31m-[m + Lib/test/test_pyrepl/test_pyrepl.py | 9 [32m+[m[31m-[m + Lib/test/test_pyrepl/test_reader.py | 12 [32m+[m[31m-[m + Lib/test/test_pyrepl/test_terminfo.py | 651 [32m+++++++++++++++++++++++++[m + Lib/test/test_pyrepl/test_unix_console.py | 40 [32m+[m[31m-[m + Lib/test/test_random.py | 3 [32m+[m + Lib/test/test_remote_pdb.py | 53 [32m+[m[31m-[m + Lib/test/test_samply_profiler.py | 244 [32m++++++++++[m + Lib/test/test_socketserver.py | 8 [32m+[m + Lib/test/test_support.py | 143 [32m++++[m[31m--[m + Lib/test/test_sysconfig.py | 2 [32m+[m[31m-[m + Lib/test/test_traceback.py | 19 [32m+[m + Lib/test/test_tracemalloc.py | 3 [32m+[m[31m-[m + Lib/test/test_tstring.py | 73 [32m+[m[31m--[m + Lib/test/test_types.py | 11 [32m+[m + Lib/test/test_unparse.py | 4 [31m-[m + Lib/test/test_urllib.py | 8 [32m+[m + Lib/test/test_uuid.py | 3 [32m+[m[31m-[m + Lib/test/test_venv.py | 4 [32m+[m[31m-[m + Lib/test/test_zipfile/test_core.py | 54 [31m---[m + Lib/tkinter/scrolledtext.py | 2 [32m+[m[31m-[m + Lib/traceback.py | 5 [32m+[m + Lib/types.py | 5 [32m+[m[31m-[m + Lib/urllib/request.py | 10 [32m+[m[31m-[m + Lib/xmlrpc/server.py | 2 [32m+[m[31m-[m + Lib/zipfile/__init__.py | 13 [31m-[m + Makefile.pre.in | 56 [32m++[m[31m-[m + Misc/ACKS | 2 [32m+[m + Misc/NEWS.d/3.11.0a1.rst | 2 [32m+[m[31m-[m + Misc/NEWS.d/3.13.0a4.rst | 2 [32m+[m[31m-[m + Misc/NEWS.d/3.14.0b1.rst | 6 [32m+[m[31m-[m + Misc/NEWS.d/next/Build/2025-07-18-17-15-00.gh-issue-135621.9cyCNb.rst | 2 [32m+[m + Misc/NEWS.d/next/C_API/2025-06-24-11-10-01.gh-issue-133296.lIEuVJ.rst | 3 [32m+[m + Misc/NEWS.d/next/C_API/2025-07-22-15-18-08.gh-issue-112068.4WvT-8.rst | 1 [32m+[m + Misc/NEWS.d/next/Core_and_Builtins/2025-07-08-23-22-08.gh-issue-132661.34ftJl.rst | 5 [32m+[m + Misc/NEWS.d/next/Core_and_Builtins/2025-07-09-11-15-42.gh-issue-136459.m4Udh8.rst | 3 [32m+[m + Misc/NEWS.d/next/Core_and_Builtins/2025-07-12-09-59-14.gh-issue-136421.ZD1rNj.rst | 1 [32m+[m + Misc/NEWS.d/next/Core_and_Builtins/2025-07-18-08-43-35.gh-issue-116738.i0HWtP.rst | 2 [32m+[m + Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-12-37-05.gh-issue-136801.XU_tF2.rst | 1 [32m+[m + Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-17-08-09.gh-issue-127598.Mx8S-y.rst | 2 [32m+[m + Misc/NEWS.d/next/Library/2025-03-19-12-41-42.gh-issue-91349.8eTOCP.rst | 3 [32m+[m + Misc/NEWS.d/next/Library/2025-05-11-11-39-05.gh-issue-133875.pUar3l.rst | 2 [32m+[m + Misc/NEWS.d/next/Library/2025-07-05-09-45-04.gh-issue-136286.N67Amr.rst | 2 [32m+[m[31m-[m + Misc/NEWS.d/next/Library/2025-07-10-00-47-37.gh-issue-136470.KlUEUG.rst | 2 [32m+[m + Misc/NEWS.d/next/Library/2025-07-11-10-23-44.gh-issue-136492.BVi5h0.rst | 1 [32m+[m + Misc/NEWS.d/next/Library/2025-07-19-11-53-19.gh-issue-135427.iJM_X2.rst | 1 [32m+[m + Misc/NEWS.d/next/Library/2025-07-19-15-40-47.gh-issue-131724.LS59nA.rst | 4 [32m+[m + Misc/NEWS.d/next/Library/2025-07-19-16-20-54.gh-issue-130645.O-dYcN.rst | 1 [32m+[m + Misc/NEWS.d/next/Library/2025-07-20-10-21-49.gh-issue-136787._0Rbp_.rst | 4 [32m+[m + Misc/NEWS.d/next/Library/2025-07-20-16-02-00.gh-issue-136874.cLC3o1.rst | 1 [32m+[m + Misc/NEWS.d/next/Library/2025-07-20-16-56-55.gh-issue-135228.n_XIao.rst | 4 [32m+[m + Misc/NEWS.d/next/Library/2025-07-21-16-10-24.gh-issue-124621.wyoWc1.rst | 1 [32m+[m + Misc/NEWS.d/next/Library/2025-07-21-22-35-50.gh-issue-136170.QUlc78.rst | 3 [32m+[m + Misc/NEWS.d/next/Security/2025-06-09-20-38-25.gh-issue-118350.KgWCcP.rst | 2 [32m+[m + Misc/NEWS.d/next/Security/2025-06-25-14-13-39.gh-issue-135661.idjQ0B.rst | 5 [31m-[m + Misc/NEWS.d/next/Tools-Demos/2025-06-11-12-14-06.gh-issue-135379.25ttXq.rst | 2 [32m+[m[31m-[m + Misc/NEWS.d/next/Tools-Demos/2025-07-05-15-10-42.gh-issue-136251.GRM6o8.rst | 1 [32m+[m + Modules/Setup.bootstrap.in | 2 [32m+[m + Modules/Setup.stdlib.in | 3 [31m-[m + Modules/_ctypes/_ctypes.c | 12 [32m+[m[31m-[m + Modules/_ctypes/ctypes.h | 2 [32m+[m[31m-[m + Modules/_datetimemodule.c | 165 [32m+++[m[31m----[m + Modules/_hashopenssl.c | 233 [32m++++++[m[31m---[m + Modules/_interpretersmodule.c | 9 [32m+[m[31m-[m + Modules/_json.c | 13 [32m+[m[31m-[m + Modules/_testcapimodule.c | 10 [32m+[m + Modules/_testinternalcapi.c | 34 [32m++[m + Modules/_threadmodule.c | 18 [32m+[m[31m-[m + Modules/_typesmodule.c | 1 [32m+[m + Modules/_zstd/_zstdmodule.c | 2 [32m+[m[31m-[m + Modules/_zstd/_zstdmodule.h | 2 [32m+[m[31m-[m + Modules/_zstd/buffer.h | 2 [32m+[m[31m-[m + Modules/_zstd/compressor.c | 2 [32m+[m[31m-[m + Modules/_zstd/decompressor.c | 2 [32m+[m[31m-[m + Modules/_zstd/zstddict.c | 2 [32m+[m[31m-[m + Modules/_zstd/zstddict.h | 2 [32m+[m[31m-[m + Modules/clinic/posixmodule.c.h | 80 [32m++[m[31m-[m + Modules/hashlib.h | 9 [32m+[m + Modules/hmacmodule.c | 2 [32m+[m[31m-[m + Modules/mmapmodule.c | 15 [32m+[m[31m-[m + Modules/posixmodule.c | 41 [32m+[m[31m-[m + Modules/syslogmodule.c | 8 [32m+[m[31m-[m + Objects/frameobject.c | 10 [32m+[m + Objects/templateobject.c | 91 [32m+[m[31m---[m + Objects/typeobject.c | 4 [32m+[m[31m-[m + Objects/unicodeobject.c | 15 [32m+[m[31m-[m + PCbuild/_freeze_module.vcxproj | 1 [32m+[m + Parser/action_helpers.c | 17 [32m+[m[31m-[m + Parser/parser.c | 2500 [32m++++++++++++++++++++++++++++++++++++++++++++++++++[m[31m--------------------------------------------[m + Parser/pegen.h | 1 [32m+[m + Python/asm_trampoline.S | 13 [32m+[m + Python/ast_unparse.c | 86 [32m+[m[31m---[m + Python/codegen.c | 10 [31m-[m + Python/critical_section.c | 18 [32m+[m + Python/emscripten_syscalls.c | 284 [32m+++++++++++[m + Python/getargs.c | 247 [32m++++[m[31m------[m + Python/instrumentation.c | 4 [32m+[m + Python/perf_jit_trampoline.c | 35 [32m+[m[31m-[m + Python/pylifecycle.c | 5 [32m+[m + Python/pystate.c | 9 [32m+[m + Tools/cases_generator/optimizer_generator.py | 6 [32m+[m[31m-[m + Tools/wasm/README.md | 10 [32m+[m[31m-[m + Tools/wasm/emscripten/__main__.py | 25 [32m+[m[31m-[m + Tools/wasm/{ => emscripten}/config.site-wasm32-emscripten | 3 [32m+[m[31m-[m + Tools/wasm/emscripten/{web_example => }/wasm_assets.py | 3 [32m+[m[31m-[m + Tools/wasm/emscripten/web_example/{python.html => index.html} | 362 [32m++++++++++++[m[31m--[m + Tools/wasm/emscripten/web_example_pyrepl_jspi/index.html | 34 [32m++[m + Tools/wasm/emscripten/web_example_pyrepl_jspi/src.mjs | 194 [32m++++++++[m + configure | 6 [32m+[m[31m-[m + configure.ac | 7 [32m+[m[31m-[m + 226 files changed, 7314 insertions(+), 3271 deletions(-)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: