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.
Files changed (32) hide show
  1. ellf_broker_sdk-7.0.8/MANIFEST.in +1 -0
  2. ellf_broker_sdk-7.0.8/PKG-INFO +18 -0
  3. ellf_broker_sdk-7.0.8/README.md +9 -0
  4. ellf_broker_sdk-7.0.8/ellf_broker_sdk/__init__.py +3 -0
  5. ellf_broker_sdk-7.0.8/ellf_broker_sdk/about.json +6 -0
  6. ellf_broker_sdk-7.0.8/ellf_broker_sdk/about.py +23 -0
  7. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/__init__.py +0 -0
  8. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/base.py +256 -0
  9. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/check.py +35 -0
  10. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/data.py +105 -0
  11. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/envs.py +79 -0
  12. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/files.py +139 -0
  13. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/full_client.py +206 -0
  14. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/jobs.py +238 -0
  15. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/nodes.py +24 -0
  16. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/secrets.py +40 -0
  17. ellf_broker_sdk-7.0.8/ellf_broker_sdk/client/worker_types.py +18 -0
  18. ellf_broker_sdk-7.0.8/ellf_broker_sdk/errors.py +115 -0
  19. ellf_broker_sdk-7.0.8/ellf_broker_sdk/models.py +519 -0
  20. ellf_broker_sdk-7.0.8/ellf_broker_sdk/py.typed +1 -0
  21. ellf_broker_sdk-7.0.8/ellf_broker_sdk/ty.py +44 -0
  22. ellf_broker_sdk-7.0.8/ellf_broker_sdk/util.py +87 -0
  23. ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/PKG-INFO +18 -0
  24. ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/SOURCES.txt +30 -0
  25. ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/dependency_links.txt +1 -0
  26. ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/not-zip-safe +1 -0
  27. ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/requires.txt +4 -0
  28. ellf_broker_sdk-7.0.8/ellf_broker_sdk.egg-info/top_level.txt +1 -0
  29. ellf_broker_sdk-7.0.8/requirements.in +6 -0
  30. ellf_broker_sdk-7.0.8/setup.cfg +4 -0
  31. ellf_broker_sdk-7.0.8/setup.py +89 -0
  32. 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,3 @@
1
+ from .client.full_client import AccessTokenCredential, Client
2
+
3
+ __all__ = ["Client", "AccessTokenCredential"]
@@ -0,0 +1,6 @@
1
+ {
2
+ "title": "Ellf Broker SDK",
3
+ "name": "ellf-broker-sdk",
4
+ "version": "7.0.8",
5
+ "summary": "Ellf Broker SDK"
6
+ }
@@ -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", "")
@@ -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)