manta-node 0.6b5.dev771__py3-none-any.whl → 0.6b5.dev773__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.
@@ -4,7 +4,7 @@ This file is auto-generated at build time by scripts/generate_version.py.
4
4
  Do not edit manually.
5
5
  """
6
6
 
7
- __version__ = "2026.10.07.a0655f05"
8
- __build_date__ = "2026.10.07"
9
- __commit_id__ = "a0655f05"
7
+ __version__ = "2026.10.08.22eaa34f"
8
+ __build_date__ = "2026.10.08"
9
+ __commit_id__ = "22eaa34f"
10
10
  __package_version__ = "0.6b5" # release-series base, moved by scripts/bump_version.py
@@ -17,6 +17,7 @@ supervisor.
17
17
 
18
18
  from __future__ import annotations
19
19
 
20
+ import sys
20
21
  import time
21
22
  from collections.abc import Callable
22
23
  from dataclasses import dataclass
@@ -128,15 +129,17 @@ def run_switch(
128
129
  nonlocal candidate_python
129
130
  candidate_python = fetch(request.target)
130
131
 
131
- if not _within(bounds.fetch, do_fetch):
132
- return _outcome(
133
- request, SeamState.FAILED, "fetch exceeded its bound (no switch)"
132
+ cause = _within(bounds.fetch, do_fetch)
133
+ if cause is not None:
134
+ return _step_failed(
135
+ request, "fetch", "fetch exceeded its bound (no switch)", cause
134
136
  )
135
137
 
136
138
  # VERIFY — the candidate must be usable in its OWN environment (NF-2): the
137
139
  # fetcher named the interpreter the candidate will run from.
138
- if not _within(bounds.verify, lambda: verify(candidate_dir, candidate_python)):
139
- return _outcome(request, SeamState.FAILED, "verification failed (no switch)")
140
+ cause = _within(bounds.verify, lambda: verify(candidate_dir, candidate_python))
141
+ if cause is not None:
142
+ return _step_failed(request, "verify", "verification failed (no switch)", cause)
140
143
 
141
144
  # FREE SPACE — refuse before staging when there is no room for a full version
142
145
  # dir (NF-12, §6.2). Refusing here, before any write, is what stops a
@@ -156,8 +159,11 @@ def run_switch(
156
159
  candidate_dir, candidate_python
157
160
  )
158
161
 
159
- if not _within(bounds.stage, do_stage):
160
- return _outcome(request, SeamState.FAILED, "stage exceeded its bound or failed")
162
+ cause = _within(bounds.stage, do_stage)
163
+ if cause is not None:
164
+ return _step_failed(
165
+ request, "stage", "stage exceeded its bound or failed", cause
166
+ )
161
167
  target = staged["target"]
162
168
 
163
169
  record = InFlight(
@@ -169,25 +175,26 @@ def run_switch(
169
175
  write_inflight(record)
170
176
 
171
177
  # SWITCH — the commit point (NF-1), bounded.
172
- try:
173
- if not _within(bounds.switch, lambda: version_tree.commit(target)):
174
- clear_inflight()
175
- _reclaim_candidate(bounds, target, last_good_dir)
176
- return _outcome(
177
- request, SeamState.FAILED, "switch exceeded its bound (repoint failed)"
178
- )
179
- except VersionTreeError as exc:
178
+ cause = _within(bounds.switch, lambda: version_tree.commit(target))
179
+ if cause is not None:
180
180
  clear_inflight()
181
181
  _reclaim_candidate(bounds, target, last_good_dir)
182
- return _outcome(request, SeamState.FAILED, f"switch failed: {exc}")
182
+ return _step_failed(request, "switch", "switch failed (repoint failed)", cause)
183
183
 
184
184
  # RESTART onto the committed version, then wait for the return window. A
185
185
  # restart that raises is a failed switch, not a crash: surface DOWN and clear
186
186
  # the record rather than letting the exception escape.
187
- if not _within(bounds.switch, restart):
187
+ cause = _within(bounds.switch, restart)
188
+ if cause is not None:
188
189
  clear_inflight()
189
190
  _reclaim_candidate(bounds, target, last_good_dir)
190
- return _outcome(request, SeamState.DOWN, "restart failed after the switch")
191
+ return _step_failed(
192
+ request,
193
+ "restart",
194
+ "restart failed after the switch",
195
+ cause,
196
+ state=SeamState.DOWN,
197
+ )
191
198
 
192
199
  if is_back(record.deadline_epoch):
193
200
  clear_inflight()
@@ -215,12 +222,19 @@ def run_switch(
215
222
  )
216
223
  try:
217
224
  version_tree.commit(version_tree.versions_dir() / last_good)
218
- if not _within(bounds.switch, restart):
219
- raise LifecycleError("restart after revert failed")
225
+ cause = _within(bounds.switch, restart)
226
+ if cause is not None:
227
+ raise LifecycleError(f"restart after revert failed: {cause}")
220
228
  except (VersionTreeError, LifecycleError) as exc:
221
229
  clear_inflight()
222
230
  _reclaim_candidate(bounds, target, last_good_dir)
223
- return _outcome(request, SeamState.DOWN, f"revert failed: {exc}")
231
+ return _step_failed(
232
+ request,
233
+ "revert",
234
+ f"revert failed: {exc}",
235
+ str(exc),
236
+ state=SeamState.DOWN,
237
+ )
224
238
  clear_inflight()
225
239
  # The candidate was superseded the moment the switch did not land: it is
226
240
  # reclaimed even though the switch failed, so repeated failing switches do
@@ -257,17 +271,43 @@ def _reclaim_candidate(
257
271
  version_tree.reclaim(bounds.retention_dirs, protect)
258
272
 
259
273
 
260
- def _within(bound: float, step: Callable[[], None]) -> bool:
261
- """Run ``step``; True if it completed, False if it raised or exceeded ``bound``.
274
+ def _within(bound: float, step: Callable[[], None]) -> str | None:
275
+ """Run ``step``; return None if it completed within ``bound``, else the cause.
262
276
 
263
277
  A step that raises is a failure of that state, not a crash: before the switch
264
- it leaves the node on its current version (NF-3). The time bound is enforced
265
- by the step itself (a subprocess timeout / an HTTP timeout); this records the
266
- comparison so the caller does not have to.
278
+ it leaves the node on its current version (NF-3). The return names the
279
+ failure — the exception, or the bound it exceeded — so the caller can record
280
+ the stage and its cause. The time bound is enforced by the step itself (a
281
+ subprocess timeout / an HTTP timeout); this records the comparison so the
282
+ caller does not have to.
267
283
  """
268
284
  start = time.monotonic()
269
285
  try:
270
286
  step()
271
- except Exception:
272
- return False
273
- return (time.monotonic() - start) <= bound
287
+ except Exception as exc:
288
+ return f"{type(exc).__name__}: {exc}"
289
+ elapsed = time.monotonic() - start
290
+ if elapsed > bound:
291
+ return f"exceeded its {bound}s bound ({elapsed:.1f}s)"
292
+ return None
293
+
294
+
295
+ def _step_failed(
296
+ request: UpdateRequest,
297
+ stage: str,
298
+ reason: str,
299
+ cause: str,
300
+ *,
301
+ state: SeamState = SeamState.FAILED,
302
+ ) -> SwitchOutcome:
303
+ """Build a failure outcome naming the stage and cause, and log it.
304
+
305
+ ``detail`` always carries ``stage`` and ``cause`` together, and the failed
306
+ stage always logs a line to stderr (the supervisor's journal) before
307
+ returning, so the outcome and the journal name the same failure.
308
+ """
309
+ print(
310
+ f"supervisor: {stage} failed for request {request.request_id}: {cause}",
311
+ file=sys.stderr,
312
+ )
313
+ return _outcome(request, state, reason, stage=stage, cause=cause)
@@ -156,15 +156,19 @@ def read_manifest(version_dir: Path) -> Version | None:
156
156
  def materialise_version(version: str, python: str) -> Path:
157
157
  """Create an immutable version dir for ``version``; return its path.
158
158
 
159
- Idempotent: an existing dir for the same manta-node version is returned
160
- unchanged (its manifest is not rewritten).
159
+ Idempotent: an existing dir is returned unchanged when its identity matches
160
+ ``version`` — the manifest's node version, or the legacy pair id it was
161
+ materialised under. The fleet path still names a candidate by its pair
162
+ (`manta-node@X+manta-common-core@Y`) and pre-materialises that dir, so
163
+ matching the pair too is what keeps its apply from being rejected as "not a
164
+ valid version dir" (#520).
161
165
  """
162
166
  if not is_safe_version_name(version):
163
167
  raise VersionTreeError(f"refusing an unsafe version dir name: {version!r}")
164
168
  target = versions_dir() / version
165
169
  if target.exists():
166
170
  existing = read_manifest(target)
167
- if existing and existing.version == version:
171
+ if existing is not None and version in (existing.version, existing.pair):
168
172
  return target
169
173
  raise VersionTreeError(
170
174
  f"{target} exists but is not a valid version dir for {version!r}"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: manta_node
3
- Version: 0.6b5.dev771
3
+ Version: 0.6b5.dev773
4
4
  Summary: Node side software of the Manta platform. It is responsible for the communication with the Manta Core and the execution of the tasks.
5
5
  Author-email: Hugo Miralles <hugo.miralles@manta-tech.io>, Matthew Thompson <matthew.thompson@manta-tech.io>
6
6
  License-Expression: AGPL-3.0-or-later
@@ -7,7 +7,7 @@ manta_node/tasks.py,sha256=o837Hi-KFVJg9LF5iOtE8oD6UuTOfrEPWR-JOdhRGeY,6983
7
7
  manta_node/update_path.py,sha256=6N_bN9z8MqnyWWfGTe1-aJFijf2tSy_IvO6VHvNSQVU,1667
8
8
  manta_node/utils.py,sha256=Hw4Iw2jYQYsfVLVJuBMt-_rMFBeml3nCDE2jiiXc8YM,1155
9
9
  manta_node/cli/__init__.py,sha256=arzO3mXDJ-E2yeeytbZfN7mCswwxdQ43_QjyeL2E5yE,134
10
- manta_node/cli/_version_generated.py,sha256=1L59ew2XMf-hqwylvtzea9LrjNYqKfYDJGvZ9J3haAk,332
10
+ manta_node/cli/_version_generated.py,sha256=u15js_niq-wLlcE1Hitgt0MvKozxGxx0dZjNV-EhUfc,332
11
11
  manta_node/cli/config_manager.py,sha256=BCju9zqzraviL3KrzRnB-vZQUY__Q5kuEqb7EdB8YdI,4409
12
12
  manta_node/cli/main.py,sha256=dZa6zrypWBvpN8hd4qpxevvlyNFhxKINCg1537NJ36w,4318
13
13
  manta_node/cli/version.py,sha256=L4lNTz-Uw_ejumy1tXJcJhcjhb-5FRcRQ6ogZM5N1Xo,5136
@@ -57,15 +57,15 @@ manta_node/supervisor/__init__.py,sha256=kgdoNce1tM4e7jvdxkdmnNJV1L92-NI2cRfYSdm
57
57
  manta_node/supervisor/bounds.py,sha256=MlUfj0tYY-US348JsxYA-Ph5pSpgoOPlRdVTwmrPtZA,3181
58
58
  manta_node/supervisor/executor.py,sha256=HwER4Q9uFI4cYTBEn5BFtRhU69Wdj9fj8-RQZ6u8T8o,8171
59
59
  manta_node/supervisor/inflight.py,sha256=GhngTFmFKi0P3yqu5P5XeRP5G-H8vG-uQcAzdjATdm0,4515
60
- manta_node/supervisor/lifecycle.py,sha256=0NJb8JlMJXXiyVgEBCoyRsDOlVLIETrgKX7uVs21NT0,10924
60
+ manta_node/supervisor/lifecycle.py,sha256=_-Tzb3Ipu_RERC5T8DeHyy7BS5RypIc8rIz2GvKRG2s,12000
61
61
  manta_node/supervisor/process.py,sha256=frbbgvhLsveXMzwmRkNmB3FEKKgqiEdG6UhffRrE_Bw,20741
62
62
  manta_node/supervisor/records.py,sha256=fUlL1SfmWpGnCbJ9956cuy0H3a7-aEF-B-naqbJiYoU,6405
63
63
  manta_node/supervisor/seam.py,sha256=4kczRku5Ctn4xRvXS-pyzaDFau7LnX5bYOn6IRNKRyE,9665
64
64
  manta_node/supervisor/sequence.py,sha256=eYu7rrk1ecc4Y2jQhhJ7CopPz3m0lOsFLJj5lT2PwBw,1685
65
- manta_node/supervisor/switch.py,sha256=Mxr2XcZVOMN6g-M4lUBRlE7d8Z8JEtt6k7nEaS3zHpE,21958
66
- manta_node-0.6b5.dev771.dist-info/licenses/LICENSE,sha256=9mBgLhiB9goQPQjg9XQJ6FjgVxYw9Q8lyOZYkPGwF_c,34484
67
- manta_node-0.6b5.dev771.dist-info/METADATA,sha256=-y1vYhJTIXSPBVHTOdK9xfcZW1vWwntTc9RXixYvcZk,4937
68
- manta_node-0.6b5.dev771.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
69
- manta_node-0.6b5.dev771.dist-info/entry_points.txt,sha256=uCwggRApalLEsYs1YxwTBN0-e9oMJAkMoBp01HaSdoY,110
70
- manta_node-0.6b5.dev771.dist-info/top_level.txt,sha256=nbT5J5WETuXM9UPoQckl29uUOq8wMYdRb9QDx5kAFeo,11
71
- manta_node-0.6b5.dev771.dist-info/RECORD,,
65
+ manta_node/supervisor/switch.py,sha256=ansCCmxru9bmmLggq8b68gmnyqbPgXF_u2Z6fo_xSMo,22283
66
+ manta_node-0.6b5.dev773.dist-info/licenses/LICENSE,sha256=9mBgLhiB9goQPQjg9XQJ6FjgVxYw9Q8lyOZYkPGwF_c,34484
67
+ manta_node-0.6b5.dev773.dist-info/METADATA,sha256=soD8Yq0AvgXQSmpobHn-TAuOENMsdutClUYPOKjZXk4,4937
68
+ manta_node-0.6b5.dev773.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
69
+ manta_node-0.6b5.dev773.dist-info/entry_points.txt,sha256=uCwggRApalLEsYs1YxwTBN0-e9oMJAkMoBp01HaSdoY,110
70
+ manta_node-0.6b5.dev773.dist-info/top_level.txt,sha256=nbT5J5WETuXM9UPoQckl29uUOq8wMYdRb9QDx5kAFeo,11
71
+ manta_node-0.6b5.dev773.dist-info/RECORD,,