harness-sdk-python 0.13.3__tar.gz → 0.14.0__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.
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/PKG-INFO +1 -1
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/pyproject.toml +1 -1
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/src/harness_sdk/run_manager.py +31 -9
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_applied_visibility.py +4 -4
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_dispatching.py +2 -2
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_settle.py +30 -25
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_stop_continue.py +42 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_stopping_window.py +9 -1
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/.gitignore +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/README.md +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/examples/__init__.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/examples/runs_app.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/src/harness_sdk/__init__.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/src/harness_sdk/fenced_postgres.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/src/harness_sdk/linear_thread.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/run_helpers.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_batches.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_branch_anchor.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_edit_dispatched.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_edit_reload.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_enqueue.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_fenced_postgres.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_input_required.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_linear_thread.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_meta.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_outcomes.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_placement.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_prepare_hooks.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_restore.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_rewind_during_run.py +0 -0
- {harness_sdk_python-0.13.3 → harness_sdk_python-0.14.0}/tests/test_steer.py +0 -0
|
@@ -17,9 +17,15 @@ or ``dispatching``), mid-run edit/reload with the stopping window,
|
|
|
17
17
|
|
|
18
18
|
``entry["dispatching"]`` is the dispatch record (absent between an applied and
|
|
19
19
|
the next dispatch or take): the payload the run callback receives.
|
|
20
|
-
``ctx.applied()``
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
``ctx.applied()`` confirms it; ``steering.take()`` may refill it — at most one
|
|
21
|
+
unconfirmed batch ever exists. Settle consumes a confirmed batch; a batch
|
|
22
|
+
still unconfirmed at settle returns to the front of its lanes. ``Complete``
|
|
23
|
+
with an unconfirmed batch settles as an error. Steer entries still
|
|
24
|
+
undelivered in the lane stay queued for the next run.
|
|
25
|
+
|
|
26
|
+
``entry["epoch"]`` counts the entry's dispatches; a ``run/stop`` carrying an
|
|
27
|
+
``epoch`` other than the live one rejects, so a replayed stop never ends a
|
|
28
|
+
later leg of the same ``runId``.
|
|
23
29
|
|
|
24
30
|
A mid-run stop or rewind opens a stopping window on the entry:
|
|
25
31
|
``stopping: {reason}`` plus, for rewinds, the staged dispatch as
|
|
@@ -534,6 +540,7 @@ class RunManager:
|
|
|
534
540
|
self._dispatch_record = dict(record)
|
|
535
541
|
self._stop_reason = None
|
|
536
542
|
entry = self._ensure_entry()
|
|
543
|
+
entry["epoch"] = (plain(entry.get("epoch")) or 0) + 1
|
|
537
544
|
entry["dispatching"] = record
|
|
538
545
|
entry.pop("error", None)
|
|
539
546
|
if messages:
|
|
@@ -570,6 +577,8 @@ class RunManager:
|
|
|
570
577
|
"run must return a RunManager outcome, got "
|
|
571
578
|
f"{type(outcome).__name__}"
|
|
572
579
|
)
|
|
580
|
+
if isinstance(outcome, RunManager.Complete) and self._dispatching:
|
|
581
|
+
raise RuntimeError("run returned Complete with an unconfirmed batch")
|
|
573
582
|
except asyncio.CancelledError:
|
|
574
583
|
self._settle(ctx)
|
|
575
584
|
self._set_status("stopped")
|
|
@@ -593,12 +602,18 @@ class RunManager:
|
|
|
593
602
|
self._ctx = None
|
|
594
603
|
self._task = None
|
|
595
604
|
self._dispatch_record = None
|
|
596
|
-
self._dispatching = []
|
|
597
605
|
entry = self._ensure_entry()
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
606
|
+
if self._dispatching:
|
|
607
|
+
self._unstage_dispatching()
|
|
608
|
+
else:
|
|
609
|
+
record = plain(entry.get("dispatching"))
|
|
610
|
+
if (
|
|
611
|
+
record is not None
|
|
612
|
+
and record["trigger"] != "steer"
|
|
613
|
+
and record["messages"]
|
|
614
|
+
):
|
|
615
|
+
self._dispatched_ids = ()
|
|
616
|
+
entry.pop("dispatching", None)
|
|
602
617
|
entry.pop("stopping", None)
|
|
603
618
|
|
|
604
619
|
def _applied(self) -> None:
|
|
@@ -1212,6 +1227,9 @@ class RunManager:
|
|
|
1212
1227
|
reason = (params or {}).get("reason", _ABSENT)
|
|
1213
1228
|
if reason is not _ABSENT and not isinstance(reason, str):
|
|
1214
1229
|
raise _reject("invalid-message", "reason must be a string")
|
|
1230
|
+
epoch = (params or {}).get("epoch", _ABSENT)
|
|
1231
|
+
if epoch is not _ABSENT and (isinstance(epoch, bool) or not isinstance(epoch, int)):
|
|
1232
|
+
raise _reject("invalid-message", "epoch must be an integer")
|
|
1215
1233
|
status = self._status()
|
|
1216
1234
|
if status != "running":
|
|
1217
1235
|
raise _reject("wrong-state", f"run/stop is rejected in {status}")
|
|
@@ -1220,6 +1238,10 @@ class RunManager:
|
|
|
1220
1238
|
raise _reject(
|
|
1221
1239
|
"wrong-state", f"runId {run_id!r} does not name the live run"
|
|
1222
1240
|
)
|
|
1241
|
+
if epoch is not _ABSENT and epoch != plain(self._entry()["epoch"]):
|
|
1242
|
+
raise _reject(
|
|
1243
|
+
"wrong-state", f"epoch {epoch} does not name the live dispatch"
|
|
1244
|
+
)
|
|
1223
1245
|
if reason is not _ABSENT:
|
|
1224
1246
|
self._stop_reason = reason
|
|
1225
1247
|
entry = _Stop()
|
|
@@ -1411,7 +1433,7 @@ class RunManager:
|
|
|
1411
1433
|
raise RuntimeError("this run has already settled")
|
|
1412
1434
|
|
|
1413
1435
|
def applied(self) -> None:
|
|
1414
|
-
"""
|
|
1436
|
+
"""Confirms the current batch durable: clears the dispatch record so settle consumes it."""
|
|
1415
1437
|
self._ensure_active()
|
|
1416
1438
|
self._manager._applied()
|
|
1417
1439
|
|
|
@@ -60,7 +60,7 @@ async def test_parked_send_applies_with_its_queue_insert():
|
|
|
60
60
|
call.finish(RunManager.Complete())
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
async def
|
|
63
|
+
async def test_unconfirmed_run_end_requeues_the_direct_dispatch():
|
|
64
64
|
script = Script()
|
|
65
65
|
async with run_host(script) as (drv, host):
|
|
66
66
|
seq = drv.post("run/enqueue", add("m1"))
|
|
@@ -69,10 +69,10 @@ async def test_unapplied_run_end_consumes_the_direct_dispatch():
|
|
|
69
69
|
assert dispatching_ids(covered_state(drv, seq)) == ["m1"]
|
|
70
70
|
call.fail(RuntimeError("boom"))
|
|
71
71
|
await drv.wait_status("error")
|
|
72
|
-
assert queue_ids(drv.replica, "queue") == []
|
|
72
|
+
assert queue_ids(drv.replica, "queue") == ["m1"]
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
async def
|
|
75
|
+
async def test_unconfirmed_run_end_requeues_the_direct_steer_send():
|
|
76
76
|
script = Script()
|
|
77
77
|
async with run_host(script) as (drv, host):
|
|
78
78
|
seq = drv.post("run/steer", add("s1"))
|
|
@@ -81,7 +81,7 @@ async def test_unapplied_run_end_consumes_the_direct_steer_send():
|
|
|
81
81
|
assert dispatching_ids(covered_state(drv, seq)) == ["s1"]
|
|
82
82
|
call.fail(RuntimeError("boom"))
|
|
83
83
|
await drv.wait_status("error")
|
|
84
|
-
assert queue_ids(drv.replica, "steerQueue") == []
|
|
84
|
+
assert queue_ids(drv.replica, "steerQueue") == ["s1"]
|
|
85
85
|
assert queue_ids(drv.replica, "queue") == []
|
|
86
86
|
|
|
87
87
|
|
|
@@ -78,7 +78,7 @@ async def test_applied_clears_dispatching_with_the_message_append():
|
|
|
78
78
|
call.finish(RunManager.Complete())
|
|
79
79
|
|
|
80
80
|
|
|
81
|
-
async def
|
|
81
|
+
async def test_unconfirmed_run_end_requeues_the_delivered_item_ahead():
|
|
82
82
|
script = Script()
|
|
83
83
|
async with run_host(script) as (drv, host):
|
|
84
84
|
drv.post("run/enqueue", add("m1"))
|
|
@@ -88,7 +88,7 @@ async def test_unapplied_run_end_consumes_the_delivered_item():
|
|
|
88
88
|
await drv.side("run/stop", {"runId": await drv.run_id()})
|
|
89
89
|
call.finish(RunManager.Stop(dispatch_queue=False))
|
|
90
90
|
await drv.wait_status("stopped")
|
|
91
|
-
assert queue_ids(drv.replica, "queue") == ["m2"]
|
|
91
|
+
assert queue_ids(drv.replica, "queue") == ["m1", "m2"]
|
|
92
92
|
assert dispatching_ids(drv.replica) == []
|
|
93
93
|
assert residence(drv, "m2") == ["queue"]
|
|
94
94
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Contract: command settlement. Every initiator settles accepted with its
|
|
2
2
|
state-visible application — sends with their insert (a lane entry or a direct
|
|
3
3
|
``dispatching`` entry), edit/reload/continue at their dispatch. The run's
|
|
4
|
-
outcome never travels through settlement:
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
outcome never travels through settlement: ``applied()`` confirms the delivered
|
|
5
|
+
batch and settle consumes it; a batch still unconfirmed at settle returns to
|
|
6
|
+
the front of its lanes, ``Complete`` with an unconfirmed batch settles as an
|
|
7
|
+
error, and any failure lands on ``state.error``."""
|
|
7
8
|
|
|
8
9
|
import asyncio
|
|
9
10
|
|
|
@@ -50,7 +51,7 @@ async def test_run_reject_lands_the_payload_on_state_error():
|
|
|
50
51
|
)
|
|
51
52
|
assert (await drv.res(seq))["type"] == "accepted"
|
|
52
53
|
await drv.wait_status("error")
|
|
53
|
-
assert queue_ids(drv.replica, "queue") == []
|
|
54
|
+
assert queue_ids(drv.replica, "queue") == ["m1"]
|
|
54
55
|
assert drv.run["error"] == {
|
|
55
56
|
"message": "payment required",
|
|
56
57
|
"reason": "payment-required",
|
|
@@ -58,18 +59,19 @@ async def test_run_reject_lands_the_payload_on_state_error():
|
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
|
|
61
|
-
async def
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
seq = drv.post("run/enqueue", add(
|
|
69
|
-
|
|
62
|
+
async def test_unconfirmed_run_end_requeues_the_send():
|
|
63
|
+
for end in [
|
|
64
|
+
lambda call: call.fail(RuntimeError("boom")),
|
|
65
|
+
lambda call: call.finish(RunManager.Error(dispatch_queue=False)),
|
|
66
|
+
]:
|
|
67
|
+
script = Script()
|
|
68
|
+
async with run_host(script) as (drv, host):
|
|
69
|
+
seq = drv.post("run/enqueue", add("m1"))
|
|
70
|
+
end(await script.next_call())
|
|
70
71
|
assert (await drv.res(seq))["type"] == "accepted"
|
|
71
72
|
await drv.wait_status("error")
|
|
72
|
-
assert queue_ids(drv.replica, "queue") == []
|
|
73
|
+
assert queue_ids(drv.replica, "queue") == ["m1"]
|
|
74
|
+
assert queue_ids(drv.replica, "steerQueue") == []
|
|
73
75
|
|
|
74
76
|
|
|
75
77
|
async def test_rewind_settles_before_the_reruns_end():
|
|
@@ -88,14 +90,14 @@ async def test_rewind_settles_before_the_reruns_end():
|
|
|
88
90
|
await drv.wait_status("stopped")
|
|
89
91
|
|
|
90
92
|
|
|
91
|
-
async def
|
|
93
|
+
async def test_unconfirmed_run_end_requeues_the_delivered_batch():
|
|
92
94
|
script = Script()
|
|
93
95
|
async with run_host(script) as (drv, host):
|
|
94
96
|
drv.batch([("run/enqueue", add("m1")), ("run/enqueue", add("m2"))])
|
|
95
97
|
call = await script.next_call()
|
|
96
98
|
call.fail(RuntimeError("boom"))
|
|
97
99
|
await drv.wait_status("error")
|
|
98
|
-
assert queue_ids(drv.replica, "queue") == []
|
|
100
|
+
assert queue_ids(drv.replica, "queue") == ["m1", "m2"]
|
|
99
101
|
assert drv.run["error"] == {"message": "boom"}
|
|
100
102
|
await drv.command("run/steer", add("s1"), terminal=False)
|
|
101
103
|
continued = await script.next_call()
|
|
@@ -104,7 +106,7 @@ async def test_unapplied_run_end_consumes_the_delivered_batch():
|
|
|
104
106
|
assert [m["id"] for m in continued.ctx.steering.take()] == ["s1"]
|
|
105
107
|
|
|
106
108
|
|
|
107
|
-
async def
|
|
109
|
+
async def test_unconfirmed_run_end_requeues_taken_steered_items():
|
|
108
110
|
script = Script()
|
|
109
111
|
async with run_host(script) as (drv, host):
|
|
110
112
|
drv.post("run/enqueue", add("m1"))
|
|
@@ -115,7 +117,7 @@ async def test_unapplied_run_end_consumes_taken_steered_items():
|
|
|
115
117
|
call.ctx.steering.take()
|
|
116
118
|
call.fail(RuntimeError("boom"))
|
|
117
119
|
await drv.wait_status("error")
|
|
118
|
-
assert queue_ids(drv.replica, "steerQueue") == []
|
|
120
|
+
assert queue_ids(drv.replica, "steerQueue") == ["s1"]
|
|
119
121
|
assert queue_ids(drv.replica, "queue") == []
|
|
120
122
|
|
|
121
123
|
|
|
@@ -130,14 +132,17 @@ async def test_applied_removes_the_entries_for_good():
|
|
|
130
132
|
assert queue_ids(drv.replica, "queue") == []
|
|
131
133
|
|
|
132
134
|
|
|
133
|
-
async def
|
|
135
|
+
async def test_complete_with_unconfirmed_batch_settles_as_error():
|
|
134
136
|
script = Script()
|
|
135
137
|
async with run_host(script) as (drv, host):
|
|
136
138
|
seq = drv.post("run/enqueue", add("m1"))
|
|
137
139
|
(await script.next_call()).finish(RunManager.Complete())
|
|
138
140
|
assert (await drv.res(seq))["type"] == "accepted"
|
|
139
|
-
await drv.wait_status("
|
|
140
|
-
assert drv.replica
|
|
141
|
+
await drv.wait_status("error")
|
|
142
|
+
assert queue_ids(drv.replica, "queue") == ["m1"]
|
|
143
|
+
assert drv.run["error"] == {
|
|
144
|
+
"message": "run returned Complete with an unconfirmed batch"
|
|
145
|
+
}
|
|
141
146
|
script.no_call()
|
|
142
147
|
|
|
143
148
|
|
|
@@ -165,9 +170,9 @@ async def test_stop_lands_while_a_steer_is_parked():
|
|
|
165
170
|
await asyncio.wait_for(call.ctx.stop_requested.wait(), 5)
|
|
166
171
|
call.finish(RunManager.Stop(dispatch_queue=False))
|
|
167
172
|
await drv.wait_status("stopped")
|
|
168
|
-
# The undelivered steer stays parked; the
|
|
173
|
+
# The undelivered steer stays parked; the unconfirmed dispatch requeues.
|
|
169
174
|
assert queue_ids(drv.replica, "steerQueue") == ["s1"]
|
|
170
|
-
assert queue_ids(drv.replica, "queue") == []
|
|
175
|
+
assert queue_ids(drv.replica, "queue") == ["m1"]
|
|
171
176
|
|
|
172
177
|
|
|
173
178
|
async def test_rewind_edit_settles_at_its_dispatch():
|
|
@@ -195,7 +200,7 @@ async def test_rewind_rerun_failure_lands_on_state_error():
|
|
|
195
200
|
assert drv.run["error"] == {"message": "payment required", "code": 402}
|
|
196
201
|
|
|
197
202
|
|
|
198
|
-
async def
|
|
203
|
+
async def test_task_cancellation_requeues_entries_and_settles_sends():
|
|
199
204
|
script = Script()
|
|
200
205
|
async with run_host(script) as (drv, host):
|
|
201
206
|
seq = drv.batch([("run/enqueue", add("m1")), ("run/enqueue", add("m2"))])
|
|
@@ -204,7 +209,7 @@ async def test_task_cancellation_consumes_entries_and_settles_sends():
|
|
|
204
209
|
task.cancel()
|
|
205
210
|
assert (await drv.res(seq))["type"] == "accepted"
|
|
206
211
|
await drv.wait_status("stopped")
|
|
207
|
-
assert queue_ids(drv.replica, "queue") == []
|
|
212
|
+
assert queue_ids(drv.replica, "queue") == ["m1", "m2"]
|
|
208
213
|
assert task.cancelled()
|
|
209
214
|
|
|
210
215
|
|
|
@@ -336,3 +336,45 @@ async def test_same_tick_send_and_continue_dispatch_one_run_with_messages():
|
|
|
336
336
|
continued.applied()
|
|
337
337
|
continued.finish(RunManager.Complete())
|
|
338
338
|
await drv.wait_status("ready")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
async def test_stop_with_the_live_epoch_is_accepted():
|
|
342
|
+
script = Script()
|
|
343
|
+
async with run_host(script) as (drv, host):
|
|
344
|
+
drv.post("run/enqueue", add("m1"))
|
|
345
|
+
call = await script.next_call()
|
|
346
|
+
call.applied()
|
|
347
|
+
await drv.wait(lambda s: bool(s["runs"]) and s["runs"][0].get("epoch") == 1)
|
|
348
|
+
pending = await drv.stop({"epoch": 1}, terminal=False)
|
|
349
|
+
call.finish(RunManager.Stop(dispatch_queue=False))
|
|
350
|
+
assert (await drv.res(pending["seq"]))["type"] == "accepted"
|
|
351
|
+
await drv.wait_status("stopped")
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
async def test_stale_epoch_stop_cannot_end_a_later_leg():
|
|
355
|
+
script = Script()
|
|
356
|
+
async with run_host(script) as (drv, host):
|
|
357
|
+
drv.post("run/enqueue", add("m1"))
|
|
358
|
+
call = await script.next_call()
|
|
359
|
+
call.applied()
|
|
360
|
+
await drv.stop(terminal=False)
|
|
361
|
+
call.finish(RunManager.Stop(dispatch_queue=False))
|
|
362
|
+
await drv.wait_status("stopped")
|
|
363
|
+
await drv.command("run/enqueue", add("m2"), terminal=False)
|
|
364
|
+
leg2 = await script.next_call()
|
|
365
|
+
assert leg2.ctx.trigger == "stop-continue"
|
|
366
|
+
await drv.wait(lambda s: s["runs"][0].get("epoch") == 2)
|
|
367
|
+
assert_rejected(await drv.stop({"epoch": 1}), "wrong-state")
|
|
368
|
+
assert drv.replica["status"] == "running"
|
|
369
|
+
leg2.applied()
|
|
370
|
+
leg2.finish(RunManager.Complete())
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
async def test_stop_rejects_a_non_integer_epoch():
|
|
374
|
+
script = Script()
|
|
375
|
+
async with run_host(script) as (drv, host):
|
|
376
|
+
drv.post("run/enqueue", add("m1"))
|
|
377
|
+
call = await script.next_call()
|
|
378
|
+
call.applied()
|
|
379
|
+
assert_rejected(await drv.stop({"epoch": "1"}), "invalid-message")
|
|
380
|
+
call.finish(RunManager.Complete())
|
|
@@ -249,6 +249,7 @@ async def test_entry_keys_are_absent_when_empty():
|
|
|
249
249
|
await drv.wait_status("running")
|
|
250
250
|
assert set(drv.run.keys()) == {
|
|
251
251
|
"runId",
|
|
252
|
+
"epoch",
|
|
252
253
|
"status",
|
|
253
254
|
"queue",
|
|
254
255
|
"steerQueue",
|
|
@@ -258,4 +259,11 @@ async def test_entry_keys_are_absent_when_empty():
|
|
|
258
259
|
await drv.wait(lambda s: "dispatching" not in s["runs"][0])
|
|
259
260
|
call.fail(RuntimeError("boom"))
|
|
260
261
|
await drv.wait_status("error")
|
|
261
|
-
assert set(drv.run.keys()) == {
|
|
262
|
+
assert set(drv.run.keys()) == {
|
|
263
|
+
"runId",
|
|
264
|
+
"epoch",
|
|
265
|
+
"status",
|
|
266
|
+
"queue",
|
|
267
|
+
"steerQueue",
|
|
268
|
+
"error",
|
|
269
|
+
}
|
|
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
|
|
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
|