Skip to content

Commit 516ec00

Browse files
committed
Merge branch 'master' of github.com:mongodb/mongo-python-driver
2 parents 87bc432 + b5f0104 commit 516ec00

37 files changed

+1121
-176
lines changed

pymongo/asynchronous/mongo_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1195,7 +1195,8 @@ def __del__(self) -> None:
11951195
ResourceWarning,
11961196
stacklevel=2,
11971197
)
1198-
except AttributeError:
1198+
except (AttributeError, TypeError):
1199+
# Ignore errors at interpreter exit.
11991200
pass
12001201

12011202
def _close_cursor_soon(

pymongo/asynchronous/pool.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,9 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
12491249
async with self.lock:
12501250
conn_id = self.next_connection_id
12511251
self.next_connection_id += 1
1252+
# Use a temporary context so that interrupt_connections can cancel creating the socket.
1253+
tmp_context = _CancellationContext()
1254+
self.active_contexts.add(tmp_context)
12521255

12531256
listeners = self.opts._event_listeners
12541257
if self.enabled_for_cmap:
@@ -1267,6 +1270,8 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
12671270
try:
12681271
sock = await _configured_socket(self.address, self.opts)
12691272
except BaseException as error:
1273+
async with self.lock:
1274+
self.active_contexts.discard(tmp_context)
12701275
if self.enabled_for_cmap:
12711276
assert listeners is not None
12721277
listeners.publish_connection_closed(
@@ -1292,6 +1297,9 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
12921297
conn = AsyncConnection(sock, self, self.address, conn_id) # type: ignore[arg-type]
12931298
async with self.lock:
12941299
self.active_contexts.add(conn.cancel_context)
1300+
self.active_contexts.discard(tmp_context)
1301+
if tmp_context.cancelled:
1302+
conn.cancel_context.cancel()
12951303
try:
12961304
if self.handshake:
12971305
await conn.hello()
@@ -1301,6 +1309,8 @@ async def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> A
13011309

13021310
await conn.authenticate()
13031311
except BaseException:
1312+
async with self.lock:
1313+
self.active_contexts.discard(conn.cancel_context)
13041314
conn.close_conn(ConnectionClosedReason.ERROR)
13051315
raise
13061316

pymongo/message.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ def _gen_find_command(
252252
if limit < 0:
253253
cmd["singleBatch"] = True
254254
if batch_size:
255+
# When limit and batchSize are equal we increase batchSize by 1 to
256+
# avoid an unnecessary killCursors.
257+
if limit == batch_size:
258+
batch_size += 1
255259
cmd["batchSize"] = batch_size
256260
if read_concern.level and not (session and session.in_transaction):
257261
cmd["readConcern"] = read_concern.document

pymongo/pool_options.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,14 @@
7070
"version": platform.mac_ver()[0],
7171
}
7272
elif sys.platform == "win32":
73+
_ver = sys.getwindowsversion()
7374
_METADATA["os"] = {
74-
"type": platform.system(),
75-
# "Windows XP", "Windows 7", "Windows 10", etc.
76-
"name": " ".join((platform.system(), platform.release())),
77-
"architecture": platform.machine(),
78-
# Windows patch level (e.g. 5.1.2600-SP3)
79-
"version": "-".join(platform.win32_ver()[1:3]),
75+
"type": "Windows",
76+
"name": "Windows",
77+
# Avoid using platform calls, see PYTHON-4455.
78+
"architecture": os.environ.get("PROCESSOR_ARCHITECTURE") or platform.machine(),
79+
# Windows patch level (e.g. 10.0.17763-SP0).
80+
"version": ".".join(map(str, _ver[:3])) + f"-SP{_ver[-1] or '0'}",
8081
}
8182
elif sys.platform.startswith("java"):
8283
_name, _ver, _arch = platform.java_ver()[-1]

pymongo/synchronous/mongo_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,8 @@ def __del__(self) -> None:
11931193
ResourceWarning,
11941194
stacklevel=2,
11951195
)
1196-
except AttributeError:
1196+
except (AttributeError, TypeError):
1197+
# Ignore errors at interpreter exit.
11971198
pass
11981199

11991200
def _close_cursor_soon(

pymongo/synchronous/pool.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,9 @@ def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> Connect
12431243
with self.lock:
12441244
conn_id = self.next_connection_id
12451245
self.next_connection_id += 1
1246+
# Use a temporary context so that interrupt_connections can cancel creating the socket.
1247+
tmp_context = _CancellationContext()
1248+
self.active_contexts.add(tmp_context)
12461249

12471250
listeners = self.opts._event_listeners
12481251
if self.enabled_for_cmap:
@@ -1261,6 +1264,8 @@ def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> Connect
12611264
try:
12621265
sock = _configured_socket(self.address, self.opts)
12631266
except BaseException as error:
1267+
with self.lock:
1268+
self.active_contexts.discard(tmp_context)
12641269
if self.enabled_for_cmap:
12651270
assert listeners is not None
12661271
listeners.publish_connection_closed(
@@ -1286,6 +1291,9 @@ def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> Connect
12861291
conn = Connection(sock, self, self.address, conn_id) # type: ignore[arg-type]
12871292
with self.lock:
12881293
self.active_contexts.add(conn.cancel_context)
1294+
self.active_contexts.discard(tmp_context)
1295+
if tmp_context.cancelled:
1296+
conn.cancel_context.cancel()
12891297
try:
12901298
if self.handshake:
12911299
conn.hello()
@@ -1295,6 +1303,8 @@ def connect(self, handler: Optional[_MongoClientErrorHandler] = None) -> Connect
12951303

12961304
conn.authenticate()
12971305
except BaseException:
1306+
with self.lock:
1307+
self.active_contexts.discard(conn.cancel_context)
12981308
conn.close_conn(ConnectionClosedReason.ERROR)
12991309
raise
13001310

requirements/typing.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
mypy==1.13.0
2-
pyright==1.1.388
2+
pyright==1.1.389
33
typing_extensions
44
-r ./encryption.txt
55
-r ./ocsp.txt

test/asynchronous/test_retryable_reads.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,8 @@ async def test_retryable_reads_in_sharded_cluster_multiple_available(self):
174174
retryReads=True,
175175
)
176176

177-
async with self.fail_point(fail_command):
178-
with self.assertRaises(AutoReconnect):
179-
await client.t.t.find_one({})
177+
with self.assertRaises(AutoReconnect):
178+
await client.t.t.find_one({})
180179

181180
# Disable failpoints on each mongos
182181
for client in mongos_clients:

test/asynchronous/test_retryable_writes.py

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2017 MongoDB, Inc.
1+
# Copyright 2017-present MongoDB, Inc.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -43,7 +43,6 @@
4343
from bson.int64 import Int64
4444
from bson.raw_bson import RawBSONDocument
4545
from bson.son import SON
46-
from pymongo.asynchronous.mongo_client import AsyncMongoClient
4746
from pymongo.errors import (
4847
AutoReconnect,
4948
ConnectionFailure,
@@ -226,47 +225,6 @@ async def test_supported_single_statement_no_retry(self):
226225
f"{msg} sent txnNumber with {event.command_name}",
227226
)
228227

229-
@async_client_context.require_no_standalone
230-
async def test_supported_single_statement_supported_cluster(self):
231-
for method, args, kwargs in retryable_single_statement_ops(self.db.retryable_write_test):
232-
msg = f"{method.__name__}(*{args!r}, **{kwargs!r})"
233-
self.listener.reset()
234-
await method(*args, **kwargs)
235-
commands_started = self.listener.started_events
236-
self.assertEqual(len(self.listener.succeeded_events), 1, msg)
237-
first_attempt = commands_started[0]
238-
self.assertIn(
239-
"lsid",
240-
first_attempt.command,
241-
f"{msg} sent no lsid with {first_attempt.command_name}",
242-
)
243-
initial_session_id = first_attempt.command["lsid"]
244-
self.assertIn(
245-
"txnNumber",
246-
first_attempt.command,
247-
f"{msg} sent no txnNumber with {first_attempt.command_name}",
248-
)
249-
250-
# There should be no retry when the failpoint is not active.
251-
if async_client_context.is_mongos or not async_client_context.test_commands_enabled:
252-
self.assertEqual(len(commands_started), 1)
253-
continue
254-
255-
initial_transaction_id = first_attempt.command["txnNumber"]
256-
retry_attempt = commands_started[1]
257-
self.assertIn(
258-
"lsid",
259-
retry_attempt.command,
260-
f"{msg} sent no lsid with {first_attempt.command_name}",
261-
)
262-
self.assertEqual(retry_attempt.command["lsid"], initial_session_id, msg)
263-
self.assertIn(
264-
"txnNumber",
265-
retry_attempt.command,
266-
f"{msg} sent no txnNumber with {first_attempt.command_name}",
267-
)
268-
self.assertEqual(retry_attempt.command["txnNumber"], initial_transaction_id, msg)
269-
270228
async def test_supported_single_statement_unsupported_cluster(self):
271229
if async_client_context.is_rs or async_client_context.is_mongos:
272230
raise SkipTest("This cluster supports retryable writes")

test/asynchronous/unified_format.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
from pymongo.asynchronous.helpers import anext
7777
from pymongo.encryption_options import _HAVE_PYMONGOCRYPT
7878
from pymongo.errors import (
79+
AutoReconnect,
7980
BulkWriteError,
8081
ClientBulkWriteException,
8182
ConfigurationError,
@@ -545,15 +546,6 @@ def maybe_skip_test(self, spec):
545546
or "Cancel server check" in spec["description"]
546547
):
547548
self.skipTest("MMAPv1 does not support retryWrites=True")
548-
if (
549-
"AsyncDatabase-level aggregate with $out includes read preference for 5.0+ server"
550-
in spec["description"]
551-
):
552-
if async_client_context.version[0] == 8:
553-
self.skipTest("waiting on PYTHON-4356")
554-
if "Aggregate with $out includes read preference for 5.0+ server" in spec["description"]:
555-
if async_client_context.version[0] == 8:
556-
self.skipTest("waiting on PYTHON-4356")
557549
if "Client side error in command starting transaction" in spec["description"]:
558550
self.skipTest("Implement PYTHON-1894")
559551
if "timeoutMS applied to entire download" in spec["description"]:
@@ -764,9 +756,10 @@ async def kill_all_sessions(self):
764756
for client in clients:
765757
try:
766758
await client.admin.command("killAllSessions", [])
767-
except OperationFailure:
759+
except (OperationFailure, AutoReconnect):
768760
# "operation was interrupted" by killing the command's
769761
# own session.
762+
# On 8.0+ killAllSessions sometimes returns a network error.
770763
pass
771764

772765
async def _databaseOperation_listCollections(self, target, *args, **kwargs):

0 commit comments

Comments
 (0)