datamasque-python 1.1.3__py3-none-any.whl → 1.1.5__py3-none-any.whl
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/client/base.py +3 -0
- datamasque/client/exceptions.py +16 -0
- datamasque/client/models/connection.py +10 -4
- datamasque/client/models/dm_instance.py +13 -0
- datamasque/client/spcs.py +179 -0
- {datamasque_python-1.1.3.dist-info → datamasque_python-1.1.5.dist-info}/METADATA +6 -1
- {datamasque_python-1.1.3.dist-info → datamasque_python-1.1.5.dist-info}/RECORD +9 -8
- {datamasque_python-1.1.3.dist-info → datamasque_python-1.1.5.dist-info}/WHEEL +0 -0
- {datamasque_python-1.1.3.dist-info → datamasque_python-1.1.5.dist-info}/licenses/LICENSE +0 -0
datamasque/client/base.py
CHANGED
|
@@ -21,6 +21,7 @@ from datamasque.client.exceptions import (
|
|
|
21
21
|
DataMasqueTransportError,
|
|
22
22
|
)
|
|
23
23
|
from datamasque.client.models.dm_instance import DataMasqueInstanceConfig
|
|
24
|
+
from datamasque.client.spcs import install_spcs_gateway_auth
|
|
24
25
|
|
|
25
26
|
logger = logging.getLogger(__name__)
|
|
26
27
|
|
|
@@ -137,6 +138,8 @@ class BaseClient:
|
|
|
137
138
|
self.verify_ssl = connection_config.verify_ssl
|
|
138
139
|
self.token_source = connection_config.token_source
|
|
139
140
|
self._session = _build_session(self.verify_ssl)
|
|
141
|
+
if connection_config.spcs_pat:
|
|
142
|
+
install_spcs_gateway_auth(self._session, connection_config.spcs_pat)
|
|
140
143
|
|
|
141
144
|
@contextmanager
|
|
142
145
|
def _maybe_suppress_insecure_warning(self) -> Iterator[None]:
|
datamasque/client/exceptions.py
CHANGED
|
@@ -84,6 +84,22 @@ class IfmAuthError(DataMasqueIfmError):
|
|
|
84
84
|
"""Raised when the IFM client cannot obtain or refresh a JWT (e.g. invalid credentials, missing scope)."""
|
|
85
85
|
|
|
86
86
|
|
|
87
|
+
class SpcsGatewayAuthError(DataMasqueException):
|
|
88
|
+
"""
|
|
89
|
+
Raised when a Snowflake SPCS app gateway rejects the configured `spcs_pat`.
|
|
90
|
+
|
|
91
|
+
The message includes the Snowflake-provided detail, request id,
|
|
92
|
+
and a hint at the likely cause
|
|
93
|
+
(for example an expired token, or a network policy that excludes your IP).
|
|
94
|
+
|
|
95
|
+
Deliberately a direct subclass of `DataMasqueException` rather than
|
|
96
|
+
`DataMasqueApiError`:
|
|
97
|
+
the client's 401 re-authenticate-and-retry path keys off `DataMasqueApiError`,
|
|
98
|
+
so keeping this outside that subtree
|
|
99
|
+
ensures a gateway rejection aborts immediately instead of looping.
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
|
|
87
103
|
class RunNotCancellableError(DataMasqueUserError):
|
|
88
104
|
"""
|
|
89
105
|
Raised when `cancel_run` is called against a run that is no longer eligible for cancellation.
|
|
@@ -48,6 +48,7 @@ class DatabaseType(Enum):
|
|
|
48
48
|
databricks_lakebase = "databricks_lakebase"
|
|
49
49
|
databricks = "databricks"
|
|
50
50
|
informix = "informix"
|
|
51
|
+
saphana = "saphana"
|
|
51
52
|
|
|
52
53
|
|
|
53
54
|
class SnowflakeStageLocation(str, Enum):
|
|
@@ -56,6 +57,7 @@ class SnowflakeStageLocation(str, Enum):
|
|
|
56
57
|
local = "local" # Not supported for production use
|
|
57
58
|
aws_s3 = "aws_s3"
|
|
58
59
|
azure_blob_storage = "azure_blob_storage"
|
|
60
|
+
spcs = "spcs" # DataMasque running inside Snowflake SPCS; staged on the container's own storage
|
|
59
61
|
|
|
60
62
|
|
|
61
63
|
class SseSelection(Enum):
|
|
@@ -233,10 +235,14 @@ class SnowflakeConnectionConfig(ConnectionConfig):
|
|
|
233
235
|
"""
|
|
234
236
|
|
|
235
237
|
database: str
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
238
|
+
# Optional because DataMasque-in-SPCS connections leave these unset: the agent uses the
|
|
239
|
+
# container's OAuth token + SNOWFLAKE_HOST/SNOWFLAKE_ACCOUNT env and the app-owned QUERY_WAREHOUSE,
|
|
240
|
+
# so user/account/storage-integration/warehouse are null for stage_location=spcs. Mirrors the app's
|
|
241
|
+
# canonical model (agent .../schemas/connection/connection.py), which types these `| None = None`.
|
|
242
|
+
user: Optional[str] = None
|
|
243
|
+
snowflake_account_id: Optional[str] = None
|
|
244
|
+
snowflake_warehouse: Optional[str] = None
|
|
245
|
+
snowflake_storage_integration_name: Optional[str] = None
|
|
240
246
|
host: str = ""
|
|
241
247
|
port: Optional[int] = None
|
|
242
248
|
db_schema: Optional[str] = Field(default=None, alias="schema")
|
|
@@ -20,6 +20,11 @@ class DataMasqueInstanceConfig(BaseModel):
|
|
|
20
20
|
the client prepends it with `Token ` when sending the `Authorization` header.
|
|
21
21
|
The client calls `token_source` on each authentication attempt,
|
|
22
22
|
so the callable is free to fetch and refresh tokens out-of-band (e.g. from a secrets manager).
|
|
23
|
+
|
|
24
|
+
`spcs_pat` is an optional Snowflake Programmatic Access Token
|
|
25
|
+
for reaching a DataMasque instance hosted on Snowpark Container Services (SPCS).
|
|
26
|
+
It layers underneath whichever DataMasque auth method
|
|
27
|
+
(`password` or `token_source`) you choose.
|
|
23
28
|
"""
|
|
24
29
|
|
|
25
30
|
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
@@ -29,6 +34,14 @@ class DataMasqueInstanceConfig(BaseModel):
|
|
|
29
34
|
password: Optional[str] = None
|
|
30
35
|
verify_ssl: bool = True
|
|
31
36
|
token_source: Optional[Callable[[], str]] = None
|
|
37
|
+
spcs_pat: Optional[str] = None
|
|
38
|
+
"""Snowflake Programmatic Access Token
|
|
39
|
+
for a DataMasque instance hosted on Snowpark Container Services (SPCS),
|
|
40
|
+
where `base_url` ends in `.snowflakecomputing.app`.
|
|
41
|
+
|
|
42
|
+
Create the token in Snowsight (User profile → Programmatic access tokens)
|
|
43
|
+
for an account that can reach the SPCS app.
|
|
44
|
+
Leave unset for instances that are not hosted on SPCS."""
|
|
32
45
|
|
|
33
46
|
@model_validator(mode="after")
|
|
34
47
|
def _validate_auth_source(self) -> "DataMasqueInstanceConfig":
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Snowflake SPCS app gateway authentication for `DataMasqueClient`.
|
|
3
|
+
|
|
4
|
+
When a DataMasque instance is hosted on Snowpark Container Services (SPCS),
|
|
5
|
+
its app ingress (`*.snowflakecomputing.app`) fronts every request
|
|
6
|
+
with a Snowflake gateway that must be cleared first.
|
|
7
|
+
We authenticate to the gateway with a Programmatic Access Token (PAT),
|
|
8
|
+
sent on `X-SF-SPCS-Authorization: Snowflake Token="<PAT>"`.
|
|
9
|
+
The gateway accepts the PAT on this alternate header
|
|
10
|
+
and strips it before forwarding to the container,
|
|
11
|
+
so DataMasque's own `Authorization: Token <key>` flow rides through untouched.
|
|
12
|
+
|
|
13
|
+
`install_spcs_gateway_auth` attaches this behaviour to a client's `requests.Session`:
|
|
14
|
+
it sets the header on the session
|
|
15
|
+
(so it is sent on every request, including the unauthenticated login)
|
|
16
|
+
and registers a response hook
|
|
17
|
+
that turns a gateway-originated rejection into a clear `SpcsGatewayAuthError`.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import re
|
|
21
|
+
from typing import Any, Optional
|
|
22
|
+
|
|
23
|
+
import requests
|
|
24
|
+
|
|
25
|
+
from datamasque.client.exceptions import SpcsGatewayAuthError
|
|
26
|
+
|
|
27
|
+
SPCS_GATEWAY_AUTH_HEADER = "X-SF-SPCS-Authorization"
|
|
28
|
+
|
|
29
|
+
# Body-shape discriminators for SPCS gateway error responses.
|
|
30
|
+
# The gateway emits JSON with `responseType` (ERROR_<UPPER_SNAKE>), `requestId`
|
|
31
|
+
# (canonical UUID), and `detail` (free text). All three must be present and
|
|
32
|
+
# match these patterns for the body to count as gateway-originated.
|
|
33
|
+
_GATEWAY_RESPONSE_TYPE_RE = re.compile(r"^ERROR_[A-Z][A-Z0-9_]+$")
|
|
34
|
+
_UUID_RE = re.compile(
|
|
35
|
+
r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
|
|
36
|
+
re.IGNORECASE,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Header-shape discriminators for "this response transited a Snowflake SPCS
|
|
40
|
+
# gateway". The Server header and the `sfc-ss-` cookie name prefix both appear
|
|
41
|
+
# on every gateway-handled response (success and error alike) and aren't
|
|
42
|
+
# plausible to spoof by accident.
|
|
43
|
+
_SPCS_GATEWAY_SERVER_VALUE = "_"
|
|
44
|
+
_SPCS_COOKIE_PREFIX = "sfc-ss-"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _has_spcs_gateway_header_signature(response: requests.Response) -> bool:
|
|
48
|
+
"""
|
|
49
|
+
True if at least one header-level Snowflake gateway marker is present.
|
|
50
|
+
|
|
51
|
+
Looks for either `Server: _` (the gateway's literal Server header value)
|
|
52
|
+
or any `Set-Cookie` carrying the `sfc-ss-` cookie name prefix.
|
|
53
|
+
Either is sufficient — both indicate the response was emitted by,
|
|
54
|
+
or transited, Snowflake's SPCS ingress.
|
|
55
|
+
"""
|
|
56
|
+
if response.headers.get("server", "").strip() == _SPCS_GATEWAY_SERVER_VALUE:
|
|
57
|
+
return True
|
|
58
|
+
# `Set-Cookie` may appear multiple times; `requests` flattens duplicates
|
|
59
|
+
# via a comma-separated value in `.headers`, but our prefix substring
|
|
60
|
+
# check is order- and count-insensitive.
|
|
61
|
+
return _SPCS_COOKIE_PREFIX in response.headers.get("set-cookie", "")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _is_spcs_gateway_error_body(response: requests.Response) -> Optional[dict]:
|
|
65
|
+
"""
|
|
66
|
+
Return the parsed body iff it is a structurally-valid gateway error.
|
|
67
|
+
|
|
68
|
+
All four conditions must hold:
|
|
69
|
+
1. The body parses as JSON and is a dict.
|
|
70
|
+
2. Keys `responseType`, `requestId`, `detail` are all present and string-typed.
|
|
71
|
+
3. `responseType` matches `^ERROR_<UPPER_SNAKE>$`.
|
|
72
|
+
4. `requestId` is a canonical 8-4-4-4-12 UUID.
|
|
73
|
+
|
|
74
|
+
Returns the parsed dict (truthy) on match, `None` on miss.
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
data = response.json()
|
|
78
|
+
except ValueError:
|
|
79
|
+
return None
|
|
80
|
+
if not isinstance(data, dict):
|
|
81
|
+
return None
|
|
82
|
+
response_type = data.get("responseType")
|
|
83
|
+
request_id = data.get("requestId")
|
|
84
|
+
detail = data.get("detail")
|
|
85
|
+
if not (isinstance(response_type, str) and isinstance(request_id, str) and isinstance(detail, str)):
|
|
86
|
+
return None
|
|
87
|
+
if _GATEWAY_RESPONSE_TYPE_RE.match(response_type) and _UUID_RE.match(request_id):
|
|
88
|
+
return data
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _hint_for_gateway_detail(detail: str) -> str:
|
|
93
|
+
"""Map common Snowflake gateway `detail` strings to a one-line cause hint."""
|
|
94
|
+
d = (detail or "").lower()
|
|
95
|
+
if "network policy" in d:
|
|
96
|
+
return (
|
|
97
|
+
"PAT requires a network policy attached to the user (or account) "
|
|
98
|
+
"that permits your current public IP. Run `CREATE NETWORK POLICY "
|
|
99
|
+
"... ALLOWED_IP_LIST = ('<your.ip>/32')` and `ALTER USER <pat-user> "
|
|
100
|
+
"SET NETWORK_POLICY = <policy>`."
|
|
101
|
+
)
|
|
102
|
+
if "invalid" in d and "token" in d:
|
|
103
|
+
return (
|
|
104
|
+
"PAT is malformed, expired, or revoked. Create a new PAT in Snowsight "
|
|
105
|
+
"(User profile -> Programmatic access tokens) and update `spcs_pat`."
|
|
106
|
+
)
|
|
107
|
+
if "expired" in d:
|
|
108
|
+
return "PAT has expired. Create a fresh one in Snowsight and update `spcs_pat`."
|
|
109
|
+
if "authentication" in d or "unauthorized" in d:
|
|
110
|
+
return (
|
|
111
|
+
"Generic auth rejection. Verify the PAT was created by a user that "
|
|
112
|
+
"has access to this SPCS app, and that any account-level network "
|
|
113
|
+
"policy includes your current public IP."
|
|
114
|
+
)
|
|
115
|
+
return "Unknown gateway rejection — see the Snowflake `detail` string above and the Snowflake PAT docs."
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _check_spcs_gateway_response(response: requests.Response) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Raise `SpcsGatewayAuthError` iff `response` is a gateway-originated rejection.
|
|
121
|
+
|
|
122
|
+
Two-layer discriminator — both must hold:
|
|
123
|
+
* **Body originated at the gateway**:
|
|
124
|
+
strict shape match on the JSON body
|
|
125
|
+
(multiple fields, typed, with format constraints)
|
|
126
|
+
via `_is_spcs_gateway_error_body`.
|
|
127
|
+
* **Response transited an SPCS gateway**:
|
|
128
|
+
header signature confirms via `_has_spcs_gateway_header_signature`.
|
|
129
|
+
|
|
130
|
+
Either layer alone could in principle false-positive on an unrelated upstream
|
|
131
|
+
that happened to emit one of those signals;
|
|
132
|
+
the conjunction is what makes the check robust.
|
|
133
|
+
Legitimate DataMasque 401s (DRF `{"detail": "..."}`)
|
|
134
|
+
have the gateway header signature but fail the body shape —
|
|
135
|
+
so they correctly flow through
|
|
136
|
+
to the client's normal re-auth-and-retry path untouched.
|
|
137
|
+
"""
|
|
138
|
+
if response.status_code not in (401, 403):
|
|
139
|
+
return
|
|
140
|
+
if not _has_spcs_gateway_header_signature(response):
|
|
141
|
+
return
|
|
142
|
+
data = _is_spcs_gateway_error_body(response)
|
|
143
|
+
if data is None:
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
response_type = data["responseType"]
|
|
147
|
+
request_id = data["requestId"]
|
|
148
|
+
detail = data["detail"]
|
|
149
|
+
hint = _hint_for_gateway_detail(detail)
|
|
150
|
+
raise SpcsGatewayAuthError(
|
|
151
|
+
f"SPCS gateway rejected the PAT (HTTP {response.status_code}, "
|
|
152
|
+
f"{response_type}). The request never reached DataMasque.\n"
|
|
153
|
+
f' Snowflake said: "{detail}"\n'
|
|
154
|
+
f" Snowflake reqId: {request_id}\n"
|
|
155
|
+
f" Likely cause: {hint}\n"
|
|
156
|
+
f" Fix in Snowsight on the account hosting this SPCS app, then retry."
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _spcs_gateway_response_hook(response: requests.Response, *args: Any, **kwargs: Any) -> None:
|
|
161
|
+
"""`requests` response hook: raise on a gateway-originated auth rejection."""
|
|
162
|
+
_check_spcs_gateway_response(response)
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def install_spcs_gateway_auth(session: requests.Session, pat: str) -> None:
|
|
166
|
+
"""
|
|
167
|
+
Configure `session` to authenticate to a Snowflake SPCS app gateway.
|
|
168
|
+
|
|
169
|
+
Sets the `X-SF-SPCS-Authorization` header on the session
|
|
170
|
+
(so it rides on every request, including the unauthenticated login)
|
|
171
|
+
and registers a response hook
|
|
172
|
+
that raises `SpcsGatewayAuthError` on a gateway rejection.
|
|
173
|
+
|
|
174
|
+
Scoping is automatic:
|
|
175
|
+
the client's session only ever talks to its own `base_url`,
|
|
176
|
+
so there is no need to match per-request hosts.
|
|
177
|
+
"""
|
|
178
|
+
session.headers[SPCS_GATEWAY_AUTH_HEADER] = f'Snowflake Token="{pat}"'
|
|
179
|
+
session.hooks["response"].append(_spcs_gateway_response_hook)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: datamasque-python
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.5
|
|
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/
|
|
@@ -74,6 +74,11 @@ Authentication is performed on the first request if ``authenticate()`` is not ca
|
|
|
74
74
|
and is automatically retried once on a 401 response.
|
|
75
75
|
``client.healthcheck()`` is available as a lightweight readiness probe that does not consume credentials.
|
|
76
76
|
|
|
77
|
+
For a DataMasque instance hosted on Snowpark Container Services (SPCS)
|
|
78
|
+
(a ``*.snowflakecomputing.app`` ``base_url``),
|
|
79
|
+
pass a Snowflake Programmatic Access Token as ``spcs_pat`` on ``DataMasqueInstanceConfig``.
|
|
80
|
+
See the `usage docs <https://datamasque-python.readthedocs.io/en/latest/usage.html>`_ for details.
|
|
81
|
+
|
|
77
82
|
Error handling
|
|
78
83
|
==============
|
|
79
84
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
datamasque/client/__init__.py,sha256=bW7Iy-iNxpaYgc_X035KAL-hmOVgr8YcYh5s398zawo,6378
|
|
2
|
-
datamasque/client/base.py,sha256=
|
|
2
|
+
datamasque/client/base.py,sha256=cXU4dluL9e1N1F2pV48iIvebv6OHt7Kh4Ka2y9vkOPo,14296
|
|
3
3
|
datamasque/client/connections.py,sha256=EFinx8fJRme0mTxuWY3d29UnmUFbsQhMaUQT0Ma2PK4,2885
|
|
4
4
|
datamasque/client/discovery.py,sha256=MTDiif6YWkv64hHXZ8YJxtRhoeKIvQVfjNYvjCIZCXE,21261
|
|
5
5
|
datamasque/client/discovery_configs.py,sha256=G-W-X_KizjjQLa-Rb6ixLjlkxbkCe4WGCOfYZrGtxIk,6420
|
|
6
6
|
datamasque/client/dmclient.py,sha256=tA7V8GEQmnsalTHUZch8YmIUynYw2ldK-mmUGbofFL4,1650
|
|
7
|
-
datamasque/client/exceptions.py,sha256=
|
|
7
|
+
datamasque/client/exceptions.py,sha256=YpM_vhnl31_02KQmx-NPP2AxR3gKYcmcxyb_35G9HVw,3755
|
|
8
8
|
datamasque/client/files.py,sha256=vc2tZgT8RdwuCObIQ7H-Qjb80H1PBFqD0nG5Y1tOA_o,3503
|
|
9
9
|
datamasque/client/ifm.py,sha256=uIMxpLIPvDiDO1m4bxezixNIUFIFY0MXWRMQNvTbpTA,11858
|
|
10
10
|
datamasque/client/license.py,sha256=pluYaSU168OC6_laB9bM9H3Vuuxs8wa9gujCJvtoJCk,1392
|
|
@@ -13,13 +13,14 @@ datamasque/client/ruleset_libraries.py,sha256=bMUMBHdxjDyFOziw9LMRy5xChPvHvCjpjA
|
|
|
13
13
|
datamasque/client/rulesets.py,sha256=Fsnsv-uAhF1DBnLI-pRpOrWJ7aCSZspcuU1r0RYTzUc,2655
|
|
14
14
|
datamasque/client/runs.py,sha256=l_JpYgelqsDKW_O6LtsHC60IMXHG9ak_RwVnuvJKE1I,7703
|
|
15
15
|
datamasque/client/settings.py,sha256=Ui8AyR2XdoW8MZ9FIrGn2jm8DLzUGWIL8i2DOTvu7hc,2898
|
|
16
|
+
datamasque/client/spcs.py,sha256=geHrBQ6WLWsMGpSMV8hmK5SFAwftrOU0DXkrR2orOmo,7532
|
|
16
17
|
datamasque/client/users.py,sha256=VCUo2CJyOw4-aO_3mp_w_0BcSoa6-h1HcReMcAMyumw,3701
|
|
17
18
|
datamasque/client/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
18
|
-
datamasque/client/models/connection.py,sha256=
|
|
19
|
+
datamasque/client/models/connection.py,sha256=CPT2wF1dVMs-N8Jca7hUhegtRk_mxPKU8XhWV43C0xE,17419
|
|
19
20
|
datamasque/client/models/data_selection.py,sha256=406yyUZ5NmLBSql2lYM1gsTWY5GWmnutmF-DjznAoLc,1904
|
|
20
21
|
datamasque/client/models/discovery.py,sha256=WHSIkFebQ5m47K_BdxY1lvCeeF23q_BbZan7Ryuh5vY,13198
|
|
21
22
|
datamasque/client/models/discovery_config.py,sha256=1CHMgSxMXuarPGfRcz9wX7jeZLn1SDw0UEe-L5naE54,1917
|
|
22
|
-
datamasque/client/models/dm_instance.py,sha256=
|
|
23
|
+
datamasque/client/models/dm_instance.py,sha256=6h0WNlI1qt6jkrYFapIvuSJFkhRCcwbpx0PLG5NsrGw,2219
|
|
23
24
|
datamasque/client/models/files.py,sha256=7hG3Q1-XYb1PF88l3ppsmBp2tjNtvQvv-RNTrOa7Nts,2784
|
|
24
25
|
datamasque/client/models/git.py,sha256=F0KdMFe2vMyL-9tp3SGKGeOlbD1gbcjQn0jpOzNzybc,1782
|
|
25
26
|
datamasque/client/models/ifm.py,sha256=j0Ef2BZYTk6MNZO6HS6mW0qWs1_wWD9PlUP_Y6WyBQI,5563
|
|
@@ -30,7 +31,7 @@ datamasque/client/models/ruleset_library.py,sha256=5UbL_M47mgPedo8xt4q5crUh4aeOV
|
|
|
30
31
|
datamasque/client/models/runs.py,sha256=UtPMGCJFLFP1f2nyVGAbl3A5_wAONrRNJ63y43Hbpi0,6213
|
|
31
32
|
datamasque/client/models/status.py,sha256=UUEDHd3CQZA1Mf-1LyhAiE4COuCWvAz5oJx5ECVIwp4,2868
|
|
32
33
|
datamasque/client/models/user.py,sha256=UGAUzgJkf78m24_zFXXoA99zdut48BXkX_ivV8yq1Vc,2043
|
|
33
|
-
datamasque_python-1.1.
|
|
34
|
-
datamasque_python-1.1.
|
|
35
|
-
datamasque_python-1.1.
|
|
36
|
-
datamasque_python-1.1.
|
|
34
|
+
datamasque_python-1.1.5.dist-info/METADATA,sha256=UHqtyZNncrDXlTzz0C9pFVFqzOJpPXpC1ku7lEwYlhM,4497
|
|
35
|
+
datamasque_python-1.1.5.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
36
|
+
datamasque_python-1.1.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
37
|
+
datamasque_python-1.1.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|