gridlinegpu 0.1.7__tar.gz → 0.1.8__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.
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/PKG-INFO +3 -1
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/README.md +2 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/pyproject.toml +1 -1
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_interfaces.py +34 -4
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_job.py +22 -8
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_release.py +1 -1
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu.egg-info/PKG-INFO +3 -1
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_client_source.py +1 -1
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_contract.py +35 -3
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_job.py +6 -6
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_job_results.py +2 -2
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/setup.cfg +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/UNICODE-LICENSE.txt +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/__init__.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/__main__.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_admission.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_capture.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_casefold.json +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_contract.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_credits.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_errors.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_history.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_outputs.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_public.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_results.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_run.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_submission.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/_source_transfer.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/cli.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/cli_auth.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/client.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu/errors.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu.egg-info/SOURCES.txt +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu.egg-info/dependency_links.txt +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu.egg-info/entry_points.txt +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu.egg-info/requires.txt +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/src/gridlinegpu.egg-info/top_level.txt +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_cli.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_cli_auth.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_client.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_admission.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_canary.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_capture.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_capture_gaps.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_credits.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_curated_run.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_errors.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_history.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_release.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_results.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_run.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_submission.py +0 -0
- {gridlinegpu-0.1.7 → gridlinegpu-0.1.8}/tests/test_source_transfer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gridlinegpu
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Python client for Gridline YAML workload routing
|
|
5
5
|
Author: Gridline GPU
|
|
6
6
|
License: Proprietary
|
|
@@ -213,6 +213,8 @@ print(job.report()["settlement"]) # reserved, held, charged and released credit
|
|
|
213
213
|
```
|
|
214
214
|
|
|
215
215
|
`job.status()` reads the current state without waiting, and `job.cancel()` requests a stop.
|
|
216
|
+
`job.isolation_tier()` names the isolation tier the job's code ran under (`kernel_cgroup` or
|
|
217
|
+
`hardened_user_separation`, see the security page), or `None` before a machine is allocated.
|
|
216
218
|
`client.history(limit=20)` lists your recent jobs, newest first. `report()` and `history()`
|
|
217
219
|
raise `report_unavailable` / `history_unavailable` until those reads are switched on for
|
|
218
220
|
the beta; the job itself is unaffected. A complete minimal
|
|
@@ -195,6 +195,8 @@ print(job.report()["settlement"]) # reserved, held, charged and released credit
|
|
|
195
195
|
```
|
|
196
196
|
|
|
197
197
|
`job.status()` reads the current state without waiting, and `job.cancel()` requests a stop.
|
|
198
|
+
`job.isolation_tier()` names the isolation tier the job's code ran under (`kernel_cgroup` or
|
|
199
|
+
`hardened_user_separation`, see the security page), or `None` before a machine is allocated.
|
|
198
200
|
`client.history(limit=20)` lists your recent jobs, newest first. `report()` and `history()`
|
|
199
201
|
raise `report_unavailable` / `history_unavailable` until those reads are switched on for
|
|
200
202
|
the beta; the job itself is unaffected. A complete minimal
|
|
@@ -603,7 +603,7 @@ def validate_source_job_status(value):
|
|
|
603
603
|
# Status v3 (job.ts): v2 plus the current task's stage, a coarse packaging step and the failure
|
|
604
604
|
# envelope. failureCode, error.stage and error.code are open words (H6): a newer server's code
|
|
605
605
|
# or stage is accepted and arrives with its own text. Structural vocabularies stay closed.
|
|
606
|
-
SOURCE_JOB_STATUS_VERSIONS = (2, 3, 4, 5, 6, 7)
|
|
606
|
+
SOURCE_JOB_STATUS_VERSIONS = (2, 3, 4, 5, 6, 7, 8)
|
|
607
607
|
SOURCE_JOB_TASK_STAGES = ("runtime_resolution", "runtime_packaging", "compute_routing")
|
|
608
608
|
SOURCE_JOB_PACKAGING_STEPS = ("dependencies", "wheels", "image", "qualification", "handoff")
|
|
609
609
|
_open_word = _pattern(r"[a-z_]{1,64}")
|
|
@@ -1244,10 +1244,38 @@ def validate_source_job_status_v7(value):
|
|
|
1244
1244
|
return value
|
|
1245
1245
|
|
|
1246
1246
|
|
|
1247
|
+
# Status v8 (job.ts, O6-C PR-D): v7 plus the run's isolation tier. The shared vectors are
|
|
1248
|
+
# job-status-v8.json.
|
|
1249
|
+
SOURCE_JOB_ISOLATION_TIERS = ("kernel_cgroup", "hardened_user_separation", "user_separation")
|
|
1250
|
+
SOURCE_JOB_STATUS_V8_FIELDS = SOURCE_JOB_STATUS_V7_FIELDS + ("isolationTier",)
|
|
1251
|
+
_JOB_STATUS_V8 = {
|
|
1252
|
+
**_JOB_STATUS_V7,
|
|
1253
|
+
"schemaVersion": _number_literal(8),
|
|
1254
|
+
"isolationTier": _nullable(_one_of(*SOURCE_JOB_ISOLATION_TIERS)),
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
|
|
1258
|
+
def validate_source_job_status_v8(value):
|
|
1259
|
+
"""GET /jobs/{jobId}?schemaVersion=8: v7 plus ``isolationTier``, at most 64 KiB (job.ts
|
|
1260
|
+
sourceJobStatusV8Schema). ``isolationTier`` is the isolation tier of the machine the job's
|
|
1261
|
+
code ran on: ``kernel_cgroup``, ``hardened_user_separation`` (the disclosed hardened tier,
|
|
1262
|
+
see the security page) or ``user_separation`` (the owner canary only); null before such a
|
|
1263
|
+
machine. Never a location, SKU or resource id."""
|
|
1264
|
+
_require(value, _JOB_STATUS_V8, "jobStatus")
|
|
1265
|
+
_status_v6_rules(value)
|
|
1266
|
+
attempts = value["launchAttempts"]
|
|
1267
|
+
if attempts is not None and (
|
|
1268
|
+
attempts["refusals"] > attempts["attempt"]
|
|
1269
|
+
or (attempts["lastRefusal"] is None) != (attempts["refusals"] == 0)):
|
|
1270
|
+
_record_fail("jobStatus.launchAttempts")
|
|
1271
|
+
_bounded(value, SOURCE_JOB_LIMITS["statusBytes"], "jobStatus")
|
|
1272
|
+
return value
|
|
1273
|
+
|
|
1274
|
+
|
|
1247
1275
|
def validate_source_job_status_answer(value):
|
|
1248
|
-
"""A status answer of any version this SDK reads (2 to
|
|
1249
|
-
server that does not serve v7, v6, v5, v4 or v3 (older, or switched off) answers the
|
|
1250
|
-
it serves; v2 to v5 stay readable for jobs without Step 10 facts (the server refuses them for
|
|
1276
|
+
"""A status answer of any version this SDK reads (2 to 8), chosen by its schemaVersion. A
|
|
1277
|
+
server that does not serve v8, v7, v6, v5, v4 or v3 (older, or switched off) answers the
|
|
1278
|
+
highest it serves; v2 to v5 stay readable for jobs without Step 10 facts (the server refuses them for
|
|
1251
1279
|
a job with a Step 10 outcome or execution state: status_version_unsupported)."""
|
|
1252
1280
|
version = value.get("schemaVersion") if type(value) is dict else None
|
|
1253
1281
|
if _number_literal(2)(version):
|
|
@@ -1262,6 +1290,8 @@ def validate_source_job_status_answer(value):
|
|
|
1262
1290
|
return validate_source_job_status_v6(value)
|
|
1263
1291
|
if _number_literal(7)(version):
|
|
1264
1292
|
return validate_source_job_status_v7(value)
|
|
1293
|
+
if _number_literal(8)(version):
|
|
1294
|
+
return validate_source_job_status_v8(value)
|
|
1265
1295
|
_record_fail("jobStatus.schemaVersion")
|
|
1266
1296
|
|
|
1267
1297
|
|
|
@@ -5,10 +5,12 @@ SDK gate is on; it is off by default.
|
|
|
5
5
|
- The fields are the receipt's (spec 5.2), snake-cased. The receipt is historical: status
|
|
6
6
|
may already have advanced past ``initial_phase``.
|
|
7
7
|
- ``status()`` reads the current bounded status (spec 5.2/5.7); it writes nothing. It asks
|
|
8
|
-
for status
|
|
9
|
-
v2, which an older server or a switched-off
|
|
8
|
+
for status v8 (O6-C PR-D: v7 plus ``isolationTier``) and also accepts v7 (credits S4b: v6
|
|
9
|
+
plus ``launchAttempts``), v6, v5, v4, v3 or v2, which an older server or a switched-off v8
|
|
10
|
+
(or v7, v6, v5, v4, v3) answers; a server refuses
|
|
10
11
|
v2-v5 for a job with a Step 10 outcome (``status_version_unsupported``). ``route()`` returns
|
|
11
|
-
the v4-
|
|
12
|
+
the v4-v8 route summary (spec 8.6), or None. ``isolation_tier()`` returns the v8 tier of the
|
|
13
|
+
machine the code ran on, or None (before one, or from an older answer).
|
|
12
14
|
- ``funding_action()`` returns the server-owned Add funds action (status v5, spec 9.3), or
|
|
13
15
|
None; ``recheck_funding()`` sends exactly that action's binding back to ask for one bounded
|
|
14
16
|
recheck of the provider's funds. Neither starts a payment; the customer pays on the
|
|
@@ -60,8 +62,8 @@ from .errors import WaitTimeout
|
|
|
60
62
|
|
|
61
63
|
_JOBS = "/api/v2/source/jobs/"
|
|
62
64
|
_JOB_ID = re.compile(r"job_[a-f0-9]{32}")
|
|
63
|
-
# Status version asked for; the answer's schemaVersion (2 to
|
|
64
|
-
_STATUS_VERSION =
|
|
65
|
+
# Status version asked for; the answer's schemaVersion (2 to 8) names the shape served.
|
|
66
|
+
_STATUS_VERSION = 8
|
|
65
67
|
# Asked once more after an answer of HTTP 500: v3 reads no route plan, launch row or Step 10
|
|
66
68
|
# record, so a server failure confined to those projections never makes status (or wait)
|
|
67
69
|
# unavailable (8-ix, 9-xii). A Step 10 job refuses v3 (S10-R16): the 500 then stands.
|
|
@@ -220,7 +222,8 @@ class JobHandle:
|
|
|
220
222
|
def status(self):
|
|
221
223
|
"""Current owner-authorized job status, bound to this job and its immutable receipt.
|
|
222
224
|
|
|
223
|
-
Returns the validated answer as served:
|
|
225
|
+
Returns the validated answer as served: v8 (v7 plus ``isolationTier``; see
|
|
226
|
+
``isolation_tier()``), v7 (v6 plus ``launchAttempts``), v6 (v5 with ``financial`` replaced by
|
|
224
227
|
``cost``, plus the Step 10 ``execution``, ``result``, ``logs`` and ``reminder`` blocks),
|
|
225
228
|
v5 (v4 plus the launch phases, the ``provider_launch`` or ``execution`` task,
|
|
226
229
|
``cleanup``, ``financial`` and the funding action; see ``funding_action()``), v4 (v3
|
|
@@ -239,6 +242,17 @@ class JobHandle:
|
|
|
239
242
|
"""
|
|
240
243
|
return _read_status(self._client, self.job_id, self._fail, self.receipt())
|
|
241
244
|
|
|
245
|
+
def isolation_tier(self):
|
|
246
|
+
"""The isolation tier of the machine this job's code ran on (status v8), or None.
|
|
247
|
+
|
|
248
|
+
Reads status exactly as ``status()`` does (same errors) and returns ``"kernel_cgroup"``,
|
|
249
|
+
``"hardened_user_separation"`` (the disclosed hardened tier: per-process memory and
|
|
250
|
+
file-size limits and a sampled memory check; see the security page) or
|
|
251
|
+
``"user_separation"`` (the owner canary only). None before such a machine, and from a
|
|
252
|
+
server that answers v7 or older.
|
|
253
|
+
"""
|
|
254
|
+
return self.status().get("isolationTier")
|
|
255
|
+
|
|
242
256
|
def funding_action(self):
|
|
243
257
|
"""The job's current Add funds action (status v5, spec 9.3 L1721), or None.
|
|
244
258
|
|
|
@@ -455,12 +469,12 @@ def _job_handle(receipt, client):
|
|
|
455
469
|
|
|
456
470
|
|
|
457
471
|
def _read_status(client, job_id, fail, receipt=None):
|
|
458
|
-
"""The job's validated status, asked as
|
|
472
|
+
"""The job's validated status, asked as v8 (once more as v3 after a 500), bound to the job
|
|
459
473
|
(and to its receipt when known)."""
|
|
460
474
|
status, headers, data = _request_status(client, job_id, fail, _STATUS_VERSION)
|
|
461
475
|
if status == 500:
|
|
462
476
|
fallback = _request_status(client, job_id, fail, _FALLBACK_STATUS_VERSION)
|
|
463
|
-
# A Step 10 job refuses v3 (S10-R16): the
|
|
477
|
+
# A Step 10 job refuses v3 (S10-R16): the v8 failure stays an unavailable read.
|
|
464
478
|
if not (fallback[0] == 409 and _error_code(fallback[2]) == "status_version_unsupported"):
|
|
465
479
|
status, headers, data = fallback
|
|
466
480
|
if status == 200:
|
|
@@ -72,5 +72,5 @@ def sdk_surface_digest(bounds=None):
|
|
|
72
72
|
# RECORD content digest plus the candidate SHA).
|
|
73
73
|
SOURCE_RELEASE_COMPATIBILITY = {
|
|
74
74
|
"contract": SOURCE_RELEASE_CONFIG_CONTRACT,
|
|
75
|
-
"surfaces": {"sdk": "
|
|
75
|
+
"surfaces": {"sdk": "2d6b1bd3408b61f57f788f3e68d502cdb1626c1bf0f486b4dfefc5dc9bd47f24"},
|
|
76
76
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gridlinegpu
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Python client for Gridline YAML workload routing
|
|
5
5
|
Author: Gridline GPU
|
|
6
6
|
License: Proprietary
|
|
@@ -213,6 +213,8 @@ print(job.report()["settlement"]) # reserved, held, charged and released credit
|
|
|
213
213
|
```
|
|
214
214
|
|
|
215
215
|
`job.status()` reads the current state without waiting, and `job.cancel()` requests a stop.
|
|
216
|
+
`job.isolation_tier()` names the isolation tier the job's code ran under (`kernel_cgroup` or
|
|
217
|
+
`hardened_user_separation`, see the security page), or `None` before a machine is allocated.
|
|
216
218
|
`client.history(limit=20)` lists your recent jobs, newest first. `report()` and `history()`
|
|
217
219
|
raise `report_unavailable` / `history_unavailable` until those reads are switched on for
|
|
218
220
|
the beta; the job itself is unaffected. A complete minimal
|
|
@@ -531,7 +531,7 @@ class ClientJobTests(ClientSourceTestCase):
|
|
|
531
531
|
self.assertEqual((status["schemaVersion"], status["phase"], status["outcome"]),
|
|
532
532
|
(3, "terminal", "cancelled"))
|
|
533
533
|
self.assertIsNone(status["error"])
|
|
534
|
-
self.assertEqual(self.server.queries, [("GET", "schemaVersion=
|
|
534
|
+
self.assertEqual(self.server.queries, [("GET", "schemaVersion=8")] * 2)
|
|
535
535
|
terminal = job_status_v3(self.server.job, "terminal")
|
|
536
536
|
_, reason, action = describe("build_failed")
|
|
537
537
|
failed = {**terminal, "outcome": "failed", "failureCode": "build_failed",
|
|
@@ -44,7 +44,7 @@ from gridlinegpu._source_interfaces import (
|
|
|
44
44
|
validate_source_admission_receipt, validate_source_admission_request, validate_source_funding_recheck_answer,
|
|
45
45
|
validate_source_job_status, validate_source_job_status_answer, validate_source_job_status_v3,
|
|
46
46
|
validate_source_job_status_v4, validate_source_job_status_v5, validate_source_job_status_v6,
|
|
47
|
-
validate_source_job_status_v7,
|
|
47
|
+
validate_source_job_status_v7, validate_source_job_status_v8,
|
|
48
48
|
validate_source_job_stop_acknowledgement,
|
|
49
49
|
validate_source_preflight_response, validate_source_preparation_envelope, validate_source_preparation_receipt,
|
|
50
50
|
validate_source_verified_set,
|
|
@@ -602,7 +602,7 @@ class SourceInterfaceTests(unittest.TestCase):
|
|
|
602
602
|
|
|
603
603
|
def test_status_answers_dispatch_on_their_version(self):
|
|
604
604
|
answers = fixtures("interfaces.json")["answers"]
|
|
605
|
-
self.assertEqual(SOURCE_JOB_STATUS_VERSIONS, (2, 3, 4, 5, 6, 7))
|
|
605
|
+
self.assertEqual(SOURCE_JOB_STATUS_VERSIONS, (2, 3, 4, 5, 6, 7, 8))
|
|
606
606
|
for name in ("jobStatus", "jobStatusV3"):
|
|
607
607
|
for case in answers[name]["valid"]:
|
|
608
608
|
with self.subTest(answer=name, case=case["name"]):
|
|
@@ -614,7 +614,7 @@ class SourceInterfaceTests(unittest.TestCase):
|
|
|
614
614
|
validate_source_job_status_answer(case["value"])
|
|
615
615
|
v3 = answers["jobStatusV3"]["valid"][0]["value"]
|
|
616
616
|
without = {key: item for key, item in v3.items() if key != "schemaVersion"}
|
|
617
|
-
wrong = [{**v3, "schemaVersion": version} for version in (1,
|
|
617
|
+
wrong = [{**v3, "schemaVersion": version} for version in (1, 9, "3", "4", True, None, 2.5)]
|
|
618
618
|
for value in wrong + [without, [], "3", None]:
|
|
619
619
|
with self.subTest(value=repr(value)[:40]):
|
|
620
620
|
with self.assertRaises(SourceRecordError) as caught:
|
|
@@ -865,6 +865,38 @@ class SourceInterfaceTests(unittest.TestCase):
|
|
|
865
865
|
with self.assertRaises(SourceRecordError):
|
|
866
866
|
validate_source_job_status_v7({**status_v6_answer({}), "schemaVersion": 7})
|
|
867
867
|
|
|
868
|
+
def test_shared_status_v8_vectors(self):
|
|
869
|
+
# O6-C PR-D parity with job.ts sourceJobStatusV8Schema: v7 plus isolationTier.
|
|
870
|
+
from gridlinegpu import _source_interfaces as interfaces
|
|
871
|
+
vectors = fixtures("job-status-v8.json")
|
|
872
|
+
self.assertEqual(vectors["contract"], "gridline-source-job-status-v8-fixtures-v1")
|
|
873
|
+
|
|
874
|
+
def v8(patch):
|
|
875
|
+
return status_v6_answer(patch, "job-status-v8.json")
|
|
876
|
+
|
|
877
|
+
answers = vectors["answers"]
|
|
878
|
+
self.assertTrue(answers["valid"] and answers["invalid"])
|
|
879
|
+
for name, patch in answers["valid"]:
|
|
880
|
+
with self.subTest(case=name):
|
|
881
|
+
value = v8(patch)
|
|
882
|
+
self.assertIs(validate_source_job_status_v8(value), value)
|
|
883
|
+
self.assertIs(validate_source_job_status_answer(value), value)
|
|
884
|
+
for name, patch, path in answers["invalid"]:
|
|
885
|
+
with self.subTest(case=name):
|
|
886
|
+
with self.assertRaises(SourceRecordError) as caught:
|
|
887
|
+
validate_source_job_status_v8(v8(patch))
|
|
888
|
+
self.assertEqual(caught.exception.code, "schema_invalid")
|
|
889
|
+
with self.assertRaises(SourceRecordError):
|
|
890
|
+
validate_source_job_status_answer(v8(patch))
|
|
891
|
+
self.assertEqual(interfaces.SOURCE_JOB_STATUS_V8_FIELDS, tuple(vectors["fields"]))
|
|
892
|
+
self.assertEqual(list(v8({})), vectors["fields"])
|
|
893
|
+
self.assertEqual(list(interfaces.SOURCE_JOB_ISOLATION_TIERS), vectors["vocabularies"]["isolationTiers"])
|
|
894
|
+
# A v8 answer is never a v7 answer, and the reverse.
|
|
895
|
+
with self.assertRaises(SourceRecordError):
|
|
896
|
+
validate_source_job_status_v7({**v8({}), "schemaVersion": 7})
|
|
897
|
+
with self.assertRaises(SourceRecordError):
|
|
898
|
+
validate_source_job_status_v8({**status_v6_answer({}, "job-status-v7.json"), "schemaVersion": 8})
|
|
899
|
+
|
|
868
900
|
def test_funding_surfaces_match_the_shared_vectors(self):
|
|
869
901
|
vectors = fixtures("job-status-v5.json")
|
|
870
902
|
billing = vectors["billing"]
|
|
@@ -247,7 +247,7 @@ class JobHandleTests(unittest.TestCase):
|
|
|
247
247
|
self.assertEqual(job.status(), job_status(RECEIPT))
|
|
248
248
|
self.server.status_version = 3
|
|
249
249
|
self.assertEqual(job.status(), job_status_v3(RECEIPT))
|
|
250
|
-
self.assertEqual(self.server.queries, [("GET", "schemaVersion=
|
|
250
|
+
self.assertEqual(self.server.queries, [("GET", "schemaVersion=8")] * 2)
|
|
251
251
|
for name, patch in fixtures("job-status-v4.json")["answers"]["valid"]:
|
|
252
252
|
with self.subTest(name=name):
|
|
253
253
|
answer = status_v4_answer(patch)
|
|
@@ -299,7 +299,7 @@ class JobHandleTests(unittest.TestCase):
|
|
|
299
299
|
self.server.phases = [_json(200, answer)]
|
|
300
300
|
self.assertEqual(job.route(), expected)
|
|
301
301
|
self.assertEqual(best_effort["route"]["estimate"], None)
|
|
302
|
-
self.assertEqual(self.server.queries, [("GET", "schemaVersion=
|
|
302
|
+
self.assertEqual(self.server.queries, [("GET", "schemaVersion=8")] * len(cases))
|
|
303
303
|
sunk = status_v4_answer({"route": "$route:sunkUnknown"})
|
|
304
304
|
self.server.phases = [_json(200, sunk)]
|
|
305
305
|
self.assertEqual(job.route()["estimate"]["totalUnknownReason"], "sunk_cost_unknown")
|
|
@@ -325,7 +325,7 @@ class JobHandleTests(unittest.TestCase):
|
|
|
325
325
|
v3 = v3_vector("routing pending after commit")
|
|
326
326
|
self.server.phases = [(500, {}, b""), _json(200, v3)]
|
|
327
327
|
self.assertEqual(job.status(), v3)
|
|
328
|
-
self.assertEqual(self.server.queries, [("GET", "schemaVersion=
|
|
328
|
+
self.assertEqual(self.server.queries, [("GET", "schemaVersion=8"), ("GET", "schemaVersion=3")])
|
|
329
329
|
# route() over the fallback is None: v3 carries no route.
|
|
330
330
|
self.server.phases = [(500, {}, b""), _json(200, v3)]
|
|
331
331
|
self.assertIsNone(job.route())
|
|
@@ -340,13 +340,13 @@ class JobHandleTests(unittest.TestCase):
|
|
|
340
340
|
with self.assertRaises(SourceSubmissionError) as caught:
|
|
341
341
|
job.status()
|
|
342
342
|
self.assertEqual((caught.exception.code, caught.exception.status), ("job_status_unavailable", 500))
|
|
343
|
-
self.assertEqual(self.server.queries, [("GET", "schemaVersion=
|
|
343
|
+
self.assertEqual(self.server.queries, [("GET", "schemaVersion=8"), ("GET", "schemaVersion=3")])
|
|
344
344
|
# Other failures are not retried as v3.
|
|
345
345
|
self.server.queries.clear()
|
|
346
346
|
self.server.phases = [(503, {}, b"{}")]
|
|
347
347
|
with self.assertRaises(SourceSubmissionError):
|
|
348
348
|
job.status()
|
|
349
|
-
self.assertEqual(self.server.queries, [("GET", "schemaVersion=
|
|
349
|
+
self.assertEqual(self.server.queries, [("GET", "schemaVersion=8")])
|
|
350
350
|
|
|
351
351
|
def test_unknown_failure_codes_and_error_stages_never_crash(self):
|
|
352
352
|
# H6: failure codes and error stages are open words. A newer server's code arrives with
|
|
@@ -576,7 +576,7 @@ class JobFundingTests(unittest.TestCase):
|
|
|
576
576
|
expected = None if answer["fundingAction"] is None else {
|
|
577
577
|
key: answer[key] for key in ("actionRequired", "fundingAction", "billing")}
|
|
578
578
|
self.assertEqual(job.funding_action(), expected)
|
|
579
|
-
self.assertEqual(set(self.server.queries), {("GET", "schemaVersion=
|
|
579
|
+
self.assertEqual(set(self.server.queries), {("GET", "schemaVersion=8")})
|
|
580
580
|
# Older servers (v4, v3, v2) carry no action.
|
|
581
581
|
for answer in (status_v4_answer({}), v3_vector(CLAIMED), job_status(RECEIPT)):
|
|
582
582
|
self.server.phases = [_json(200, answer)]
|
|
@@ -950,7 +950,7 @@ class JobStatusV6Tests(unittest.TestCase):
|
|
|
950
950
|
with self.assertRaises(SourceSubmissionError) as caught:
|
|
951
951
|
job.status()
|
|
952
952
|
self.assertEqual(caught.exception.code, "remote_contract_invalid")
|
|
953
|
-
self.assertEqual(set(self.server.queries), {("GET", "schemaVersion=
|
|
953
|
+
self.assertEqual(set(self.server.queries), {("GET", "schemaVersion=8")})
|
|
954
954
|
|
|
955
955
|
def test_wait_returns_the_structured_result_with_separate_states(self):
|
|
956
956
|
running = vector("running: started, advisory progress, result pending, no receipt yet")
|
|
@@ -1014,7 +1014,7 @@ class JobStatusV6Tests(unittest.TestCase):
|
|
|
1014
1014
|
with self.assertRaises(SourceSubmissionError) as caught:
|
|
1015
1015
|
job.status()
|
|
1016
1016
|
self.assertEqual((caught.exception.code, caught.exception.status), ("job_status_unavailable", 500))
|
|
1017
|
-
self.assertEqual(self.server.queries, [("GET", "schemaVersion=
|
|
1017
|
+
self.assertEqual(self.server.queries, [("GET", "schemaVersion=8"), ("GET", "schemaVersion=3")])
|
|
1018
1018
|
|
|
1019
1019
|
|
|
1020
1020
|
class JobCostTests(unittest.TestCase):
|
|
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
|
|
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
|