From c0bd9b1b36ff81ce3e238ace8696525158aceb1d Mon Sep 17 00:00:00 2001 From: Ronan Merrick Date: Sat, 27 Jun 2026 14:50:09 +0100 Subject: [PATCH 1/8] PYTHON-5904 --- pymongo/asynchronous/pool.py | 3 ++- pymongo/synchronous/pool.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/pymongo/asynchronous/pool.py b/pymongo/asynchronous/pool.py index 60acb93fcd..8c8d7ca267 100644 --- a/pymongo/asynchronous/pool.py +++ b/pymongo/asynchronous/pool.py @@ -294,7 +294,8 @@ async def _hello( hello.logical_session_timeout_minutes is not None and hello.is_readable ) self.logical_session_timeout_minutes: Optional[int] = hello.logical_session_timeout_minutes - self.hello_ok = hello.hello_ok + # PYTHON-5904 + self.hello_ok = self.hello_ok or hello.hello_ok self.is_repl = hello.server_type in ( SERVER_TYPE.RSPrimary, SERVER_TYPE.RSSecondary, diff --git a/pymongo/synchronous/pool.py b/pymongo/synchronous/pool.py index b3929b674a..48b0010f5c 100644 --- a/pymongo/synchronous/pool.py +++ b/pymongo/synchronous/pool.py @@ -294,7 +294,8 @@ def _hello( hello.logical_session_timeout_minutes is not None and hello.is_readable ) self.logical_session_timeout_minutes: Optional[int] = hello.logical_session_timeout_minutes - self.hello_ok = hello.hello_ok + # PYTHON-5904 + self.hello_ok = self.hello_ok or hello.hello_ok self.is_repl = hello.server_type in ( SERVER_TYPE.RSPrimary, SERVER_TYPE.RSSecondary, From 2293e362ef5f3d7ceefa425be5e0c1fe0a8be639 Mon Sep 17 00:00:00 2001 From: Ronan Merrick Date: Tue, 30 Jun 2026 10:38:52 +0100 Subject: [PATCH 2/8] added regression tests for PYTHON-5904 --- test/asynchronous/test_hello_latched.py | 67 ++++++++++++++++++++++ test/test_hello_latched.py | 74 +++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 test/asynchronous/test_hello_latched.py create mode 100644 test/test_hello_latched.py diff --git a/test/asynchronous/test_hello_latched.py b/test/asynchronous/test_hello_latched.py new file mode 100644 index 0000000000..127d093937 --- /dev/null +++ b/test/asynchronous/test_hello_latched.py @@ -0,0 +1,67 @@ +import unittest +from unittest.mock import AsyncMock +from types import SimpleNamespace +from pymongo.asynchronous.pool import AsyncConnection + +class TestHelloLatched(unittest.IsolatedAsyncioTestCase): + + def setUp(self): + self._sent = [] + + def create_connection(self) -> AsyncConnection: + """Returns a minimal connection object for _hello""" + conn = object.__new__(AsyncConnection) + conn.hello_ok = False + conn.performed_handshake = True + conn.opts = SimpleNamespace( + server_api = None, + load_balanced = False, + _credentials = None + ) + + return conn + + async def mock_conn_command(self, db, cmd, **kwargs): + """Returns mocked hello and ismaster results for conn.command""" + self._sent.append(cmd.copy()) + if cmd.get("ismaster") == 1: + return {"ok":1, "helloOk": True, "ismaster": True, "maxWireVersion": 25} + return {"ok":1, "isWritablePrimary": True, "maxWireVersion": 25} + + + async def test_hello_is_latched(self): + """ + Regression Test for PYTHON-5904 + Tests for connection hello_ok persistence when connection + Switches from ismaster to hello + """ + conn = self.create_connection() + conn.command = AsyncMock(side_effect=self.mock_conn_command) + + # First hello + await conn._hello(None, None) + # Verify hello_ok is True + self.assertTrue(conn.hello_ok) + # Verify command sent is ismaster + self.assertEqual(self._sent[0].get("ismaster"), 1) + self.assertEqual(self._sent[0].get("helloOk"), True) + + # Second hello + await conn._hello(None, None) + # Verify hello_ok has not changed + self.assertTrue(conn.hello_ok) + # Verify command sent is hello + self.assertEqual(self._sent[1].get("hello"), 1) + self.assertIsNone(self._sent[1].get("ismaster", None)) + + # Third hello + await conn._hello(None, None) + # Verify connection continues to use hello + self.assertEqual(self._sent[2].get("hello"), 1) + self.assertIsNone(self._sent[1].get("ismaster", None)) + + +if __name__ == "__main__": + unittest.main() + + diff --git a/test/test_hello_latched.py b/test/test_hello_latched.py new file mode 100644 index 0000000000..303686fe17 --- /dev/null +++ b/test/test_hello_latched.py @@ -0,0 +1,74 @@ +import unittest +from unittest.mock import Mock +from types import SimpleNamespace +from pymongo.synchronous.pool import Connection + + +class TestHelloLatched(unittest.TestCase): + """ + Test to Ensure that hello_ok remains latched + when the RTT thread switches to hello + """ + + def setUp(self): + self._sent = [] + + def create_connection(self) -> Connection: + """Returns a minimal connection object""" + conn = object.__new__(Connection) + conn.hello_ok = False + # Isolate this test to post-handshake hello_ok persistence + conn.performed_handshake = True + conn.opts = SimpleNamespace( + server_api = None, + load_balanced = False, + _credentials = None + ) + + return conn + + def mock_connection_command(self, db, cmd, **kwargs): + """Returns mocked hello and ismaster results for conn.command""" + self._sent.append(cmd.copy()) + if cmd.get("ismaster") == 1: + return {"ok":1, "helloOk": True, "ismaster": True, "maxWireVersion": 25} + return {"ok":1, "isWritablePrimary": True, "maxWireVersion": 25} + + + def test_hello_is_latched(self): + """ + Regression Test for PYTHON-5904 + Tests for connection hello_ok persistence when connection + Switches from ismaster to hello + """ + + conn = self.create_connection() + conn.command = Mock(side_effect=self.mock_connection_command) + + # First hello + conn._hello(None, None) + # Verify hello_ok is True + self.assertTrue(conn.hello_ok) + # Verify command sent is ismaster + self.assertEqual(self._sent[0].get("ismaster"), 1) + self.assertEqual(self._sent[0].get("helloOk"), True) + + # Second hello + conn._hello(None, None) + # Verify hello_ok has not changed + self.assertTrue(conn.hello_ok) + # Verify command sent is hello + self.assertEqual(self._sent[1].get("hello"), 1) + self.assertIsNone(self._sent[1].get("ismaster", None)) + + # Third hello + conn._hello(None, None) + # Verify connection continues to use hello + self.assertEqual(self._sent[2].get("hello"), 1) + self.assertIsNone(self._sent[1].get("ismaster", None)) + + +if __name__ == "__main__": + unittest.main() + + From b7961a7da98e8b1527d6c873c5cbab21f95006ce Mon Sep 17 00:00:00 2001 From: Ronan Merrick Date: Tue, 30 Jun 2026 16:46:02 +0100 Subject: [PATCH 3/8] PR changes --- pymongo/synchronous/pool.py | 3 +- test/asynchronous/test_hello_latched.py | 18 +++++- test/test_hello_latched.py | 74 ------------------------- 3 files changed, 18 insertions(+), 77 deletions(-) delete mode 100644 test/test_hello_latched.py diff --git a/pymongo/synchronous/pool.py b/pymongo/synchronous/pool.py index 48b0010f5c..b3929b674a 100644 --- a/pymongo/synchronous/pool.py +++ b/pymongo/synchronous/pool.py @@ -294,8 +294,7 @@ def _hello( hello.logical_session_timeout_minutes is not None and hello.is_readable ) self.logical_session_timeout_minutes: Optional[int] = hello.logical_session_timeout_minutes - # PYTHON-5904 - self.hello_ok = self.hello_ok or hello.hello_ok + self.hello_ok = hello.hello_ok self.is_repl = hello.server_type in ( SERVER_TYPE.RSPrimary, SERVER_TYPE.RSSecondary, diff --git a/test/asynchronous/test_hello_latched.py b/test/asynchronous/test_hello_latched.py index 127d093937..e173301c98 100644 --- a/test/asynchronous/test_hello_latched.py +++ b/test/asynchronous/test_hello_latched.py @@ -1,3 +1,19 @@ +# Copyright 2022-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test that connection continues to use hello after ismaster returns hello_ok""" + import unittest from unittest.mock import AsyncMock from types import SimpleNamespace @@ -58,7 +74,7 @@ async def test_hello_is_latched(self): await conn._hello(None, None) # Verify connection continues to use hello self.assertEqual(self._sent[2].get("hello"), 1) - self.assertIsNone(self._sent[1].get("ismaster", None)) + self.assertIsNone(self._sent[2].get("ismaster", None)) if __name__ == "__main__": diff --git a/test/test_hello_latched.py b/test/test_hello_latched.py deleted file mode 100644 index 303686fe17..0000000000 --- a/test/test_hello_latched.py +++ /dev/null @@ -1,74 +0,0 @@ -import unittest -from unittest.mock import Mock -from types import SimpleNamespace -from pymongo.synchronous.pool import Connection - - -class TestHelloLatched(unittest.TestCase): - """ - Test to Ensure that hello_ok remains latched - when the RTT thread switches to hello - """ - - def setUp(self): - self._sent = [] - - def create_connection(self) -> Connection: - """Returns a minimal connection object""" - conn = object.__new__(Connection) - conn.hello_ok = False - # Isolate this test to post-handshake hello_ok persistence - conn.performed_handshake = True - conn.opts = SimpleNamespace( - server_api = None, - load_balanced = False, - _credentials = None - ) - - return conn - - def mock_connection_command(self, db, cmd, **kwargs): - """Returns mocked hello and ismaster results for conn.command""" - self._sent.append(cmd.copy()) - if cmd.get("ismaster") == 1: - return {"ok":1, "helloOk": True, "ismaster": True, "maxWireVersion": 25} - return {"ok":1, "isWritablePrimary": True, "maxWireVersion": 25} - - - def test_hello_is_latched(self): - """ - Regression Test for PYTHON-5904 - Tests for connection hello_ok persistence when connection - Switches from ismaster to hello - """ - - conn = self.create_connection() - conn.command = Mock(side_effect=self.mock_connection_command) - - # First hello - conn._hello(None, None) - # Verify hello_ok is True - self.assertTrue(conn.hello_ok) - # Verify command sent is ismaster - self.assertEqual(self._sent[0].get("ismaster"), 1) - self.assertEqual(self._sent[0].get("helloOk"), True) - - # Second hello - conn._hello(None, None) - # Verify hello_ok has not changed - self.assertTrue(conn.hello_ok) - # Verify command sent is hello - self.assertEqual(self._sent[1].get("hello"), 1) - self.assertIsNone(self._sent[1].get("ismaster", None)) - - # Third hello - conn._hello(None, None) - # Verify connection continues to use hello - self.assertEqual(self._sent[2].get("hello"), 1) - self.assertIsNone(self._sent[1].get("ismaster", None)) - - -if __name__ == "__main__": - unittest.main() - - From d6aad634f51858feffd1232d64779091fad80351 Mon Sep 17 00:00:00 2001 From: Ronan Merrick Date: Wed, 1 Jul 2026 11:29:40 +0100 Subject: [PATCH 4/8] added synchronous files and improved comment --- pymongo/asynchronous/pool.py | 3 +- pymongo/synchronous/pool.py | 4 +- test/asynchronous/test_hello_latched.py | 22 +++---- test/test_hello_latched.py | 79 +++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 15 deletions(-) create mode 100644 test/test_hello_latched.py diff --git a/pymongo/asynchronous/pool.py b/pymongo/asynchronous/pool.py index 8c8d7ca267..f0a32cebd4 100644 --- a/pymongo/asynchronous/pool.py +++ b/pymongo/asynchronous/pool.py @@ -294,7 +294,8 @@ async def _hello( hello.logical_session_timeout_minutes is not None and hello.is_readable ) self.logical_session_timeout_minutes: Optional[int] = hello.logical_session_timeout_minutes - # PYTHON-5904 + # hello_ok is set from helloOk, which is only returned from ismaster + # don't overwrite this when connection switches to hello self.hello_ok = self.hello_ok or hello.hello_ok self.is_repl = hello.server_type in ( SERVER_TYPE.RSPrimary, diff --git a/pymongo/synchronous/pool.py b/pymongo/synchronous/pool.py index b3929b674a..5d76766464 100644 --- a/pymongo/synchronous/pool.py +++ b/pymongo/synchronous/pool.py @@ -294,7 +294,9 @@ def _hello( hello.logical_session_timeout_minutes is not None and hello.is_readable ) self.logical_session_timeout_minutes: Optional[int] = hello.logical_session_timeout_minutes - self.hello_ok = hello.hello_ok + # hello_ok is set from helloOk, which is only returned from ismaster + # don't overwrite this when connection switches to hello + self.hello_ok = self.hello_ok or hello.hello_ok self.is_repl = hello.server_type in ( SERVER_TYPE.RSPrimary, SERVER_TYPE.RSSecondary, diff --git a/test/asynchronous/test_hello_latched.py b/test/asynchronous/test_hello_latched.py index e173301c98..c989737f51 100644 --- a/test/asynchronous/test_hello_latched.py +++ b/test/asynchronous/test_hello_latched.py @@ -14,13 +14,16 @@ """Test that connection continues to use hello after ismaster returns hello_ok""" +from __future__ import annotations + import unittest -from unittest.mock import AsyncMock from types import SimpleNamespace -from pymongo.asynchronous.pool import AsyncConnection +from unittest.mock import AsyncMock -class TestHelloLatched(unittest.IsolatedAsyncioTestCase): +from pymongo.asynchronous.pool import AsyncConnection + +class TestHelloLatched(unittest.IsolatedAsyncioTestCase): def setUp(self): self._sent = [] @@ -29,11 +32,7 @@ def create_connection(self) -> AsyncConnection: conn = object.__new__(AsyncConnection) conn.hello_ok = False conn.performed_handshake = True - conn.opts = SimpleNamespace( - server_api = None, - load_balanced = False, - _credentials = None - ) + conn.opts = SimpleNamespace(server_api=None, load_balanced=False, _credentials=None) return conn @@ -41,9 +40,8 @@ async def mock_conn_command(self, db, cmd, **kwargs): """Returns mocked hello and ismaster results for conn.command""" self._sent.append(cmd.copy()) if cmd.get("ismaster") == 1: - return {"ok":1, "helloOk": True, "ismaster": True, "maxWireVersion": 25} - return {"ok":1, "isWritablePrimary": True, "maxWireVersion": 25} - + return {"ok": 1, "helloOk": True, "ismaster": True, "maxWireVersion": 25} + return {"ok": 1, "isWritablePrimary": True, "maxWireVersion": 25} async def test_hello_is_latched(self): """ @@ -79,5 +77,3 @@ async def test_hello_is_latched(self): if __name__ == "__main__": unittest.main() - - diff --git a/test/test_hello_latched.py b/test/test_hello_latched.py new file mode 100644 index 0000000000..421669d150 --- /dev/null +++ b/test/test_hello_latched.py @@ -0,0 +1,79 @@ +# Copyright 2022-present MongoDB, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Test that connection continues to use hello after ismaster returns hello_ok""" + +from __future__ import annotations + +import unittest +from types import SimpleNamespace +from unittest.mock import SyncMock + +from pymongo.synchronous.pool import Connection + + +class TestHelloLatched(unittest.TestCase): + def setUp(self): + self._sent = [] + + def create_connection(self) -> Connection: + """Returns a minimal connection object for _hello""" + conn = object.__new__(Connection) + conn.hello_ok = False + conn.performed_handshake = True + conn.opts = SimpleNamespace(server_api=None, load_balanced=False, _credentials=None) + + return conn + + def mock_conn_command(self, db, cmd, **kwargs): + """Returns mocked hello and ismaster results for conn.command""" + self._sent.append(cmd.copy()) + if cmd.get("ismaster") == 1: + return {"ok": 1, "helloOk": True, "ismaster": True, "maxWireVersion": 25} + return {"ok": 1, "isWritablePrimary": True, "maxWireVersion": 25} + + def test_hello_is_latched(self): + """ + Regression Test for PYTHON-5904 + Tests for connection hello_ok persistence when connection + Switches from ismaster to hello + """ + conn = self.create_connection() + conn.command = SyncMock(side_effect=self.mock_conn_command) + + # First hello + conn._hello(None, None) + # Verify hello_ok is True + self.assertTrue(conn.hello_ok) + # Verify command sent is ismaster + self.assertEqual(self._sent[0].get("ismaster"), 1) + self.assertEqual(self._sent[0].get("helloOk"), True) + + # Second hello + conn._hello(None, None) + # Verify hello_ok has not changed + self.assertTrue(conn.hello_ok) + # Verify command sent is hello + self.assertEqual(self._sent[1].get("hello"), 1) + self.assertIsNone(self._sent[1].get("ismaster", None)) + + # Third hello + conn._hello(None, None) + # Verify connection continues to use hello + self.assertEqual(self._sent[2].get("hello"), 1) + self.assertIsNone(self._sent[2].get("ismaster", None)) + + +if __name__ == "__main__": + unittest.main() From 1c674b77a809128981d2dfa9d37ab334fc73df4d Mon Sep 17 00:00:00 2001 From: Ronan Merrick Date: Thu, 2 Jul 2026 17:21:00 +0100 Subject: [PATCH 5/8] updated synchro configuration and generated synchronous test file --- test/asynchronous/test_hello_latched.py | 4 +++- test/test_hello_latched.py | 8 +++++--- tools/synchro.py | 2 ++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/test/asynchronous/test_hello_latched.py b/test/asynchronous/test_hello_latched.py index c989737f51..36657bb4fa 100644 --- a/test/asynchronous/test_hello_latched.py +++ b/test/asynchronous/test_hello_latched.py @@ -1,4 +1,4 @@ -# Copyright 2022-present MongoDB, Inc. +# Copyright 2026-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ from pymongo.asynchronous.pool import AsyncConnection +_IS_SYNC = False + class TestHelloLatched(unittest.IsolatedAsyncioTestCase): def setUp(self): diff --git a/test/test_hello_latched.py b/test/test_hello_latched.py index 421669d150..a1d7d3b2ff 100644 --- a/test/test_hello_latched.py +++ b/test/test_hello_latched.py @@ -1,4 +1,4 @@ -# Copyright 2022-present MongoDB, Inc. +# Copyright 2026-present MongoDB, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,10 +18,12 @@ import unittest from types import SimpleNamespace -from unittest.mock import SyncMock +from unittest.mock import Mock from pymongo.synchronous.pool import Connection +_IS_SYNC = True + class TestHelloLatched(unittest.TestCase): def setUp(self): @@ -50,7 +52,7 @@ def test_hello_is_latched(self): Switches from ismaster to hello """ conn = self.create_connection() - conn.command = SyncMock(side_effect=self.mock_conn_command) + conn.command = Mock(side_effect=self.mock_conn_command) # First hello conn._hello(None, None) diff --git a/tools/synchro.py b/tools/synchro.py index 8132167e71..3cc2a44138 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -140,6 +140,7 @@ "_async_create_connection": "_create_connection", "pymongo.asynchronous.srv_resolver._SrvResolver.get_hosts": "pymongo.synchronous.srv_resolver._SrvResolver.get_hosts", "dns.asyncresolver.resolve": "dns.resolver.resolve", + "AsyncMock": "Mock", } docstring_replacements: dict[tuple[str, str], str] = { @@ -244,6 +245,7 @@ def async_only_test(f: Path) -> bool: "test_gridfs_spec.py", "test_handshake_unified.py", "test_heartbeat_monitoring.py", + "test_hello_latched.py", "test_index_management.py", "test_json_util_integration.py", "test_load_balancer.py", From 1a3c11b861d2802f710cd9e455074be46b7eec5e Mon Sep 17 00:00:00 2001 From: Ronan Merrick Date: Wed, 8 Jul 2026 19:59:00 +0100 Subject: [PATCH 6/8] changed test_hello_latched.py to use AsyncUnitTest --- test/asynchronous/test_hello_latched.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/asynchronous/test_hello_latched.py b/test/asynchronous/test_hello_latched.py index 36657bb4fa..175635e3d4 100644 --- a/test/asynchronous/test_hello_latched.py +++ b/test/asynchronous/test_hello_latched.py @@ -19,13 +19,13 @@ import unittest from types import SimpleNamespace from unittest.mock import AsyncMock - from pymongo.asynchronous.pool import AsyncConnection +from test.asynchronous import AsyncUnitTest _IS_SYNC = False -class TestHelloLatched(unittest.IsolatedAsyncioTestCase): +class TestHelloLatched(AsyncUnitTest): def setUp(self): self._sent = [] From 3f5e5971cf8c0cf76c418e8cb819957cd56448b2 Mon Sep 17 00:00:00 2001 From: Ronan Merrick Date: Thu, 9 Jul 2026 19:23:02 +0100 Subject: [PATCH 7/8] narrowed unasync replacement for test_hello_latched.py --- test/asynchronous/test_hello_latched.py | 9 +++++++-- test/test_hello_latched.py | 11 ++++++++--- tools/synchro.py | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/test/asynchronous/test_hello_latched.py b/test/asynchronous/test_hello_latched.py index 175635e3d4..38dc12f94b 100644 --- a/test/asynchronous/test_hello_latched.py +++ b/test/asynchronous/test_hello_latched.py @@ -18,7 +18,12 @@ import unittest from types import SimpleNamespace -from unittest.mock import AsyncMock + +# Use a test-specific alias here because a generic AsyncMock replacement caused +# unintended rewrites in generated sync tests. Keeping the alias narrow limits +# the unasync/synchro transform to this test. +from unittest.mock import AsyncMock as AsyncLatchedHelloMock + from pymongo.asynchronous.pool import AsyncConnection from test.asynchronous import AsyncUnitTest @@ -52,7 +57,7 @@ async def test_hello_is_latched(self): Switches from ismaster to hello """ conn = self.create_connection() - conn.command = AsyncMock(side_effect=self.mock_conn_command) + conn.command = AsyncLatchedHelloMock(side_effect=self.mock_conn_command) # First hello await conn._hello(None, None) diff --git a/test/test_hello_latched.py b/test/test_hello_latched.py index a1d7d3b2ff..6c05aa0b63 100644 --- a/test/test_hello_latched.py +++ b/test/test_hello_latched.py @@ -18,14 +18,19 @@ import unittest from types import SimpleNamespace -from unittest.mock import Mock + +# Use a test-specific alias here because a generic AsyncMock replacement caused +# unintended rewrites in generated sync tests. Keeping the alias narrow limits +# the unasync/synchro transform to this test. +from unittest.mock import Mock as LatchedHelloMock from pymongo.synchronous.pool import Connection +from test import UnitTest _IS_SYNC = True -class TestHelloLatched(unittest.TestCase): +class TestHelloLatched(UnitTest): def setUp(self): self._sent = [] @@ -52,7 +57,7 @@ def test_hello_is_latched(self): Switches from ismaster to hello """ conn = self.create_connection() - conn.command = Mock(side_effect=self.mock_conn_command) + conn.command = LatchedHelloMock(side_effect=self.mock_conn_command) # First hello conn._hello(None, None) diff --git a/tools/synchro.py b/tools/synchro.py index 8b32f06c40..3764e53769 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -140,7 +140,7 @@ "_async_create_connection": "_create_connection", "pymongo.asynchronous.srv_resolver._SrvResolver.get_hosts": "pymongo.synchronous.srv_resolver._SrvResolver.get_hosts", "dns.asyncresolver.resolve": "dns.resolver.resolve", - "AsyncMock": "Mock", + "AsyncLatchedHelloMock": "LatchedHelloMock", } docstring_replacements: dict[tuple[str, str], str] = { From e7b22d57681941fdcae44dfa17840a8315a85f80 Mon Sep 17 00:00:00 2001 From: Ronan Merrick Date: Fri, 10 Jul 2026 16:13:43 +0100 Subject: [PATCH 8/8] inlined mock_conn_command in test_hello_latched and removed the mock object --- test/asynchronous/test_hello_latched.py | 22 +++++++++------------- test/test_hello_latched.py | 22 +++++++++------------- tools/synchro.py | 1 - 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/test/asynchronous/test_hello_latched.py b/test/asynchronous/test_hello_latched.py index 38dc12f94b..20ad7cedff 100644 --- a/test/asynchronous/test_hello_latched.py +++ b/test/asynchronous/test_hello_latched.py @@ -19,11 +19,6 @@ import unittest from types import SimpleNamespace -# Use a test-specific alias here because a generic AsyncMock replacement caused -# unintended rewrites in generated sync tests. Keeping the alias narrow limits -# the unasync/synchro transform to this test. -from unittest.mock import AsyncMock as AsyncLatchedHelloMock - from pymongo.asynchronous.pool import AsyncConnection from test.asynchronous import AsyncUnitTest @@ -43,21 +38,22 @@ def create_connection(self) -> AsyncConnection: return conn - async def mock_conn_command(self, db, cmd, **kwargs): - """Returns mocked hello and ismaster results for conn.command""" - self._sent.append(cmd.copy()) - if cmd.get("ismaster") == 1: - return {"ok": 1, "helloOk": True, "ismaster": True, "maxWireVersion": 25} - return {"ok": 1, "isWritablePrimary": True, "maxWireVersion": 25} - async def test_hello_is_latched(self): """ Regression Test for PYTHON-5904 Tests for connection hello_ok persistence when connection Switches from ismaster to hello """ + + async def mock_conn_command(db, cmd, **kwargs): + """Returns mocked hello and ismaster results for conn.command""" + self._sent.append(cmd.copy()) + if cmd.get("ismaster") == 1: + return {"ok": 1, "helloOk": True, "ismaster": True, "maxWireVersion": 25} + return {"ok": 1, "isWritablePrimary": True, "maxWireVersion": 25} + conn = self.create_connection() - conn.command = AsyncLatchedHelloMock(side_effect=self.mock_conn_command) + conn.command = mock_conn_command # First hello await conn._hello(None, None) diff --git a/test/test_hello_latched.py b/test/test_hello_latched.py index 6c05aa0b63..8bc8e995a6 100644 --- a/test/test_hello_latched.py +++ b/test/test_hello_latched.py @@ -19,11 +19,6 @@ import unittest from types import SimpleNamespace -# Use a test-specific alias here because a generic AsyncMock replacement caused -# unintended rewrites in generated sync tests. Keeping the alias narrow limits -# the unasync/synchro transform to this test. -from unittest.mock import Mock as LatchedHelloMock - from pymongo.synchronous.pool import Connection from test import UnitTest @@ -43,21 +38,22 @@ def create_connection(self) -> Connection: return conn - def mock_conn_command(self, db, cmd, **kwargs): - """Returns mocked hello and ismaster results for conn.command""" - self._sent.append(cmd.copy()) - if cmd.get("ismaster") == 1: - return {"ok": 1, "helloOk": True, "ismaster": True, "maxWireVersion": 25} - return {"ok": 1, "isWritablePrimary": True, "maxWireVersion": 25} - def test_hello_is_latched(self): """ Regression Test for PYTHON-5904 Tests for connection hello_ok persistence when connection Switches from ismaster to hello """ + + def mock_conn_command(db, cmd, **kwargs): + """Returns mocked hello and ismaster results for conn.command""" + self._sent.append(cmd.copy()) + if cmd.get("ismaster") == 1: + return {"ok": 1, "helloOk": True, "ismaster": True, "maxWireVersion": 25} + return {"ok": 1, "isWritablePrimary": True, "maxWireVersion": 25} + conn = self.create_connection() - conn.command = LatchedHelloMock(side_effect=self.mock_conn_command) + conn.command = mock_conn_command # First hello conn._hello(None, None) diff --git a/tools/synchro.py b/tools/synchro.py index 3764e53769..f9c3f23c8c 100644 --- a/tools/synchro.py +++ b/tools/synchro.py @@ -140,7 +140,6 @@ "_async_create_connection": "_create_connection", "pymongo.asynchronous.srv_resolver._SrvResolver.get_hosts": "pymongo.synchronous.srv_resolver._SrvResolver.get_hosts", "dns.asyncresolver.resolve": "dns.resolver.resolve", - "AsyncLatchedHelloMock": "LatchedHelloMock", } docstring_replacements: dict[tuple[str, str], str] = {