ellf-broker-sdk 7.0.8__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.
- ellf_broker_sdk-7.0.8/MANIFEST.in +1 -0
- ellf_broker_sdk-7.0.8/PKG-INFO +18 -0
- ellf_broker_sdk-7.0.8/README.md +9 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/__init__.py +3 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/about.json +6 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/about.py +23 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/__init__.py +0 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/base.py +256 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/check.py +35 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/data.py +105 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/envs.py +79 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/files.py +139 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/full_client.py +206 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/jobs.py +238 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/nodes.py +24 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/secrets.py +40 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/worker_types.py +18 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/errors.py +115 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/models.py +519 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/py.typed +1 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/ty.py +44 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk/util.py +87 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/PKG-INFO +18 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/SOURCES.txt +30 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/dependency_links.txt +1 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/not-zip-safe +1 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/requires.txt +4 -0
- ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/top_level.txt +1 -0
- ellf_broker_sdk-7.0.8/requirements.in +6 -0
- ellf_broker_sdk-7.0.8/setup.cfg +4 -0
- ellf_broker_sdk-7.0.8/setup.py +89 -0
- ellf_broker_sdk-7.0.8/tests/test_client.py +16 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
include requirements.in
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ellf-broker-sdk
|
|
3
|
+
Version: 7.0.8
|
|
4
|
+
Summary: Ellf Broker SDK
|
|
5
|
+
Home-page: https://github.com/explosion/ellf
|
|
6
|
+
Author: ExplosionAI GmbH
|
|
7
|
+
Author-email: contact@explosion.ai
|
|
8
|
+
License: All rights reserved
|
|
9
|
+
Requires-Dist: httpx
|
|
10
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
11
|
+
Requires-Dist: pydantic>=2.0.0
|
|
12
|
+
Requires-Dist: pyjwt
|
|
13
|
+
Dynamic: author
|
|
14
|
+
Dynamic: author-email
|
|
15
|
+
Dynamic: home-page
|
|
16
|
+
Dynamic: license
|
|
17
|
+
Dynamic: requires-dist
|
|
18
|
+
Dynamic: summary
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<a href="https://explosion.ai"><img src="https://explosion.ai/assets/img/logo.svg" width="125" height="125" align="right" /></a>
|
|
2
|
+
|
|
3
|
+
# Ellf Broker SDK
|
|
4
|
+
|
|
5
|
+
The `models.py` is auto-generated using the OpenAPI spec and [`datamodel-code-generator`](https://koxudaxi.github.io/datamodel-code-generator/). To generate the SDK, make sure you have all requirements installed and run the following from the project root directory:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pdcli dev generate-sdks --sdks broker
|
|
9
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This file originates in shared/shared_about.py, and
|
|
3
|
+
is copied verbatim into the different projects. It should
|
|
4
|
+
not be edited directly, edit the shared version and replicate
|
|
5
|
+
it everywhere.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
PWD = Path(__file__).parent
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
with (PWD / "about.json").open("r", encoding="utf8") as file_:
|
|
15
|
+
_about_data = json.load(file_)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
__name__ = _about_data["name"]
|
|
19
|
+
__title__ = _about_data.get("title", "")
|
|
20
|
+
__description__ = _about_data.get("description", "")
|
|
21
|
+
__summary__ = _about_data.get("summary", "")
|
|
22
|
+
__version__ = _about_data["version"]
|
|
23
|
+
__prog__ = _about_data.get("prog", "")
|
|
File without changes
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import IO, Any, Iterator, TypeVar
|
|
3
|
+
|
|
4
|
+
import httpx
|
|
5
|
+
from pydantic import BaseModel, TypeAdapter
|
|
6
|
+
|
|
7
|
+
from .. import errors
|
|
8
|
+
|
|
9
|
+
_ReturnModelT = TypeVar("_ReturnModelT")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RawIteratorIO(IO[bytes]):
|
|
13
|
+
"""
|
|
14
|
+
Creates a IO[bytes] from a bytes iterator.
|
|
15
|
+
|
|
16
|
+
Adapted from https://stackoverflow.com/a/12604375/7426717
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, it: Iterator[bytes]) -> None:
|
|
20
|
+
self._it = it
|
|
21
|
+
self._memory = b""
|
|
22
|
+
|
|
23
|
+
super().__init__()
|
|
24
|
+
|
|
25
|
+
def _read1(self, n: int | None = None) -> bytes:
|
|
26
|
+
while not self._memory:
|
|
27
|
+
try:
|
|
28
|
+
next_memory = next(self._it)
|
|
29
|
+
except StopIteration:
|
|
30
|
+
break
|
|
31
|
+
else:
|
|
32
|
+
self._memory = next_memory
|
|
33
|
+
|
|
34
|
+
chunk = self._memory[:n]
|
|
35
|
+
self._memory = self._memory[len(chunk) :]
|
|
36
|
+
|
|
37
|
+
return chunk
|
|
38
|
+
|
|
39
|
+
def read(self, n: int | None = None) -> bytes:
|
|
40
|
+
chunks: list[bytes] = []
|
|
41
|
+
if n is None or n < 0:
|
|
42
|
+
while True:
|
|
43
|
+
m = self._read1()
|
|
44
|
+
if not m:
|
|
45
|
+
break
|
|
46
|
+
chunks.append(m)
|
|
47
|
+
else:
|
|
48
|
+
while n > 0:
|
|
49
|
+
m = self._read1(n)
|
|
50
|
+
if not m:
|
|
51
|
+
break
|
|
52
|
+
n -= len(m)
|
|
53
|
+
chunks.append(m)
|
|
54
|
+
return b"".join(chunks)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
CHUNK_SIZE = 1024
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class BaseClient:
|
|
61
|
+
name: str
|
|
62
|
+
path: str
|
|
63
|
+
_sync_client: httpx.Client
|
|
64
|
+
_async_client: httpx.AsyncClient
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self, sync_client: httpx.Client, async_client: httpx.AsyncClient, path: str
|
|
68
|
+
) -> None:
|
|
69
|
+
self._sync_client = sync_client
|
|
70
|
+
self._async_client = async_client
|
|
71
|
+
if path.startswith("/"):
|
|
72
|
+
path = path[1:]
|
|
73
|
+
self.name = path.split("/")[-1]
|
|
74
|
+
self.path = f"/{path}"
|
|
75
|
+
|
|
76
|
+
def request(
|
|
77
|
+
self,
|
|
78
|
+
method: str,
|
|
79
|
+
*,
|
|
80
|
+
endpoint: str,
|
|
81
|
+
params: None | BaseModel | dict[str, Any] = None,
|
|
82
|
+
data: None | BaseModel | dict[str, Any] = None,
|
|
83
|
+
files: dict[str, Any] | None = None,
|
|
84
|
+
headers: dict[str, str] | None = None,
|
|
85
|
+
page: int | None = None,
|
|
86
|
+
size: int | None = None,
|
|
87
|
+
params_model: type[BaseModel] | None = None,
|
|
88
|
+
body_model: type[BaseModel] | None = None,
|
|
89
|
+
return_model: type[_ReturnModelT] | None = None,
|
|
90
|
+
stream: bool = False,
|
|
91
|
+
timeout: (
|
|
92
|
+
httpx._types.TimeoutTypes | httpx._client.UseClientDefault
|
|
93
|
+
) = httpx.USE_CLIENT_DEFAULT,
|
|
94
|
+
) -> _ReturnModelT | IO[bytes] | None:
|
|
95
|
+
req = self._get_validated_request(
|
|
96
|
+
method=method,
|
|
97
|
+
endpoint=endpoint,
|
|
98
|
+
params=params,
|
|
99
|
+
data=data,
|
|
100
|
+
files=files,
|
|
101
|
+
params_model=params_model,
|
|
102
|
+
body_model=body_model,
|
|
103
|
+
headers=headers,
|
|
104
|
+
page=page,
|
|
105
|
+
size=size,
|
|
106
|
+
timeout=timeout,
|
|
107
|
+
)
|
|
108
|
+
response = self._sync_client.send(req, stream=stream)
|
|
109
|
+
self._raise_and_handle_errors(response)
|
|
110
|
+
content = None
|
|
111
|
+
if (
|
|
112
|
+
response.headers.get("content-type") == "application/json"
|
|
113
|
+
and response.status_code != 204
|
|
114
|
+
):
|
|
115
|
+
content = response.json()
|
|
116
|
+
if return_model is not None:
|
|
117
|
+
return self._convert_response_to_model(content, return_model)
|
|
118
|
+
return RawIteratorIO(response.iter_bytes(chunk_size=CHUNK_SIZE))
|
|
119
|
+
|
|
120
|
+
async def request_async(
|
|
121
|
+
self,
|
|
122
|
+
method: str,
|
|
123
|
+
*,
|
|
124
|
+
endpoint: str,
|
|
125
|
+
params: None | BaseModel | dict[str, Any] = None,
|
|
126
|
+
data: None | BaseModel | dict[str, Any] = None,
|
|
127
|
+
files: dict[str, Any] | None = None,
|
|
128
|
+
headers: dict[str, str] | None = None,
|
|
129
|
+
page: int | None = None,
|
|
130
|
+
size: int | None = None,
|
|
131
|
+
params_model: type[BaseModel] | None = None,
|
|
132
|
+
body_model: type[BaseModel] | None = None,
|
|
133
|
+
return_model: type[_ReturnModelT] | None = None,
|
|
134
|
+
stream: bool = False,
|
|
135
|
+
timeout: (
|
|
136
|
+
httpx._types.TimeoutTypes | httpx._client.UseClientDefault
|
|
137
|
+
) = httpx.USE_CLIENT_DEFAULT,
|
|
138
|
+
) -> _ReturnModelT | IO[bytes] | None:
|
|
139
|
+
req = self._get_validated_request(
|
|
140
|
+
method=method,
|
|
141
|
+
endpoint=endpoint,
|
|
142
|
+
params=params,
|
|
143
|
+
data=data,
|
|
144
|
+
files=files,
|
|
145
|
+
params_model=params_model,
|
|
146
|
+
body_model=body_model,
|
|
147
|
+
headers=headers,
|
|
148
|
+
page=page,
|
|
149
|
+
size=size,
|
|
150
|
+
timeout=timeout,
|
|
151
|
+
)
|
|
152
|
+
response = await self._async_client.send(req, stream=stream)
|
|
153
|
+
self._raise_and_handle_errors(response)
|
|
154
|
+
content = None
|
|
155
|
+
if (
|
|
156
|
+
response.headers.get("content-type") == "application/json"
|
|
157
|
+
and response.status_code != 204
|
|
158
|
+
):
|
|
159
|
+
content = response.json()
|
|
160
|
+
if return_model is not None:
|
|
161
|
+
return self._convert_response_to_model(content, return_model)
|
|
162
|
+
return RawIteratorIO(response.iter_bytes(CHUNK_SIZE))
|
|
163
|
+
|
|
164
|
+
def _get_validated_request(
|
|
165
|
+
self,
|
|
166
|
+
*,
|
|
167
|
+
method: str,
|
|
168
|
+
endpoint: str,
|
|
169
|
+
params: None | BaseModel | dict[str, Any] = None,
|
|
170
|
+
data: None | BaseModel | dict[str, Any] = None,
|
|
171
|
+
files: dict[str, Any] | None = None,
|
|
172
|
+
params_model: type[BaseModel] | None = None,
|
|
173
|
+
body_model: type[BaseModel] | None = None,
|
|
174
|
+
headers: dict[str, str] | None = None,
|
|
175
|
+
page: int | None = None,
|
|
176
|
+
size: int | None = None,
|
|
177
|
+
timeout: (
|
|
178
|
+
httpx._types.TimeoutTypes | httpx._client.UseClientDefault
|
|
179
|
+
) = httpx.USE_CLIENT_DEFAULT,
|
|
180
|
+
) -> httpx.Request:
|
|
181
|
+
url = self.path
|
|
182
|
+
if endpoint:
|
|
183
|
+
if endpoint.startswith("/"):
|
|
184
|
+
url = endpoint
|
|
185
|
+
else:
|
|
186
|
+
url = f"{self.path}/{endpoint}"
|
|
187
|
+
local_params: dict[str, Any] = {}
|
|
188
|
+
if page is not None:
|
|
189
|
+
local_params["page"] = page
|
|
190
|
+
if size is not None:
|
|
191
|
+
local_params["size"] = size
|
|
192
|
+
if params is not None:
|
|
193
|
+
if isinstance(params, dict):
|
|
194
|
+
params.update(local_params)
|
|
195
|
+
if params_model is not None:
|
|
196
|
+
# if params_model is passed, use Pydantic to validate params
|
|
197
|
+
params = params_model(**params)
|
|
198
|
+
params = params.model_dump()
|
|
199
|
+
elif isinstance(params, BaseModel):
|
|
200
|
+
params = params.model_dump(exclude_none=True, exclude_defaults=True)
|
|
201
|
+
params.update(local_params)
|
|
202
|
+
content = None
|
|
203
|
+
if data is not None:
|
|
204
|
+
if isinstance(data, dict):
|
|
205
|
+
if body_model is not None:
|
|
206
|
+
body = body_model(**data)
|
|
207
|
+
content = body.model_dump_json()
|
|
208
|
+
else:
|
|
209
|
+
content = json.dumps(data)
|
|
210
|
+
elif isinstance(data, BaseModel):
|
|
211
|
+
content = data.model_dump_json()
|
|
212
|
+
req = self._sync_client.build_request(
|
|
213
|
+
method,
|
|
214
|
+
url,
|
|
215
|
+
headers=headers,
|
|
216
|
+
params=params,
|
|
217
|
+
content=content,
|
|
218
|
+
files=files,
|
|
219
|
+
timeout=timeout,
|
|
220
|
+
)
|
|
221
|
+
return req
|
|
222
|
+
|
|
223
|
+
def _raise_and_handle_errors(self, response: httpx.Response) -> None:
|
|
224
|
+
try:
|
|
225
|
+
response.raise_for_status()
|
|
226
|
+
except httpx.HTTPStatusError as e:
|
|
227
|
+
status = e.response.status_code
|
|
228
|
+
error_type: str | None = None
|
|
229
|
+
sdk_error: type[errors.BrokerError] | None = None
|
|
230
|
+
detail_message: str | None = None
|
|
231
|
+
try:
|
|
232
|
+
error_data = e.response.json()
|
|
233
|
+
except json.JSONDecodeError:
|
|
234
|
+
error_data = {}
|
|
235
|
+
detail = error_data.get("detail")
|
|
236
|
+
if detail and isinstance(detail, dict):
|
|
237
|
+
error_type = detail.get("type")
|
|
238
|
+
detail_message = detail.get("message", json.dumps(detail))
|
|
239
|
+
else:
|
|
240
|
+
detail_message = json.dumps(error_data)
|
|
241
|
+
if error_type:
|
|
242
|
+
sdk_error = getattr(errors, error_type, None)
|
|
243
|
+
if sdk_error:
|
|
244
|
+
raise sdk_error(detail=detail_message or "") from e
|
|
245
|
+
elif status == 401:
|
|
246
|
+
raise errors.AuthError(detail=detail_message or "") from e
|
|
247
|
+
else:
|
|
248
|
+
raise
|
|
249
|
+
|
|
250
|
+
def _convert_response_to_model(
|
|
251
|
+
self, data: Any, return_model: type[_ReturnModelT]
|
|
252
|
+
) -> _ReturnModelT | None:
|
|
253
|
+
"""Convert the JSON response to the correct Pydantic model"""
|
|
254
|
+
if data is None:
|
|
255
|
+
return data
|
|
256
|
+
return TypeAdapter(return_model).validate_python(data)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import cast
|
|
2
|
+
|
|
3
|
+
from ..models import (
|
|
4
|
+
CheckProgressRequest,
|
|
5
|
+
CheckProgressResponse,
|
|
6
|
+
CheckStartRequest,
|
|
7
|
+
CheckStartResponse,
|
|
8
|
+
)
|
|
9
|
+
from .base import BaseClient
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Check(BaseClient):
|
|
13
|
+
def start(self, body: CheckStartRequest) -> CheckStartResponse:
|
|
14
|
+
res = self.request(
|
|
15
|
+
"POST", endpoint="start", data=body, return_model=CheckStartResponse
|
|
16
|
+
)
|
|
17
|
+
return cast(CheckStartResponse, res)
|
|
18
|
+
|
|
19
|
+
async def start_async(self, body: CheckStartRequest) -> CheckStartResponse:
|
|
20
|
+
res = await self.request_async(
|
|
21
|
+
"POST", endpoint="start", data=body, return_model=CheckStartResponse
|
|
22
|
+
)
|
|
23
|
+
return cast(CheckStartResponse, res)
|
|
24
|
+
|
|
25
|
+
def progress(self, body: CheckProgressRequest) -> CheckProgressResponse:
|
|
26
|
+
res = self.request(
|
|
27
|
+
"POST", endpoint="progress", data=body, return_model=CheckProgressResponse
|
|
28
|
+
)
|
|
29
|
+
return cast(CheckProgressResponse, res)
|
|
30
|
+
|
|
31
|
+
async def progress_async(self, body: CheckProgressRequest) -> CheckProgressResponse:
|
|
32
|
+
res = await self.request_async(
|
|
33
|
+
"POST", endpoint="progress", data=body, return_model=CheckProgressResponse
|
|
34
|
+
)
|
|
35
|
+
return cast(CheckProgressResponse, res)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from typing import Any, Literal, cast
|
|
2
|
+
|
|
3
|
+
from ..models import Dataset, DatasetCreating, PageDatasetExample
|
|
4
|
+
from ..ty import Page
|
|
5
|
+
from .base import BaseClient
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Data(BaseClient):
|
|
9
|
+
def all(
|
|
10
|
+
self,
|
|
11
|
+
session: bool | None = None,
|
|
12
|
+
*,
|
|
13
|
+
page: int | None = None,
|
|
14
|
+
size: int | None = None,
|
|
15
|
+
) -> Page[str]:
|
|
16
|
+
res = self.request(
|
|
17
|
+
"GET",
|
|
18
|
+
endpoint="all",
|
|
19
|
+
data={"session": session},
|
|
20
|
+
page=page,
|
|
21
|
+
size=size,
|
|
22
|
+
return_model=Page[str],
|
|
23
|
+
)
|
|
24
|
+
return cast(Page[str], res)
|
|
25
|
+
|
|
26
|
+
async def all_async(
|
|
27
|
+
self,
|
|
28
|
+
session: bool | None = None,
|
|
29
|
+
*,
|
|
30
|
+
page: int | None = None,
|
|
31
|
+
size: int | None = None,
|
|
32
|
+
) -> Page[str]:
|
|
33
|
+
res = await self.request_async(
|
|
34
|
+
"GET",
|
|
35
|
+
endpoint="all",
|
|
36
|
+
data={"session": session},
|
|
37
|
+
page=page,
|
|
38
|
+
size=size,
|
|
39
|
+
return_model=Page[str],
|
|
40
|
+
)
|
|
41
|
+
return cast(Page[str], res)
|
|
42
|
+
|
|
43
|
+
def create(self, body: DatasetCreating) -> Dataset:
|
|
44
|
+
res = self.request("POST", endpoint="create", data=body, return_model=Dataset)
|
|
45
|
+
return cast(Dataset, res)
|
|
46
|
+
|
|
47
|
+
async def create_async(self, body: DatasetCreating) -> Dataset:
|
|
48
|
+
res = await self.request_async(
|
|
49
|
+
"POST", endpoint="create", data=body, return_model=Dataset
|
|
50
|
+
)
|
|
51
|
+
return cast(Dataset, res)
|
|
52
|
+
|
|
53
|
+
def read_examples(
|
|
54
|
+
self,
|
|
55
|
+
datasets: list[str],
|
|
56
|
+
*,
|
|
57
|
+
page: int | None = None,
|
|
58
|
+
size: int | None = None,
|
|
59
|
+
order: Literal["asc", "desc"] | None = "asc",
|
|
60
|
+
) -> PageDatasetExample:
|
|
61
|
+
params: dict[str, Any] = {}
|
|
62
|
+
if page is not None:
|
|
63
|
+
params["page"] = page
|
|
64
|
+
if size is not None:
|
|
65
|
+
params["size"] = size
|
|
66
|
+
if order is not None:
|
|
67
|
+
params["order"] = order
|
|
68
|
+
|
|
69
|
+
data = {"datasets": datasets}
|
|
70
|
+
|
|
71
|
+
res = self.request(
|
|
72
|
+
"POST",
|
|
73
|
+
endpoint="read-examples",
|
|
74
|
+
params=params,
|
|
75
|
+
data=data,
|
|
76
|
+
return_model=PageDatasetExample,
|
|
77
|
+
)
|
|
78
|
+
return cast(PageDatasetExample, res)
|
|
79
|
+
|
|
80
|
+
async def read_examples_async(
|
|
81
|
+
self,
|
|
82
|
+
datasets: list[str],
|
|
83
|
+
*,
|
|
84
|
+
page: int | None = None,
|
|
85
|
+
size: int | None = None,
|
|
86
|
+
order: Literal["asc", "desc"] | None = "asc",
|
|
87
|
+
) -> PageDatasetExample:
|
|
88
|
+
params: dict[str, Any] = {}
|
|
89
|
+
if page is not None:
|
|
90
|
+
params["page"] = page
|
|
91
|
+
if size is not None:
|
|
92
|
+
params["size"] = size
|
|
93
|
+
if order is not None:
|
|
94
|
+
params["order"] = order
|
|
95
|
+
|
|
96
|
+
data = {"datasets": datasets}
|
|
97
|
+
|
|
98
|
+
res = await self.request_async(
|
|
99
|
+
"POST",
|
|
100
|
+
endpoint="read-examples",
|
|
101
|
+
params=params,
|
|
102
|
+
data=data,
|
|
103
|
+
return_model=PageDatasetExample,
|
|
104
|
+
)
|
|
105
|
+
return cast(PageDatasetExample, res)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
from typing import Any, cast
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from ..models import RecipesMeta
|
|
6
|
+
from .base import BaseClient
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BuildImageResponse(BaseModel):
|
|
10
|
+
"""Response from the build-image endpoint."""
|
|
11
|
+
|
|
12
|
+
image_uri: str = Field(..., title="Image Uri")
|
|
13
|
+
meta: list[Any] = Field(..., title="Meta")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Envs(BaseClient):
|
|
17
|
+
def get_recipes_meta(self, package_name: str, package_version: str) -> RecipesMeta:
|
|
18
|
+
res = self.request(
|
|
19
|
+
"GET",
|
|
20
|
+
endpoint=f"{package_name}/{package_version}/meta",
|
|
21
|
+
return_model=RecipesMeta,
|
|
22
|
+
)
|
|
23
|
+
return cast(RecipesMeta, res)
|
|
24
|
+
|
|
25
|
+
async def get_recipes_meta_async(
|
|
26
|
+
self, package_name: str, package_version: str
|
|
27
|
+
) -> RecipesMeta:
|
|
28
|
+
res = await self.request_async(
|
|
29
|
+
"GET",
|
|
30
|
+
endpoint=f"{package_name}/{package_version}/meta",
|
|
31
|
+
return_model=RecipesMeta,
|
|
32
|
+
)
|
|
33
|
+
return cast(RecipesMeta, res)
|
|
34
|
+
|
|
35
|
+
def build_image(
|
|
36
|
+
self,
|
|
37
|
+
package_name: str,
|
|
38
|
+
package_version: str,
|
|
39
|
+
base_image: str,
|
|
40
|
+
tar_data_b64: str,
|
|
41
|
+
meta_json: str,
|
|
42
|
+
) -> BuildImageResponse:
|
|
43
|
+
"""Build an ad-hoc container image for a recipe package."""
|
|
44
|
+
res = self.request(
|
|
45
|
+
"POST",
|
|
46
|
+
endpoint="build-image",
|
|
47
|
+
data={
|
|
48
|
+
"package_name": package_name,
|
|
49
|
+
"package_version": package_version,
|
|
50
|
+
"base_image": base_image,
|
|
51
|
+
"tar_data": tar_data_b64,
|
|
52
|
+
"meta_json": meta_json,
|
|
53
|
+
},
|
|
54
|
+
return_model=BuildImageResponse,
|
|
55
|
+
)
|
|
56
|
+
return cast(BuildImageResponse, res)
|
|
57
|
+
|
|
58
|
+
async def build_image_async(
|
|
59
|
+
self,
|
|
60
|
+
package_name: str,
|
|
61
|
+
package_version: str,
|
|
62
|
+
base_image: str,
|
|
63
|
+
tar_data_b64: str,
|
|
64
|
+
meta_json: str,
|
|
65
|
+
) -> BuildImageResponse:
|
|
66
|
+
"""Build an ad-hoc container image for a recipe package (async)."""
|
|
67
|
+
res = await self.request_async(
|
|
68
|
+
"POST",
|
|
69
|
+
endpoint="build-image",
|
|
70
|
+
data={
|
|
71
|
+
"package_name": package_name,
|
|
72
|
+
"package_version": package_version,
|
|
73
|
+
"base_image": base_image,
|
|
74
|
+
"tar_data": tar_data_b64,
|
|
75
|
+
"meta_json": meta_json,
|
|
76
|
+
},
|
|
77
|
+
return_model=BuildImageResponse,
|
|
78
|
+
)
|
|
79
|
+
return cast(BuildImageResponse, res)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
from typing import IO, cast
|
|
2
|
+
|
|
3
|
+
import httpx
|
|
4
|
+
|
|
5
|
+
from ..models import (
|
|
6
|
+
Copying,
|
|
7
|
+
CopyPlan,
|
|
8
|
+
Deleting,
|
|
9
|
+
Downloading,
|
|
10
|
+
FileStats,
|
|
11
|
+
Listing,
|
|
12
|
+
PathList,
|
|
13
|
+
RsyncPlan,
|
|
14
|
+
SignedUrl,
|
|
15
|
+
Statting,
|
|
16
|
+
)
|
|
17
|
+
from .base import BaseClient
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Files(BaseClient):
|
|
21
|
+
def get_signed_url(self, *, path: str, method: str) -> SignedUrl:
|
|
22
|
+
res = self.request(
|
|
23
|
+
"POST",
|
|
24
|
+
endpoint="signed-url",
|
|
25
|
+
return_model=SignedUrl,
|
|
26
|
+
data={"path": path, "method": method},
|
|
27
|
+
)
|
|
28
|
+
return cast(SignedUrl, res)
|
|
29
|
+
|
|
30
|
+
async def get_signed_url_async(self, *, path: str, method: str) -> SignedUrl:
|
|
31
|
+
res = await self.request_async(
|
|
32
|
+
"POST",
|
|
33
|
+
endpoint="signed-url",
|
|
34
|
+
return_model=SignedUrl,
|
|
35
|
+
data={"path": path, "method": method},
|
|
36
|
+
)
|
|
37
|
+
return cast(SignedUrl, res)
|
|
38
|
+
|
|
39
|
+
def upload(
|
|
40
|
+
self, file: IO[bytes], *, dest: str, overwrite: bool, make_dirs: bool
|
|
41
|
+
) -> None:
|
|
42
|
+
self.request(
|
|
43
|
+
"POST",
|
|
44
|
+
endpoint="upload",
|
|
45
|
+
files={"file": file},
|
|
46
|
+
params={"dest": dest, "overwrite": overwrite, "make_dirs": make_dirs},
|
|
47
|
+
return_model=SignedUrl,
|
|
48
|
+
timeout=httpx.Timeout(5.0, read=300.0, write=300.0),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
async def upload_async(
|
|
52
|
+
self, file: IO[bytes], *, dest: str, overwrite: bool, make_dirs: bool
|
|
53
|
+
) -> None:
|
|
54
|
+
await self.request_async(
|
|
55
|
+
"POST",
|
|
56
|
+
endpoint="upload",
|
|
57
|
+
files={"file": file},
|
|
58
|
+
params={"dest": dest, "overwrite": overwrite, "make_dirs": make_dirs},
|
|
59
|
+
return_model=None,
|
|
60
|
+
timeout=httpx.Timeout(5.0, read=300.0, write=300.0),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def copy(self, body: Copying) -> None:
|
|
64
|
+
self.request("POST", endpoint="copy", data=body, return_model=None)
|
|
65
|
+
|
|
66
|
+
async def copy_async(self, body: Copying) -> None:
|
|
67
|
+
await self.request_async("POST", endpoint="copy", data=body, return_model=None)
|
|
68
|
+
|
|
69
|
+
def delete(self, body: Deleting) -> None:
|
|
70
|
+
self.request("POST", endpoint="delete", data=body, return_model=None)
|
|
71
|
+
|
|
72
|
+
async def delete_async(self, body: Deleting) -> None:
|
|
73
|
+
await self.request_async(
|
|
74
|
+
"POST", endpoint="delete", data=body, return_model=None
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def stat(self, body: Statting) -> FileStats:
|
|
78
|
+
res = self.request("POST", endpoint="stat", data=body, return_model=FileStats)
|
|
79
|
+
return cast(FileStats, res)
|
|
80
|
+
|
|
81
|
+
async def stat_async(self, body: Statting) -> FileStats:
|
|
82
|
+
res = await self.request_async(
|
|
83
|
+
"POST", endpoint="stat", data=body, return_model=FileStats
|
|
84
|
+
)
|
|
85
|
+
return cast(FileStats, res)
|
|
86
|
+
|
|
87
|
+
def list_dir(self, body: Listing) -> PathList:
|
|
88
|
+
res = self.request(
|
|
89
|
+
"POST",
|
|
90
|
+
endpoint="list-dir",
|
|
91
|
+
data=body,
|
|
92
|
+
return_model=PathList,
|
|
93
|
+
timeout=httpx.Timeout(5.0, read=20.0),
|
|
94
|
+
)
|
|
95
|
+
return cast(PathList, res)
|
|
96
|
+
|
|
97
|
+
async def list_dir_async(self, body: Listing) -> PathList:
|
|
98
|
+
res = await self.request_async(
|
|
99
|
+
"POST",
|
|
100
|
+
endpoint="list-dir",
|
|
101
|
+
data=body,
|
|
102
|
+
return_model=PathList,
|
|
103
|
+
timeout=httpx.Timeout(5.0, read=20.0),
|
|
104
|
+
)
|
|
105
|
+
return cast(PathList, res)
|
|
106
|
+
|
|
107
|
+
def plan_directory_copy(self, body: Copying) -> CopyPlan:
|
|
108
|
+
res = self.request(
|
|
109
|
+
"POST", endpoint="plan-directory-copy", data=body, return_model=CopyPlan
|
|
110
|
+
)
|
|
111
|
+
return cast(CopyPlan, res)
|
|
112
|
+
|
|
113
|
+
async def plan_directory_copy_async(self, body: Copying) -> CopyPlan:
|
|
114
|
+
res = await self.request_async(
|
|
115
|
+
"POST", endpoint="plan-directory-copy", data=body, return_model=CopyPlan
|
|
116
|
+
)
|
|
117
|
+
return cast(CopyPlan, res)
|
|
118
|
+
|
|
119
|
+
def plan_directory_rsync(self, body: Copying) -> RsyncPlan:
|
|
120
|
+
res = self.request(
|
|
121
|
+
"POST", endpoint="plan-directory-rsync", data=body, return_model=RsyncPlan
|
|
122
|
+
)
|
|
123
|
+
return cast(RsyncPlan, res)
|
|
124
|
+
|
|
125
|
+
async def plan_directory_rsync_async(self, body: Copying) -> RsyncPlan:
|
|
126
|
+
res = await self.request_async(
|
|
127
|
+
"POST", endpoint="plan-directory-rsync", data=body, return_model=RsyncPlan
|
|
128
|
+
)
|
|
129
|
+
return cast(RsyncPlan, res)
|
|
130
|
+
|
|
131
|
+
def download(self, body: Downloading) -> IO[bytes]:
|
|
132
|
+
res = self.request("POST", endpoint="download", data=body, stream=True)
|
|
133
|
+
return cast(IO[bytes], res)
|
|
134
|
+
|
|
135
|
+
async def download_async(self, body: Downloading) -> IO[bytes]:
|
|
136
|
+
res = await self.request_async(
|
|
137
|
+
"POST", endpoint="download", data=body, stream=True
|
|
138
|
+
)
|
|
139
|
+
return cast(IO[bytes], res)
|