datamasque-python 1.1.5__tar.gz → 1.1.6__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.1.5 → datamasque_python-1.1.6}/HISTORY.rst +5 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/PKG-INFO +1 -1
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/__init__.py +3 -0
- datamasque_python-1.1.6/datamasque/client/discovery_config_libraries.py +172 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/dmclient.py +2 -0
- datamasque_python-1.1.6/datamasque/client/models/discovery_config_library.py +33 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/pyproject.toml +1 -1
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/setup.cfg +1 -1
- datamasque_python-1.1.6/tests/test_discovery_config_libraries.py +625 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/uv.lock +90 -90
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/.editorconfig +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/.github/workflows/ci.yml +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/.github/workflows/release-testpypi.yml +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/.github/workflows/release.yml +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/.gitignore +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/.readthedocs.yaml +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/CONTRIBUTING.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/LICENSE +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/MANIFEST.in +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/Makefile +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/NOTICE +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/README.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/base.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/connections.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/discovery.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/discovery_configs.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/exceptions.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/files.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/ifm.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/license.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/__init__.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/connection.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/data_selection.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/discovery.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/discovery_config.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/dm_instance.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/files.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/git.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/ifm.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/license.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/pagination.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/ruleset.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/ruleset_library.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/runs.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/status.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/models/user.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/py.typed +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/ruleset_libraries.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/rulesets.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/runs.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/settings.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/spcs.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/datamasque/client/users.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/Makefile +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/client.models.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/client.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/conf.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/contributing.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/history.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/index.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/installation.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/make.bat +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/modules.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/readme.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/docs/usage.rst +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/__init__.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/conftest.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/helpers.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_base.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_connections.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_discovery.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_discovery_configs.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_files.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_ifm.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_license.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_pagination.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_ruleset_library.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_rulesets.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_runs.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_settings.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_spcs.py +0 -0
- {datamasque_python-1.1.5 → datamasque_python-1.1.6}/tests/test_users.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datamasque-python
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.6
|
|
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/
|
|
@@ -80,6 +80,7 @@ from datamasque.client.models.discovery import (
|
|
|
80
80
|
TableConstraints,
|
|
81
81
|
)
|
|
82
82
|
from datamasque.client.models.discovery_config import DiscoveryConfig, DiscoveryConfigId, DiscoveryConfigType
|
|
83
|
+
from datamasque.client.models.discovery_config_library import DiscoveryConfigLibrary, DiscoveryConfigLibraryId
|
|
83
84
|
from datamasque.client.models.dm_instance import DataMasqueInstanceConfig
|
|
84
85
|
from datamasque.client.models.files import (
|
|
85
86
|
DataMasqueFile,
|
|
@@ -148,6 +149,8 @@ __all__ = [
|
|
|
148
149
|
"DatabricksConnectionConfig",
|
|
149
150
|
"DiscoveryConfig",
|
|
150
151
|
"DiscoveryConfigId",
|
|
152
|
+
"DiscoveryConfigLibrary",
|
|
153
|
+
"DiscoveryConfigLibraryId",
|
|
151
154
|
"DiscoveryConfigNotFoundError",
|
|
152
155
|
"DiscoveryConfigType",
|
|
153
156
|
"DiscoveryMatch",
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from datamasque.client.base import BaseClient
|
|
5
|
+
from datamasque.client.exceptions import DataMasqueApiError
|
|
6
|
+
from datamasque.client.models.discovery_config import DiscoveryConfigType
|
|
7
|
+
from datamasque.client.models.discovery_config_library import DiscoveryConfigLibrary, DiscoveryConfigLibraryId
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DiscoveryConfigLibraryClient(BaseClient):
|
|
13
|
+
"""Discovery config library CRUD API methods. Mixed into `DataMasqueClient`."""
|
|
14
|
+
|
|
15
|
+
def list_discovery_config_libraries(self) -> list[DiscoveryConfigLibrary]:
|
|
16
|
+
"""
|
|
17
|
+
Lists all discovery config libraries.
|
|
18
|
+
|
|
19
|
+
Unlike most list endpoints, this one is not paginated;
|
|
20
|
+
the server returns every library in a single response.
|
|
21
|
+
Note: the YAML content is not included in the list response for performance.
|
|
22
|
+
Use `get_discovery_config_library` to retrieve the full library with its YAML body.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
response = self.make_request("GET", "/api/discovery/config-libraries/")
|
|
26
|
+
return [DiscoveryConfigLibrary.model_validate(item) for item in response.json()]
|
|
27
|
+
|
|
28
|
+
def get_discovery_config_library(self, library_id: DiscoveryConfigLibraryId) -> DiscoveryConfigLibrary:
|
|
29
|
+
"""Retrieves a single discovery config library by ID, including its YAML content."""
|
|
30
|
+
|
|
31
|
+
response = self.make_request("GET", f"/api/discovery/config-libraries/{library_id}/")
|
|
32
|
+
return DiscoveryConfigLibrary.model_validate(response.json())
|
|
33
|
+
|
|
34
|
+
def _get_discovery_config_library_id_by_name(
|
|
35
|
+
self, name: str, config_type: DiscoveryConfigType, namespace: str
|
|
36
|
+
) -> Optional[DiscoveryConfigLibraryId]:
|
|
37
|
+
"""
|
|
38
|
+
Returns the ID of the library with the given name, type, and namespace, or `None` if there is none.
|
|
39
|
+
|
|
40
|
+
The listing is filtered by name to keep the response small;
|
|
41
|
+
type and namespace are matched client-side,
|
|
42
|
+
since the API's namespace filter cannot select the default (empty) namespace.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
response = self.make_request(
|
|
46
|
+
"GET",
|
|
47
|
+
"/api/discovery/config-libraries/",
|
|
48
|
+
params={"name_exact": name},
|
|
49
|
+
)
|
|
50
|
+
entries = [DiscoveryConfigLibrary.model_validate(item) for item in response.json()]
|
|
51
|
+
matches = [
|
|
52
|
+
entry
|
|
53
|
+
for entry in entries
|
|
54
|
+
if entry.name == name and entry.namespace == namespace and entry.config_type is config_type
|
|
55
|
+
]
|
|
56
|
+
if not matches:
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
if matches[0].id is None:
|
|
60
|
+
raise DataMasqueApiError(
|
|
61
|
+
"Server returned a discovery config library list entry without an `id`.",
|
|
62
|
+
response=response,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
return matches[0].id
|
|
66
|
+
|
|
67
|
+
def get_discovery_config_library_by_name(
|
|
68
|
+
self, name: str, config_type: DiscoveryConfigType, namespace: str = ""
|
|
69
|
+
) -> Optional[DiscoveryConfigLibrary]:
|
|
70
|
+
"""
|
|
71
|
+
Looks for a discovery config library matching the given name, type, and namespace (case-sensitive, exact match).
|
|
72
|
+
|
|
73
|
+
Library names are unique per type within a namespace,
|
|
74
|
+
so a type is required to identify a single library.
|
|
75
|
+
Returns it (with full YAML content) if found, otherwise `None`.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
library_id = self._get_discovery_config_library_id_by_name(name, config_type, namespace)
|
|
79
|
+
if library_id is None:
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
return self.get_discovery_config_library(library_id)
|
|
83
|
+
|
|
84
|
+
def create_discovery_config_library(self, library: DiscoveryConfigLibrary) -> DiscoveryConfigLibrary:
|
|
85
|
+
"""
|
|
86
|
+
Creates a new discovery config library on the server.
|
|
87
|
+
|
|
88
|
+
Sets the library's server-assigned fields
|
|
89
|
+
(`id`, `is_valid`, `validation_error`, `created`, `modified`) and returns the library.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
data = library.model_dump(exclude_none=True, by_alias=True, mode="json")
|
|
93
|
+
response = self.make_request("POST", "/api/discovery/config-libraries/", data=data)
|
|
94
|
+
created = DiscoveryConfigLibrary.model_validate(response.json())
|
|
95
|
+
library.id = created.id
|
|
96
|
+
library.is_valid = created.is_valid
|
|
97
|
+
library.validation_error = created.validation_error
|
|
98
|
+
library.created = created.created
|
|
99
|
+
library.modified = created.modified
|
|
100
|
+
logger.info('Creation of discovery config library "%s" successful', library.name)
|
|
101
|
+
return library
|
|
102
|
+
|
|
103
|
+
def update_discovery_config_library(self, library: DiscoveryConfigLibrary) -> DiscoveryConfigLibrary:
|
|
104
|
+
"""
|
|
105
|
+
Performs a full update of the discovery config library.
|
|
106
|
+
|
|
107
|
+
The library must have its `id` set (i.e., it must have been previously created or retrieved from the server)
|
|
108
|
+
and its `yaml` content present.
|
|
109
|
+
A library's `config_type` is fixed at creation and cannot be changed by an update.
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
if library.id is None:
|
|
113
|
+
raise ValueError("Cannot update a discovery config library that has not been created yet (id is None)")
|
|
114
|
+
|
|
115
|
+
if library.yaml is None:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
"Cannot update a discovery config library without YAML content (yaml is None); "
|
|
118
|
+
"list results omit YAML, so fetch the full library with `get_discovery_config_library` first"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
data = library.model_dump(exclude_none=True, by_alias=True, mode="json")
|
|
122
|
+
response = self.make_request("PUT", f"/api/discovery/config-libraries/{library.id}/", data=data)
|
|
123
|
+
updated = DiscoveryConfigLibrary.model_validate(response.json())
|
|
124
|
+
library.is_valid = updated.is_valid
|
|
125
|
+
library.validation_error = updated.validation_error
|
|
126
|
+
library.modified = updated.modified
|
|
127
|
+
logger.debug('Update of discovery config library "%s" successful', library.name)
|
|
128
|
+
return library
|
|
129
|
+
|
|
130
|
+
def create_or_update_discovery_config_library(self, library: DiscoveryConfigLibrary) -> DiscoveryConfigLibrary:
|
|
131
|
+
"""
|
|
132
|
+
Creates the library, or updates the existing one with the same name, namespace, and config type.
|
|
133
|
+
|
|
134
|
+
Sets the library's `id` property.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
library_id = self._get_discovery_config_library_id_by_name(library.name, library.config_type, library.namespace)
|
|
138
|
+
if library_id is not None:
|
|
139
|
+
library.id = library_id
|
|
140
|
+
return self.update_discovery_config_library(library)
|
|
141
|
+
|
|
142
|
+
return self.create_discovery_config_library(library)
|
|
143
|
+
|
|
144
|
+
def delete_discovery_config_library_by_id_if_exists(
|
|
145
|
+
self, library_id: DiscoveryConfigLibraryId, *, force: bool = False
|
|
146
|
+
) -> None:
|
|
147
|
+
"""
|
|
148
|
+
Deletes the discovery config library with the given ID.
|
|
149
|
+
|
|
150
|
+
No-op if the library does not exist.
|
|
151
|
+
|
|
152
|
+
If the library is imported by any discovery configs,
|
|
153
|
+
the server will return 409 Conflict unless `force=True` is passed.
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
params = {"force": "true"} if force else None
|
|
157
|
+
self._delete_if_exists(f"/api/discovery/config-libraries/{library_id}/", params=params)
|
|
158
|
+
|
|
159
|
+
def delete_discovery_config_library_by_name_if_exists(
|
|
160
|
+
self, name: str, config_type: DiscoveryConfigType, namespace: str = "", *, force: bool = False
|
|
161
|
+
) -> None:
|
|
162
|
+
"""
|
|
163
|
+
Deletes the discovery config library with the given name, type, and namespace.
|
|
164
|
+
|
|
165
|
+
Library names are unique per type within a namespace,
|
|
166
|
+
so a type is required to identify a single library.
|
|
167
|
+
No-op if no such library exists.
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
library_id = self._get_discovery_config_library_id_by_name(name, config_type, namespace)
|
|
171
|
+
if library_id is not None:
|
|
172
|
+
self.delete_discovery_config_library_by_id_if_exists(library_id, force=force)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from datamasque.client.base import FileOrContent, UploadFile
|
|
2
2
|
from datamasque.client.connections import ConnectionClient
|
|
3
3
|
from datamasque.client.discovery import DiscoveryClient
|
|
4
|
+
from datamasque.client.discovery_config_libraries import DiscoveryConfigLibraryClient
|
|
4
5
|
from datamasque.client.discovery_configs import DiscoveryConfigClient
|
|
5
6
|
from datamasque.client.files import FileClient
|
|
6
7
|
from datamasque.client.license import LicenseClient
|
|
@@ -22,6 +23,7 @@ class DataMasqueClient(
|
|
|
22
23
|
RunClient,
|
|
23
24
|
DiscoveryClient,
|
|
24
25
|
DiscoveryConfigClient,
|
|
26
|
+
DiscoveryConfigLibraryClient,
|
|
25
27
|
UserClient,
|
|
26
28
|
SettingsClient,
|
|
27
29
|
):
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import NewType, Optional
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
5
|
+
|
|
6
|
+
from datamasque.client.models.discovery_config import DiscoveryConfigType
|
|
7
|
+
from datamasque.client.models.status import ValidationStatus
|
|
8
|
+
|
|
9
|
+
DiscoveryConfigLibraryId = NewType("DiscoveryConfigLibraryId", str)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class DiscoveryConfigLibrary(BaseModel):
|
|
13
|
+
"""
|
|
14
|
+
Represents a named, namespaced, persisted YAML discovery config library.
|
|
15
|
+
|
|
16
|
+
Library names are unique per config type within a namespace,
|
|
17
|
+
so a database and a file library may share a name.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
model_config = ConfigDict(extra="allow", populate_by_name=True)
|
|
21
|
+
|
|
22
|
+
name: str
|
|
23
|
+
config_type: DiscoveryConfigType
|
|
24
|
+
namespace: str = ""
|
|
25
|
+
yaml: Optional[str] = Field(default=None, alias="config_yaml")
|
|
26
|
+
# Server-populated read-only fields, excluded from request bodies.
|
|
27
|
+
id: Optional[DiscoveryConfigLibraryId] = Field(default=None, exclude=True)
|
|
28
|
+
is_valid: Optional[ValidationStatus] = Field(default=None, exclude=True)
|
|
29
|
+
"""Validation status; libraries are validated synchronously on create/update."""
|
|
30
|
+
validation_error: Optional[str] = Field(default=None, exclude=True)
|
|
31
|
+
"""Human-readable validation error, or `None` when valid."""
|
|
32
|
+
created: Optional[datetime] = Field(default=None, exclude=True)
|
|
33
|
+
modified: Optional[datetime] = Field(default=None, exclude=True)
|