datamasque-python 1.5.0__tar.gz → 1.5.1__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.
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/HISTORY.rst +8 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/PKG-INFO +1 -1
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/base.py +5 -1
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/ifm.py +4 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/dm_instance.py +7 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/ifm.py +5 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/pyproject.toml +1 -1
- datamasque_python-1.5.1/tests/test_timeouts.py +58 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/uv.lock +1 -1
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/.editorconfig +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/.github/workflows/ci.yml +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/.github/workflows/release-testpypi.yml +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/.github/workflows/release.yml +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/.gitignore +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/.readthedocs.yaml +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/CONTRIBUTING.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/LICENSE +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/MANIFEST.in +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/Makefile +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/NOTICE +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/README.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/RELEASING.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/__init__.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/connections.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/discovery.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/discovery_config_libraries.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/discovery_configs.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/dmclient.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/exceptions.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/files.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/license.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/__init__.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/connection.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/data_selection.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/discovery.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/discovery_config.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/discovery_config_library.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/files.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/git.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/license.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/pagination.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/rg_config.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/ruleset.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/ruleset_library.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/runs.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/safe_data_preview.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/status.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/table_reference.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/user.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/py.typed +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/rg_configs.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/ruleset_libraries.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/rulesets.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/runs.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/settings.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/spcs.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/table_references.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/users.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/Makefile +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/client.models.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/client.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/conf.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/contributing.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/history.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/index.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/installation.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/make.bat +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/modules.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/readme.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/docs/usage.rst +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/__init__.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/conftest.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/helpers.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_base.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_connections.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_discovery.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_discovery_config_libraries.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_discovery_configs.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_files.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_ifm.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_license.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_pagination.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_rg_configs.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_ruleset_library.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_rulesets.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_runs.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_safe_data_preview.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_settings.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_spcs.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_table_references.py +0 -0
- {datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_users.py +0 -0
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
History
|
|
3
3
|
=======
|
|
4
4
|
|
|
5
|
+
1.5.1 (2026-11-05)
|
|
6
|
+
------------------
|
|
7
|
+
|
|
8
|
+
* Every request now has a connect and a read timeout,
|
|
9
|
+
so a server that stops answering raises ``DataMasqueTransportError`` instead of hanging the caller.
|
|
10
|
+
Set them with ``connect_timeout_seconds`` (default 10) and ``read_timeout_seconds`` (default 300)
|
|
11
|
+
on the instance config models.
|
|
12
|
+
|
|
5
13
|
1.5.0 (2026-10-07)
|
|
6
14
|
------------------
|
|
7
15
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: datamasque-python
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.1
|
|
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/
|
|
@@ -138,6 +138,7 @@ class BaseClient:
|
|
|
138
138
|
self.verify_ssl = connection_config.verify_ssl
|
|
139
139
|
self.token_source = connection_config.token_source
|
|
140
140
|
self._session = _build_session(self.verify_ssl)
|
|
141
|
+
self._timeout = (connection_config.connect_timeout_seconds, connection_config.read_timeout_seconds)
|
|
141
142
|
if connection_config.spcs_pat:
|
|
142
143
|
install_spcs_gateway_auth(self._session, connection_config.spcs_pat)
|
|
143
144
|
|
|
@@ -252,8 +253,11 @@ class BaseClient:
|
|
|
252
253
|
params=params,
|
|
253
254
|
headers=headers,
|
|
254
255
|
files=files_payload,
|
|
256
|
+
timeout=self._timeout,
|
|
255
257
|
)
|
|
256
|
-
return self._session.request(
|
|
258
|
+
return self._session.request(
|
|
259
|
+
method, url, json=data, params=params, headers=headers, timeout=self._timeout
|
|
260
|
+
)
|
|
257
261
|
except requests.RequestException as e:
|
|
258
262
|
raise DataMasqueTransportError(f"Failed to reach DataMasque server at {url}: {e}") from e
|
|
259
263
|
|
|
@@ -85,6 +85,7 @@ class DataMasqueIfmClient:
|
|
|
85
85
|
# One session for both admin-server (JWT login/refresh) and IFM (data plane)
|
|
86
86
|
# traffic -- different hosts, but a single session handles per-host pooling.
|
|
87
87
|
self._session = _build_session(self.verify_ssl)
|
|
88
|
+
self._timeout = (connection_config.connect_timeout_seconds, connection_config.read_timeout_seconds)
|
|
88
89
|
|
|
89
90
|
def authenticate(self) -> None:
|
|
90
91
|
"""Obtain an access (and refresh) token from the admin server, or via `token_source`."""
|
|
@@ -101,6 +102,7 @@ class DataMasqueIfmClient:
|
|
|
101
102
|
response = self._session.post(
|
|
102
103
|
login_url,
|
|
103
104
|
json={"username": self.username, "password": self.password},
|
|
105
|
+
timeout=self._timeout,
|
|
104
106
|
)
|
|
105
107
|
except requests.RequestException as e:
|
|
106
108
|
raise DataMasqueTransportError(f"Failed to reach admin server at {login_url}: {e}") from e
|
|
@@ -127,6 +129,7 @@ class DataMasqueIfmClient:
|
|
|
127
129
|
response = self._session.post(
|
|
128
130
|
refresh_url,
|
|
129
131
|
json={"refresh": self.refresh_token},
|
|
132
|
+
timeout=self._timeout,
|
|
130
133
|
)
|
|
131
134
|
except requests.RequestException as e:
|
|
132
135
|
raise DataMasqueTransportError(f"Failed to reach admin server at {refresh_url}: {e}") from e
|
|
@@ -194,6 +197,7 @@ class DataMasqueIfmClient:
|
|
|
194
197
|
json=json_body,
|
|
195
198
|
params=params,
|
|
196
199
|
headers={"Authorization": f"Bearer {self.access_token}"},
|
|
200
|
+
timeout=self._timeout,
|
|
197
201
|
)
|
|
198
202
|
except requests.RequestException as e:
|
|
199
203
|
raise DataMasqueTransportError(f"Failed to reach IFM server at {url}: {e}") from e
|
|
@@ -4,6 +4,9 @@ from pydantic import BaseModel, ConfigDict, model_validator
|
|
|
4
4
|
|
|
5
5
|
from datamasque.client.exceptions import DataMasqueUserError
|
|
6
6
|
|
|
7
|
+
DEFAULT_CONNECT_TIMEOUT_SECONDS = 10.0
|
|
8
|
+
DEFAULT_READ_TIMEOUT_SECONDS = 300.0
|
|
9
|
+
|
|
7
10
|
|
|
8
11
|
class DataMasqueInstanceConfig(BaseModel):
|
|
9
12
|
"""
|
|
@@ -42,6 +45,10 @@ class DataMasqueInstanceConfig(BaseModel):
|
|
|
42
45
|
Create the token in Snowsight (User profile → Programmatic access tokens)
|
|
43
46
|
for an account that can reach the SPCS app.
|
|
44
47
|
Leave unset for instances that are not hosted on SPCS."""
|
|
48
|
+
connect_timeout_seconds: float = DEFAULT_CONNECT_TIMEOUT_SECONDS
|
|
49
|
+
"""How long a request may take to connect, including the TLS handshake."""
|
|
50
|
+
read_timeout_seconds: float = DEFAULT_READ_TIMEOUT_SECONDS
|
|
51
|
+
"""How long a request may wait for the server between bytes of its response."""
|
|
45
52
|
|
|
46
53
|
@model_validator(mode="after")
|
|
47
54
|
def _validate_auth_source(self) -> "DataMasqueInstanceConfig":
|
|
@@ -6,6 +6,7 @@ from typing import Any, Callable, Optional
|
|
|
6
6
|
from pydantic import BaseModel, ConfigDict, model_validator
|
|
7
7
|
|
|
8
8
|
from datamasque.client.exceptions import DataMasqueUserError
|
|
9
|
+
from datamasque.client.models.dm_instance import DEFAULT_CONNECT_TIMEOUT_SECONDS, DEFAULT_READ_TIMEOUT_SECONDS
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class RulesetPlanOptions(BaseModel):
|
|
@@ -167,6 +168,10 @@ class DataMasqueIfmInstanceConfig(BaseModel):
|
|
|
167
168
|
password: Optional[str] = None
|
|
168
169
|
verify_ssl: bool = True
|
|
169
170
|
token_source: Optional[Callable[[], str]] = None
|
|
171
|
+
connect_timeout_seconds: float = DEFAULT_CONNECT_TIMEOUT_SECONDS
|
|
172
|
+
"""How long a request may take to connect, including the TLS handshake."""
|
|
173
|
+
read_timeout_seconds: float = DEFAULT_READ_TIMEOUT_SECONDS
|
|
174
|
+
"""How long a request may wait for the server between bytes of its response."""
|
|
170
175
|
|
|
171
176
|
@model_validator(mode="after")
|
|
172
177
|
def _validate_auth_source(self) -> "DataMasqueIfmInstanceConfig":
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Every request the clients send has a connect and a read timeout."""
|
|
2
|
+
|
|
3
|
+
import requests_mock
|
|
4
|
+
|
|
5
|
+
from datamasque.client import DataMasqueClient, DataMasqueIfmClient, DataMasqueIfmInstanceConfig
|
|
6
|
+
from datamasque.client.models.dm_instance import DEFAULT_CONNECT_TIMEOUT_SECONDS, DEFAULT_READ_TIMEOUT_SECONDS
|
|
7
|
+
|
|
8
|
+
ADMIN = "http://admin.test"
|
|
9
|
+
IFM = "http://ifm.test"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_a_request_gets_the_default_timeouts(client):
|
|
13
|
+
with requests_mock.Mocker() as m:
|
|
14
|
+
m.get("http://test-server/api/healthcheck/", status_code=200)
|
|
15
|
+
client.healthcheck()
|
|
16
|
+
|
|
17
|
+
assert m.request_history[0].timeout == (DEFAULT_CONNECT_TIMEOUT_SECONDS, DEFAULT_READ_TIMEOUT_SECONDS)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_the_timeouts_come_from_the_config(config):
|
|
21
|
+
client = DataMasqueClient(config.model_copy(update={"connect_timeout_seconds": 3, "read_timeout_seconds": 900}))
|
|
22
|
+
|
|
23
|
+
with requests_mock.Mocker() as m:
|
|
24
|
+
m.post("http://test-server/api/auth/token/login/", json={"key": "test_token"}, status_code=200)
|
|
25
|
+
client.authenticate()
|
|
26
|
+
|
|
27
|
+
assert m.request_history[0].timeout == (3, 900)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_every_ifm_request_gets_the_config_timeouts():
|
|
31
|
+
client = DataMasqueIfmClient(
|
|
32
|
+
DataMasqueIfmInstanceConfig(
|
|
33
|
+
admin_server_base_url=ADMIN,
|
|
34
|
+
ifm_base_url=IFM,
|
|
35
|
+
username="ifm_user",
|
|
36
|
+
password="ifm_password",
|
|
37
|
+
read_timeout_seconds=60,
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
client.access_token = "access-1"
|
|
41
|
+
client.refresh_token = "refresh-1"
|
|
42
|
+
|
|
43
|
+
with requests_mock.Mocker() as m:
|
|
44
|
+
m.get(
|
|
45
|
+
f"{IFM}/ruleset-plans/",
|
|
46
|
+
[{"status_code": 401}, {"json": {"items": [], "total": 0, "limit": 100, "offset": 0}, "status_code": 200}],
|
|
47
|
+
)
|
|
48
|
+
m.post(f"{ADMIN}/api/auth/jwt/refresh/", status_code=401)
|
|
49
|
+
m.post(f"{ADMIN}/api/auth/jwt/login/", json={"access_token": "ACC", "refresh_token": "REF"}, status_code=200)
|
|
50
|
+
client.list_ruleset_plans()
|
|
51
|
+
|
|
52
|
+
assert [request.path for request in m.request_history] == [
|
|
53
|
+
"/ruleset-plans/",
|
|
54
|
+
"/api/auth/jwt/refresh/",
|
|
55
|
+
"/api/auth/jwt/login/",
|
|
56
|
+
"/ruleset-plans/",
|
|
57
|
+
]
|
|
58
|
+
assert {request.timeout for request in m.request_history} == {(DEFAULT_CONNECT_TIMEOUT_SECONDS, 60)}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/discovery_config_libraries.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/data_selection.py
RENAMED
|
File without changes
|
|
File without changes
|
{datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/discovery_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/ruleset_library.py
RENAMED
|
File without changes
|
|
File without changes
|
{datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/safe_data_preview.py
RENAMED
|
File without changes
|
|
File without changes
|
{datamasque_python-1.5.0 → datamasque_python-1.5.1}/datamasque/client/models/table_reference.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{datamasque_python-1.5.0 → datamasque_python-1.5.1}/tests/test_discovery_config_libraries.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|