poetry-plugin-ivcap 0.4.2__py3-none-any.whl → 0.5.0__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.
@@ -3,6 +3,7 @@
3
3
  # Use of this source code is governed by a BSD-style license that can be
4
4
  # found in the LICENSE file. See the AUTHORS file for names of contributors.
5
5
  #
6
+ import argparse
6
7
  import os
7
8
  import re
8
9
  import subprocess
@@ -12,6 +13,9 @@ import uuid
12
13
  import humanize
13
14
  import subprocess
14
15
  import requests
16
+ import time
17
+ import json
18
+
15
19
 
16
20
  from .constants import DEF_POLICY, PLUGIN_NAME, POLICY_OPT, SERVICE_FILE_OPT, SERVICE_ID_OPT, DEF_IVCAP_BASE_URL
17
21
 
@@ -143,20 +147,16 @@ def exec_job(data, args, is_silent, line):
143
147
  requests.Response: The response object from the API call.
144
148
  """
145
149
  # Parse 'args' for run options
150
+ p = exec_parser()
146
151
  if not isinstance(args, list) or len(args) < 1:
147
- raise Exception("args must be a list with at least one element")
148
- file_name = args[0]
149
- timeout = 5 # default timeout
150
- if len(args) == 1:
151
- pass # only file_name provided
152
- elif len(args) == 3 and args[1] == '--timeout':
153
- try:
154
- timeout = int(args[2])
155
- except ValueError:
156
- raise Exception("Timeout value must be an integer")
157
- else:
158
- raise Exception("args must be [file_name] or [file_name, '--timeout', value]")
159
-
152
+ p.error("missing request file name")
153
+ # raise Exception("args must be a list with at least one element")
154
+ file_name = args.pop(0)
155
+ if file_name == "-h" or file_name == "--help":
156
+ p.print_help()
157
+ return
158
+ pa = p.parse_args(args)
159
+ timeout = 0 if pa.stream else pa.timeout
160
160
  # Get access token using ivcap CLI
161
161
  try:
162
162
  token = subprocess.check_output(
@@ -202,34 +202,6 @@ def exec_job(data, args, is_silent, line):
202
202
  except Exception as e:
203
203
  raise RuntimeError(f"Job submission failed: {e}")
204
204
 
205
- # Handle response according to requirements
206
- import time
207
- import json
208
-
209
- def handle_response(resp):
210
- content_type = resp.headers.get("content-type", "")
211
- if resp.status_code >= 300:
212
- line(f"<warning>WARNING: Received status code {resp.status_code}</warning>")
213
- line(f"<info>Headers: {str(resp.headers)}</info>")
214
- line(f"<info>Body: {str(resp.text)}</info>")
215
- elif resp.status_code == 200:
216
- if "application/json" in content_type:
217
- try:
218
- parsed = resp.json()
219
- status = parsed.get("status")
220
- if status and (not status in ["succeeded", "failed", "error"]):
221
- return status
222
- print(json.dumps(parsed, indent=2, sort_keys=True))
223
- return None
224
- except Exception as e:
225
- line(f"<warning>Failed to parse JSON response: {e}</warning>")
226
- line(f"<warning>Headers: {str(resp.headers)}</warning>")
227
- else:
228
- line(f"<info>Headers: {str(resp.headers)}</info>")
229
- else:
230
- line(f"<warning>Received status code {resp.status_code}</warning>")
231
- line(f"<warning>Headers: {str(resp.headers)}</warning>")
232
- return "unknown"
233
205
 
234
206
  if response.status_code == 202:
235
207
  try:
@@ -241,34 +213,110 @@ def exec_job(data, args, is_silent, line):
241
213
  retry_later = payload.get("retry-later", 5)
242
214
  if not is_silent:
243
215
  line(f"<debug>Job '{job_id}' accepted, but no result yet. Polling in {retry_later} seconds.</debug>")
244
- while True:
245
- time.sleep(retry_later)
246
- poll_headers = {
247
- "Authorization": f"Bearer {token}"
248
- }
249
- poll_resp = requests.get(location, headers=poll_headers)
250
- if poll_resp.status_code == 202:
251
- try:
252
- poll_payload = poll_resp.json()
253
- location = poll_payload.get("location", location)
254
- retry_later = poll_payload.get("retry-later", retry_later)
255
- if not is_silent:
256
- line(f"<debug>Still processing. Next poll in {retry_later} seconds.</debug>")
257
- except Exception as e:
258
- line(f"<error>Failed to parse polling response: {e}</error>")
259
- break
260
- else:
261
- status = handle_response(poll_resp)
262
- if status:
263
- if not is_silent:
264
- line(f"<debug>Status: '{status}'. Next poll in {retry_later} seconds.</debug>")
265
- else:
266
- break
216
+ if pa.stream:
217
+ stream_result(location, token, pa)
218
+ else:
219
+ poll_for_result(location, retry_later, token, is_silent, line)
267
220
 
268
221
  except Exception as e:
269
222
  line(f"<error>Failed to handle 202 response: {e}</error>")
270
223
  else:
271
- handle_response(response)
224
+ handle_response(response, line)
225
+
226
+ def handle_response(resp, line):
227
+ content_type = resp.headers.get("content-type", "")
228
+ if resp.status_code >= 300:
229
+ line(f"<warning>WARNING: Received status code {resp.status_code}</warning>")
230
+ line(f"<info>Headers: {str(resp.headers)}</info>")
231
+ line(f"<info>Body: {str(resp.text)}</info>")
232
+ elif resp.status_code == 200:
233
+ if "application/json" in content_type:
234
+ try:
235
+ parsed = resp.json()
236
+ status = parsed.get("status")
237
+ if status and (not status in ["succeeded", "failed", "error"]):
238
+ return status
239
+ print(json.dumps(parsed, indent=2, sort_keys=True))
240
+ return None
241
+ except Exception as e:
242
+ line(f"<warning>Failed to parse JSON response: {e}</warning>")
243
+ line(f"<warning>Headers: {str(resp.headers)}</warning>")
244
+ else:
245
+ line(f"<info>Headers: {str(resp.headers)}</info>")
246
+ else:
247
+ line(f"<warning>Received status code {resp.status_code}</warning>")
248
+ line(f"<warning>Headers: {str(resp.headers)}</warning>")
249
+ return "unknown"
250
+
251
+ def poll_for_result(location, retry_later, token, is_silent, line):
252
+ while True:
253
+ time.sleep(retry_later)
254
+ poll_headers = {
255
+ "Authorization": f"Bearer {token}"
256
+ }
257
+ poll_resp = requests.get(location, headers=poll_headers)
258
+ if poll_resp.status_code == 202:
259
+ try:
260
+ poll_payload = poll_resp.json()
261
+ location = poll_payload.get("location", location)
262
+ retry_later = poll_payload.get("retry-later", retry_later)
263
+ if not is_silent:
264
+ line(f"<debug>Still processing. Next poll in {retry_later} seconds.</debug>")
265
+ except Exception as e:
266
+ line(f"<error>Failed to parse polling response: {e}</error>")
267
+ break
268
+ else:
269
+ status = handle_response(poll_resp, line)
270
+ if status:
271
+ if not is_silent:
272
+ line(f"<debug>Status: '{status}'. Next poll in {retry_later} seconds.</debug>")
273
+ else:
274
+ break
275
+
276
+ def stream_result(location, token, pa):
277
+ """
278
+ Stream the result content from the given location using the provided token.
279
+ """
280
+ headers = {
281
+ "Authorization": f"Bearer {token}",
282
+ "Accept": "text/event-stream"
283
+ }
284
+ with requests.get(location + "/events", stream=True, headers=headers, timeout=(5, 65)) as r:
285
+ r.raise_for_status()
286
+ for row in r.iter_lines(decode_unicode=True, chunk_size=1):
287
+ if row is None:
288
+ continue
289
+ if row.startswith(":"):
290
+ # comment/heartbeat
291
+ continue
292
+ print_sse_row(row, pa) # raw SSE lines (e.g., "data: {...}", "event: message")
293
+
294
+ def print_sse_row(row, pa):
295
+ if pa.raw_events:
296
+ print(row)
297
+ elif row.startswith("data: "):
298
+ # JSON data
299
+ print("----")
300
+ try:
301
+ data = json.loads(row[6:])
302
+ print(json.dumps(data, indent=2, sort_keys=True))
303
+ except json.JSONDecodeError as e:
304
+ print(f"Failed to decode JSON: {e}")
305
+
306
+ def exec_parser():
307
+ p = argparse.ArgumentParser(prog="poetry ivcap job-exec request_file --")
308
+ p.add_argument("--timeout", type=nonneg_int, default=5, help="[5] seconds; 0 allowed")
309
+ p.add_argument("--with-result-content", dest="with_result_content", action="store_true",
310
+ help="include result content in the response")
311
+ p.add_argument("--stream", action="store_true", help="stream the result content")
312
+ p.add_argument("--raw-events", action="store_true", help="print raw SSE events")
313
+ return p
314
+
315
+ def nonneg_int(s: str) -> int:
316
+ v = int(s)
317
+ if v < 0:
318
+ raise argparse.ArgumentTypeError("timeout must be >= 0")
319
+ return v
272
320
 
273
321
  def get_account_id(data, line, is_silent=False):
274
322
  check_ivcap_cmd(line)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: poetry-plugin-ivcap
3
- Version: 0.4.2
3
+ Version: 0.5.0
4
4
  Summary: A custom Poetry command for IVCAP deployments
5
5
  License: MIT
6
6
  Author: Max Ott
@@ -1,11 +1,11 @@
1
1
  poetry_plugin_ivcap/constants.py,sha256=zsUnw-cS3uTqRlkAsWPYBeMpn5Wpz7r4ByjYaFZQFUo,1318
2
2
  poetry_plugin_ivcap/docker.py,sha256=Pny5uF7juKy8Jld7D-5euAtgzguDZ8E2SRn-romk-Sg,7409
3
- poetry_plugin_ivcap/ivcap.py,sha256=89c0vZxul5H6thRXDpRY3_j4VP3i3MKHu9N4el2nI4Q,11312
3
+ poetry_plugin_ivcap/ivcap.py,sha256=tXu3vj7FOTrE4Jc9RFoLxOnNdBT5NAkyTB-_lGMgaZI,12804
4
4
  poetry_plugin_ivcap/plugin.py,sha256=NWYdqjvlw3cqg4DVcEcZLGAVuL1aa9o0GmQGNZ6wfbI,5506
5
5
  poetry_plugin_ivcap/util.py,sha256=bcjjbKoV_pAgeuC7Ws9XbJa3phFpNVyrRAlFJ1VubRg,3429
6
- poetry_plugin_ivcap-0.4.2.dist-info/AUTHORS.md,sha256=s9xR4_HAHQgbNlj505LViebt5AtACQmhPf92aJvNYgg,88
7
- poetry_plugin_ivcap-0.4.2.dist-info/LICENSE,sha256=dsQrDPPwW7iJs9pxahgJKDW8RNPf5FyXG70MFUlxcuk,1587
8
- poetry_plugin_ivcap-0.4.2.dist-info/METADATA,sha256=9r9Ch1MYCHOBl1WUFANmY0ZXIRgU4D5epZj--8HSrcs,3032
9
- poetry_plugin_ivcap-0.4.2.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
10
- poetry_plugin_ivcap-0.4.2.dist-info/entry_points.txt,sha256=3xagEFBkGgrVe8WyjmhlHLr4JDEWPN_W4DwxnIBWbNY,74
11
- poetry_plugin_ivcap-0.4.2.dist-info/RECORD,,
6
+ poetry_plugin_ivcap-0.5.0.dist-info/AUTHORS.md,sha256=s9xR4_HAHQgbNlj505LViebt5AtACQmhPf92aJvNYgg,88
7
+ poetry_plugin_ivcap-0.5.0.dist-info/LICENSE,sha256=dsQrDPPwW7iJs9pxahgJKDW8RNPf5FyXG70MFUlxcuk,1587
8
+ poetry_plugin_ivcap-0.5.0.dist-info/METADATA,sha256=HMskOhMdt6JyvVzTycjXqm32sOOxJUbTMeEnKPLICnc,3032
9
+ poetry_plugin_ivcap-0.5.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
10
+ poetry_plugin_ivcap-0.5.0.dist-info/entry_points.txt,sha256=3xagEFBkGgrVe8WyjmhlHLr4JDEWPN_W4DwxnIBWbNY,74
11
+ poetry_plugin_ivcap-0.5.0.dist-info/RECORD,,