From 2018de6474b53b867e3de070a5ce447228b7220d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 3 Sep 2026 14:10:53 -0500 Subject: [PATCH 1/2] PYTHON-5874 Fix test_fork.py failures on Python 3.15 due to fork() DeprecationWarning Python 3.12 emits a DeprecationWarning when os.fork() runs in a multi-threaded process. The fork tests intentionally fork with the client's background threads running, so the warning is expected; suppress it at the fork site so the error-level warning filter does not fail the tests. Removes the 3.15 skipIf stopgap added in PYTHON-5837. --- test/__init__.py | 12 +++++++++++- test/asynchronous/__init__.py | 12 +++++++++++- test/test_fork.py | 6 ++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index 7aff0a4620..2de2681ac8 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -950,7 +950,17 @@ def _target() -> None: ctx = multiprocessing.get_context("fork") proc = ctx.Process(target=_target) - proc.start() + # Python 3.12+ warns when os.fork() runs in a multi-threaded process. The + # warning is a general thread-count heuristic; benign here because the only + # extra threads are pymongo's, whose locks register_at_fork resets in the + # child. Suppressed only in this helper, not globally (PYTHON-5874). + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=r".*use of fork\(\) may lead to deadlocks.*", + category=DeprecationWarning, + ) + proc.start() try: yield proc # type: ignore finally: diff --git a/test/asynchronous/__init__.py b/test/asynchronous/__init__.py index 4ecaa589cf..bf5c1df8b2 100644 --- a/test/asynchronous/__init__.py +++ b/test/asynchronous/__init__.py @@ -950,7 +950,17 @@ def _target() -> None: ctx = multiprocessing.get_context("fork") proc = ctx.Process(target=_target) - proc.start() + # Python 3.12+ warns when os.fork() runs in a multi-threaded process. The + # warning is a general thread-count heuristic; benign here because the only + # extra threads are pymongo's, whose locks register_at_fork resets in the + # child. Suppressed only in this helper, not globally (PYTHON-5874). + with warnings.catch_warnings(): + warnings.filterwarnings( + "ignore", + message=r".*use of fork\(\) may lead to deadlocks.*", + category=DeprecationWarning, + ) + proc.start() try: yield proc # type: ignore finally: diff --git a/test/test_fork.py b/test/test_fork.py index 4b33dc76dc..e8a551d058 100644 --- a/test/test_fork.py +++ b/test/test_fork.py @@ -29,6 +29,8 @@ from test.utils_shared import is_greenthread_patched +# self.fork suppresses the Python 3.12+ fork() DeprecationWarning; it is +# benign for these intentional forks (PYTHON-5874). @unittest.skipIf( not hasattr(os, "register_at_fork"), "register_at_fork not available in this version of Python" ) @@ -36,10 +38,6 @@ is_greenthread_patched(), "gevent does not support POSIX-style forking.", ) -@unittest.skipIf( - sys.version_info >= (3, 15), - "fork() in multi-threaded processes is deprecated in Python 3.15+ (PYTHON-5874)", -) class TestFork(IntegrationTest): def test_lock_client(self): # Forks the client with some items locked. From 6c9882029fac77d943cf841adfea5301b2713fd6 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 3 Sep 2026 18:43:31 -0500 Subject: [PATCH 2/2] PYTHON-5874 Reword fork() DeprecationWarning comment Clarify that pymongo's locks are reset via register_at_fork in the child. --- test/__init__.py | 4 ++-- test/asynchronous/__init__.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index 2de2681ac8..eb588abec9 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -952,8 +952,8 @@ def _target() -> None: proc = ctx.Process(target=_target) # Python 3.12+ warns when os.fork() runs in a multi-threaded process. The # warning is a general thread-count heuristic; benign here because the only - # extra threads are pymongo's, whose locks register_at_fork resets in the - # child. Suppressed only in this helper, not globally (PYTHON-5874). + # extra threads are pymongo's, whose locks are reset via register_at_fork + # in the child. Suppressed only in this helper, not globally (PYTHON-5874). with warnings.catch_warnings(): warnings.filterwarnings( "ignore", diff --git a/test/asynchronous/__init__.py b/test/asynchronous/__init__.py index bf5c1df8b2..d31e653705 100644 --- a/test/asynchronous/__init__.py +++ b/test/asynchronous/__init__.py @@ -952,8 +952,8 @@ def _target() -> None: proc = ctx.Process(target=_target) # Python 3.12+ warns when os.fork() runs in a multi-threaded process. The # warning is a general thread-count heuristic; benign here because the only - # extra threads are pymongo's, whose locks register_at_fork resets in the - # child. Suppressed only in this helper, not globally (PYTHON-5874). + # extra threads are pymongo's, whose locks are reset via register_at_fork + # in the child. Suppressed only in this helper, not globally (PYTHON-5874). with warnings.catch_warnings(): warnings.filterwarnings( "ignore",