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.
@@ -0,0 +1,332 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING, List
4
+
5
+ import httpx
6
+
7
+ if TYPE_CHECKING:
8
+ from . import Client
9
+ from codeapi.types import (
10
+ CodeInfo,
11
+ CodeType,
12
+ CodeZip,
13
+ DataZip,
14
+ EnvVars,
15
+ Job,
16
+ JobStage,
17
+ JobStatus,
18
+ JobType,
19
+ JsonData,
20
+ )
21
+
22
+
23
+ class StoredCliClient:
24
+ def __init__(self, client: Client):
25
+ self._client = client
26
+
27
+ def run(
28
+ self,
29
+ code_id: str,
30
+ command: str,
31
+ env_vars: EnvVars | dict | None = None,
32
+ data_zip: DataZip | None = None,
33
+ timeout: float | None = None,
34
+ ) -> Job:
35
+ """Runs stored code.
36
+
37
+ Args:
38
+ code_id (str): The code ID.
39
+ command (str): The command to run.
40
+ env_vars (EnvVars | dict | None): Optional environment variables.
41
+ data_zip (DataZip | None): Optional data zip.
42
+ timeout (float | None): The timeout for the job.
43
+
44
+ Returns:
45
+ Job: The created job.
46
+
47
+ Raises:
48
+ HTTPException: If the request fails.
49
+ """
50
+ url = f"{self._client.base_url}/jobs/code/{code_id}/run/cli"
51
+
52
+ files = self._client._prepare_files(data_zip=data_zip)
53
+ data = {"command": command}
54
+ if env_vars:
55
+ data["env_vars"] = EnvVars(env_vars).json_str
56
+ params = {"timeout": timeout} if timeout else {}
57
+
58
+ try:
59
+ response = httpx.post(
60
+ url,
61
+ headers=self._client.api_key_header,
62
+ files=files or None,
63
+ data=data,
64
+ params=params,
65
+ )
66
+ response.raise_for_status()
67
+ return Job(**response.json())
68
+ except httpx.HTTPStatusError as e:
69
+ raise self._client._get_http_exception(httpx_error=e)
70
+
71
+ def list_info(self) -> list[CodeInfo]:
72
+ """List all stored CLI code.
73
+
74
+ Returns:
75
+ list[CodeInfo]: List of CLI code info.
76
+ """
77
+ return self._client.code.list_info(code_type=CodeType.CLI)
78
+
79
+ def delete(self, code_id: str) -> str:
80
+ """Delete stored CLI code.
81
+
82
+ Args:
83
+ code_id (str): The code ID to delete.
84
+
85
+ Returns:
86
+ str: Deletion confirmation message.
87
+
88
+ Raises:
89
+ ValueError: If the code_id is not CLI code.
90
+ """
91
+ # Verify this is actually CLI code
92
+ code_info = self._client.code.get_info(code_id)
93
+ if code_info.code_type != CodeType.CLI:
94
+ raise ValueError(
95
+ f"Code '{code_id}' is {code_info.code_type}, not CLI code. "
96
+ "Cannot delete non-CLI code from CLI client."
97
+ )
98
+
99
+ return self._client.code.delete(code_id)
100
+
101
+
102
+ class CliJobsClient:
103
+ def __init__(self, client):
104
+ self._client = client
105
+
106
+ def list(
107
+ self,
108
+ job_status: JobStatus | None = None,
109
+ job_stage: JobStage | None = None,
110
+ ) -> List[Job]:
111
+ """List CLI jobs.
112
+
113
+ Args:
114
+ job_status (JobStatus | None): Filter by job status.
115
+ job_stage (JobStage | None): Filter by job stage.
116
+
117
+ Returns:
118
+ list[Job]: List of CLI jobs.
119
+ """
120
+ return self._client.jobs.list(
121
+ job_type=JobType.RUN_CLI,
122
+ job_status=job_status,
123
+ job_stage=job_stage,
124
+ )
125
+
126
+ def get_latest(self) -> Job | None:
127
+ """Get the most recent CLI job.
128
+
129
+ Returns:
130
+ Job | None: The most recent CLI job, or None if no jobs exist.
131
+ """
132
+ jobs = self.list()
133
+ return jobs[0] if jobs else None
134
+
135
+ def list_queued(self) -> List[Job]:
136
+ """Get all queued CLI jobs.
137
+
138
+ Returns:
139
+ List[Job]: List of queued CLI jobs.
140
+ """
141
+ return self.list(job_status=JobStatus.QUEUED)
142
+
143
+ def list_scheduled(self) -> List[Job]:
144
+ """Get all scheduled CLI jobs.
145
+
146
+ Returns:
147
+ List[Job]: List of scheduled CLI jobs.
148
+ """
149
+ return self.list(job_status=JobStatus.SCHEDULED)
150
+
151
+ def list_started(self) -> List[Job]:
152
+ """Get all started CLI jobs.
153
+
154
+ Returns:
155
+ list[Job]: List of started CLI jobs.
156
+ """
157
+ return self.list(job_status=JobStatus.STARTED)
158
+
159
+ def list_deferred(self) -> List[Job]:
160
+ """Get all deferred CLI jobs.
161
+
162
+ Returns:
163
+ List[Job]: List of deferred CLI jobs.
164
+ """
165
+ return self.list(job_status=JobStatus.DEFERRED)
166
+
167
+ def list_canceled(self) -> List[Job]:
168
+ """Get all canceled CLI jobs.
169
+
170
+ Returns:
171
+ List[Job]: List of canceled CLI jobs.
172
+ """
173
+ return self.list(job_status=JobStatus.CANCELED)
174
+
175
+ def list_stopped(self) -> List[Job]:
176
+ """Get all stopped CLI jobs.
177
+
178
+ Returns:
179
+ List[Job]: List of stopped CLI jobs.
180
+ """
181
+ return self.list(job_status=JobStatus.STOPPED)
182
+
183
+ def list_failed(self) -> List[Job]:
184
+ """Get all failed CLI jobs.
185
+
186
+ Returns:
187
+ list[Job]: List of failed CLI jobs.
188
+ """
189
+ return self.list(job_status=JobStatus.FAILED)
190
+
191
+ def list_finished(self) -> List[Job]:
192
+ """Get all finished CLI jobs.
193
+
194
+ Returns:
195
+ list[Job]: List of finished CLI jobs.
196
+ """
197
+ return self.list(job_status=JobStatus.FINISHED)
198
+
199
+ def list_timed_out(self) -> List[Job]:
200
+ """Get all timed out CLI jobs.
201
+
202
+ Returns:
203
+ List[Job]: List of timed out CLI jobs.
204
+ """
205
+ return self.list(job_status=JobStatus.TIMEOUT)
206
+
207
+ def list_pre_running(self) -> List[Job]:
208
+ """Get all pre-running CLI jobs.
209
+
210
+ Returns:
211
+ List[Job]: List of pre-running CLI jobs.
212
+ """
213
+ return self.list(job_stage=JobStage.PRE_RUNNING)
214
+
215
+ def list_running(self) -> List[Job]:
216
+ """Get all running CLI jobs.
217
+
218
+ Returns:
219
+ List[Job]: List of running CLI jobs.
220
+ """
221
+ return self.list(job_stage=JobStage.RUNNING)
222
+
223
+ def list_post_running(self) -> List[Job]:
224
+ """Get all post-running CLI jobs.
225
+
226
+ Returns:
227
+ List[Job]: List of post-running CLI jobs.
228
+ """
229
+ return self.list(job_stage=JobStage.POST_RUNNING)
230
+
231
+
232
+ class CliClient:
233
+ def __init__(self, client: Client):
234
+ self._client = client
235
+ self.stored = StoredCliClient(client)
236
+ self.jobs = CliJobsClient(client)
237
+
238
+ def run(
239
+ self,
240
+ code_zip: CodeZip,
241
+ command: str,
242
+ env_vars: EnvVars | dict | None = None,
243
+ data_zip: DataZip | None = None,
244
+ timeout: float | None = None,
245
+ ) -> Job:
246
+ """Starts a CLI job from code zip.
247
+
248
+ Args:
249
+ code_zip (CodeZip): The code zip.
250
+ command (str): The command to run.
251
+ env_vars (EnvVars | dict | None): Optional environment variables.
252
+ data_zip (DataZip | None): Optional data zip.
253
+ timeout (float | None): The timeout for the job.
254
+
255
+ Returns:
256
+ Job: The created job.
257
+
258
+ Raises:
259
+ HTTPException: If the request fails.
260
+ """
261
+ url = f"{self._client.base_url}/jobs/code/run/cli"
262
+
263
+ files = self._client._prepare_files(code_zip=code_zip, data_zip=data_zip)
264
+ data = {"command": command}
265
+ if env_vars:
266
+ data["env_vars"] = EnvVars(env_vars).json_str
267
+ params = {"timeout": timeout} if timeout else {}
268
+
269
+ try:
270
+ response = httpx.post(
271
+ url,
272
+ headers=self._client.api_key_header,
273
+ files=files or None,
274
+ data=data,
275
+ params=params,
276
+ )
277
+ response.raise_for_status()
278
+ return Job(**response.json())
279
+ except httpx.HTTPStatusError as e:
280
+ raise self._client._get_http_exception(httpx_error=e)
281
+
282
+ def upload(
283
+ self,
284
+ code_zip: CodeZip,
285
+ code_name: str,
286
+ metadata: JsonData | dict | None = None,
287
+ ) -> str:
288
+ """Upload CLI code.
289
+
290
+ Args:
291
+ code_zip (CodeZip): The code zip.
292
+ code_name (str): The name of the code.
293
+ metadata (JsonData | dict | None): The JSON metadata of the code.
294
+
295
+ Returns:
296
+ str: The code ID.
297
+ """
298
+ return self._client.code.upload(
299
+ code_zip=code_zip,
300
+ code_name=code_name,
301
+ code_type=CodeType.CLI,
302
+ metadata=metadata,
303
+ )
304
+
305
+ def ingest(
306
+ self,
307
+ code_zip: CodeZip,
308
+ code_name: str,
309
+ metadata: JsonData | dict | None = None,
310
+ build_pexenv: bool = False,
311
+ pexenv_python: str | None = None,
312
+ ) -> Job:
313
+ """Ingest CLI code.
314
+
315
+ Args:
316
+ code_zip (CodeZip): The code zip.
317
+ code_name (str): The name of the code.
318
+ metadata (JsonData | dict | None): The JSON metadata of the code.
319
+ build_pexenv (bool): Whether to build the pex venv.
320
+ pexenv_python: (str | None): Python interpreter for the pex venv.
321
+
322
+ Returns:
323
+ Job: The code ingestion job.
324
+ """
325
+ return self._client.code.ingest(
326
+ code_zip=code_zip,
327
+ code_name=code_name,
328
+ code_type=CodeType.CLI,
329
+ metadata=metadata,
330
+ build_pexenv=build_pexenv,
331
+ pexenv_python=pexenv_python,
332
+ )
@@ -0,0 +1,203 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING
5
+
6
+ import httpx
7
+
8
+ from codeapi.types import CodeInfo, CodeType, CodeZip, Job, JsonData
9
+
10
+ if TYPE_CHECKING:
11
+ from . import Client
12
+
13
+
14
+ class CodeClient:
15
+ def __init__(self, client: Client):
16
+ self._client = client
17
+
18
+ def upload(
19
+ self,
20
+ code_zip: CodeZip,
21
+ code_name: str,
22
+ code_type: CodeType,
23
+ metadata: JsonData | dict | None = None,
24
+ ) -> str:
25
+ """Upload code.
26
+
27
+ Args:
28
+ code_zip (CodeZip): The code zip.
29
+ code_name (str): The name of the code.
30
+ code_type (CodeType): The type of the code.
31
+ metadata (JsonData | dict | None): The JSON metadata of the code.
32
+
33
+ Returns:
34
+ str: The code ID.
35
+
36
+ Raises:
37
+ HTTPException: If the request fails.
38
+ """
39
+ url = f"{self._client.base_url}/code/upload"
40
+
41
+ files = self._client._prepare_files(code_zip=code_zip, metadata=metadata)
42
+ data = {"code_name": code_name, "code_type": code_type}
43
+
44
+ try:
45
+ response = httpx.post(
46
+ url, headers=self._client.api_key_header, files=files, data=data
47
+ )
48
+ response.raise_for_status()
49
+ return str(response.json()).strip()
50
+ except httpx.HTTPStatusError as e:
51
+ raise self._client._get_http_exception(httpx_error=e)
52
+
53
+ def download(self, code_id: str, zip_path: Path | str | None = None) -> CodeZip:
54
+ """Download code and optionally saves it to a file.
55
+
56
+ Args:
57
+ code_id (str): The code ID.
58
+ zip_path (Path | str | None): The path to save the code zip file to.
59
+
60
+ Returns:
61
+ CodeZip: The downloaded code zip.
62
+
63
+ Raises:
64
+ HTTPException: If the request fails.
65
+ """
66
+ url = f"{self._client.base_url}/code/{code_id}/download"
67
+
68
+ try:
69
+ with httpx.stream(
70
+ "GET", url, headers=self._client.api_key_header
71
+ ) as response:
72
+ zip_bytes = self._client._handle_stream_download(response, zip_path)
73
+ except httpx.HTTPStatusError as e:
74
+ raise self._client._get_http_exception(httpx_error=e)
75
+
76
+ return CodeZip(zip_bytes=zip_bytes)
77
+
78
+ def delete(self, code_id: str) -> str:
79
+ """Deletes code by code_id.
80
+
81
+ Args:
82
+ code_id (str): The code ID.
83
+
84
+ Returns:
85
+ str: Confirmation message.
86
+
87
+ Raises:
88
+ HTTPException: If the request fails.
89
+ """
90
+ url = f"{self._client.base_url}/code/{code_id}"
91
+ try:
92
+ response = httpx.delete(url, headers=self._client.api_key_header)
93
+ response.raise_for_status()
94
+ return f"Code with id={code_id} deleted"
95
+ except httpx.HTTPStatusError as e:
96
+ raise self._client._get_http_exception(httpx_error=e)
97
+
98
+ def get_info(self, code_id: str) -> CodeInfo:
99
+ """Gets the code info.
100
+
101
+ Args:
102
+ code_id (str): The code ID.
103
+
104
+ Returns:
105
+ CodeInfo: The code info.
106
+
107
+ Raises:
108
+ HTTPException: If the request fails.
109
+ """
110
+ url = f"{self._client.base_url}/code/{code_id}/info"
111
+ try:
112
+ response = httpx.get(url, headers=self._client.api_key_header)
113
+ response.raise_for_status()
114
+ return CodeInfo(**response.json())
115
+ except httpx.HTTPStatusError as e:
116
+ raise self._client._get_http_exception(httpx_error=e)
117
+
118
+ def list_info(self, code_type: CodeType | None = None) -> list[CodeInfo]:
119
+ """Gets list of code infos.
120
+
121
+ Args:
122
+ code_type (CodeType | None): The type of code to list.
123
+
124
+ Returns:
125
+ list[CodeInfo]: The list of code infos.
126
+
127
+ Raises:
128
+ HTTPException: If the request fails.
129
+ """
130
+ url = f"{self._client.base_url}/code/info"
131
+ try:
132
+ params = {"code_type": code_type} if code_type else None
133
+ response = httpx.get(
134
+ url, headers=self._client.api_key_header, params=params
135
+ )
136
+ response.raise_for_status()
137
+ return [CodeInfo(**item) for item in response.json()]
138
+ except httpx.HTTPStatusError as e:
139
+ raise self._client._get_http_exception(httpx_error=e)
140
+
141
+ def get_metadata(self, code_id: str) -> JsonData:
142
+ """Gets code metadata.
143
+
144
+ Args:
145
+ code_id (str): The ID of the code.
146
+
147
+ Returns:
148
+ JsonData: The metadata.
149
+
150
+ Raises:
151
+ HTTPException: If the request fails.
152
+ """
153
+ url = f"{self._client.base_url}/code/{code_id}/metadata"
154
+ try:
155
+ response = httpx.get(url, headers=self._client.api_key_header)
156
+ response.raise_for_status()
157
+ return JsonData(response.json())
158
+ except httpx.HTTPStatusError as e:
159
+ raise self._client._get_http_exception(httpx_error=e)
160
+
161
+ def ingest(
162
+ self,
163
+ code_zip: CodeZip,
164
+ code_name: str,
165
+ code_type: CodeType,
166
+ metadata: JsonData | dict | None = None,
167
+ build_pexenv: bool = False,
168
+ pexenv_python: str | None = None,
169
+ ) -> Job:
170
+ """Starts a code ingestion job.
171
+
172
+ Args:
173
+ code_zip (CodeZip): The code zip.
174
+ code_name (str): The name of the code.
175
+ code_type (CodeType): The type of the code.
176
+ metadata (JsonData | dict | None): The JSON metadata of the code.
177
+ build_pexenv (bool): Whether to build the pex venv.
178
+ pexenv_python: (str | None): Python interpreter for the pex venv.
179
+
180
+ Returns:
181
+ Job: The code ingestion job.
182
+
183
+ Raises:
184
+ HTTPException: If the request fails.
185
+ """
186
+ url = f"{self._client.base_url}/jobs/code/ingest"
187
+
188
+ files = self._client._prepare_files(code_zip=code_zip, metadata=metadata)
189
+ data = {
190
+ "code_name": code_name,
191
+ "code_type": code_type,
192
+ "build_pexenv": build_pexenv,
193
+ "pexenv_python": pexenv_python,
194
+ }
195
+
196
+ try:
197
+ response = httpx.post(
198
+ url, headers=self._client.api_key_header, files=files, data=data
199
+ )
200
+ response.raise_for_status()
201
+ return Job(**response.json())
202
+ except httpx.HTTPStatusError as e:
203
+ raise self._client._get_http_exception(httpx_error=e)