abysshub 0.0.1.dev14__tar.gz → 0.0.1.dev16__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.
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/PKG-INFO +7 -5
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/README.md +6 -4
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/pyproject.toml +1 -1
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/src/abysshub/_client.py +17 -7
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/.gitignore +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/LICENSE +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/src/abysshub/__init__.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/src/abysshub/_error.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/src/abysshub/_files.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/src/abysshub/_http.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/src/abysshub/_run.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/src/abysshub/_types.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/src/abysshub/py.typed +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/tests/caller.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/tests/fake_server.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/tests/test_client.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/tests/test_files.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/tests/test_http.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/tests/test_package.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/tests/test_replay.py +0 -0
- {abysshub-0.0.1.dev14 → abysshub-0.0.1.dev16}/tests/test_timeout.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: abysshub
|
|
3
|
-
Version: 0.0.1.
|
|
3
|
+
Version: 0.0.1.dev16
|
|
4
4
|
Summary: The official Python library for the Abyss API.
|
|
5
5
|
Project-URL: Homepage, https://abysshub.com
|
|
6
6
|
Project-URL: Source, https://github.com/Abysshub/abysshub-sdk
|
|
@@ -17,10 +17,10 @@ Description-Content-Type: text/markdown
|
|
|
17
17
|
|
|
18
18
|
# abysshub
|
|
19
19
|
|
|
20
|
-
The official Python library for the [Abyss](https://abysshub.com) API.
|
|
21
|
-
|
|
22
20
|
Pre-release: the Abyss API opens soon.
|
|
23
21
|
|
|
22
|
+
The official Python library for the [Abyss](https://abysshub.com) API.
|
|
23
|
+
|
|
24
24
|
```python
|
|
25
25
|
from abysshub import Abyss, AbyssError
|
|
26
26
|
|
|
@@ -68,7 +68,7 @@ run = abyss.run(widget, input, max_price=None, idempotency_key=None, timeout=Non
|
|
|
68
68
|
- **`idempotency_key`:** each call presses with a fresh UUID `Idempotency-Key` unless you give one. Retries send the same one, so a run is never made or charged twice.
|
|
69
69
|
- **`timeout`:** the seconds to wait at most (see [Timeouts](#timeouts)).
|
|
70
70
|
|
|
71
|
-
The press holds until the run ends. When the hold ends early (a deploy cuts it, the API's holds are all taken, or an hour passes), `run()` re-attaches with `GET <Location>?wait=true` until the run ends. Re-attaching has no limit and is not a retry. Connecting has 10 s, and 30 s of silence counts as a cut.
|
|
71
|
+
The press holds until the run ends. When the hold ends early (a deploy cuts it, the network goes down, the API's holds are all taken, or an hour passes), `run()` re-attaches with `GET <Location>?wait=true` until the run ends. Re-attaching has no limit and is not a retry. Connecting has 10 s, and 30 s of silence counts as a cut.
|
|
72
72
|
|
|
73
73
|
A **run** is a dataclass with `/v1`'s fields: `id`, `widget`, `status` (`queued`, `running`, `succeeded` or `failed`), `price`, `result` (the Widget's JSON, a `dict` when it wrote an object), `output_files`, `error`, `created_at`, `started_at` and `ended_at`.
|
|
74
74
|
|
|
@@ -146,7 +146,9 @@ A request is sent again, at most `max_retries` times (2 by default), only after:
|
|
|
146
146
|
- **`429 rate_limited` and `409 idempotency_key_in_use`:** after the `Retry-After` seconds;
|
|
147
147
|
- **a `5xx`:** a short backoff (0.5 s, then 1 s).
|
|
148
148
|
|
|
149
|
-
When the last try fails too, it raises. Every other refusal raises at once
|
|
149
|
+
When the last try fails too, it raises. Every other refusal raises at once.
|
|
150
|
+
|
|
151
|
+
**Re-attaching never counts as a retry.** A re-attach that drops, or meets a `429`, `409` or `5xx`, is tried again with no limit until the network is back, pausing 0.5 s and doubling to at most 10 s (or the `Retry-After` seconds). Only `timeout` stops it.
|
|
150
152
|
|
|
151
153
|
## Timeouts
|
|
152
154
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# abysshub
|
|
2
2
|
|
|
3
|
-
The official Python library for the [Abyss](https://abysshub.com) API.
|
|
4
|
-
|
|
5
3
|
Pre-release: the Abyss API opens soon.
|
|
6
4
|
|
|
5
|
+
The official Python library for the [Abyss](https://abysshub.com) API.
|
|
6
|
+
|
|
7
7
|
```python
|
|
8
8
|
from abysshub import Abyss, AbyssError
|
|
9
9
|
|
|
@@ -51,7 +51,7 @@ run = abyss.run(widget, input, max_price=None, idempotency_key=None, timeout=Non
|
|
|
51
51
|
- **`idempotency_key`:** each call presses with a fresh UUID `Idempotency-Key` unless you give one. Retries send the same one, so a run is never made or charged twice.
|
|
52
52
|
- **`timeout`:** the seconds to wait at most (see [Timeouts](#timeouts)).
|
|
53
53
|
|
|
54
|
-
The press holds until the run ends. When the hold ends early (a deploy cuts it, the API's holds are all taken, or an hour passes), `run()` re-attaches with `GET <Location>?wait=true` until the run ends. Re-attaching has no limit and is not a retry. Connecting has 10 s, and 30 s of silence counts as a cut.
|
|
54
|
+
The press holds until the run ends. When the hold ends early (a deploy cuts it, the network goes down, the API's holds are all taken, or an hour passes), `run()` re-attaches with `GET <Location>?wait=true` until the run ends. Re-attaching has no limit and is not a retry. Connecting has 10 s, and 30 s of silence counts as a cut.
|
|
55
55
|
|
|
56
56
|
A **run** is a dataclass with `/v1`'s fields: `id`, `widget`, `status` (`queued`, `running`, `succeeded` or `failed`), `price`, `result` (the Widget's JSON, a `dict` when it wrote an object), `output_files`, `error`, `created_at`, `started_at` and `ended_at`.
|
|
57
57
|
|
|
@@ -129,7 +129,9 @@ A request is sent again, at most `max_retries` times (2 by default), only after:
|
|
|
129
129
|
- **`429 rate_limited` and `409 idempotency_key_in_use`:** after the `Retry-After` seconds;
|
|
130
130
|
- **a `5xx`:** a short backoff (0.5 s, then 1 s).
|
|
131
131
|
|
|
132
|
-
When the last try fails too, it raises. Every other refusal raises at once
|
|
132
|
+
When the last try fails too, it raises. Every other refusal raises at once.
|
|
133
|
+
|
|
134
|
+
**Re-attaching never counts as a retry.** A re-attach that drops, or meets a `429`, `409` or `5xx`, is tried again with no limit until the network is back, pausing 0.5 s and doubling to at most 10 s (or the `Retry-After` seconds). Only `timeout` stops it.
|
|
133
135
|
|
|
134
136
|
## Timeouts
|
|
135
137
|
|
|
@@ -31,6 +31,9 @@ REATTACH_PAUSE = 1.0
|
|
|
31
31
|
RETRY_PAUSE = 0.5
|
|
32
32
|
"""Seconds before the first retry after a drop or a 5xx; the pause doubles with each retry."""
|
|
33
33
|
|
|
34
|
+
RETRY_PAUSE_CEILING = 10.0
|
|
35
|
+
"""The longest pause between two tries, in seconds, which only re-attaching, with no limit, reaches."""
|
|
36
|
+
|
|
34
37
|
|
|
35
38
|
def _version() -> str:
|
|
36
39
|
try:
|
|
@@ -348,11 +351,13 @@ class Abyss:
|
|
|
348
351
|
return _expect(self._send(call, "GET", url), is_, message)
|
|
349
352
|
|
|
350
353
|
def _hold(self, call: _Call, url: str, done: Callable[[_Reply], bool]) -> _Reply:
|
|
351
|
-
"""Reads ``url`` until ``done``. Re-attaching has no limit and is not a retry
|
|
352
|
-
|
|
354
|
+
"""Reads ``url`` until ``done``. Re-attaching has no limit and is not a retry: a
|
|
355
|
+
re-attach that drops, or meets a 429, 409 or 5xx, is tried again until the network
|
|
356
|
+
is back, and only the call's ``timeout`` stops it."""
|
|
357
|
+
reply = self._send(call, "GET", url, retries=math.inf)
|
|
353
358
|
while not done(reply):
|
|
354
359
|
self._sleep(call, REATTACH_PAUSE)
|
|
355
|
-
reply = self._send(call, "GET", url)
|
|
360
|
+
reply = self._send(call, "GET", url, retries=math.inf)
|
|
356
361
|
return reply
|
|
357
362
|
|
|
358
363
|
def _widget_url(self, widget: str) -> str:
|
|
@@ -375,9 +380,12 @@ class Abyss:
|
|
|
375
380
|
url: str,
|
|
376
381
|
headers: dict[str, str] | None = None,
|
|
377
382
|
content: bytes | None = None,
|
|
383
|
+
retries: float | None = None,
|
|
378
384
|
) -> _Reply:
|
|
379
385
|
"""Sends one request to `/v1`. A drop before any headers, a 429, a 409 and a 5xx
|
|
380
|
-
are sent again, at most ``
|
|
386
|
+
are sent again, at most ``retries`` times (``max_retries`` when left out); every
|
|
387
|
+
other refusal raises at once."""
|
|
388
|
+
limit = self.max_retries if retries is None else retries
|
|
381
389
|
sent_headers = {
|
|
382
390
|
"Authorization": f"Bearer {self._api_key}",
|
|
383
391
|
"Accept": "application/json",
|
|
@@ -393,7 +401,7 @@ class Abyss:
|
|
|
393
401
|
raise self._timed_out(call) from None
|
|
394
402
|
except httpx.TransportError as error:
|
|
395
403
|
self._check(call)
|
|
396
|
-
if attempt >=
|
|
404
|
+
if attempt >= limit:
|
|
397
405
|
raise AbyssError(
|
|
398
406
|
"connection_error",
|
|
399
407
|
"The connection dropped, or timed out, before the API answered.",
|
|
@@ -409,7 +417,7 @@ class Abyss:
|
|
|
409
417
|
call.seen = run
|
|
410
418
|
return _Reply(parsed, run, answer.text is None, answer.headers.get("location"), request_id)
|
|
411
419
|
pause = _retry_pause(answer, attempt)
|
|
412
|
-
if pause is None or attempt >=
|
|
420
|
+
if pause is None or attempt >= limit:
|
|
413
421
|
raise _refusal(answer.status, parsed, request_id)
|
|
414
422
|
self._sleep(call, pause)
|
|
415
423
|
attempt += 1
|
|
@@ -427,7 +435,9 @@ def _retry_pause(answer: Answer, attempt: int) -> float | None:
|
|
|
427
435
|
|
|
428
436
|
|
|
429
437
|
def _backoff(attempt: int) -> float:
|
|
430
|
-
|
|
438
|
+
# The exponent stops growing once the ceiling is reached, so an unlimited re-attach
|
|
439
|
+
# never makes a float too large.
|
|
440
|
+
return min(RETRY_PAUSE * 2 ** min(attempt, 16), RETRY_PAUSE_CEILING)
|
|
431
441
|
|
|
432
442
|
|
|
433
443
|
def _expect(reply: _Reply, is_: Callable[[Any], bool], message: str) -> Any:
|
|
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
|