codeapi-client 0.4.1__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.
- codeapi/__init__.py +17 -0
- codeapi/client/__init__.py +37 -0
- codeapi/client/_async/__init__.py +357 -0
- codeapi/client/_async/_app.py +369 -0
- codeapi/client/_async/_cli.py +334 -0
- codeapi/client/_async/_code.py +211 -0
- codeapi/client/_async/_jobs.py +512 -0
- codeapi/client/_async/_mcp.py +445 -0
- codeapi/client/_async/_web.py +343 -0
- codeapi/client/_base.py +118 -0
- codeapi/client/_sync/__init__.py +347 -0
- codeapi/client/_sync/_app.py +367 -0
- codeapi/client/_sync/_cli.py +332 -0
- codeapi/client/_sync/_code.py +203 -0
- codeapi/client/_sync/_jobs.py +497 -0
- codeapi/client/_sync/_mcp.py +442 -0
- codeapi/client/_sync/_web.py +341 -0
- codeapi/client/_utils.py +61 -0
- codeapi/client/py.typed +0 -0
- codeapi/types/__init__.py +77 -0
- codeapi/types/_api.py +30 -0
- codeapi/types/_base.py +21 -0
- codeapi/types/_code.py +31 -0
- codeapi/types/_enums.py +150 -0
- codeapi/types/_env.py +65 -0
- codeapi/types/_exc.py +35 -0
- codeapi/types/_job.py +67 -0
- codeapi/types/_json.py +67 -0
- codeapi/types/_stream.py +36 -0
- codeapi/types/_swarm.py +85 -0
- codeapi/types/_time.py +46 -0
- codeapi/types/_zips.py +466 -0
- codeapi/types/py.typed +0 -0
- codeapi_client-0.4.1.dist-info/METADATA +14 -0
- codeapi_client-0.4.1.dist-info/RECORD +36 -0
- codeapi_client-0.4.1.dist-info/WHEEL +4 -0
codeapi/types/_zips.py
ADDED
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import base64
|
|
5
|
+
import zipfile
|
|
6
|
+
from io import BytesIO, IOBase
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import BinaryIO, Type, TypeVar
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel, field_serializer, field_validator
|
|
11
|
+
from typing_extensions import Self
|
|
12
|
+
|
|
13
|
+
from codeapi.types import JsonData
|
|
14
|
+
|
|
15
|
+
from ._enums import ExitType
|
|
16
|
+
|
|
17
|
+
EMPTY_ZIP_BYTES = b"\x50\x4b\x05\x06" + b"\x00" * 18
|
|
18
|
+
|
|
19
|
+
DOTENV_FILE = ".env"
|
|
20
|
+
PEXENV_FILE = "venv.pex"
|
|
21
|
+
PEXENV_CMD = f"./{PEXENV_FILE}"
|
|
22
|
+
REQUIREMENTS_TXT = "requirements.txt"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
TZip = TypeVar("TZip", bound="ZipBytes")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ZipBytes(BaseModel):
|
|
29
|
+
zip_bytes: bytes = EMPTY_ZIP_BYTES
|
|
30
|
+
|
|
31
|
+
@field_serializer("zip_bytes")
|
|
32
|
+
def serialize_bytes(self, value: bytes) -> str:
|
|
33
|
+
return base64.b64encode(value).decode()
|
|
34
|
+
|
|
35
|
+
@field_validator("zip_bytes", mode="before")
|
|
36
|
+
@classmethod
|
|
37
|
+
def deserialize_bytes(cls, value: bytes | str) -> bytes:
|
|
38
|
+
if isinstance(value, bytes):
|
|
39
|
+
return value
|
|
40
|
+
return base64.b64decode(value)
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def files(self) -> list[str]:
|
|
44
|
+
return self.list_files()
|
|
45
|
+
|
|
46
|
+
def list_files(self) -> list[str]:
|
|
47
|
+
with zipfile.ZipFile(BytesIO(self.zip_bytes)) as zf:
|
|
48
|
+
return zf.namelist()
|
|
49
|
+
|
|
50
|
+
def has_file(self, file: str) -> bool:
|
|
51
|
+
return file in self.list_files()
|
|
52
|
+
|
|
53
|
+
def print_files(self) -> None:
|
|
54
|
+
with zipfile.ZipFile(BytesIO(self.zip_bytes)) as zf:
|
|
55
|
+
return zf.printdir()
|
|
56
|
+
|
|
57
|
+
def read_file(self, file: str) -> str:
|
|
58
|
+
with zipfile.ZipFile(BytesIO(self.zip_bytes)) as zf:
|
|
59
|
+
return zf.read(name=file).decode()
|
|
60
|
+
|
|
61
|
+
def read_file_bytes(self, file: str) -> bytes:
|
|
62
|
+
with zipfile.ZipFile(BytesIO(self.zip_bytes)) as zf:
|
|
63
|
+
return zf.read(name=file)
|
|
64
|
+
|
|
65
|
+
def read_files(self, files: list[str]) -> list[str]:
|
|
66
|
+
with zipfile.ZipFile(BytesIO(self.zip_bytes)) as zf:
|
|
67
|
+
return [zf.read(name=file).decode() for file in files]
|
|
68
|
+
|
|
69
|
+
def read_files_bytes(self, files: list[str]) -> list[bytes]:
|
|
70
|
+
with zipfile.ZipFile(BytesIO(self.zip_bytes)) as zf:
|
|
71
|
+
return [zf.read(name=file) for file in files]
|
|
72
|
+
|
|
73
|
+
def add_empty_file(self, name: str, arcdir: str = ".") -> Self:
|
|
74
|
+
return self.add_file(arcdir=arcdir, name=name, file=b"")
|
|
75
|
+
|
|
76
|
+
def add_file(
|
|
77
|
+
self, file: Path | str | bytes, name: str | None = None, arcdir: str = "."
|
|
78
|
+
) -> Self:
|
|
79
|
+
names = [name] if name is not None else None
|
|
80
|
+
return self.add_files(files=[file], names=names, arcdir=arcdir)
|
|
81
|
+
|
|
82
|
+
def add_files(
|
|
83
|
+
self,
|
|
84
|
+
files: list[Path | str | bytes],
|
|
85
|
+
names: list[str] | None = None,
|
|
86
|
+
arcdir: str = ".",
|
|
87
|
+
) -> Self:
|
|
88
|
+
if names is not None and len(files) != len(names):
|
|
89
|
+
raise ValueError("files and names must have the same length")
|
|
90
|
+
|
|
91
|
+
arcnames = set()
|
|
92
|
+
mem_zip = BytesIO()
|
|
93
|
+
with zipfile.ZipFile(BytesIO(self.zip_bytes), "r") as zf_old:
|
|
94
|
+
with zipfile.ZipFile(mem_zip, "w") as zf_new:
|
|
95
|
+
for i, file in enumerate(files):
|
|
96
|
+
if isinstance(file, bytes):
|
|
97
|
+
name = names[i] if names is not None else None
|
|
98
|
+
if not name:
|
|
99
|
+
raise ValueError("name required when file is of type bytes")
|
|
100
|
+
arcname = (arcdir.rstrip("/") + "/" + name).removeprefix("./")
|
|
101
|
+
arcnames.add(arcname)
|
|
102
|
+
zf_new.writestr(arcname, file)
|
|
103
|
+
else:
|
|
104
|
+
name = names[i] if names is not None else Path(file).name
|
|
105
|
+
if not name:
|
|
106
|
+
name = Path(file).name
|
|
107
|
+
arcname = (arcdir.rstrip("/") + "/" + name).removeprefix("./")
|
|
108
|
+
arcnames.add(arcname)
|
|
109
|
+
zf_new.write(str(file), arcname)
|
|
110
|
+
for f in set(zf_old.namelist()) - arcnames:
|
|
111
|
+
zf_new.writestr(f, zf_old.read(f))
|
|
112
|
+
|
|
113
|
+
self.zip_bytes = mem_zip.getvalue()
|
|
114
|
+
return self
|
|
115
|
+
|
|
116
|
+
def delete_file(self, file: str) -> Self:
|
|
117
|
+
return self.delete_files(files=[file])
|
|
118
|
+
|
|
119
|
+
def delete_files(self, files: list[str]) -> Self:
|
|
120
|
+
mem_zip = BytesIO()
|
|
121
|
+
with zipfile.ZipFile(BytesIO(self.zip_bytes), "r") as zf_old:
|
|
122
|
+
with zipfile.ZipFile(mem_zip, "w") as zf_new:
|
|
123
|
+
for f in zf_old.namelist():
|
|
124
|
+
if f not in files:
|
|
125
|
+
zf_new.writestr(f, zf_old.read(f))
|
|
126
|
+
self.zip_bytes = mem_zip.getvalue()
|
|
127
|
+
return self
|
|
128
|
+
|
|
129
|
+
def extract_file(
|
|
130
|
+
self, file: str, path: Path | str = ".", flatten: bool = False
|
|
131
|
+
) -> Self:
|
|
132
|
+
with zipfile.ZipFile(BytesIO(self.zip_bytes)) as zf:
|
|
133
|
+
if flatten:
|
|
134
|
+
target_path = Path(path) / Path(file).name
|
|
135
|
+
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
136
|
+
target_path.write_bytes(zf.read(name=file))
|
|
137
|
+
elif Path(file).name in str(path):
|
|
138
|
+
target_path = Path(path)
|
|
139
|
+
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
140
|
+
target_path.write_bytes(zf.read(name=file))
|
|
141
|
+
else:
|
|
142
|
+
zf.extract(member=file, path=str(path))
|
|
143
|
+
return self
|
|
144
|
+
|
|
145
|
+
def extract(self, path: Path | str, files: list[str] | None = None) -> Self:
|
|
146
|
+
with zipfile.ZipFile(BytesIO(self.zip_bytes)) as zf:
|
|
147
|
+
for file_info in zf.infolist():
|
|
148
|
+
if ".." in file_info.filename or file_info.filename.startswith("/"):
|
|
149
|
+
raise ValueError("invalid file paths in archive")
|
|
150
|
+
zf.extractall(path=str(path), members=files)
|
|
151
|
+
return self
|
|
152
|
+
|
|
153
|
+
def to_zipfile(self, path: Path | str) -> None:
|
|
154
|
+
Path(path).write_bytes(data=self.zip_bytes)
|
|
155
|
+
|
|
156
|
+
def to_bytesio(self) -> BytesIO:
|
|
157
|
+
return BytesIO(self.zip_bytes)
|
|
158
|
+
|
|
159
|
+
def to_codezip(self) -> CodeZip:
|
|
160
|
+
return CodeZip(zip_bytes=self.zip_bytes)
|
|
161
|
+
|
|
162
|
+
def to_datazip(self) -> DataZip:
|
|
163
|
+
return DataZip(zip_bytes=self.zip_bytes)
|
|
164
|
+
|
|
165
|
+
@classmethod
|
|
166
|
+
def from_zipfile(cls: Type[TZip], path: Path | str) -> TZip:
|
|
167
|
+
path = Path(path)
|
|
168
|
+
if not path.is_file():
|
|
169
|
+
raise ValueError(f"{path} is not a file")
|
|
170
|
+
|
|
171
|
+
zip_bytes = path.read_bytes()
|
|
172
|
+
if not cls.is_valid_zip(zip_bytes=zip_bytes):
|
|
173
|
+
raise ValueError(f"{path} is not a valid zip file")
|
|
174
|
+
return cls(zip_bytes=zip_bytes)
|
|
175
|
+
|
|
176
|
+
@classmethod
|
|
177
|
+
def from_dir(
|
|
178
|
+
cls: Type[TZip],
|
|
179
|
+
dir: Path | str,
|
|
180
|
+
include_dir: bool = False,
|
|
181
|
+
exclude: list[str] = ["__pycache__", ".venv"],
|
|
182
|
+
) -> TZip:
|
|
183
|
+
dir = Path(dir)
|
|
184
|
+
if not dir.is_dir():
|
|
185
|
+
raise ValueError(f"{dir} is not a directory")
|
|
186
|
+
|
|
187
|
+
zip_dir = dir.parent if include_dir else dir
|
|
188
|
+
mem_zip = BytesIO()
|
|
189
|
+
with zipfile.ZipFile(mem_zip, "w") as zf:
|
|
190
|
+
for file in dir.rglob("*"):
|
|
191
|
+
if all(excl not in str(file) for excl in exclude):
|
|
192
|
+
zf.write(file, file.relative_to(zip_dir))
|
|
193
|
+
|
|
194
|
+
return cls(zip_bytes=mem_zip.getvalue())
|
|
195
|
+
|
|
196
|
+
@classmethod
|
|
197
|
+
def from_bytes(cls: Type[TZip], zip_bytes: bytes | BinaryIO) -> TZip:
|
|
198
|
+
if isinstance(zip_bytes, (BinaryIO, IOBase)):
|
|
199
|
+
zip_bytes.seek(0)
|
|
200
|
+
zip_bytes = zip_bytes.read()
|
|
201
|
+
if not cls.is_valid_zip(zip_bytes=zip_bytes):
|
|
202
|
+
raise ValueError("given bytes are not a valid zip-archive")
|
|
203
|
+
return cls(zip_bytes=zip_bytes)
|
|
204
|
+
|
|
205
|
+
@classmethod
|
|
206
|
+
def from_file(cls: Type[TZip], file: Path | str, arcdir: str = ".") -> TZip:
|
|
207
|
+
return cls().add_file(file=file, arcdir=arcdir)
|
|
208
|
+
|
|
209
|
+
@classmethod
|
|
210
|
+
def from_files(
|
|
211
|
+
cls: Type[TZip],
|
|
212
|
+
files: list[Path | str | bytes],
|
|
213
|
+
names: list[str] | None = None,
|
|
214
|
+
arcdir: str = ".",
|
|
215
|
+
) -> TZip:
|
|
216
|
+
return cls().add_files(files=files, names=names, arcdir=arcdir)
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def is_valid_zip(zip_bytes: bytes) -> bool:
|
|
220
|
+
try:
|
|
221
|
+
with zipfile.ZipFile(BytesIO(zip_bytes)) as zf:
|
|
222
|
+
return zf.testzip() is None
|
|
223
|
+
except zipfile.BadZipFile:
|
|
224
|
+
return False
|
|
225
|
+
|
|
226
|
+
async def list_files_async(self) -> list[str]:
|
|
227
|
+
return await asyncio.to_thread(self.list_files)
|
|
228
|
+
|
|
229
|
+
async def print_files_async(self) -> None:
|
|
230
|
+
return await asyncio.to_thread(self.print_files)
|
|
231
|
+
|
|
232
|
+
async def read_file_async(self, file: str) -> str:
|
|
233
|
+
return await asyncio.to_thread(self.read_file, file)
|
|
234
|
+
|
|
235
|
+
async def read_file_bytes_async(self, file: str) -> bytes:
|
|
236
|
+
return await asyncio.to_thread(self.read_file_bytes, file)
|
|
237
|
+
|
|
238
|
+
async def read_files_async(self, files: list[str]) -> list[str]:
|
|
239
|
+
return await asyncio.to_thread(self.read_files, files)
|
|
240
|
+
|
|
241
|
+
async def read_files_bytes_async(self, files: list[str]) -> list[bytes]:
|
|
242
|
+
return await asyncio.to_thread(self.read_files_bytes, files)
|
|
243
|
+
|
|
244
|
+
async def add_file_async(self, file: Path | str, arcdir: str = ".") -> Self:
|
|
245
|
+
return await asyncio.to_thread(self.add_file, file, arcdir)
|
|
246
|
+
|
|
247
|
+
async def add_empty_file_async(self, name: str, arcdir: str = ".") -> Self:
|
|
248
|
+
return await asyncio.to_thread(self.add_empty_file, name, arcdir)
|
|
249
|
+
|
|
250
|
+
async def add_files_async(
|
|
251
|
+
self,
|
|
252
|
+
files: list[Path | str | bytes],
|
|
253
|
+
names: list[str] | None = None,
|
|
254
|
+
arcdir: str = ".",
|
|
255
|
+
) -> Self:
|
|
256
|
+
return await asyncio.to_thread(
|
|
257
|
+
self.add_files, files=files, names=names, arcdir=arcdir
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
async def delete_file_async(self, file: str) -> Self:
|
|
261
|
+
return await asyncio.to_thread(self.delete_file, file)
|
|
262
|
+
|
|
263
|
+
async def delete_files_async(self, files: list[str]) -> Self:
|
|
264
|
+
return await asyncio.to_thread(self.delete_files, files)
|
|
265
|
+
|
|
266
|
+
async def extract_file_async(
|
|
267
|
+
self, file: str, path: Path | str, flatten: bool = False
|
|
268
|
+
) -> Self:
|
|
269
|
+
return await asyncio.to_thread(self.extract_file, file, path, flatten)
|
|
270
|
+
|
|
271
|
+
async def extract_async(
|
|
272
|
+
self, path: Path | str, files: list[str] | None = None
|
|
273
|
+
) -> Self:
|
|
274
|
+
return await asyncio.to_thread(self.extract, path, files)
|
|
275
|
+
|
|
276
|
+
async def to_zipfile_async(self, path: Path | str) -> None:
|
|
277
|
+
return await asyncio.to_thread(self.to_zipfile, path)
|
|
278
|
+
|
|
279
|
+
async def to_bytesio_async(self) -> BytesIO:
|
|
280
|
+
return await asyncio.to_thread(self.to_bytesio)
|
|
281
|
+
|
|
282
|
+
async def to_codezip_async(self) -> CodeZip:
|
|
283
|
+
return await asyncio.to_thread(self.to_codezip)
|
|
284
|
+
|
|
285
|
+
async def to_datazip_async(self) -> DataZip:
|
|
286
|
+
return await asyncio.to_thread(self.to_datazip)
|
|
287
|
+
|
|
288
|
+
@classmethod
|
|
289
|
+
async def from_zipfile_async(cls: Type[TZip], path: Path | str) -> TZip:
|
|
290
|
+
return await asyncio.to_thread(cls.from_zipfile, path)
|
|
291
|
+
|
|
292
|
+
@classmethod
|
|
293
|
+
async def from_dir_async(
|
|
294
|
+
cls: Type[TZip],
|
|
295
|
+
directory: Path | str,
|
|
296
|
+
include_dir: bool = False,
|
|
297
|
+
exclude: list[str] = ["__pycache__", ".venv"],
|
|
298
|
+
) -> TZip:
|
|
299
|
+
return await asyncio.to_thread(cls.from_dir, directory, include_dir, exclude)
|
|
300
|
+
|
|
301
|
+
@classmethod
|
|
302
|
+
async def from_bytes_async(cls: Type[TZip], zip_bytes: bytes | BinaryIO) -> TZip:
|
|
303
|
+
return await asyncio.to_thread(cls.from_bytes, zip_bytes)
|
|
304
|
+
|
|
305
|
+
@classmethod
|
|
306
|
+
async def from_file_async(
|
|
307
|
+
cls: Type[TZip], file: Path | str, arcdir: str = "."
|
|
308
|
+
) -> TZip:
|
|
309
|
+
return await asyncio.to_thread(cls.from_file, file, arcdir)
|
|
310
|
+
|
|
311
|
+
@classmethod
|
|
312
|
+
async def from_files_async(
|
|
313
|
+
cls: Type[TZip],
|
|
314
|
+
files: list[Path | str | bytes],
|
|
315
|
+
names: list[str] | None = None,
|
|
316
|
+
arcdir: str = ".",
|
|
317
|
+
) -> TZip:
|
|
318
|
+
return await asyncio.to_thread(
|
|
319
|
+
cls.from_files, files=files, names=names, arcdir=arcdir
|
|
320
|
+
)
|
|
321
|
+
|
|
322
|
+
@staticmethod
|
|
323
|
+
async def is_valid_zip_async(zip_bytes: bytes) -> bool:
|
|
324
|
+
return await asyncio.to_thread(ZipBytes.is_valid_zip, zip_bytes)
|
|
325
|
+
|
|
326
|
+
@property
|
|
327
|
+
def n_bytes(self) -> int:
|
|
328
|
+
return len(self.zip_bytes)
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def n_files(self) -> int:
|
|
332
|
+
return len(self.files)
|
|
333
|
+
|
|
334
|
+
def __str__(self):
|
|
335
|
+
return f"{self.__class__.__name__}(n_bytes={self.n_bytes})"
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
class CodeZip(ZipBytes):
|
|
339
|
+
@property
|
|
340
|
+
def code_id(self) -> str | None:
|
|
341
|
+
if ".code_id" not in self.list_files():
|
|
342
|
+
return None
|
|
343
|
+
return self.read_file(".code_id").strip()
|
|
344
|
+
|
|
345
|
+
@code_id.setter
|
|
346
|
+
def code_id(self, value):
|
|
347
|
+
self.add_file(file=f"{value}".encode(), name=".code_id")
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def has_dotenv(self) -> bool:
|
|
351
|
+
return DOTENV_FILE in self.list_files()
|
|
352
|
+
|
|
353
|
+
@property
|
|
354
|
+
def has_requirements(self) -> bool:
|
|
355
|
+
return REQUIREMENTS_TXT in self.list_files()
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def requirements(self) -> list[str] | None:
|
|
359
|
+
if not self.has_requirements:
|
|
360
|
+
return None
|
|
361
|
+
return self.read_file(REQUIREMENTS_TXT).splitlines()
|
|
362
|
+
|
|
363
|
+
@property
|
|
364
|
+
def has_pexenv(self) -> bool:
|
|
365
|
+
return PEXENV_FILE in self.list_files()
|
|
366
|
+
|
|
367
|
+
@property
|
|
368
|
+
def pexenv_bytes(self) -> bytes | None:
|
|
369
|
+
if not self.has_requirements:
|
|
370
|
+
return None
|
|
371
|
+
return self.read_file_bytes(PEXENV_FILE)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
class DataZip(ZipBytes):
|
|
375
|
+
@property
|
|
376
|
+
def data_id(self) -> str | None:
|
|
377
|
+
if ".data_id" not in self.list_files():
|
|
378
|
+
return None
|
|
379
|
+
return self.read_file(".data_id").strip()
|
|
380
|
+
|
|
381
|
+
@data_id.setter
|
|
382
|
+
def data_id(self, value):
|
|
383
|
+
self.add_file(file=f"{value}".encode(), name=".data_id")
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
class JobResult(ZipBytes):
|
|
387
|
+
@property
|
|
388
|
+
def job_id(self) -> str | None:
|
|
389
|
+
if ".job_id" not in self.list_files():
|
|
390
|
+
return None
|
|
391
|
+
return self.read_file(".job_id").rstrip("\n") or None
|
|
392
|
+
|
|
393
|
+
@property
|
|
394
|
+
def code_id(self) -> str | None:
|
|
395
|
+
if ".code_id" not in self.list_files():
|
|
396
|
+
return None
|
|
397
|
+
return self.read_file(".code_id").rstrip("\n") or None
|
|
398
|
+
|
|
399
|
+
@property
|
|
400
|
+
def stdout(self) -> str:
|
|
401
|
+
if ".stdout" not in self.list_files():
|
|
402
|
+
return ""
|
|
403
|
+
return self.read_file(".stdout").rstrip("\n")
|
|
404
|
+
|
|
405
|
+
@property
|
|
406
|
+
def stderr(self) -> str:
|
|
407
|
+
if ".stderr" not in self.list_files():
|
|
408
|
+
return ""
|
|
409
|
+
return self.read_file(".stderr").rstrip("\n")
|
|
410
|
+
|
|
411
|
+
@property
|
|
412
|
+
def output(self) -> str:
|
|
413
|
+
if ".output" not in self.list_files():
|
|
414
|
+
return ""
|
|
415
|
+
return self.read_file(".output").rstrip("\n")
|
|
416
|
+
|
|
417
|
+
@property
|
|
418
|
+
def exit_code(self) -> int:
|
|
419
|
+
if ".exit_code" not in self.list_files():
|
|
420
|
+
return 0
|
|
421
|
+
return int(self.read_file(".exit_code"))
|
|
422
|
+
|
|
423
|
+
@property
|
|
424
|
+
def cmd(self) -> str:
|
|
425
|
+
if ".cmd" not in self.list_files():
|
|
426
|
+
return ""
|
|
427
|
+
return self.read_file(".cmd")
|
|
428
|
+
|
|
429
|
+
@property
|
|
430
|
+
def cwd(self) -> str:
|
|
431
|
+
if ".cwd" not in self.list_files():
|
|
432
|
+
return ""
|
|
433
|
+
return self.read_file(".cwd")
|
|
434
|
+
|
|
435
|
+
@property
|
|
436
|
+
def exit_type(self) -> ExitType:
|
|
437
|
+
if ".exit_type" not in self.list_files():
|
|
438
|
+
return ExitType.NORMAL
|
|
439
|
+
return ExitType(self.read_file(".exit_type"))
|
|
440
|
+
|
|
441
|
+
@property
|
|
442
|
+
def result_json(self) -> JsonData:
|
|
443
|
+
if "code_execution.json" in self.list_files():
|
|
444
|
+
return JsonData(data=self.read_file_bytes("code_execution.json"))
|
|
445
|
+
if "code_ingestion.json" in self.list_files():
|
|
446
|
+
return JsonData(data=self.read_file_bytes("code_ingestion.json"))
|
|
447
|
+
return JsonData()
|
|
448
|
+
|
|
449
|
+
@property
|
|
450
|
+
def n_artifacts(self) -> int:
|
|
451
|
+
return len(self.list_artifacts())
|
|
452
|
+
|
|
453
|
+
@property
|
|
454
|
+
def artifacts(self) -> list[str]:
|
|
455
|
+
return self.list_artifacts()
|
|
456
|
+
|
|
457
|
+
def list_artifacts(self) -> list[str]:
|
|
458
|
+
return [x for x in self.list_files() if x.startswith("artifacts/")]
|
|
459
|
+
|
|
460
|
+
def extract_artifact(self, artifact: str, directory: str = "."):
|
|
461
|
+
path = f"{directory.rstrip('/')}/{artifact.lstrip('artifacts/')}"
|
|
462
|
+
self.extract_file(file=artifact, path=path)
|
|
463
|
+
|
|
464
|
+
def extract_artifacts(self, directory: str = "."):
|
|
465
|
+
for artifact in self.list_artifacts():
|
|
466
|
+
self.extract_artifact(artifact=artifact, directory=directory)
|
codeapi/types/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codeapi-client
|
|
3
|
+
Version: 0.4.1
|
|
4
|
+
Summary: CodeAPI Client
|
|
5
|
+
Requires-Python: >=3.10
|
|
6
|
+
Requires-Dist: aiofiles>=25.1.0
|
|
7
|
+
Requires-Dist: fastapi==0.118.3
|
|
8
|
+
Requires-Dist: fastmcp>=2.12.4
|
|
9
|
+
Requires-Dist: httpx>=0.28.1
|
|
10
|
+
Requires-Dist: psutil>=7.1.0
|
|
11
|
+
Requires-Dist: pydantic>=2.12.2
|
|
12
|
+
Requires-Dist: python-dotenv>=1.1.1
|
|
13
|
+
Requires-Dist: strenum>=0.4.15
|
|
14
|
+
Requires-Dist: typing-extensions>=4.15.0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
codeapi/__init__.py,sha256=sJX-IniQsZJ6KoZd3VT86baPfT2c5kfFjocYfAsAgvM,254
|
|
2
|
+
codeapi/client/__init__.py,sha256=f0Fi_ZVGax_oevqr6jYeSUlb3M4jFNN0LpEAOnHEBz8,711
|
|
3
|
+
codeapi/client/_base.py,sha256=WIRgt667SsZHfzSR2NYMNnY2PEhgEUV4gNKbr_4SEGc,4406
|
|
4
|
+
codeapi/client/_utils.py,sha256=-0QCNoIQ2TrEFAWG7jdPgwjFG6aB2PTj7uskyp5V9F8,1429
|
|
5
|
+
codeapi/client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
codeapi/client/_async/__init__.py,sha256=10KUVLd8PE23TLnTlTC_fFbofp3TMtNM5akItEWqAyM,10382
|
|
7
|
+
codeapi/client/_async/_app.py,sha256=ak5py4o2tVtW4omw_Baxhz-F-honA8dRexReSZ0Y6SY,10766
|
|
8
|
+
codeapi/client/_async/_cli.py,sha256=G2av964evWrCLppyzgH6RsHrXkRSsMybGReR4OutIJ4,9782
|
|
9
|
+
codeapi/client/_async/_code.py,sha256=zeA3pm08mE3tEVNlEE4MLw8_GlyRg3trQ8pVN247rWQ,7128
|
|
10
|
+
codeapi/client/_async/_jobs.py,sha256=o6jYRcFIKlN_GscHmHX7SFLCH1OUKtNu5STpcUQ4ogg,17571
|
|
11
|
+
codeapi/client/_async/_mcp.py,sha256=8xnYXndts5USB3GtJA1vT15WsEUrKPxjnGmkM4m1xxo,13648
|
|
12
|
+
codeapi/client/_async/_web.py,sha256=_q84Jhh7chnBe4RASI70jdV6nNJqVdksXfkoTELzsi0,9711
|
|
13
|
+
codeapi/client/_sync/__init__.py,sha256=hoXwlCf2jUWHrnUAbg29AMI-48bQdqmOkXw-wbaffgE,9689
|
|
14
|
+
codeapi/client/_sync/_app.py,sha256=dzUJO-Xy4gLqNxoFNyzxxf1Kg-dMDi_Z5xBoijlW5Ow,10266
|
|
15
|
+
codeapi/client/_sync/_cli.py,sha256=FgreWU1-AxR1HYMU0zyBOp2lhbgtrGjwjxS__M4lkPI,9298
|
|
16
|
+
codeapi/client/_sync/_code.py,sha256=NoYGVlyYbqRwnXO357_kGCwmGDIYTHGJwTMOdVWIM-U,6439
|
|
17
|
+
codeapi/client/_sync/_jobs.py,sha256=wbjazYAWL2X62lR1BgK_GC6JpXOk-AcQ12JNjt9ddFw,16363
|
|
18
|
+
codeapi/client/_sync/_mcp.py,sha256=1jQPLUWqbSmItenTVmkADhJlXTic6uiHj8Als5I1vMs,13026
|
|
19
|
+
codeapi/client/_sync/_web.py,sha256=vGjZOEaaNoT5swIjmZOPKAYtCe6t_shDQBpCa9JQ_Pw,9223
|
|
20
|
+
codeapi/types/__init__.py,sha256=YtSeCdCAdjLI5GKlOMKYqipYZU9E9XBwZ01w27mV2oI,1511
|
|
21
|
+
codeapi/types/_api.py,sha256=D9uccciZSG7wnRDOhuxDibPMKJgalezBheMYm6rdfYQ,554
|
|
22
|
+
codeapi/types/_base.py,sha256=ZN2q-1PC1OT2ud1Yl6ASlAQPfu0gJX8BtnVKI17aH7k,638
|
|
23
|
+
codeapi/types/_code.py,sha256=04ke5_twnhTKzXDLl-0P3V6XbuZnB41VwToQt4n8TXo,928
|
|
24
|
+
codeapi/types/_enums.py,sha256=zJmux3wC4QFUki9t4guJUi-kFpJhsVgRnuLTj1QVO6s,3689
|
|
25
|
+
codeapi/types/_env.py,sha256=Jf7FkBm1vHzsYVp9HQUZqhrnHqKcy7srV8gzuYt2PkM,2065
|
|
26
|
+
codeapi/types/_exc.py,sha256=wb8qyM9i633mfhZ0kUgKg5VfgCgZD07Drokskl1wCJ8,1218
|
|
27
|
+
codeapi/types/_job.py,sha256=5TZB55FXrAhhanZy3DeLWYJScJQMdkk4HoLuqL2QgOc,1834
|
|
28
|
+
codeapi/types/_json.py,sha256=U70zwFL00FnNTZwIJ94IdJnSu3xmO3qJvwgUkV1P4qA,2026
|
|
29
|
+
codeapi/types/_stream.py,sha256=72jKc7jmdpFg8KfUJhTyxgoFi2jqBuQ2hhjuxr8UMv4,965
|
|
30
|
+
codeapi/types/_swarm.py,sha256=4Dnn5hoO3zrbqeUO3vAaP4tPHRPacw5_09GmfMk20dU,2703
|
|
31
|
+
codeapi/types/_time.py,sha256=1Tca-eYr2qOjNV-jgI4WT3fQf4uIOOD-SlcH3x-XzA8,916
|
|
32
|
+
codeapi/types/_zips.py,sha256=IysInLwMNOC424jna8nbA3NukDGUoQcIuZvnLAEW30Q,15850
|
|
33
|
+
codeapi/types/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
codeapi_client-0.4.1.dist-info/METADATA,sha256=JQJMexXDKBLajd9x3hK7TUv4KjuAy1D6A2uNAWRHE8M,399
|
|
35
|
+
codeapi_client-0.4.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
36
|
+
codeapi_client-0.4.1.dist-info/RECORD,,
|