abyssale 1.0.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.
- abyssale-1.0.0/.gitignore +15 -0
- abyssale-1.0.0/CHANGELOG.md +66 -0
- abyssale-1.0.0/LICENSE +21 -0
- abyssale-1.0.0/PKG-INFO +171 -0
- abyssale-1.0.0/README.md +111 -0
- abyssale-1.0.0/pyproject.toml +87 -0
- abyssale-1.0.0/src/abyssale/__init__.py +102 -0
- abyssale-1.0.0/src/abyssale/_async_client.py +310 -0
- abyssale-1.0.0/src/abyssale/_client.py +488 -0
- abyssale-1.0.0/src/abyssale/_config.py +113 -0
- abyssale-1.0.0/src/abyssale/_errors.py +148 -0
- abyssale-1.0.0/src/abyssale/_generated.py +3009 -0
- abyssale-1.0.0/src/abyssale/_polling.py +193 -0
- abyssale-1.0.0/src/abyssale/_retry.py +208 -0
- abyssale-1.0.0/src/abyssale/_transport.py +155 -0
- abyssale-1.0.0/src/abyssale/_version.py +15 -0
- abyssale-1.0.0/src/abyssale/models.py +165 -0
- abyssale-1.0.0/src/abyssale/py.typed +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The format follows
|
|
4
|
+
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to
|
|
5
|
+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
**Every release names the API version it was generated from.** The API is versioned by release date
|
|
8
|
+
(`vYYYY-MM-DD`) and one version is maintained at a time, so the pairing below tells you which
|
|
9
|
+
contract a given SDK release models. The SDK's own version is independent — regenerating against a
|
|
10
|
+
newer API version is a normal change, and whether it is a patch, minor or major depends on what the
|
|
11
|
+
API changed.
|
|
12
|
+
|
|
13
|
+
| SDK | API version | |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| 1.0.0 | `v2026-08-20` | [spec](https://api-reference.abyssale.com/api.yaml) |
|
|
16
|
+
|
|
17
|
+
## [1.0.0] — 2026-08-20
|
|
18
|
+
|
|
19
|
+
_Generated from API version **`v2026-08-20`**._
|
|
20
|
+
|
|
21
|
+
First release. Covers **every operation in that spec** — 18 of them — plus two polling helpers over
|
|
22
|
+
its status endpoints. Response models are generated from its schemas.
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- `Abyssale` and `AsyncAbyssale`, over `httpx`. Both are context managers and both accept a
|
|
27
|
+
caller-supplied `httpx` client.
|
|
28
|
+
- All 18 endpoints: auth; design list/read/format read; sync and async generation; multipage PDF;
|
|
29
|
+
generation-request status; file read; fonts; projects list/create; export; dynamic image URL;
|
|
30
|
+
workspace templates, categories, duplication and duplication status.
|
|
31
|
+
- `wait_for_generation_request` and `wait_for_duplication_request` — exponential backoff with
|
|
32
|
+
jitter, a 30-minute default deadline, and a budget of three *consecutive* transient failures that
|
|
33
|
+
resets on any successful poll. Partial success resolves: a finalized request carrying both banners
|
|
34
|
+
and per-format errors is a result, not an exception. Only a request that finalized with no banners
|
|
35
|
+
at all and at least one error raises.
|
|
36
|
+
- Retries, following the spec's error contract: 5xx on idempotent methods only
|
|
37
|
+
(every POST bills credits, and a 504 does not mean the render did not happen); the full ladder for
|
|
38
|
+
a `429` carrying `Retry-After`; exactly one one-second probe for a bare `429`, because
|
|
39
|
+
`rate_limit_exceeded` conflates spent credits with the gateway's per-second ceiling;
|
|
40
|
+
`feature_not_in_plan` never retried, whatever headers the response carries — no window makes a
|
|
41
|
+
plan restriction clear.
|
|
42
|
+
- `max_retry_wait` (`$ABYSSALE_MAX_RETRY_WAIT_MS`, default 30s) — the longest single `Retry-After`
|
|
43
|
+
the SDK will sleep through on your behalf. The rate limiter can name a cool-off of ~1700s once a
|
|
44
|
+
quota is spent, and `max_retries` multiplies it, so honouring it blindly would turn one call into
|
|
45
|
+
83 minutes of silence with no way to intervene. Past the bound the call fails immediately with
|
|
46
|
+
`AbyssaleRateLimitError`, `retry_after` carrying the server's figure so the decision is yours.
|
|
47
|
+
Applies to any server-named wait, including a 5xx that carries `Retry-After` and one absorbed by a
|
|
48
|
+
`wait_for_*` poll — a 30-minute deadline has room to sleep off a 28-minute cool-off in one go, and
|
|
49
|
+
the bound is what refuses it. The SDK's own backoff and the bare-`429` probe are unaffected. Pass
|
|
50
|
+
`math.inf` to wait however long the server asks.
|
|
51
|
+
- An exception hierarchy under `AbyssaleError`, built from the API's single error envelope — the
|
|
52
|
+
machine-readable `id` is always on the exception, and `errors` holds the per-field problems when
|
|
53
|
+
the failure was a payload problem.
|
|
54
|
+
- Pydantic response models generated from the spec, with the Alpha design-import surface stripped by
|
|
55
|
+
`scripts/fetch_spec.py`, since the spec marks that surface Alpha.
|
|
56
|
+
- Seven runnable examples in `examples/`, each exercising a documented operation end to end.
|
|
57
|
+
|
|
58
|
+
### Notes
|
|
59
|
+
|
|
60
|
+
- **Request bodies are plain dicts.** The `elements` schema is an `anyOf` of ten deliberately
|
|
61
|
+
overlapping branches with no discriminator, and the API accepts unknown element names by design,
|
|
62
|
+
so bodies are passed through untouched rather than modelled.
|
|
63
|
+
- **Parsing never fails a successful response.** Unknown fields are kept, and a field the spec calls
|
|
64
|
+
required but the response omits does not raise — the spec is hand-maintained and the API is the
|
|
65
|
+
authority.
|
|
66
|
+
- Requires Python 3.10+.
|
abyssale-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Abyssale
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
abyssale-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: abyssale
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Official Python SDK for the Abyssale API — image, video and PDF generation.
|
|
5
|
+
Project-URL: Homepage, https://www.abyssale.com
|
|
6
|
+
Project-URL: Documentation, https://developers.abyssale.com/sdks/python
|
|
7
|
+
Project-URL: API Reference, https://api-reference.abyssale.com
|
|
8
|
+
Project-URL: Repository, https://github.com/getabyssale/abyssale-python-sdk
|
|
9
|
+
Project-URL: Issues, https://github.com/getabyssale/abyssale-python-sdk/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/getabyssale/abyssale-python-sdk/blob/main/CHANGELOG.md
|
|
11
|
+
Author: Abyssale
|
|
12
|
+
License: MIT License
|
|
13
|
+
|
|
14
|
+
Copyright (c) 2025 Abyssale
|
|
15
|
+
|
|
16
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
17
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
18
|
+
in the Software without restriction, including without limitation the rights
|
|
19
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
20
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
21
|
+
furnished to do so, subject to the following conditions:
|
|
22
|
+
|
|
23
|
+
The above copyright notice and this permission notice shall be included in all
|
|
24
|
+
copies or substantial portions of the Software.
|
|
25
|
+
|
|
26
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
27
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
28
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
29
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
30
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
31
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
32
|
+
SOFTWARE.
|
|
33
|
+
License-File: LICENSE
|
|
34
|
+
Keywords: abyssale,api,banner,image-generation,pdf,sdk,video
|
|
35
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
36
|
+
Classifier: Intended Audience :: Developers
|
|
37
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
38
|
+
Classifier: Programming Language :: Python :: 3
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
43
|
+
Classifier: Typing :: Typed
|
|
44
|
+
Requires-Python: >=3.10
|
|
45
|
+
Requires-Dist: httpx>=0.27
|
|
46
|
+
Requires-Dist: pydantic>=2.7
|
|
47
|
+
Provides-Extra: codegen
|
|
48
|
+
Requires-Dist: datamodel-code-generator>=0.25; extra == 'codegen'
|
|
49
|
+
Requires-Dist: pyyaml>=6; extra == 'codegen'
|
|
50
|
+
Provides-Extra: dev
|
|
51
|
+
Requires-Dist: anyio>=4; extra == 'dev'
|
|
52
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
53
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
54
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
55
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
56
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
57
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
58
|
+
Requires-Dist: twine>=5; extra == 'dev'
|
|
59
|
+
Description-Content-Type: text/markdown
|
|
60
|
+
|
|
61
|
+
# Abyssale Python SDK
|
|
62
|
+
|
|
63
|
+
Official Python client for the [Abyssale API](https://developers.abyssale.com) — generate images,
|
|
64
|
+
videos, HTML5 banners and print-ready PDFs from your designs.
|
|
65
|
+
|
|
66
|
+
## Install
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install abyssale
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Requires Python 3.10+. This release models API version **`v2026-08-20`** — see
|
|
73
|
+
[CHANGELOG.md](https://github.com/getabyssale/abyssale-python-sdk/blob/main/CHANGELOG.md) for the SDK-to-API version pairing, and `abyssale.__api_version__` to
|
|
74
|
+
read it at runtime.
|
|
75
|
+
|
|
76
|
+
## Quick start
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from abyssale import Abyssale
|
|
80
|
+
|
|
81
|
+
with Abyssale() as client: # reads ABYSSALE_API_KEY
|
|
82
|
+
design = client.get_design("64238d01-d402-474b-8c2d-fbc957e9d290")
|
|
83
|
+
|
|
84
|
+
banner = client.generate_image(design.id, {
|
|
85
|
+
"elements": {"title": {"payload": "Hello World"}},
|
|
86
|
+
"template_format_name": "facebook-post",
|
|
87
|
+
})
|
|
88
|
+
print(banner.file.cdn_url)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Async is the same surface:
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
import asyncio
|
|
95
|
+
from abyssale import AsyncAbyssale
|
|
96
|
+
|
|
97
|
+
async def main():
|
|
98
|
+
async with AsyncAbyssale() as client:
|
|
99
|
+
accepted = await client.generate_multi_format_media(design_id, {
|
|
100
|
+
"elements": {"title": {"payload": "Summer Sale"}},
|
|
101
|
+
"template_format_names": ["facebook-feed", "instagram-post"],
|
|
102
|
+
})
|
|
103
|
+
result = await client.wait_for_generation_request(accepted.generation_request_id)
|
|
104
|
+
for banner in result.banners:
|
|
105
|
+
print(banner.file.cdn_url)
|
|
106
|
+
|
|
107
|
+
asyncio.run(main())
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Errors
|
|
111
|
+
|
|
112
|
+
Methods return the result and **raise** on failure. Branch on the API's machine-readable `id`, not
|
|
113
|
+
on the message:
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
from abyssale import AbyssaleAPIError, AbyssaleRateLimitError
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
client.generate_image(design_id, {...})
|
|
120
|
+
except AbyssaleRateLimitError as err:
|
|
121
|
+
if err.id == "feature_not_in_plan":
|
|
122
|
+
... # your plan excludes this design type
|
|
123
|
+
except AbyssaleAPIError as err:
|
|
124
|
+
print(err.status, err.id, err.message, err.errors)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
`err.errors` holds the per-field problems (`path`, `code`, `message`) when the request body was the
|
|
128
|
+
problem, and is `None` otherwise.
|
|
129
|
+
|
|
130
|
+
Transient failures are retried for you: 5xx on reads, and `429`s that carry a `Retry-After`. A `429`
|
|
131
|
+
without one gets a single one-second probe, because the status is shared by "out of credits"
|
|
132
|
+
(permanent) and the gateway's per-second ceiling (clears immediately). Writes are never retried on a
|
|
133
|
+
5xx — a timed-out generation may still have been billed.
|
|
134
|
+
|
|
135
|
+
A `Retry-After` is only waited out up to `max_retry_wait` (30s by default). The rate limiter can name
|
|
136
|
+
a cool-off of half an hour once a quota is spent, and sleeping through that — times `max_retries` —
|
|
137
|
+
turns one call into an hour of silence. Past the bound the call fails immediately instead, with
|
|
138
|
+
`err.retry_after` carrying the server's figure so you can decide what to do with it. Pass
|
|
139
|
+
`max_retry_wait=math.inf` if you do want to wait however long the server asks.
|
|
140
|
+
|
|
141
|
+
## Configuration
|
|
142
|
+
|
|
143
|
+
| Variable | Default | Purpose |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `ABYSSALE_API_KEY` | — | Required, unless you pass `api_key=`. |
|
|
146
|
+
| `ABYSSALE_TIMEOUT_MS` | `30000` | Per-attempt request timeout. |
|
|
147
|
+
| `ABYSSALE_MAX_RETRIES` | `3` | `0` disables retries. |
|
|
148
|
+
| `ABYSSALE_MAX_RETRY_WAIT_MS` | `30000` | Longest `Retry-After` to wait out. `inf` to never give up. |
|
|
149
|
+
|
|
150
|
+
Every one can be overridden per client: `Abyssale(api_key=..., timeout=60, max_retries=0)`.
|
|
151
|
+
|
|
152
|
+
## Examples
|
|
153
|
+
|
|
154
|
+
Runnable scripts are in [`examples/`](https://github.com/getabyssale/abyssale-python-sdk/blob/main/examples). Each one names its own command:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
ABYSSALE_API_KEY=your-key python examples/generate_image.py
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Contributing
|
|
161
|
+
|
|
162
|
+
See [AGENTS.md](https://github.com/getabyssale/abyssale-python-sdk/blob/main/AGENTS.md) for the architecture, how to regenerate the models from the OpenAPI spec,
|
|
163
|
+
and how to add an endpoint.
|
|
164
|
+
|
|
165
|
+
## Links
|
|
166
|
+
|
|
167
|
+
- [Abyssale](https://www.abyssale.com) — the product this SDK talks to
|
|
168
|
+
- [Documentation](https://developers.abyssale.com/sdks/python)
|
|
169
|
+
- [API reference](https://api-reference.abyssale.com)
|
|
170
|
+
- [OpenAPI spec](https://api-reference.abyssale.com/api.yaml) — the contract this SDK is generated from
|
|
171
|
+
- [Source](https://github.com/getabyssale/abyssale-python-sdk)
|
abyssale-1.0.0/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Abyssale Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python client for the [Abyssale API](https://developers.abyssale.com) — generate images,
|
|
4
|
+
videos, HTML5 banners and print-ready PDFs from your designs.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install abyssale
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Requires Python 3.10+. This release models API version **`v2026-08-20`** — see
|
|
13
|
+
[CHANGELOG.md](https://github.com/getabyssale/abyssale-python-sdk/blob/main/CHANGELOG.md) for the SDK-to-API version pairing, and `abyssale.__api_version__` to
|
|
14
|
+
read it at runtime.
|
|
15
|
+
|
|
16
|
+
## Quick start
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from abyssale import Abyssale
|
|
20
|
+
|
|
21
|
+
with Abyssale() as client: # reads ABYSSALE_API_KEY
|
|
22
|
+
design = client.get_design("64238d01-d402-474b-8c2d-fbc957e9d290")
|
|
23
|
+
|
|
24
|
+
banner = client.generate_image(design.id, {
|
|
25
|
+
"elements": {"title": {"payload": "Hello World"}},
|
|
26
|
+
"template_format_name": "facebook-post",
|
|
27
|
+
})
|
|
28
|
+
print(banner.file.cdn_url)
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Async is the same surface:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
import asyncio
|
|
35
|
+
from abyssale import AsyncAbyssale
|
|
36
|
+
|
|
37
|
+
async def main():
|
|
38
|
+
async with AsyncAbyssale() as client:
|
|
39
|
+
accepted = await client.generate_multi_format_media(design_id, {
|
|
40
|
+
"elements": {"title": {"payload": "Summer Sale"}},
|
|
41
|
+
"template_format_names": ["facebook-feed", "instagram-post"],
|
|
42
|
+
})
|
|
43
|
+
result = await client.wait_for_generation_request(accepted.generation_request_id)
|
|
44
|
+
for banner in result.banners:
|
|
45
|
+
print(banner.file.cdn_url)
|
|
46
|
+
|
|
47
|
+
asyncio.run(main())
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Errors
|
|
51
|
+
|
|
52
|
+
Methods return the result and **raise** on failure. Branch on the API's machine-readable `id`, not
|
|
53
|
+
on the message:
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from abyssale import AbyssaleAPIError, AbyssaleRateLimitError
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
client.generate_image(design_id, {...})
|
|
60
|
+
except AbyssaleRateLimitError as err:
|
|
61
|
+
if err.id == "feature_not_in_plan":
|
|
62
|
+
... # your plan excludes this design type
|
|
63
|
+
except AbyssaleAPIError as err:
|
|
64
|
+
print(err.status, err.id, err.message, err.errors)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`err.errors` holds the per-field problems (`path`, `code`, `message`) when the request body was the
|
|
68
|
+
problem, and is `None` otherwise.
|
|
69
|
+
|
|
70
|
+
Transient failures are retried for you: 5xx on reads, and `429`s that carry a `Retry-After`. A `429`
|
|
71
|
+
without one gets a single one-second probe, because the status is shared by "out of credits"
|
|
72
|
+
(permanent) and the gateway's per-second ceiling (clears immediately). Writes are never retried on a
|
|
73
|
+
5xx — a timed-out generation may still have been billed.
|
|
74
|
+
|
|
75
|
+
A `Retry-After` is only waited out up to `max_retry_wait` (30s by default). The rate limiter can name
|
|
76
|
+
a cool-off of half an hour once a quota is spent, and sleeping through that — times `max_retries` —
|
|
77
|
+
turns one call into an hour of silence. Past the bound the call fails immediately instead, with
|
|
78
|
+
`err.retry_after` carrying the server's figure so you can decide what to do with it. Pass
|
|
79
|
+
`max_retry_wait=math.inf` if you do want to wait however long the server asks.
|
|
80
|
+
|
|
81
|
+
## Configuration
|
|
82
|
+
|
|
83
|
+
| Variable | Default | Purpose |
|
|
84
|
+
|---|---|---|
|
|
85
|
+
| `ABYSSALE_API_KEY` | — | Required, unless you pass `api_key=`. |
|
|
86
|
+
| `ABYSSALE_TIMEOUT_MS` | `30000` | Per-attempt request timeout. |
|
|
87
|
+
| `ABYSSALE_MAX_RETRIES` | `3` | `0` disables retries. |
|
|
88
|
+
| `ABYSSALE_MAX_RETRY_WAIT_MS` | `30000` | Longest `Retry-After` to wait out. `inf` to never give up. |
|
|
89
|
+
|
|
90
|
+
Every one can be overridden per client: `Abyssale(api_key=..., timeout=60, max_retries=0)`.
|
|
91
|
+
|
|
92
|
+
## Examples
|
|
93
|
+
|
|
94
|
+
Runnable scripts are in [`examples/`](https://github.com/getabyssale/abyssale-python-sdk/blob/main/examples). Each one names its own command:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
ABYSSALE_API_KEY=your-key python examples/generate_image.py
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Contributing
|
|
101
|
+
|
|
102
|
+
See [AGENTS.md](https://github.com/getabyssale/abyssale-python-sdk/blob/main/AGENTS.md) for the architecture, how to regenerate the models from the OpenAPI spec,
|
|
103
|
+
and how to add an endpoint.
|
|
104
|
+
|
|
105
|
+
## Links
|
|
106
|
+
|
|
107
|
+
- [Abyssale](https://www.abyssale.com) — the product this SDK talks to
|
|
108
|
+
- [Documentation](https://developers.abyssale.com/sdks/python)
|
|
109
|
+
- [API reference](https://api-reference.abyssale.com)
|
|
110
|
+
- [OpenAPI spec](https://api-reference.abyssale.com/api.yaml) — the contract this SDK is generated from
|
|
111
|
+
- [Source](https://github.com/getabyssale/abyssale-python-sdk)
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "abyssale"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Official Python SDK for the Abyssale API — image, video and PDF generation."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { file = "LICENSE" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Abyssale" }]
|
|
13
|
+
keywords = ["abyssale", "banner", "image-generation", "video", "pdf", "api", "sdk"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 5 - Production/Stable",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Programming Language :: Python :: 3.11",
|
|
21
|
+
"Programming Language :: Python :: 3.12",
|
|
22
|
+
"Programming Language :: Python :: 3.13",
|
|
23
|
+
"Typing :: Typed",
|
|
24
|
+
]
|
|
25
|
+
dependencies = [
|
|
26
|
+
"httpx>=0.27",
|
|
27
|
+
"pydantic>=2.7",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://www.abyssale.com"
|
|
32
|
+
Documentation = "https://developers.abyssale.com/sdks/python"
|
|
33
|
+
"API Reference" = "https://api-reference.abyssale.com"
|
|
34
|
+
Repository = "https://github.com/getabyssale/abyssale-python-sdk"
|
|
35
|
+
Issues = "https://github.com/getabyssale/abyssale-python-sdk/issues"
|
|
36
|
+
Changelog = "https://github.com/getabyssale/abyssale-python-sdk/blob/main/CHANGELOG.md"
|
|
37
|
+
|
|
38
|
+
[project.optional-dependencies]
|
|
39
|
+
dev = [
|
|
40
|
+
"pytest>=8",
|
|
41
|
+
"pytest-asyncio>=0.23",
|
|
42
|
+
"respx>=0.21",
|
|
43
|
+
"anyio>=4",
|
|
44
|
+
"mypy>=1.10",
|
|
45
|
+
"ruff>=0.5",
|
|
46
|
+
"build>=1.2",
|
|
47
|
+
"twine>=5",
|
|
48
|
+
]
|
|
49
|
+
# Only needed to regenerate `src/abyssale/_generated.py` — not required to use or test the SDK.
|
|
50
|
+
codegen = [
|
|
51
|
+
"datamodel-code-generator>=0.25",
|
|
52
|
+
"PyYAML>=6",
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
[tool.hatch.build.targets.wheel]
|
|
56
|
+
packages = ["src/abyssale"]
|
|
57
|
+
|
|
58
|
+
[tool.hatch.build.targets.sdist]
|
|
59
|
+
include = ["src/abyssale", "README.md", "LICENSE", "CHANGELOG.md"]
|
|
60
|
+
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
testpaths = ["tests"]
|
|
63
|
+
asyncio_mode = "auto"
|
|
64
|
+
|
|
65
|
+
[tool.ruff]
|
|
66
|
+
line-length = 120
|
|
67
|
+
target-version = "py310"
|
|
68
|
+
# The generated models are formatted by the code generator, not by us. Reformatting them here
|
|
69
|
+
# would make every regeneration produce a diff against itself.
|
|
70
|
+
extend-exclude = ["src/abyssale/_generated.py"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.lint]
|
|
73
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
74
|
+
|
|
75
|
+
[tool.ruff.lint.per-file-ignores]
|
|
76
|
+
"src/abyssale/_generated.py" = ["ALL"]
|
|
77
|
+
|
|
78
|
+
[tool.mypy]
|
|
79
|
+
python_version = "3.10"
|
|
80
|
+
strict = true
|
|
81
|
+
files = ["src/abyssale"]
|
|
82
|
+
|
|
83
|
+
# The generated models are not hand-written and are excluded from strict typing, exactly as
|
|
84
|
+
# `generated.ts` is excluded from review in the Node SDK.
|
|
85
|
+
[[tool.mypy.overrides]]
|
|
86
|
+
module = "abyssale._generated"
|
|
87
|
+
ignore_errors = true
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Official Python SDK for the Abyssale API — image, video and PDF generation.
|
|
2
|
+
|
|
3
|
+
from abyssale import Abyssale
|
|
4
|
+
|
|
5
|
+
with Abyssale() as client: # reads ABYSSALE_API_KEY
|
|
6
|
+
design = client.get_design("64238d01-d402-474b-8c2d-fbc957e9d290")
|
|
7
|
+
banner = client.generate_image(design.id, {
|
|
8
|
+
"elements": {"title": {"payload": "Hello World"}},
|
|
9
|
+
"template_format_name": "facebook-post",
|
|
10
|
+
})
|
|
11
|
+
print(banner.file.cdn_url)
|
|
12
|
+
|
|
13
|
+
Async is the same surface: ``from abyssale import AsyncAbyssale``.
|
|
14
|
+
|
|
15
|
+
Errors raise. Every failure is an :class:`AbyssaleError`; a non-2xx is an :class:`AbyssaleAPIError`
|
|
16
|
+
carrying the API's machine-readable ``id``, which is what you branch on.
|
|
17
|
+
|
|
18
|
+
Docs: https://developers.abyssale.com/sdks/python
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from ._async_client import AsyncAbyssale
|
|
22
|
+
from ._client import Abyssale
|
|
23
|
+
from ._errors import (
|
|
24
|
+
AbyssaleAPIError,
|
|
25
|
+
AbyssaleAuthError,
|
|
26
|
+
AbyssaleConfigError,
|
|
27
|
+
AbyssaleConnectionError,
|
|
28
|
+
AbyssaleError,
|
|
29
|
+
AbyssaleNotFoundError,
|
|
30
|
+
AbyssalePollingError,
|
|
31
|
+
AbyssaleRateLimitError,
|
|
32
|
+
)
|
|
33
|
+
from ._version import __api_version__, __version__
|
|
34
|
+
from .models import (
|
|
35
|
+
AuthResult,
|
|
36
|
+
Banner,
|
|
37
|
+
Design,
|
|
38
|
+
DesignAnimation,
|
|
39
|
+
DesignDetail,
|
|
40
|
+
DesignElement,
|
|
41
|
+
DesignFormat,
|
|
42
|
+
DesignFormatDetail,
|
|
43
|
+
DesignListItem,
|
|
44
|
+
DesignPage,
|
|
45
|
+
DesignPageElement,
|
|
46
|
+
DuplicatedDesign,
|
|
47
|
+
DuplicationRequest,
|
|
48
|
+
DuplicationRequestStatus,
|
|
49
|
+
DynamicImageResponse,
|
|
50
|
+
ErrorResponse,
|
|
51
|
+
ExportAccepted,
|
|
52
|
+
Font,
|
|
53
|
+
GenerationRequestAccepted,
|
|
54
|
+
GenerationRequestStatus,
|
|
55
|
+
Project,
|
|
56
|
+
ProjectSummary,
|
|
57
|
+
TextToImageProperties,
|
|
58
|
+
WorkspaceTemplate,
|
|
59
|
+
WorkspaceTemplateCategory,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
__all__ = [
|
|
63
|
+
"Abyssale",
|
|
64
|
+
"AsyncAbyssale",
|
|
65
|
+
"__version__",
|
|
66
|
+
"__api_version__",
|
|
67
|
+
# Errors
|
|
68
|
+
"AbyssaleAPIError",
|
|
69
|
+
"AbyssaleAuthError",
|
|
70
|
+
"AbyssaleConfigError",
|
|
71
|
+
"AbyssaleConnectionError",
|
|
72
|
+
"AbyssaleError",
|
|
73
|
+
"AbyssaleNotFoundError",
|
|
74
|
+
"AbyssalePollingError",
|
|
75
|
+
"AbyssaleRateLimitError",
|
|
76
|
+
# Models
|
|
77
|
+
"AuthResult",
|
|
78
|
+
"Banner",
|
|
79
|
+
"Design",
|
|
80
|
+
"DesignAnimation",
|
|
81
|
+
"DesignDetail",
|
|
82
|
+
"DesignElement",
|
|
83
|
+
"DesignFormat",
|
|
84
|
+
"DesignFormatDetail",
|
|
85
|
+
"DesignListItem",
|
|
86
|
+
"DesignPage",
|
|
87
|
+
"DesignPageElement",
|
|
88
|
+
"DuplicatedDesign",
|
|
89
|
+
"DuplicationRequest",
|
|
90
|
+
"DuplicationRequestStatus",
|
|
91
|
+
"DynamicImageResponse",
|
|
92
|
+
"ErrorResponse",
|
|
93
|
+
"ExportAccepted",
|
|
94
|
+
"Font",
|
|
95
|
+
"GenerationRequestAccepted",
|
|
96
|
+
"GenerationRequestStatus",
|
|
97
|
+
"Project",
|
|
98
|
+
"ProjectSummary",
|
|
99
|
+
"TextToImageProperties",
|
|
100
|
+
"WorkspaceTemplate",
|
|
101
|
+
"WorkspaceTemplateCategory",
|
|
102
|
+
]
|