Merge branch 'master' of github.com:mongodb/mongo-python-driver

This commit is contained in:
Steven Silvester 2025-07-14 17:16:57 -05:00
commit 87876b575b
No known key found for this signature in database
GPG Key ID: B1BF5EC3A8B32F91
7 changed files with 66 additions and 57 deletions

View File

@ -837,7 +837,6 @@ pymongo.GEOHAYSTACK is removed
Removed :attr:`pymongo.GEOHAYSTACK`. Replace with "geoHaystack" or create a
2d index and use $geoNear or $geoWithin instead.
See https://dochub.mongodb.org/core/4.4-deprecate-geoHaystack.
UUIDLegacy is removed
---------------------

View File

@ -84,6 +84,7 @@ class TestClientBulkWrite(AsyncIntegrationTest):
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Note: tests 1 and 2 are in test_read_write_concern_spec.py
class TestClientBulkWriteCRUD(AsyncIntegrationTest):
async def asyncSetUp(self):
await super().asyncSetUp()
@ -92,7 +93,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.max_message_size_bytes = await async_client_context.max_message_size_bytes
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_batch_splits_if_num_operations_too_large(self):
async def test_3_batch_splits_if_num_operations_too_large(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -116,7 +117,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(first_event.operation_id, second_event.operation_id)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_batch_splits_if_ops_payload_too_large(self):
async def test_4_batch_splits_if_ops_payload_too_large(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -148,7 +149,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
@async_client_context.require_version_min(8, 0, 0, -24)
@async_client_context.require_failCommand_fail_point
async def test_collects_write_concern_errors_across_batches(self):
async def test_5_collects_write_concern_errors_across_batches(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(
event_listeners=[listener],
@ -189,7 +190,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(len(bulk_write_events), 2)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_collects_write_errors_across_batches_unordered(self):
async def test_6_collects_write_errors_across_batches_unordered(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -218,7 +219,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(len(bulk_write_events), 2)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_collects_write_errors_across_batches_ordered(self):
async def test_6_collects_write_errors_across_batches_ordered(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -247,7 +248,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(len(bulk_write_events), 1)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_handles_cursor_requiring_getMore(self):
async def test_7_handles_cursor_requiring_getMore(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -287,7 +288,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
@async_client_context.require_version_min(8, 0, 0, -24)
@async_client_context.require_no_standalone
async def test_handles_cursor_requiring_getMore_within_transaction(self):
async def test_8_handles_cursor_requiring_getMore_within_transaction(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -329,7 +330,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
@async_client_context.require_version_min(8, 0, 0, -24)
@async_client_context.require_failCommand_fail_point
async def test_handles_getMore_error(self):
async def test_9_handles_getMore_error(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -382,7 +383,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertTrue(kill_cursors_event)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_returns_error_if_unacknowledged_too_large_insert(self):
async def test_10_returns_error_if_unacknowledged_too_large_insert(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -441,7 +442,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
return num_models, models
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_no_batch_splits_if_new_namespace_is_not_too_large(self):
async def test_11_no_batch_splits_if_new_namespace_is_not_too_large(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -471,7 +472,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(event.command["nsInfo"][0]["ns"], "db.coll")
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_batch_splits_if_new_namespace_is_too_large(self):
async def test_11_batch_splits_if_new_namespace_is_too_large(self):
listener = OvertCommandListener()
client = await self.async_rs_or_single_client(event_listeners=[listener])
@ -508,25 +509,27 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(second_event.command["nsInfo"][0]["ns"], namespace)
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_returns_error_if_no_writes_can_be_added_to_ops(self):
async def test_12_returns_error_if_no_writes_can_be_added_to_ops(self):
client = await self.async_rs_or_single_client()
# Document too large.
b_repeated = "b" * self.max_message_size_bytes
models = [InsertOne(namespace="db.coll", document={"a": b_repeated})]
with self.assertRaises(DocumentTooLarge):
with self.assertRaises(DocumentTooLarge) as context:
await client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
# Namespace too large.
c_repeated = "c" * self.max_message_size_bytes
namespace = f"db.{c_repeated}"
models = [InsertOne(namespace=namespace, document={"a": "b"})]
with self.assertRaises(DocumentTooLarge):
with self.assertRaises(DocumentTooLarge) as context:
await client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
@async_client_context.require_version_min(8, 0, 0, -24)
@unittest.skipUnless(_HAVE_PYMONGOCRYPT, "pymongocrypt is not installed")
async def test_returns_error_if_auto_encryption_configured(self):
async def test_13_returns_error_if_auto_encryption_configured(self):
opts = AutoEncryptionOpts(
key_vault_namespace="db.coll",
kms_providers={"aws": {"accessKeyId": "foo", "secretAccessKey": "bar"}},
@ -536,6 +539,7 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
models = [InsertOne(namespace="db.coll", document={"a": "b"})]
with self.assertRaises(InvalidOperation) as context:
await client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
self.assertIn(
"bulk_write does not currently support automatic encryption", context.exception._message
)
@ -579,6 +583,8 @@ class TestClientBulkWriteCRUD(AsyncIntegrationTest):
self.assertEqual(result.update_results[1].did_upsert, True)
self.assertEqual(result.update_results[2].did_upsert, False)
# Note: test 14 is optional and intentionally not implemented because we provide multiple APIs to specify explain.
@async_client_context.require_version_min(8, 0, 0, -24)
async def test_15_unacknowledged_write_across_batches(self):
listener = OvertCommandListener()

View File

@ -1306,7 +1306,7 @@ class TestCustomEndpoint(AsyncEncryptionIntegrationTest):
kms_providers_invalid = copy.deepcopy(kms_providers)
kms_providers_invalid["azure"]["identityPlatformEndpoint"] = "doesnotexist.invalid:443"
kms_providers_invalid["gcp"]["endpoint"] = "doesnotexist.invalid:443"
kms_providers_invalid["kmip"]["endpoint"] = "doesnotexist.local:5698"
kms_providers_invalid["kmip"]["endpoint"] = "doesnotexist.invalid:5698"
self.client_encryption_invalid = self.create_client_encryption(
kms_providers=kms_providers_invalid,
key_vault_namespace="keyvault.datakeys",
@ -1364,15 +1364,10 @@ class TestCustomEndpoint(AsyncEncryptionIntegrationTest):
},
)
@unittest.skipUnless(any(AWS_CREDS.values()), "AWS environment credentials are not set")
async def test_04_aws_endpoint_invalid_port(self):
master_key = {
"region": "us-east-1",
"key": ("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0"),
"endpoint": "kms.us-east-1.amazonaws.com:12345",
}
with self.assertRaisesRegex(EncryptionError, "kms.us-east-1.amazonaws.com:12345"):
await self.client_encryption.create_data_key("aws", master_key=master_key)
async def test_04_kmip_endpoint_invalid_port(self):
master_key = {"keyId": "1", "endpoint": "localhost:12345"}
with self.assertRaisesRegex(EncryptionError, "localhost:12345"):
await self.client_encryption.create_data_key("kmip", master_key=master_key)
@unittest.skipUnless(any(AWS_CREDS.values()), "AWS environment credentials are not set")
async def test_05_aws_endpoint_wrong_region(self):
@ -1478,7 +1473,7 @@ class TestCustomEndpoint(AsyncEncryptionIntegrationTest):
self.assertEqual("test", await self.client_encryption_invalid.decrypt(encrypted))
async def test_12_kmip_master_key_invalid_endpoint(self):
key = {"keyId": "1", "endpoint": "doesnotexist.local:5698"}
key = {"keyId": "1", "endpoint": "doesnotexist.invalid:5698"}
with self.assertRaisesRegex(EncryptionError, self.kmip_host_error):
await self.client_encryption.create_data_key("kmip", key)
@ -2166,7 +2161,7 @@ class TestKmsTLSOptions(AsyncEncryptionIntegrationTest):
await self.client_encryption_invalid_hostname.create_data_key("aws", key)
async def test_02_azure(self):
key = {"keyVaultEndpoint": "doesnotexist.local", "keyName": "foo"}
key = {"keyVaultEndpoint": "doesnotexist.invalid", "keyName": "foo"}
# Missing client cert error.
with self.assertRaisesRegex(EncryptionError, self.cert_error):
await self.client_encryption_no_client_cert.create_data_key("azure", key)
@ -2241,7 +2236,7 @@ class TestKmsTLSOptions(AsyncEncryptionIntegrationTest):
await self.client_encryption_with_names.create_data_key("aws:with_tls", key)
async def test_06_named_kms_providers_apply_tls_options_azure(self):
key = {"keyVaultEndpoint": "doesnotexist.local", "keyName": "foo"}
key = {"keyVaultEndpoint": "doesnotexist.invalid", "keyName": "foo"}
# Missing client cert error.
with self.assertRaisesRegex(EncryptionError, self.cert_error):
await self.client_encryption_with_names.create_data_key("azure:no_client_cert", key)

View File

@ -180,6 +180,8 @@ class TestReadWriteConcernSpec(AsyncIntegrationTest):
WriteConcern(w=async_client_context.w, wtimeout=1), WTimeoutError
)
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Test 1 (included here instead of test_client_bulk_write.py)
@async_client_context.require_failCommand_fail_point
async def test_error_includes_errInfo(self):
expected_wce = {
@ -214,6 +216,8 @@ class TestReadWriteConcernSpec(AsyncIntegrationTest):
}
self.assertEqual(ctx.exception.details, expected_details)
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Test 2 (included here instead of test_client_bulk_write.py)
@async_client_context.require_version_min(4, 9)
async def test_write_error_details_exposes_errinfo(self):
listener = OvertCommandListener()

View File

@ -84,6 +84,7 @@ class TestClientBulkWrite(IntegrationTest):
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Note: tests 1 and 2 are in test_read_write_concern_spec.py
class TestClientBulkWriteCRUD(IntegrationTest):
def setUp(self):
super().setUp()
@ -92,7 +93,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.max_message_size_bytes = client_context.max_message_size_bytes
@client_context.require_version_min(8, 0, 0, -24)
def test_batch_splits_if_num_operations_too_large(self):
def test_3_batch_splits_if_num_operations_too_large(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -116,7 +117,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(first_event.operation_id, second_event.operation_id)
@client_context.require_version_min(8, 0, 0, -24)
def test_batch_splits_if_ops_payload_too_large(self):
def test_4_batch_splits_if_ops_payload_too_large(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -148,7 +149,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
@client_context.require_version_min(8, 0, 0, -24)
@client_context.require_failCommand_fail_point
def test_collects_write_concern_errors_across_batches(self):
def test_5_collects_write_concern_errors_across_batches(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(
event_listeners=[listener],
@ -189,7 +190,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(len(bulk_write_events), 2)
@client_context.require_version_min(8, 0, 0, -24)
def test_collects_write_errors_across_batches_unordered(self):
def test_6_collects_write_errors_across_batches_unordered(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -218,7 +219,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(len(bulk_write_events), 2)
@client_context.require_version_min(8, 0, 0, -24)
def test_collects_write_errors_across_batches_ordered(self):
def test_6_collects_write_errors_across_batches_ordered(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -247,7 +248,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(len(bulk_write_events), 1)
@client_context.require_version_min(8, 0, 0, -24)
def test_handles_cursor_requiring_getMore(self):
def test_7_handles_cursor_requiring_getMore(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -287,7 +288,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
@client_context.require_version_min(8, 0, 0, -24)
@client_context.require_no_standalone
def test_handles_cursor_requiring_getMore_within_transaction(self):
def test_8_handles_cursor_requiring_getMore_within_transaction(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -329,7 +330,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
@client_context.require_version_min(8, 0, 0, -24)
@client_context.require_failCommand_fail_point
def test_handles_getMore_error(self):
def test_9_handles_getMore_error(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -382,7 +383,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertTrue(kill_cursors_event)
@client_context.require_version_min(8, 0, 0, -24)
def test_returns_error_if_unacknowledged_too_large_insert(self):
def test_10_returns_error_if_unacknowledged_too_large_insert(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -437,7 +438,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
return num_models, models
@client_context.require_version_min(8, 0, 0, -24)
def test_no_batch_splits_if_new_namespace_is_not_too_large(self):
def test_11_no_batch_splits_if_new_namespace_is_not_too_large(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -467,7 +468,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(event.command["nsInfo"][0]["ns"], "db.coll")
@client_context.require_version_min(8, 0, 0, -24)
def test_batch_splits_if_new_namespace_is_too_large(self):
def test_11_batch_splits_if_new_namespace_is_too_large(self):
listener = OvertCommandListener()
client = self.rs_or_single_client(event_listeners=[listener])
@ -504,25 +505,27 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(second_event.command["nsInfo"][0]["ns"], namespace)
@client_context.require_version_min(8, 0, 0, -24)
def test_returns_error_if_no_writes_can_be_added_to_ops(self):
def test_12_returns_error_if_no_writes_can_be_added_to_ops(self):
client = self.rs_or_single_client()
# Document too large.
b_repeated = "b" * self.max_message_size_bytes
models = [InsertOne(namespace="db.coll", document={"a": b_repeated})]
with self.assertRaises(DocumentTooLarge):
with self.assertRaises(DocumentTooLarge) as context:
client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
# Namespace too large.
c_repeated = "c" * self.max_message_size_bytes
namespace = f"db.{c_repeated}"
models = [InsertOne(namespace=namespace, document={"a": "b"})]
with self.assertRaises(DocumentTooLarge):
with self.assertRaises(DocumentTooLarge) as context:
client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
@client_context.require_version_min(8, 0, 0, -24)
@unittest.skipUnless(_HAVE_PYMONGOCRYPT, "pymongocrypt is not installed")
def test_returns_error_if_auto_encryption_configured(self):
def test_13_returns_error_if_auto_encryption_configured(self):
opts = AutoEncryptionOpts(
key_vault_namespace="db.coll",
kms_providers={"aws": {"accessKeyId": "foo", "secretAccessKey": "bar"}},
@ -532,6 +535,7 @@ class TestClientBulkWriteCRUD(IntegrationTest):
models = [InsertOne(namespace="db.coll", document={"a": "b"})]
with self.assertRaises(InvalidOperation) as context:
client.bulk_write(models=models)
self.assertIsNone(context.exception.partial_result)
self.assertIn(
"bulk_write does not currently support automatic encryption", context.exception._message
)
@ -575,6 +579,8 @@ class TestClientBulkWriteCRUD(IntegrationTest):
self.assertEqual(result.update_results[1].did_upsert, True)
self.assertEqual(result.update_results[2].did_upsert, False)
# Note: test 14 is optional and intentionally not implemented because we provide multiple APIs to specify explain.
@client_context.require_version_min(8, 0, 0, -24)
def test_15_unacknowledged_write_across_batches(self):
listener = OvertCommandListener()

View File

@ -1302,7 +1302,7 @@ class TestCustomEndpoint(EncryptionIntegrationTest):
kms_providers_invalid = copy.deepcopy(kms_providers)
kms_providers_invalid["azure"]["identityPlatformEndpoint"] = "doesnotexist.invalid:443"
kms_providers_invalid["gcp"]["endpoint"] = "doesnotexist.invalid:443"
kms_providers_invalid["kmip"]["endpoint"] = "doesnotexist.local:5698"
kms_providers_invalid["kmip"]["endpoint"] = "doesnotexist.invalid:5698"
self.client_encryption_invalid = self.create_client_encryption(
kms_providers=kms_providers_invalid,
key_vault_namespace="keyvault.datakeys",
@ -1358,15 +1358,10 @@ class TestCustomEndpoint(EncryptionIntegrationTest):
},
)
@unittest.skipUnless(any(AWS_CREDS.values()), "AWS environment credentials are not set")
def test_04_aws_endpoint_invalid_port(self):
master_key = {
"region": "us-east-1",
"key": ("arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0"),
"endpoint": "kms.us-east-1.amazonaws.com:12345",
}
with self.assertRaisesRegex(EncryptionError, "kms.us-east-1.amazonaws.com:12345"):
self.client_encryption.create_data_key("aws", master_key=master_key)
def test_04_kmip_endpoint_invalid_port(self):
master_key = {"keyId": "1", "endpoint": "localhost:12345"}
with self.assertRaisesRegex(EncryptionError, "localhost:12345"):
self.client_encryption.create_data_key("kmip", master_key=master_key)
@unittest.skipUnless(any(AWS_CREDS.values()), "AWS environment credentials are not set")
def test_05_aws_endpoint_wrong_region(self):
@ -1472,7 +1467,7 @@ class TestCustomEndpoint(EncryptionIntegrationTest):
self.assertEqual("test", self.client_encryption_invalid.decrypt(encrypted))
def test_12_kmip_master_key_invalid_endpoint(self):
key = {"keyId": "1", "endpoint": "doesnotexist.local:5698"}
key = {"keyId": "1", "endpoint": "doesnotexist.invalid:5698"}
with self.assertRaisesRegex(EncryptionError, self.kmip_host_error):
self.client_encryption.create_data_key("kmip", key)
@ -2158,7 +2153,7 @@ class TestKmsTLSOptions(EncryptionIntegrationTest):
self.client_encryption_invalid_hostname.create_data_key("aws", key)
def test_02_azure(self):
key = {"keyVaultEndpoint": "doesnotexist.local", "keyName": "foo"}
key = {"keyVaultEndpoint": "doesnotexist.invalid", "keyName": "foo"}
# Missing client cert error.
with self.assertRaisesRegex(EncryptionError, self.cert_error):
self.client_encryption_no_client_cert.create_data_key("azure", key)
@ -2233,7 +2228,7 @@ class TestKmsTLSOptions(EncryptionIntegrationTest):
self.client_encryption_with_names.create_data_key("aws:with_tls", key)
def test_06_named_kms_providers_apply_tls_options_azure(self):
key = {"keyVaultEndpoint": "doesnotexist.local", "keyName": "foo"}
key = {"keyVaultEndpoint": "doesnotexist.invalid", "keyName": "foo"}
# Missing client cert error.
with self.assertRaisesRegex(EncryptionError, self.cert_error):
self.client_encryption_with_names.create_data_key("azure:no_client_cert", key)

View File

@ -178,6 +178,8 @@ class TestReadWriteConcernSpec(IntegrationTest):
self.disable_replication(client_context.client)
self.assertWriteOpsRaise(WriteConcern(w=client_context.w, wtimeout=1), WTimeoutError)
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Test 1 (included here instead of test_client_bulk_write.py)
@client_context.require_failCommand_fail_point
def test_error_includes_errInfo(self):
expected_wce = {
@ -212,6 +214,8 @@ class TestReadWriteConcernSpec(IntegrationTest):
}
self.assertEqual(ctx.exception.details, expected_details)
# https://github.com/mongodb/specifications/tree/master/source/crud/tests
# Test 2 (included here instead of test_client_bulk_write.py)
@client_context.require_version_min(4, 9)
def test_write_error_details_exposes_errinfo(self):
listener = OvertCommandListener()