squarepeg 0.2.0__tar.gz → 0.2.1__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 (54) hide show
  1. {squarepeg-0.2.0 → squarepeg-0.2.1}/PKG-INFO +1 -1
  2. {squarepeg-0.2.0 → squarepeg-0.2.1}/pyproject.toml +1 -1
  3. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/k8s/runner.py +49 -17
  4. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg.egg-info/PKG-INFO +1 -1
  5. {squarepeg-0.2.0 → squarepeg-0.2.1}/README.md +0 -0
  6. {squarepeg-0.2.0 → squarepeg-0.2.1}/setup.cfg +0 -0
  7. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/__init__.py +0 -0
  8. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/__main__.py +0 -0
  9. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/cli.py +0 -0
  10. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/config.py +0 -0
  11. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/dockerargs.py +0 -0
  12. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/env_interpolation.py +0 -0
  13. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/errors.py +0 -0
  14. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/k8s/__init__.py +0 -0
  15. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/k8s/apierrors.py +0 -0
  16. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/k8s/dryrun.py +0 -0
  17. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/k8s/events.py +0 -0
  18. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/k8s/logs.py +0 -0
  19. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/k8s/orphans.py +0 -0
  20. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/k8s/podstate.py +0 -0
  21. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/k8s/session.py +0 -0
  22. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/labels.py +0 -0
  23. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/log.py +0 -0
  24. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/manifest.py +0 -0
  25. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/merge.py +0 -0
  26. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/naming.py +0 -0
  27. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/quantities.py +0 -0
  28. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/runspec.py +0 -0
  29. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/ui.py +0 -0
  30. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg/volumes.py +0 -0
  31. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg.egg-info/SOURCES.txt +0 -0
  32. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg.egg-info/dependency_links.txt +0 -0
  33. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg.egg-info/entry_points.txt +0 -0
  34. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg.egg-info/requires.txt +0 -0
  35. {squarepeg-0.2.0 → squarepeg-0.2.1}/squarepeg.egg-info/top_level.txt +0 -0
  36. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_apierrors.py +0 -0
  37. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_cli.py +0 -0
  38. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_config.py +0 -0
  39. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_dockerargs.py +0 -0
  40. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_dryrun.py +0 -0
  41. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_env_interpolation.py +0 -0
  42. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_events.py +0 -0
  43. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_integration.py +0 -0
  44. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_logs.py +0 -0
  45. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_manifest.py +0 -0
  46. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_merge.py +0 -0
  47. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_naming.py +0 -0
  48. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_orphans.py +0 -0
  49. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_podstate.py +0 -0
  50. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_quantities.py +0 -0
  51. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_runner.py +0 -0
  52. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_session.py +0 -0
  53. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_ui.py +0 -0
  54. {squarepeg-0.2.0 → squarepeg-0.2.1}/tests/test_volumes.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: squarepeg
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Run a docker-run-style command as a Kubernetes Pod or Job, streaming its output like a local process
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "squarepeg"
7
- version = "0.2.0"
7
+ version = "0.2.1"
8
8
  description = "Run a docker-run-style command as a Kubernetes Pod or Job, streaming its output like a local process"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -22,9 +22,9 @@ from squarepeg.runspec import RunSpec
22
22
  # kept as module-level aliases: existing callers/tests referencing these names still work
23
23
  _wrap_api_exception = wrap_api_exception
24
24
 
25
- # how long a single watch call may block waiting for the pod to become terminal,
26
- # once it has already started; the loop simply re-watches if this elapses.
27
- TERMINAL_WATCH_POLL_SECONDS = 3600
25
+ # how long a single watch call may block waiting for the pod to become terminal, once it
26
+ # has already started; the loop simply re-watches if this elapses.
27
+ TERMINAL_WATCH_POLL_SECONDS = 5
28
28
 
29
29
 
30
30
  def create_resource(session, spec: RunSpec, manifest: dict) -> None:
@@ -49,13 +49,17 @@ def discover_job_pod(session, job_name: str, timeout: int) -> str:
49
49
  ):
50
50
  return event["object"].metadata.name
51
51
  except ApiException as exc:
52
- raise _wrap_api_exception(exc, f"failed to watch for the pod created by job {job_name!r}") from exc
52
+ raise _wrap_api_exception(
53
+ exc, f"failed to watch for the pod created by job {job_name!r}"
54
+ ) from exc
53
55
  finally:
54
56
  watcher.stop()
55
57
  raise RunnerError(f"timed out waiting for job {job_name!r} to create a pod")
56
58
 
57
59
 
58
- def wait_until_running_or_terminal(session, pod_name: str, timeout: int, *, quiet: bool = False) -> str:
60
+ def wait_until_running_or_terminal(
61
+ session, pod_name: str, timeout: int, *, quiet: bool = False
62
+ ) -> str:
59
63
  """Wait until the pod is Running/Succeeded/Failed, or fail fast on a known-bad waiting reason.
60
64
 
61
65
  This bounds only the *startup* window (image pull, scheduling, container create); once the
@@ -82,17 +86,23 @@ def _watch_for_start(session, pod_name: str, timeout: int) -> str:
82
86
  for cs in pod.status.container_statuses or []:
83
87
  waiting = cs.state.waiting
84
88
  if waiting is not None and waiting.reason in FAIL_FAST_REASONS:
85
- raise RunnerError(f"container failed to start ({waiting.reason}): {waiting.message}")
89
+ raise RunnerError(
90
+ f"container failed to start ({waiting.reason}): {waiting.message}"
91
+ )
86
92
  if phase in ("Running", "Succeeded", "Failed"):
87
93
  return phase
88
94
  except ApiException as exc:
89
95
  raise _wrap_api_exception(exc, f"failed to watch pod {pod_name!r}") from exc
90
96
  finally:
91
97
  watcher.stop()
92
- raise RunnerError(f"timed out after {timeout}s waiting for pod {pod_name!r} to start")
98
+ raise RunnerError(
99
+ f"timed out after {timeout}s waiting for pod {pod_name!r} to start"
100
+ )
93
101
 
94
102
 
95
- def wait_for_terminal(session, pod_name: str, stop_event: threading.Event) -> str | None:
103
+ def wait_for_terminal(
104
+ session, pod_name: str, stop_event: threading.Event
105
+ ) -> str | None:
96
106
  """Wait, with no overall timeout, until the pod is Succeeded/Failed, or stop_event is set."""
97
107
  while not stop_event.is_set():
98
108
  watcher = watch.Watch()
@@ -116,7 +126,9 @@ def wait_for_terminal(session, pod_name: str, stop_event: threading.Event) -> st
116
126
  return None
117
127
 
118
128
 
119
- def extract_exit_code(session, pod_name: str, container_name: str) -> tuple[int, str | None]:
129
+ def extract_exit_code(
130
+ session, pod_name: str, container_name: str
131
+ ) -> tuple[int, str | None]:
120
132
  try:
121
133
  pod = session.core.read_namespaced_pod(pod_name, session.namespace)
122
134
  except ApiException as exc:
@@ -168,7 +180,9 @@ class _InterruptHandler:
168
180
  level="warn",
169
181
  )
170
182
  else:
171
- chatter(f"leaving {self.mode} {self.name!r} running", quiet=False, level="warn")
183
+ chatter(
184
+ f"leaving {self.mode} {self.name!r} running", quiet=False, level="warn"
185
+ )
172
186
 
173
187
  @property
174
188
  def interrupted(self) -> bool:
@@ -186,7 +200,10 @@ def run_manifest(session, spec: RunSpec, manifest: dict) -> int:
186
200
  with _InterruptHandler(name, spec.mode, spec.quiet) as handler:
187
201
  log_thread = None
188
202
  try:
189
- with ui.Status(f"creating {spec.mode} {name!r} in namespace {session.namespace!r}", quiet=spec.quiet):
203
+ with ui.Status(
204
+ f"creating {spec.mode} {name!r} in namespace {session.namespace!r}",
205
+ quiet=spec.quiet,
206
+ ):
190
207
  create_resource(session, spec, manifest)
191
208
 
192
209
  if spec.mode == "job":
@@ -207,7 +224,9 @@ def run_manifest(session, spec: RunSpec, manifest: dict) -> int:
207
224
  slow_after=20,
208
225
  spinner_delay=0.0,
209
226
  ):
210
- phase = wait_until_running_or_terminal(session, pod_name, spec.timeout, quiet=spec.quiet)
227
+ phase = wait_until_running_or_terminal(
228
+ session, pod_name, spec.timeout, quiet=spec.quiet
229
+ )
211
230
 
212
231
  chatter(
213
232
  f"streaming logs from pod {pod_name!r} (waiting for it to finish; Ctrl+C to stop)",
@@ -243,9 +262,15 @@ def run_manifest(session, spec: RunSpec, manifest: dict) -> int:
243
262
  if handler.interrupted:
244
263
  raise InterruptError(f"interrupted while running {spec.mode} {name!r}")
245
264
 
246
- exit_code, reason = extract_exit_code(session, pod_name, spec.container_name)
265
+ exit_code, reason = extract_exit_code(
266
+ session, pod_name, spec.container_name
267
+ )
247
268
  if reason == "OOMKilled":
248
- chatter(f"container was OOMKilled (exit code {exit_code})", quiet=spec.quiet, level="error")
269
+ chatter(
270
+ f"container was OOMKilled (exit code {exit_code})",
271
+ quiet=spec.quiet,
272
+ level="error",
273
+ )
249
274
  return exit_code
250
275
  finally:
251
276
  handler.stop_event.set()
@@ -261,9 +286,16 @@ def run_manifest(session, spec: RunSpec, manifest: dict) -> int:
261
286
  level="warn",
262
287
  )
263
288
  if spec.orphan_sweep and not handler.abandoned:
264
- own_run_id = manifest.get("metadata", {}).get("labels", {}).get(RUN_ID_LABEL)
289
+ own_run_id = (
290
+ manifest.get("metadata", {}).get("labels", {}).get(RUN_ID_LABEL)
291
+ )
265
292
  try:
266
- with ui.Status("sweeping orphaned resources from previous runs", quiet=spec.quiet):
293
+ with ui.Status(
294
+ "sweeping orphaned resources from previous runs",
295
+ quiet=spec.quiet,
296
+ ):
267
297
  sweep_orphans(session, spec, exclude_run_id=own_run_id)
268
298
  except Exception as exc:
269
- chatter(f"orphan sweep skipped: {exc}", quiet=spec.quiet, level="warn")
299
+ chatter(
300
+ f"orphan sweep skipped: {exc}", quiet=spec.quiet, level="warn"
301
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: squarepeg
3
- Version: 0.2.0
3
+ Version: 0.2.1
4
4
  Summary: Run a docker-run-style command as a Kubernetes Pod or Job, streaming its output like a local process
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes