From 52bb7399638388d7b91d2db7808d23ce51c32e5e Mon Sep 17 00:00:00 2001 From: Jib Date: Thu, 25 Jan 2024 17:01:51 -0500 Subject: [PATCH 1/8] RuntimeError fix: Check for explicit runtime error message --- pymongo/periodic_executor.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pymongo/periodic_executor.py b/pymongo/periodic_executor.py index 25d519187f..4c07a14913 100644 --- a/pymongo/periodic_executor.py +++ b/pymongo/periodic_executor.py @@ -91,7 +91,14 @@ def open(self) -> None: thread.daemon = True self._thread = weakref.proxy(thread) _register_executor(self) - thread.start() + try: + thread.start() + except RuntimeError as e: + if str(e) == "can't create new thread at interpreter shutdown": + # Result of change + self._thread = None + return + raise def close(self, dummy: Any = None) -> None: """Stop. To restart, call open(). From 8e91c9e22c51f5e64da174c86ea97cb60476388f Mon Sep 17 00:00:00 2001 From: Jib Date: Tue, 30 Jan 2024 12:18:51 -0500 Subject: [PATCH 2/8] Added test that error fires for anything above version 3.12 --- pymongo/periodic_executor.py | 7 +++++-- test/test_monitor.py | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/pymongo/periodic_executor.py b/pymongo/periodic_executor.py index 4c07a14913..255ad2b23b 100644 --- a/pymongo/periodic_executor.py +++ b/pymongo/periodic_executor.py @@ -23,6 +23,8 @@ from pymongo.lock import _create_lock +_THREAD_START_ON_SHUTDOWN_ERR = "can't create new thread at interpreter shutdown" + class PeriodicExecutor: def __init__( @@ -91,11 +93,12 @@ def open(self) -> None: thread.daemon = True self._thread = weakref.proxy(thread) _register_executor(self) + # Mitigation to RuntimeError firing when thread starts on shutdown + # https://github.com/python/cpython/issues/114570 try: thread.start() except RuntimeError as e: - if str(e) == "can't create new thread at interpreter shutdown": - # Result of change + if str(e) == _THREAD_START_ON_SHUTDOWN_ERR: self._thread = None return raise diff --git a/test/test_monitor.py b/test/test_monitor.py index 0495a8cbc7..6dfdd51356 100644 --- a/test/test_monitor.py +++ b/test/test_monitor.py @@ -16,6 +16,7 @@ from __future__ import annotations import gc +import subprocess import sys from functools import partial @@ -79,6 +80,17 @@ def test_cleanup_executors_on_client_close(self): for executor in executors: wait_until(lambda: executor._stopped, f"closed executor: {executor._name}", timeout=5) + @unittest.skipIf(sys.version_info[:2] >= (3, 12), reason="Python version must be (>=3.12)") + def test_no_thread_start_runtime_err_on_shutdown(self): + """Test we silence noisy runtime errors fired when the MongoClient spawns a new thread + on process shutdown.""" + command = ["python", "-c", "'from pymongo import MongoClient; c = MongoClient()'"] + completed_process: subprocess.CompletedProcess = subprocess.run( + " ".join(command), shell=True, capture_output=True + ) + + assert not completed_process.stderr + if __name__ == "__main__": unittest.main() From ea7a37fd9625ebf15ace463f4ae8601e8cd6cc48 Mon Sep 17 00:00:00 2001 From: Jib Date: Wed, 31 Jan 2024 13:24:33 -0500 Subject: [PATCH 3/8] Leverage assertFalse; additional check for is_finalizing --- pymongo/periodic_executor.py | 3 ++- test/test_monitor.py | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pymongo/periodic_executor.py b/pymongo/periodic_executor.py index 255ad2b23b..75f58e1741 100644 --- a/pymongo/periodic_executor.py +++ b/pymongo/periodic_executor.py @@ -16,6 +16,7 @@ from __future__ import annotations +import sys import threading import time import weakref @@ -98,7 +99,7 @@ def open(self) -> None: try: thread.start() except RuntimeError as e: - if str(e) == _THREAD_START_ON_SHUTDOWN_ERR: + if _THREAD_START_ON_SHUTDOWN_ERR in str(e) or sys.is_finalizing(): self._thread = None return raise diff --git a/test/test_monitor.py b/test/test_monitor.py index 6dfdd51356..8ccec7fd0b 100644 --- a/test/test_monitor.py +++ b/test/test_monitor.py @@ -80,16 +80,16 @@ def test_cleanup_executors_on_client_close(self): for executor in executors: wait_until(lambda: executor._stopped, f"closed executor: {executor._name}", timeout=5) - @unittest.skipIf(sys.version_info[:2] >= (3, 12), reason="Python version must be (>=3.12)") def test_no_thread_start_runtime_err_on_shutdown(self): """Test we silence noisy runtime errors fired when the MongoClient spawns a new thread on process shutdown.""" - command = ["python", "-c", "'from pymongo import MongoClient; c = MongoClient()'"] + command = [sys.executable, "-c", "'from pymongo import MongoClient; c = MongoClient()'"] completed_process: subprocess.CompletedProcess = subprocess.run( " ".join(command), shell=True, capture_output=True ) - assert not completed_process.stderr + self.assertFalse(completed_process.stderr) + self.assertFalse(completed_process.stdout) if __name__ == "__main__": From 4eb02121e12a421452edf885c0a00b538fe4aa75 Mon Sep 17 00:00:00 2001 From: Jib Date: Wed, 31 Jan 2024 15:40:56 -0500 Subject: [PATCH 4/8] set to 'intepreter shutdown' --- pymongo/periodic_executor.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pymongo/periodic_executor.py b/pymongo/periodic_executor.py index 75f58e1741..9e9ead61fc 100644 --- a/pymongo/periodic_executor.py +++ b/pymongo/periodic_executor.py @@ -24,8 +24,6 @@ from pymongo.lock import _create_lock -_THREAD_START_ON_SHUTDOWN_ERR = "can't create new thread at interpreter shutdown" - class PeriodicExecutor: def __init__( @@ -99,7 +97,7 @@ def open(self) -> None: try: thread.start() except RuntimeError as e: - if _THREAD_START_ON_SHUTDOWN_ERR in str(e) or sys.is_finalizing(): + if "interpreter shutdown" in str(e) or sys.is_finalizing(): self._thread = None return raise From c139c8b50f0b81aeeec636ff9139432dba1348b3 Mon Sep 17 00:00:00 2001 From: Jib Date: Thu, 1 Feb 2024 15:39:22 -0500 Subject: [PATCH 5/8] updated the changelog.rst language --- doc/changelog.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/changelog.rst b/doc/changelog.rst index c5fd8758dd..009de6c183 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -28,6 +28,8 @@ PyMongo 4.7 brings a number of improvements including: >>> orjson.dumps({'a': Int64(1)}, default=json_util.default, option=orjson.OPT_PASSTHROUGH_SUBCLASS) .. _orjson: https://github.com/ijl/orjson +- Fixed a bug appearing in Python 3.12 where :attr:`RuntimeError: can't create new thread at interpreter shutdown` + fires when a MongoClient's :class:`PeriodicExecutor` thread starts as the python interpreter is shutting down. Changes in Version 4.6.1 ------------------------ From deaf3ee0cababe5e4f018f981695459b5eaf41a7 Mon Sep 17 00:00:00 2001 From: Jib Date: Thu, 1 Feb 2024 17:39:04 -0500 Subject: [PATCH 6/8] Update doc/changelog.rst --- doc/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 009de6c183..8e5ea08d8c 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -29,7 +29,7 @@ PyMongo 4.7 brings a number of improvements including: .. _orjson: https://github.com/ijl/orjson - Fixed a bug appearing in Python 3.12 where :attr:`RuntimeError: can't create new thread at interpreter shutdown` - fires when a MongoClient's :class:`PeriodicExecutor` thread starts as the python interpreter is shutting down. + could be written to stderr when a MongoClient's :class:`PeriodicExecutor` thread starts as the python interpreter is shutting down. Changes in Version 4.6.1 ------------------------ From 40a9fecf45827a40fe19a6b7871ed417199c48bd Mon Sep 17 00:00:00 2001 From: Jib Date: Fri, 2 Feb 2024 10:31:04 -0500 Subject: [PATCH 7/8] fix D000 lint-manual error --- doc/changelog.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 009de6c183..da28bf4ca3 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -28,8 +28,9 @@ PyMongo 4.7 brings a number of improvements including: >>> orjson.dumps({'a': Int64(1)}, default=json_util.default, option=orjson.OPT_PASSTHROUGH_SUBCLASS) .. _orjson: https://github.com/ijl/orjson -- Fixed a bug appearing in Python 3.12 where :attr:`RuntimeError: can't create new thread at interpreter shutdown` - fires when a MongoClient's :class:`PeriodicExecutor` thread starts as the python interpreter is shutting down. + +- Fixed a bug appearing in Python 3.12 where "RuntimeError: can't create new thread at interpreter shutdown" + fires when a MongoClient's thread starts as the python interpreter is shutting down. Changes in Version 4.6.1 ------------------------ From f6b3fcf31d9bad35a1386086d15eb1755be4f22b Mon Sep 17 00:00:00 2001 From: Jib Date: Fri, 2 Feb 2024 10:34:30 -0500 Subject: [PATCH 8/8] fixed merge conflicts --- doc/changelog.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 8e5ea08d8c..3feae4c2a4 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -28,8 +28,9 @@ PyMongo 4.7 brings a number of improvements including: >>> orjson.dumps({'a': Int64(1)}, default=json_util.default, option=orjson.OPT_PASSTHROUGH_SUBCLASS) .. _orjson: https://github.com/ijl/orjson -- Fixed a bug appearing in Python 3.12 where :attr:`RuntimeError: can't create new thread at interpreter shutdown` - could be written to stderr when a MongoClient's :class:`PeriodicExecutor` thread starts as the python interpreter is shutting down. + +- Fixed a bug appearing in Python 3.12 where "RuntimeError: can't create new thread at interpreter shutdown" + could be written to stderr when a MongoClient's thread starts as the python interpreter is shutting down. Changes in Version 4.6.1 ------------------------