diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 81b3043eb7e75bc..bd87bbe681ba2b5 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -5568,6 +5568,25 @@ def test_fork(self): else: assert_python_ok("-c", code, PYTHONMALLOC="malloc_debug") + def test_fork_does_not_warn_when_single_threaded(self): + # gh-157870: on macOS, Rosetta 2 adds a thread of its own to every + # translated x86-64 process; it must not count as a thread of ours. + code = """if 1: + import os, warnings + with warnings.catch_warnings(record=True) as ws: + warnings.simplefilter("always", DeprecationWarning) + pid = os.fork() + if pid == 0: + os._exit(0) # child + # Waiting allows an error in the child to hit stderr. + exitcode = os.wait()[1] + assert exitcode == 0, f"child exited {exitcode}" + assert not ws, [str(w.message) for w in ws] + """ + _, out, err = assert_python_ok("-c", code, PYTHONOPTIMIZE='0') + self.assertEqual(err.decode("utf-8"), "") + self.assertEqual(out.decode("utf-8"), "") + @unittest.skipUnless(sys.platform in ("linux", "android", "darwin"), "Only Linux and macOS detect this today.") @unittest.skipIf(_testcapi is None, "requires _testcapi") diff --git a/Misc/NEWS.d/next/macOS/2026-10-01-03-18-15.gh-issue-157870.WQ1hGY.rst b/Misc/NEWS.d/next/macOS/2026-10-01-03-18-15.gh-issue-157870.WQ1hGY.rst new file mode 100644 index 000000000000000..ff39a8aef66605c --- /dev/null +++ b/Misc/NEWS.d/next/macOS/2026-10-01-03-18-15.gh-issue-157870.WQ1hGY.rst @@ -0,0 +1,4 @@ +Fix a spurious :exc:`DeprecationWarning` about a multi-threaded process from +:func:`os.fork` and :func:`os.forkpty` when an x86-64 build of Python runs +under Rosetta 2 on Apple Silicon Macs. The thread that Rosetta adds to every +translated process is no longer counted. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 0a451b8a833e67e..58f793bd53f66c9 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -8565,6 +8565,23 @@ static Py_ssize_t get_number_of_os_threads(void) if (task_threads(macos_task, &macos_threads, &macos_n_threads) == KERN_SUCCESS) { num_python_threads = macos_n_threads; +#if defined(__x86_64__) + // Rosetta 2 runs a thread of its own in every translated + // process. It is not one of ours, so do not count it. + for (mach_msg_type_number_t i = 0; i < macos_n_threads; i++) { + thread_extended_info_data_t info; + mach_msg_type_number_t count = THREAD_EXTENDED_INFO_COUNT; + kern_return_t kr = thread_info( + macos_threads[i], THREAD_EXTENDED_INFO, + (thread_info_t)&info, &count); + if (kr == KERN_SUCCESS && + strncmp(info.pth_name, "com.apple.rosetta.exceptionserver", + sizeof(info.pth_name)) == 0) + { + num_python_threads--; + } + } +#endif } } #elif defined(__linux__)