datamasque-python 1.2.5__tar.gz → 1.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/HISTORY.rst +17 -0
  2. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/PKG-INFO +1 -1
  3. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/__init__.py +2 -0
  4. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/connection.py +36 -2
  5. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/pyproject.toml +1 -1
  6. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_connections.py +75 -6
  7. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/uv.lock +1 -1
  8. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/.editorconfig +0 -0
  9. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/.github/workflows/ci.yml +0 -0
  10. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/.github/workflows/release-testpypi.yml +0 -0
  11. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/.github/workflows/release.yml +0 -0
  12. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/.gitignore +0 -0
  13. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/.readthedocs.yaml +0 -0
  14. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/CONTRIBUTING.rst +0 -0
  15. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/LICENSE +0 -0
  16. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/MANIFEST.in +0 -0
  17. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/Makefile +0 -0
  18. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/NOTICE +0 -0
  19. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/README.rst +0 -0
  20. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/RELEASING.rst +0 -0
  21. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/base.py +0 -0
  22. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/connections.py +0 -0
  23. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/discovery.py +0 -0
  24. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/discovery_config_libraries.py +0 -0
  25. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/discovery_configs.py +0 -0
  26. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/dmclient.py +0 -0
  27. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/exceptions.py +0 -0
  28. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/files.py +0 -0
  29. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/ifm.py +0 -0
  30. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/license.py +0 -0
  31. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/__init__.py +0 -0
  32. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/data_selection.py +0 -0
  33. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/discovery.py +0 -0
  34. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/discovery_config.py +0 -0
  35. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/discovery_config_library.py +0 -0
  36. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/dm_instance.py +0 -0
  37. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/files.py +0 -0
  38. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/git.py +0 -0
  39. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/ifm.py +0 -0
  40. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/license.py +0 -0
  41. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/pagination.py +0 -0
  42. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/rg_config.py +0 -0
  43. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/ruleset.py +0 -0
  44. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/ruleset_library.py +0 -0
  45. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/runs.py +0 -0
  46. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/safe_data_preview.py +0 -0
  47. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/status.py +0 -0
  48. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/table_reference.py +0 -0
  49. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/models/user.py +0 -0
  50. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/py.typed +0 -0
  51. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/rg_configs.py +0 -0
  52. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/ruleset_libraries.py +0 -0
  53. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/rulesets.py +0 -0
  54. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/runs.py +0 -0
  55. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/settings.py +0 -0
  56. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/spcs.py +0 -0
  57. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/table_references.py +0 -0
  58. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/datamasque/client/users.py +0 -0
  59. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/Makefile +0 -0
  60. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/client.models.rst +0 -0
  61. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/client.rst +0 -0
  62. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/conf.py +0 -0
  63. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/contributing.rst +0 -0
  64. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/history.rst +0 -0
  65. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/index.rst +0 -0
  66. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/installation.rst +0 -0
  67. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/make.bat +0 -0
  68. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/modules.rst +0 -0
  69. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/readme.rst +0 -0
  70. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/docs/usage.rst +0 -0
  71. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/__init__.py +0 -0
  72. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/conftest.py +0 -0
  73. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/helpers.py +0 -0
  74. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_base.py +0 -0
  75. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_discovery.py +0 -0
  76. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_discovery_config_libraries.py +0 -0
  77. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_discovery_configs.py +0 -0
  78. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_files.py +0 -0
  79. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_ifm.py +0 -0
  80. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_license.py +0 -0
  81. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_pagination.py +0 -0
  82. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_rg_configs.py +0 -0
  83. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_ruleset_library.py +0 -0
  84. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_rulesets.py +0 -0
  85. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_runs.py +0 -0
  86. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_safe_data_preview.py +0 -0
  87. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_settings.py +0 -0
  88. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_spcs.py +0 -0
  89. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_table_references.py +0 -0
  90. {datamasque_python-1.2.5 → datamasque_python-1.3.0}/tests/test_users.py +0 -0
@@ -2,6 +2,23 @@
2
2
  History
3
3
  =======
4
4
 
5
+ 1.3.0 (2026-09-04)
6
+ ------------------
7
+
8
+ * Added ``CosmosDbConnectionConfig`` and the ``cosmosdb`` ``DatabaseType``, for Azure Cosmos DB.
9
+ It differs from ``MongoConnectionConfig`` by ``db_type`` and three defaults: ``port`` 10255,
10
+ ``tls`` on, because Cosmos DB only accepts TLS connections, and ``retry_writes`` off, because
11
+ Cosmos DB rejects retryable inserts.
12
+
13
+ * ``DatabaseConnectionConfig`` now rejects the ``documentdb`` and ``cosmosdb`` database types with a
14
+ message naming the class to use, as it already did for MongoDB, DynamoDB, Snowflake and Databricks.
15
+
16
+ * The MongoDB serializer prunes ``tls`` and ``retry_writes`` against the concrete class's default
17
+ rather than MongoDB's, so a Cosmos DB config that sets either the other way now sends it instead
18
+ of leaving the server to apply its own default.
19
+
20
+ Requires server version 3.26.17
21
+
5
22
  1.2.5 (2026-08-17)
6
23
  ------------------
7
24
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: datamasque-python
3
- Version: 1.2.5
3
+ Version: 1.3.0
4
4
  Summary: Official Python client for the DataMasque data-masking API.
5
5
  Project-URL: Homepage, https://datamasque.com/
6
6
  Project-URL: Documentation, https://datamasque-python.readthedocs.io/
@@ -31,6 +31,7 @@ from datamasque.client.models.connection import (
31
31
  AzureConnectionConfig,
32
32
  ConnectionConfig,
33
33
  ConnectionId,
34
+ CosmosDbConnectionConfig,
34
35
  DatabaseConnectionConfig,
35
36
  DatabaseType,
36
37
  DatabricksConnectionConfig,
@@ -203,6 +204,7 @@ __all__ = [
203
204
  "DiscoveryConfigNotFoundError",
204
205
  "DiscoveryConfigType",
205
206
  "DiscoveryMatch",
207
+ "CosmosDbConnectionConfig",
206
208
  "DocumentDbConnectionConfig",
207
209
  "DynamoConnectionConfig",
208
210
  "FailedToStartError",
@@ -45,6 +45,7 @@ class DatabaseType(Enum):
45
45
  snowflake = "snowflake"
46
46
  mongodb = "mongodb"
47
47
  documentdb = "documentdb"
48
+ cosmosdb = "cosmosdb"
48
49
  databricks_lakebase = "databricks_lakebase"
49
50
  databricks = "databricks"
50
51
  informix = "informix"
@@ -184,8 +185,11 @@ class MongoConnectionConfig(ConnectionConfig):
184
185
  password = d.pop("password", None)
185
186
  if password:
186
187
  d["dbpassword"] = password
188
+ # `tls` and `retry_writes` are pruned against this class's own default, not MongoDB's:
189
+ # the subclasses invert them, so a hardcoded comparison would drop the very value the
190
+ # caller set and let the server apply its default instead.
191
+ defaults = type(self).model_fields
187
192
  if not d.get("tls"):
188
- d.pop("tls", None)
189
193
  d.pop("tls_ca_file", None)
190
194
  d.pop("tls_allow_invalid_certificates", None)
191
195
  else:
@@ -193,9 +197,11 @@ class MongoConnectionConfig(ConnectionConfig):
193
197
  d.pop("tls_ca_file", None)
194
198
  if not d.get("tls_allow_invalid_certificates"):
195
199
  d.pop("tls_allow_invalid_certificates", None)
200
+ if d.get("tls") == defaults["tls"].default:
201
+ d.pop("tls", None)
196
202
  if not d.get("direct_connection"):
197
203
  d.pop("direct_connection", None)
198
- if d.get("retry_writes", True):
204
+ if d.get("retry_writes") == defaults["retry_writes"].default:
199
205
  d.pop("retry_writes", None)
200
206
  if not d.get("replica_set"):
201
207
  d.pop("replica_set", None)
@@ -229,6 +235,29 @@ class DocumentDbConnectionConfig(MongoConnectionConfig):
229
235
  return DatabaseType.documentdb
230
236
 
231
237
 
238
+ class CosmosDbConnectionConfig(MongoConnectionConfig):
239
+ """
240
+ Connection configuration for an Azure Cosmos DB account.
241
+
242
+ Cosmos DB listens on 10255, only accepts TLS connections and rejects retryable inserts, so it
243
+ differs from `MongoConnectionConfig` by `db_type`/`database_type` and those three defaults.
244
+
245
+ They are defaults, not constraints. Setting one the other way sends it to the server: with
246
+ retryable writes on, masking still runs (it only updates) but the run-history insert is
247
+ rejected, so the run is not recorded.
248
+ """
249
+
250
+ # Narrowing the inherited Literal is a deliberate Pydantic discriminator override.
251
+ db_type: Literal["cosmosdb"] = "cosmosdb" # type: ignore[assignment]
252
+ port: int = 10255
253
+ tls: bool = True
254
+ retry_writes: bool = False
255
+
256
+ @property
257
+ def database_type(self) -> DatabaseType:
258
+ return DatabaseType.cosmosdb
259
+
260
+
232
261
  class SnowflakeConnectionConfig(ConnectionConfig):
233
262
  """
234
263
  Connection configuration for a Snowflake database.
@@ -339,6 +368,10 @@ class DatabaseConnectionConfig(ConnectionConfig):
339
368
  raise ValueError("For Snowflake, use the SnowflakeConnectionConfig class instead")
340
369
  if self.database_type is DatabaseType.mongodb:
341
370
  raise ValueError("For MongoDB, use the MongoConnectionConfig class instead")
371
+ if self.database_type is DatabaseType.documentdb:
372
+ raise ValueError("For AWS DocumentDB, use the DocumentDbConnectionConfig class instead")
373
+ if self.database_type is DatabaseType.cosmosdb:
374
+ raise ValueError("For Azure Cosmos DB, use the CosmosDbConnectionConfig class instead")
342
375
  if self.database_type is DatabaseType.databricks:
343
376
  raise ValueError("For Databricks SQL Warehouse, use the DatabricksConnectionConfig class instead")
344
377
  return self
@@ -490,6 +523,7 @@ DB_TYPE_MAP: dict[str, type[ConnectionConfig]] = {
490
523
  DatabaseType.dynamodb.value: DynamoConnectionConfig,
491
524
  DatabaseType.mongodb.value: MongoConnectionConfig,
492
525
  DatabaseType.documentdb.value: DocumentDbConnectionConfig,
526
+ DatabaseType.cosmosdb.value: CosmosDbConnectionConfig,
493
527
  DatabaseType.snowflake.value: SnowflakeConnectionConfig,
494
528
  DatabaseType.mssql_linked.value: MssqlLinkedServerConnectionConfig,
495
529
  DatabaseType.databricks.value: DatabricksConnectionConfig,
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "datamasque-python"
3
- version = "1.2.5"
3
+ version = "1.3.0"
4
4
  description = "Official Python client for the DataMasque data-masking API."
5
5
  authors = [
6
6
  { name = "DataMasque Ltd" },
@@ -7,6 +7,7 @@ from datamasque.client.exceptions import DataMasqueApiError, DataMasqueException
7
7
  from datamasque.client.models.connection import (
8
8
  AzureConnectionConfig,
9
9
  ConnectionId,
10
+ CosmosDbConnectionConfig,
10
11
  DatabaseConnectionConfig,
11
12
  DatabaseType,
12
13
  DatabricksConnectionConfig,
@@ -1360,17 +1361,85 @@ def test_connection_config_dispatch_picks_documentdb_subclass():
1360
1361
  assert conn.database_type is DatabaseType.documentdb
1361
1362
 
1362
1363
 
1363
- def test_database_connection_config_rejects_mongodb_database_type():
1364
- """`DatabaseConnectionConfig` is for SQL engines; MongoDB users must use `MongoConnectionConfig`."""
1365
- with pytest.raises(ValueError, match="For MongoDB"):
1364
+ def _cosmosdb_connection(**overrides) -> CosmosDbConnectionConfig:
1365
+ """Build a Cosmos DB config, leaving the port to the connection type's own default."""
1366
+ return CosmosDbConnectionConfig(
1367
+ name="cosmos",
1368
+ host="dtq-cosmos.mongo.cosmos.azure.com",
1369
+ database="people",
1370
+ user="dtq-cosmos",
1371
+ password="hunter2",
1372
+ **overrides,
1373
+ )
1374
+
1375
+
1376
+ def test_cosmosdb_connection_defaults_port_tls_and_retry_writes():
1377
+ """
1378
+ Cosmos DB listens on 10255, only accepts TLS connections and rejects retryable writes.
1379
+
1380
+ The config carries all three as defaults. `tls` and `retry_writes` are omitted from the payload
1381
+ because the server defaults the same way for this connection type; the port is always sent.
1382
+ """
1383
+ conn = _cosmosdb_connection()
1384
+ d = conn.model_dump(exclude_none=True, by_alias=True, mode="json")
1385
+ assert d["db_type"] == "cosmosdb"
1386
+ assert d["mask_type"] == "database"
1387
+ assert conn.port == 10255
1388
+ assert d["port"] == 10255
1389
+ assert conn.tls is True
1390
+ assert conn.retry_writes is False
1391
+ assert "tls" not in d
1392
+ assert "retry_writes" not in d
1393
+ assert conn.database_type is DatabaseType.cosmosdb
1394
+
1395
+
1396
+ def test_cosmosdb_connection_sends_values_that_differ_from_its_own_defaults():
1397
+ """
1398
+ The Mongo serializer prunes a field whose value matches the default the server would apply.
1399
+
1400
+ Cosmos DB inverts two of MongoDB's defaults, so pruning against MongoDB's would drop exactly
1401
+ the value the caller set and leave the server applying the opposite.
1402
+ """
1403
+ d = _cosmosdb_connection(tls=False, retry_writes=True).model_dump(exclude_none=True, by_alias=True, mode="json")
1404
+ assert d["tls"] is False
1405
+ assert d["retry_writes"] is True
1406
+
1407
+
1408
+ def test_connection_config_dispatch_picks_cosmosdb_subclass():
1409
+ payload = {
1410
+ "id": "cosmos-id-1",
1411
+ "name": "cosmos",
1412
+ "mask_type": "database",
1413
+ "db_type": "cosmosdb",
1414
+ "host": "dtq-cosmos.mongo.cosmos.azure.com",
1415
+ "database": "people",
1416
+ }
1417
+ conn = validate_connection(payload)
1418
+ assert isinstance(conn, CosmosDbConnectionConfig)
1419
+ assert conn.database_type is DatabaseType.cosmosdb
1420
+
1421
+
1422
+ @pytest.mark.parametrize(
1423
+ ("database_type", "message"),
1424
+ [
1425
+ (DatabaseType.mongodb, "For MongoDB"),
1426
+ (DatabaseType.documentdb, "For AWS DocumentDB"),
1427
+ (DatabaseType.cosmosdb, "For Azure Cosmos DB"),
1428
+ ],
1429
+ )
1430
+ def test_database_connection_config_rejects_document_store_database_types(
1431
+ database_type: DatabaseType, message: str
1432
+ ) -> None:
1433
+ """`DatabaseConnectionConfig` is for SQL engines; each document store has its own class."""
1434
+ with pytest.raises(ValueError, match=message):
1366
1435
  DatabaseConnectionConfig(
1367
- name="mongo",
1368
- host="mongo.example",
1436
+ name="doc-store",
1437
+ host="doc-store.example",
1369
1438
  port=27017,
1370
1439
  database="people",
1371
1440
  user="alice",
1372
1441
  password="hunter2",
1373
- database_type=DatabaseType.mongodb,
1442
+ database_type=database_type,
1374
1443
  )
1375
1444
 
1376
1445
 
@@ -419,7 +419,7 @@ toml = [
419
419
 
420
420
  [[package]]
421
421
  name = "datamasque-python"
422
- version = "1.2.5"
422
+ version = "1.3.0"
423
423
  source = { editable = "." }
424
424
  dependencies = [
425
425
  { name = "pydantic" },