From 0b35dffc95ddd357bef32866bf3313de5b0c0c84 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 17 Jul 2025 14:50:14 -0500 Subject: [PATCH 1/5] add to docstring --- pymongo/asynchronous/cursor.py | 2 ++ pymongo/synchronous/cursor.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/pymongo/asynchronous/cursor.py b/pymongo/asynchronous/cursor.py index 02954fb559..51efab4f43 100644 --- a/pymongo/asynchronous/cursor.py +++ b/pymongo/asynchronous/cursor.py @@ -767,6 +767,8 @@ async def explain(self) -> _DocumentType: :meth:`~pymongo.asynchronous.database.AsyncDatabase.command` to run the explain command directly. + .. note:: The timeout of this method can be set using :func:`pymongo.timeout`. + .. seealso:: The MongoDB documentation on `explain `_. """ c = self.clone() diff --git a/pymongo/synchronous/cursor.py b/pymongo/synchronous/cursor.py index ba35316516..e49141e811 100644 --- a/pymongo/synchronous/cursor.py +++ b/pymongo/synchronous/cursor.py @@ -765,6 +765,8 @@ def explain(self) -> _DocumentType: :meth:`~pymongo.database.Database.command` to run the explain command directly. + .. note:: The timeout of this method can be set using :func:`pymongo.timeout`. + .. seealso:: The MongoDB documentation on `explain `_. """ c = self.clone() From f0e3e22676bdc22951403a6cf09e65a850a2394d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 17 Jul 2025 14:51:26 -0500 Subject: [PATCH 2/5] add test --- test/asynchronous/test_cursor.py | 18 ++++++++++++++++++ test/test_cursor.py | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/test/asynchronous/test_cursor.py b/test/asynchronous/test_cursor.py index 8d2dbf532e..f8ee65c572 100644 --- a/test/asynchronous/test_cursor.py +++ b/test/asynchronous/test_cursor.py @@ -362,6 +362,24 @@ async def test_explain_with_read_concern(self): self.assertEqual(len(started), 1) self.assertNotIn("readConcern", started[0].command) + # https://github.com/mongodb/specifications/blob/master/source/crud/tests/README.md#14-explain-helpers-allow-users-to-specify-maxtimems + async def test_explain_csot(self): + # Create a MongoClient with command monitoring enabled (referred to as client). + listener = AllowListEventListener("explain") + client = await self.rs_or_single_client(event_listeners=[listener]) + + # Create a collection, referred to as collection, with the namespace explain-test.collection. + collection = client["explain-test"]["collection"] + + # Run an explained find on collection. The find will have the query predicate { name: 'john doe' }. Specify a maxTimeMS value of 2000ms for the explain. + with pymongo.timeout(2.0): + self.assertTrue(await collection.find({"name": "john doe"}).explain()) + + # Obtain the command started event for the explain. Confirm that the top-level explain command should has a maxTimeMS value of 2000. + started = listener.started_events + self.assertEqual(len(started), 1) + assert 1990 < started[0].command["maxTimeMS"] <= 2000 + async def test_hint(self): db = self.db with self.assertRaises(TypeError): diff --git a/test/test_cursor.py b/test/test_cursor.py index 4902d9e4df..7925c63fc3 100644 --- a/test/test_cursor.py +++ b/test/test_cursor.py @@ -354,6 +354,24 @@ def test_explain_with_read_concern(self): self.assertEqual(len(started), 1) self.assertNotIn("readConcern", started[0].command) + # https://github.com/mongodb/specifications/blob/master/source/crud/tests/README.md#14-explain-helpers-allow-users-to-specify-maxtimems + def test_explain_csot(self): + # Create a MongoClient with command monitoring enabled (referred to as client). + listener = AllowListEventListener("explain") + client = self.rs_or_single_client(event_listeners=[listener]) + + # Create a collection, referred to as collection, with the namespace explain-test.collection. + collection = client["explain-test"]["collection"] + + # Run an explained find on collection. The find will have the query predicate { name: 'john doe' }. Specify a maxTimeMS value of 2000ms for the explain. + with pymongo.timeout(2.0): + self.assertTrue(collection.find({"name": "john doe"}).explain()) + + # Obtain the command started event for the explain. Confirm that the top-level explain command should has a maxTimeMS value of 2000. + started = listener.started_events + self.assertEqual(len(started), 1) + assert 1990 < started[0].command["maxTimeMS"] <= 2000 + def test_hint(self): db = self.db with self.assertRaises(TypeError): From fb5b9d4b371708a739528d3b14f23f045df63066 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 17 Jul 2025 19:49:12 -0500 Subject: [PATCH 3/5] fix test --- test/asynchronous/test_cursor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/asynchronous/test_cursor.py b/test/asynchronous/test_cursor.py index f8ee65c572..7052665afd 100644 --- a/test/asynchronous/test_cursor.py +++ b/test/asynchronous/test_cursor.py @@ -366,7 +366,7 @@ async def test_explain_with_read_concern(self): async def test_explain_csot(self): # Create a MongoClient with command monitoring enabled (referred to as client). listener = AllowListEventListener("explain") - client = await self.rs_or_single_client(event_listeners=[listener]) + client = await self.async_rs_or_single_client(event_listeners=[listener]) # Create a collection, referred to as collection, with the namespace explain-test.collection. collection = client["explain-test"]["collection"] From a8d9e88b28280943dca05bc81e6f4c0514df7f88 Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 17 Jul 2025 20:05:30 -0500 Subject: [PATCH 4/5] loosen bound --- test/asynchronous/test_cursor.py | 2 +- test/test_cursor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/asynchronous/test_cursor.py b/test/asynchronous/test_cursor.py index 7052665afd..935e685712 100644 --- a/test/asynchronous/test_cursor.py +++ b/test/asynchronous/test_cursor.py @@ -378,7 +378,7 @@ async def test_explain_csot(self): # Obtain the command started event for the explain. Confirm that the top-level explain command should has a maxTimeMS value of 2000. started = listener.started_events self.assertEqual(len(started), 1) - assert 1990 < started[0].command["maxTimeMS"] <= 2000 + assert 1900 < started[0].command["maxTimeMS"] <= 2000 async def test_hint(self): db = self.db diff --git a/test/test_cursor.py b/test/test_cursor.py index 7925c63fc3..06b8184695 100644 --- a/test/test_cursor.py +++ b/test/test_cursor.py @@ -370,7 +370,7 @@ def test_explain_csot(self): # Obtain the command started event for the explain. Confirm that the top-level explain command should has a maxTimeMS value of 2000. started = listener.started_events self.assertEqual(len(started), 1) - assert 1990 < started[0].command["maxTimeMS"] <= 2000 + assert 1900 < started[0].command["maxTimeMS"] <= 2000 def test_hint(self): db = self.db From 0b60462f63a0ce522490f5dc22bd37766a38231d Mon Sep 17 00:00:00 2001 From: Steven Silvester Date: Thu, 17 Jul 2025 20:35:48 -0500 Subject: [PATCH 5/5] loosen bound --- test/asynchronous/test_cursor.py | 2 +- test/test_cursor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/asynchronous/test_cursor.py b/test/asynchronous/test_cursor.py index 935e685712..53b3289fb8 100644 --- a/test/asynchronous/test_cursor.py +++ b/test/asynchronous/test_cursor.py @@ -378,7 +378,7 @@ async def test_explain_csot(self): # Obtain the command started event for the explain. Confirm that the top-level explain command should has a maxTimeMS value of 2000. started = listener.started_events self.assertEqual(len(started), 1) - assert 1900 < started[0].command["maxTimeMS"] <= 2000 + assert 1500 < started[0].command["maxTimeMS"] <= 2000 async def test_hint(self): db = self.db diff --git a/test/test_cursor.py b/test/test_cursor.py index 06b8184695..d0bd48e747 100644 --- a/test/test_cursor.py +++ b/test/test_cursor.py @@ -370,7 +370,7 @@ def test_explain_csot(self): # Obtain the command started event for the explain. Confirm that the top-level explain command should has a maxTimeMS value of 2000. started = listener.started_events self.assertEqual(len(started), 1) - assert 1900 < started[0].command["maxTimeMS"] <= 2000 + assert 1500 < started[0].command["maxTimeMS"] <= 2000 def test_hint(self): db = self.db