Skip to content

Commit f6e35d9

Browse files
authored
PYTHON-4945 - Fix test cleanups for mongoses, use asyncio locks, propagate CancelledErrors (#1996)
1 parent 3d64532 commit f6e35d9

23 files changed

+76
-43
lines changed

pymongo/asynchronous/mongo_client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"""
3333
from __future__ import annotations
3434

35+
import asyncio
3536
import contextlib
3637
import os
3738
import warnings
@@ -2036,6 +2037,8 @@ async def _process_kill_cursors(self) -> None:
20362037
for address, cursor_id, conn_mgr in pinned_cursors:
20372038
try:
20382039
await self._cleanup_cursor_lock(cursor_id, address, conn_mgr, None, False)
2040+
except asyncio.CancelledError:
2041+
raise
20392042
except Exception as exc:
20402043
if isinstance(exc, InvalidOperation) and self._topology._closed:
20412044
# Raise the exception when client is closed so that it
@@ -2050,6 +2053,8 @@ async def _process_kill_cursors(self) -> None:
20502053
for address, cursor_ids in address_to_cursor_ids.items():
20512054
try:
20522055
await self._kill_cursors(cursor_ids, address, topology, session=None)
2056+
except asyncio.CancelledError:
2057+
raise
20532058
except Exception as exc:
20542059
if isinstance(exc, InvalidOperation) and self._topology._closed:
20552060
raise
@@ -2064,6 +2069,8 @@ async def _process_periodic_tasks(self) -> None:
20642069
try:
20652070
await self._process_kill_cursors()
20662071
await self._topology.update_pool()
2072+
except asyncio.CancelledError:
2073+
raise
20672074
except Exception as exc:
20682075
if isinstance(exc, InvalidOperation) and self._topology._closed:
20692076
return

pymongo/asynchronous/monitor.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from __future__ import annotations
1818

19+
import asyncio
1920
import atexit
2021
import logging
2122
import time
@@ -26,7 +27,7 @@
2627
from pymongo._csot import MovingMinimum
2728
from pymongo.errors import NetworkTimeout, NotPrimaryError, OperationFailure, _OperationCancelled
2829
from pymongo.hello import Hello
29-
from pymongo.lock import _create_lock
30+
from pymongo.lock import _async_create_lock
3031
from pymongo.logger import _SDAM_LOGGER, _debug_log, _SDAMStatusMessage
3132
from pymongo.periodic_executor import _shutdown_executors
3233
from pymongo.pool_options import _is_faas
@@ -276,7 +277,7 @@ async def _check_server(self) -> ServerDescription:
276277
await self._reset_connection()
277278
if isinstance(error, _OperationCancelled):
278279
raise
279-
self._rtt_monitor.reset()
280+
await self._rtt_monitor.reset()
280281
# Server type defaults to Unknown.
281282
return ServerDescription(address, error=error)
282283

@@ -315,9 +316,9 @@ async def _check_once(self) -> ServerDescription:
315316
self._cancel_context = conn.cancel_context
316317
response, round_trip_time = await self._check_with_socket(conn)
317318
if not response.awaitable:
318-
self._rtt_monitor.add_sample(round_trip_time)
319+
await self._rtt_monitor.add_sample(round_trip_time)
319320

320-
avg_rtt, min_rtt = self._rtt_monitor.get()
321+
avg_rtt, min_rtt = await self._rtt_monitor.get()
321322
sd = ServerDescription(address, response, avg_rtt, min_round_trip_time=min_rtt)
322323
if self._publish:
323324
assert self._listeners is not None
@@ -413,6 +414,8 @@ def _get_seedlist(self) -> Optional[list[tuple[str, Any]]]:
413414
if len(seedlist) == 0:
414415
# As per the spec: this should be treated as a failure.
415416
raise Exception
417+
except asyncio.CancelledError:
418+
raise
416419
except Exception:
417420
# As per the spec, upon encountering an error:
418421
# - An error must not be raised
@@ -441,28 +444,28 @@ def __init__(self, topology: Topology, topology_settings: TopologySettings, pool
441444
self._pool = pool
442445
self._moving_average = MovingAverage()
443446
self._moving_min = MovingMinimum()
444-
self._lock = _create_lock()
447+
self._lock = _async_create_lock()
445448

446449
async def close(self) -> None:
447450
self.gc_safe_close()
448451
# Increment the generation and maybe close the socket. If the executor
449452
# thread has the socket checked out, it will be closed when checked in.
450453
await self._pool.reset()
451454

452-
def add_sample(self, sample: float) -> None:
455+
async def add_sample(self, sample: float) -> None:
453456
"""Add a RTT sample."""
454-
with self._lock:
457+
async with self._lock:
455458
self._moving_average.add_sample(sample)
456459
self._moving_min.add_sample(sample)
457460

458-
def get(self) -> tuple[Optional[float], float]:
461+
async def get(self) -> tuple[Optional[float], float]:
459462
"""Get the calculated average, or None if no samples yet and the min."""
460-
with self._lock:
463+
async with self._lock:
461464
return self._moving_average.get(), self._moving_min.get()
462465

463-
def reset(self) -> None:
466+
async def reset(self) -> None:
464467
"""Reset the average RTT."""
465-
with self._lock:
468+
async with self._lock:
466469
self._moving_average.reset()
467470
self._moving_min.reset()
468471

@@ -472,10 +475,12 @@ async def _run(self) -> None:
472475
# heartbeat protocol (MongoDB 4.4+).
473476
# XXX: Skip check if the server is unknown?
474477
rtt = await self._ping()
475-
self.add_sample(rtt)
478+
await self.add_sample(rtt)
476479
except ReferenceError:
477480
# Topology was garbage-collected.
478481
await self.close()
482+
except asyncio.CancelledError:
483+
raise
479484
except Exception:
480485
await self._pool.reset()
481486

pymongo/asynchronous/pool.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -704,6 +704,8 @@ def _close_conn(self) -> None:
704704
# shutdown.
705705
try:
706706
self.conn.close()
707+
except asyncio.CancelledError:
708+
raise
707709
except Exception: # noqa: S110
708710
pass
709711

pymongo/network_layer.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ async def async_receive_data(
271271
)
272272
for task in pending:
273273
task.cancel()
274-
await asyncio.wait(pending)
274+
if pending:
275+
await asyncio.wait(pending)
275276
if len(done) == 0:
276277
raise socket.timeout("timed out")
277278
if read_task in done:

pymongo/synchronous/mongo_client.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"""
3333
from __future__ import annotations
3434

35+
import asyncio
3536
import contextlib
3637
import os
3738
import warnings
@@ -2030,6 +2031,8 @@ def _process_kill_cursors(self) -> None:
20302031
for address, cursor_id, conn_mgr in pinned_cursors:
20312032
try:
20322033
self._cleanup_cursor_lock(cursor_id, address, conn_mgr, None, False)
2034+
except asyncio.CancelledError:
2035+
raise
20332036
except Exception as exc:
20342037
if isinstance(exc, InvalidOperation) and self._topology._closed:
20352038
# Raise the exception when client is closed so that it
@@ -2044,6 +2047,8 @@ def _process_kill_cursors(self) -> None:
20442047
for address, cursor_ids in address_to_cursor_ids.items():
20452048
try:
20462049
self._kill_cursors(cursor_ids, address, topology, session=None)
2050+
except asyncio.CancelledError:
2051+
raise
20472052
except Exception as exc:
20482053
if isinstance(exc, InvalidOperation) and self._topology._closed:
20492054
raise
@@ -2058,6 +2063,8 @@ def _process_periodic_tasks(self) -> None:
20582063
try:
20592064
self._process_kill_cursors()
20602065
self._topology.update_pool()
2066+
except asyncio.CancelledError:
2067+
raise
20612068
except Exception as exc:
20622069
if isinstance(exc, InvalidOperation) and self._topology._closed:
20632070
return

pymongo/synchronous/monitor.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from __future__ import annotations
1818

19+
import asyncio
1920
import atexit
2021
import logging
2122
import time
@@ -413,6 +414,8 @@ def _get_seedlist(self) -> Optional[list[tuple[str, Any]]]:
413414
if len(seedlist) == 0:
414415
# As per the spec: this should be treated as a failure.
415416
raise Exception
417+
except asyncio.CancelledError:
418+
raise
416419
except Exception:
417420
# As per the spec, upon encountering an error:
418421
# - An error must not be raised
@@ -476,6 +479,8 @@ def _run(self) -> None:
476479
except ReferenceError:
477480
# Topology was garbage-collected.
478481
self.close()
482+
except asyncio.CancelledError:
483+
raise
479484
except Exception:
480485
self._pool.reset()
481486

pymongo/synchronous/pool.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -702,6 +702,8 @@ def _close_conn(self) -> None:
702702
# shutdown.
703703
try:
704704
self.conn.close()
705+
except asyncio.CancelledError:
706+
raise
705707
except Exception: # noqa: S110
706708
pass
707709

test/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import asyncio
1919
import gc
20+
import logging
2021
import multiprocessing
2122
import os
2223
import signal
@@ -25,6 +26,7 @@
2526
import sys
2627
import threading
2728
import time
29+
import traceback
2830
import unittest
2931
import warnings
3032
from asyncio import iscoroutinefunction
@@ -191,6 +193,8 @@ def _connect(self, host, port, **kwargs):
191193
client.close()
192194

193195
def _init_client(self):
196+
self.mongoses = []
197+
self.connection_attempts = []
194198
self.client = self._connect(host, port)
195199
if self.client is not None:
196200
# Return early when connected to dataLake as mongohoused does not

test/asynchronous/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import asyncio
1919
import gc
20+
import logging
2021
import multiprocessing
2122
import os
2223
import signal
@@ -25,6 +26,7 @@
2526
import sys
2627
import threading
2728
import time
29+
import traceback
2830
import unittest
2931
import warnings
3032
from asyncio import iscoroutinefunction
@@ -191,6 +193,8 @@ async def _connect(self, host, port, **kwargs):
191193
await client.close()
192194

193195
async def _init_client(self):
196+
self.mongoses = []
197+
self.connection_attempts = []
194198
self.client = await self._connect(host, port)
195199
if self.client is not None:
196200
# Return early when connected to dataLake as mongohoused does not

test/asynchronous/test_auth_spec.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
sys.path[0:0] = [""]
2626

2727
from test import unittest
28-
from test.unified_format import generate_test_classes
28+
from test.asynchronous.unified_format import generate_test_classes
2929

3030
from pymongo import AsyncMongoClient
3131
from pymongo.asynchronous.auth_oidc import OIDCCallback

0 commit comments

Comments
 (0)