poofbg 0.1.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.
- poofbg-0.1.0/.gitignore +48 -0
- poofbg-0.1.0/LICENSE +21 -0
- poofbg-0.1.0/PKG-INFO +232 -0
- poofbg-0.1.0/README.md +203 -0
- poofbg-0.1.0/pyproject.toml +81 -0
- poofbg-0.1.0/src/poof/__init__.py +364 -0
- poofbg-0.1.0/src/poof/exceptions.py +96 -0
- poofbg-0.1.0/src/poof/py.typed +0 -0
poofbg-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
lib/
|
|
15
|
+
lib64/
|
|
16
|
+
parts/
|
|
17
|
+
sdist/
|
|
18
|
+
var/
|
|
19
|
+
wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
.installed.cfg
|
|
22
|
+
*.egg
|
|
23
|
+
|
|
24
|
+
# Virtual environments
|
|
25
|
+
.venv/
|
|
26
|
+
venv/
|
|
27
|
+
ENV/
|
|
28
|
+
env/
|
|
29
|
+
|
|
30
|
+
# IDE
|
|
31
|
+
.idea/
|
|
32
|
+
.vscode/
|
|
33
|
+
*.swp
|
|
34
|
+
*.swo
|
|
35
|
+
*~
|
|
36
|
+
|
|
37
|
+
# Testing
|
|
38
|
+
.pytest_cache/
|
|
39
|
+
.coverage
|
|
40
|
+
htmlcov/
|
|
41
|
+
.tox/
|
|
42
|
+
.nox/
|
|
43
|
+
|
|
44
|
+
# mypy
|
|
45
|
+
.mypy_cache/
|
|
46
|
+
|
|
47
|
+
# ruff
|
|
48
|
+
.ruff_cache/
|
poofbg-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Poof
|
|
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.
|
poofbg-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: poofbg
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python SDK for the Poof background removal API
|
|
5
|
+
Project-URL: Homepage, https://poof.bg
|
|
6
|
+
Project-URL: Documentation, https://docs.poof.bg
|
|
7
|
+
Project-URL: Repository, https://github.com/poof-bg/py
|
|
8
|
+
Project-URL: Issues, https://github.com/poof-bg/py/issues
|
|
9
|
+
Author-email: Poof <support@poof.bg>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: api,background-removal,image-processing,poof,sdk
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Requires-Dist: httpx>=0.24.0
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# poofbg
|
|
31
|
+
|
|
32
|
+
The official Python SDK for the [Poof](https://poof.bg) background removal API.
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pip install poofbg
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from poof import Poof
|
|
44
|
+
|
|
45
|
+
# Initialize the client
|
|
46
|
+
client = Poof(api_key="your-api-key")
|
|
47
|
+
|
|
48
|
+
# Remove background from an image
|
|
49
|
+
result = client.remove_background("photo.jpg")
|
|
50
|
+
|
|
51
|
+
# Save the result
|
|
52
|
+
result.save("output.png")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Usage
|
|
56
|
+
|
|
57
|
+
### Remove Background
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from poof import Poof
|
|
61
|
+
|
|
62
|
+
client = Poof(api_key="your-api-key")
|
|
63
|
+
|
|
64
|
+
# From a file path
|
|
65
|
+
result = client.remove_background("image.jpg")
|
|
66
|
+
|
|
67
|
+
# From bytes
|
|
68
|
+
with open("image.jpg", "rb") as f:
|
|
69
|
+
image_bytes = f.read()
|
|
70
|
+
result = client.remove_background(image_bytes)
|
|
71
|
+
|
|
72
|
+
# From a file object
|
|
73
|
+
with open("image.jpg", "rb") as f:
|
|
74
|
+
result = client.remove_background(f)
|
|
75
|
+
|
|
76
|
+
# Save the result
|
|
77
|
+
result.save("output.png")
|
|
78
|
+
|
|
79
|
+
# Or get the bytes directly
|
|
80
|
+
image_data = result.data
|
|
81
|
+
# or: image_data = bytes(result)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Options
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
# Different output format
|
|
88
|
+
result = client.remove_background("photo.jpg", format="webp")
|
|
89
|
+
|
|
90
|
+
# Crop to subject bounds
|
|
91
|
+
result = client.remove_background("photo.jpg", crop=True)
|
|
92
|
+
|
|
93
|
+
# RGB output with background color (no transparency)
|
|
94
|
+
result = client.remove_background(
|
|
95
|
+
"photo.jpg",
|
|
96
|
+
channels="rgb",
|
|
97
|
+
bg_color="#ffffff"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
# Different size presets
|
|
101
|
+
result = client.remove_background("photo.jpg", size="preview")
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Result Metadata
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
result = client.remove_background("photo.jpg")
|
|
108
|
+
|
|
109
|
+
print(f"Image size: {result.width}x{result.height}")
|
|
110
|
+
print(f"Processing time: {result.processing_time_ms}ms")
|
|
111
|
+
print(f"Content type: {result.content_type}")
|
|
112
|
+
print(f"Request ID: {result.request_id}")
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Account Info
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
info = client.me()
|
|
119
|
+
|
|
120
|
+
print(f"Plan: {info['plan']}")
|
|
121
|
+
print(f"Credits used: {info['usedCredits']} / {info['maxCredits']}")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Context Manager
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
with Poof(api_key="your-api-key") as client:
|
|
128
|
+
result = client.remove_background("photo.jpg")
|
|
129
|
+
result.save("output.png")
|
|
130
|
+
# Client is automatically closed
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Error Handling
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from poof import (
|
|
137
|
+
Poof,
|
|
138
|
+
PoofError,
|
|
139
|
+
AuthError,
|
|
140
|
+
RateLimitError,
|
|
141
|
+
PaymentRequiredError,
|
|
142
|
+
ValidationError,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
client = Poof(api_key="your-api-key")
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
result = client.remove_background("photo.jpg")
|
|
149
|
+
except AuthError as e:
|
|
150
|
+
print(f"Authentication failed: {e.message}")
|
|
151
|
+
except PaymentRequiredError as e:
|
|
152
|
+
print(f"Insufficient credits: {e.message}")
|
|
153
|
+
except RateLimitError as e:
|
|
154
|
+
print(f"Rate limited: {e.message}")
|
|
155
|
+
except ValidationError as e:
|
|
156
|
+
print(f"Invalid request: {e.message}")
|
|
157
|
+
except PoofError as e:
|
|
158
|
+
print(f"API error: {e.message} (code: {e.code})")
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
All exceptions include:
|
|
162
|
+
- `message`: Human-readable error description
|
|
163
|
+
- `code`: Machine-readable error code
|
|
164
|
+
- `details`: Additional troubleshooting info
|
|
165
|
+
- `request_id`: Unique ID for support
|
|
166
|
+
- `status_code`: HTTP status code
|
|
167
|
+
|
|
168
|
+
## Configuration
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
# Custom timeout (default: 60 seconds)
|
|
172
|
+
client = Poof(api_key="...", timeout=120)
|
|
173
|
+
|
|
174
|
+
# Custom base URL
|
|
175
|
+
client = Poof(api_key="...", base_url="https://api.example.com/v1")
|
|
176
|
+
|
|
177
|
+
# Bring your own httpx client
|
|
178
|
+
import httpx
|
|
179
|
+
|
|
180
|
+
with httpx.Client(timeout=30, http2=True) as http_client:
|
|
181
|
+
client = Poof(api_key="...", httpx_client=http_client)
|
|
182
|
+
result = client.remove_background("photo.jpg")
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## API Reference
|
|
186
|
+
|
|
187
|
+
### `Poof(api_key, *, base_url, timeout, httpx_client)`
|
|
188
|
+
|
|
189
|
+
Create a new Poof client.
|
|
190
|
+
|
|
191
|
+
| Parameter | Type | Default | Description |
|
|
192
|
+
|-----------|------|---------|-------------|
|
|
193
|
+
| `api_key` | `str` | required | Your Poof API key |
|
|
194
|
+
| `base_url` | `str` | `https://api.poof.bg/v1` | API base URL |
|
|
195
|
+
| `timeout` | `float` | `60.0` | Request timeout in seconds |
|
|
196
|
+
| `httpx_client` | `httpx.Client` | `None` | Custom HTTP client |
|
|
197
|
+
|
|
198
|
+
### `client.remove_background(image, *, format, channels, bg_color, size, crop)`
|
|
199
|
+
|
|
200
|
+
Remove background from an image.
|
|
201
|
+
|
|
202
|
+
| Parameter | Type | Default | Description |
|
|
203
|
+
|-----------|------|---------|-------------|
|
|
204
|
+
| `image` | `str \| Path \| bytes \| BinaryIO` | required | Image to process |
|
|
205
|
+
| `format` | `"png" \| "jpg" \| "webp"` | `"png"` | Output format |
|
|
206
|
+
| `channels` | `"rgba" \| "rgb"` | `"rgba"` | Color channels |
|
|
207
|
+
| `bg_color` | `str` | `None` | Background color (when `channels="rgb"`) |
|
|
208
|
+
| `size` | `"full" \| "preview" \| "small" \| "medium" \| "large"` | `"full"` | Output size |
|
|
209
|
+
| `crop` | `bool` | `False` | Crop to subject bounds |
|
|
210
|
+
|
|
211
|
+
Returns a `RemoveBackgroundResult` with:
|
|
212
|
+
- `data`: Image bytes
|
|
213
|
+
- `content_type`: MIME type
|
|
214
|
+
- `width`, `height`: Image dimensions
|
|
215
|
+
- `processing_time_ms`: Processing time
|
|
216
|
+
- `request_id`: Request ID for support
|
|
217
|
+
- `save(path)`: Save to file
|
|
218
|
+
|
|
219
|
+
### `client.me()`
|
|
220
|
+
|
|
221
|
+
Get account information.
|
|
222
|
+
|
|
223
|
+
Returns an `AccountInfo` dict:
|
|
224
|
+
- `organizationId`: Organization ID
|
|
225
|
+
- `plan`: Plan name
|
|
226
|
+
- `maxCredits`: Total credits
|
|
227
|
+
- `usedCredits`: Used credits
|
|
228
|
+
- `autoRechargeThreshold`: Auto-recharge threshold (if enabled)
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT
|
poofbg-0.1.0/README.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# poofbg
|
|
2
|
+
|
|
3
|
+
The official Python SDK for the [Poof](https://poof.bg) background removal API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install poofbg
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from poof import Poof
|
|
15
|
+
|
|
16
|
+
# Initialize the client
|
|
17
|
+
client = Poof(api_key="your-api-key")
|
|
18
|
+
|
|
19
|
+
# Remove background from an image
|
|
20
|
+
result = client.remove_background("photo.jpg")
|
|
21
|
+
|
|
22
|
+
# Save the result
|
|
23
|
+
result.save("output.png")
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Remove Background
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from poof import Poof
|
|
32
|
+
|
|
33
|
+
client = Poof(api_key="your-api-key")
|
|
34
|
+
|
|
35
|
+
# From a file path
|
|
36
|
+
result = client.remove_background("image.jpg")
|
|
37
|
+
|
|
38
|
+
# From bytes
|
|
39
|
+
with open("image.jpg", "rb") as f:
|
|
40
|
+
image_bytes = f.read()
|
|
41
|
+
result = client.remove_background(image_bytes)
|
|
42
|
+
|
|
43
|
+
# From a file object
|
|
44
|
+
with open("image.jpg", "rb") as f:
|
|
45
|
+
result = client.remove_background(f)
|
|
46
|
+
|
|
47
|
+
# Save the result
|
|
48
|
+
result.save("output.png")
|
|
49
|
+
|
|
50
|
+
# Or get the bytes directly
|
|
51
|
+
image_data = result.data
|
|
52
|
+
# or: image_data = bytes(result)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Options
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
# Different output format
|
|
59
|
+
result = client.remove_background("photo.jpg", format="webp")
|
|
60
|
+
|
|
61
|
+
# Crop to subject bounds
|
|
62
|
+
result = client.remove_background("photo.jpg", crop=True)
|
|
63
|
+
|
|
64
|
+
# RGB output with background color (no transparency)
|
|
65
|
+
result = client.remove_background(
|
|
66
|
+
"photo.jpg",
|
|
67
|
+
channels="rgb",
|
|
68
|
+
bg_color="#ffffff"
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Different size presets
|
|
72
|
+
result = client.remove_background("photo.jpg", size="preview")
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Result Metadata
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
result = client.remove_background("photo.jpg")
|
|
79
|
+
|
|
80
|
+
print(f"Image size: {result.width}x{result.height}")
|
|
81
|
+
print(f"Processing time: {result.processing_time_ms}ms")
|
|
82
|
+
print(f"Content type: {result.content_type}")
|
|
83
|
+
print(f"Request ID: {result.request_id}")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Account Info
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
info = client.me()
|
|
90
|
+
|
|
91
|
+
print(f"Plan: {info['plan']}")
|
|
92
|
+
print(f"Credits used: {info['usedCredits']} / {info['maxCredits']}")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Context Manager
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
with Poof(api_key="your-api-key") as client:
|
|
99
|
+
result = client.remove_background("photo.jpg")
|
|
100
|
+
result.save("output.png")
|
|
101
|
+
# Client is automatically closed
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Error Handling
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from poof import (
|
|
108
|
+
Poof,
|
|
109
|
+
PoofError,
|
|
110
|
+
AuthError,
|
|
111
|
+
RateLimitError,
|
|
112
|
+
PaymentRequiredError,
|
|
113
|
+
ValidationError,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
client = Poof(api_key="your-api-key")
|
|
117
|
+
|
|
118
|
+
try:
|
|
119
|
+
result = client.remove_background("photo.jpg")
|
|
120
|
+
except AuthError as e:
|
|
121
|
+
print(f"Authentication failed: {e.message}")
|
|
122
|
+
except PaymentRequiredError as e:
|
|
123
|
+
print(f"Insufficient credits: {e.message}")
|
|
124
|
+
except RateLimitError as e:
|
|
125
|
+
print(f"Rate limited: {e.message}")
|
|
126
|
+
except ValidationError as e:
|
|
127
|
+
print(f"Invalid request: {e.message}")
|
|
128
|
+
except PoofError as e:
|
|
129
|
+
print(f"API error: {e.message} (code: {e.code})")
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
All exceptions include:
|
|
133
|
+
- `message`: Human-readable error description
|
|
134
|
+
- `code`: Machine-readable error code
|
|
135
|
+
- `details`: Additional troubleshooting info
|
|
136
|
+
- `request_id`: Unique ID for support
|
|
137
|
+
- `status_code`: HTTP status code
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
# Custom timeout (default: 60 seconds)
|
|
143
|
+
client = Poof(api_key="...", timeout=120)
|
|
144
|
+
|
|
145
|
+
# Custom base URL
|
|
146
|
+
client = Poof(api_key="...", base_url="https://api.example.com/v1")
|
|
147
|
+
|
|
148
|
+
# Bring your own httpx client
|
|
149
|
+
import httpx
|
|
150
|
+
|
|
151
|
+
with httpx.Client(timeout=30, http2=True) as http_client:
|
|
152
|
+
client = Poof(api_key="...", httpx_client=http_client)
|
|
153
|
+
result = client.remove_background("photo.jpg")
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## API Reference
|
|
157
|
+
|
|
158
|
+
### `Poof(api_key, *, base_url, timeout, httpx_client)`
|
|
159
|
+
|
|
160
|
+
Create a new Poof client.
|
|
161
|
+
|
|
162
|
+
| Parameter | Type | Default | Description |
|
|
163
|
+
|-----------|------|---------|-------------|
|
|
164
|
+
| `api_key` | `str` | required | Your Poof API key |
|
|
165
|
+
| `base_url` | `str` | `https://api.poof.bg/v1` | API base URL |
|
|
166
|
+
| `timeout` | `float` | `60.0` | Request timeout in seconds |
|
|
167
|
+
| `httpx_client` | `httpx.Client` | `None` | Custom HTTP client |
|
|
168
|
+
|
|
169
|
+
### `client.remove_background(image, *, format, channels, bg_color, size, crop)`
|
|
170
|
+
|
|
171
|
+
Remove background from an image.
|
|
172
|
+
|
|
173
|
+
| Parameter | Type | Default | Description |
|
|
174
|
+
|-----------|------|---------|-------------|
|
|
175
|
+
| `image` | `str \| Path \| bytes \| BinaryIO` | required | Image to process |
|
|
176
|
+
| `format` | `"png" \| "jpg" \| "webp"` | `"png"` | Output format |
|
|
177
|
+
| `channels` | `"rgba" \| "rgb"` | `"rgba"` | Color channels |
|
|
178
|
+
| `bg_color` | `str` | `None` | Background color (when `channels="rgb"`) |
|
|
179
|
+
| `size` | `"full" \| "preview" \| "small" \| "medium" \| "large"` | `"full"` | Output size |
|
|
180
|
+
| `crop` | `bool` | `False` | Crop to subject bounds |
|
|
181
|
+
|
|
182
|
+
Returns a `RemoveBackgroundResult` with:
|
|
183
|
+
- `data`: Image bytes
|
|
184
|
+
- `content_type`: MIME type
|
|
185
|
+
- `width`, `height`: Image dimensions
|
|
186
|
+
- `processing_time_ms`: Processing time
|
|
187
|
+
- `request_id`: Request ID for support
|
|
188
|
+
- `save(path)`: Save to file
|
|
189
|
+
|
|
190
|
+
### `client.me()`
|
|
191
|
+
|
|
192
|
+
Get account information.
|
|
193
|
+
|
|
194
|
+
Returns an `AccountInfo` dict:
|
|
195
|
+
- `organizationId`: Organization ID
|
|
196
|
+
- `plan`: Plan name
|
|
197
|
+
- `maxCredits`: Total credits
|
|
198
|
+
- `usedCredits`: Used credits
|
|
199
|
+
- `autoRechargeThreshold`: Auto-recharge threshold (if enabled)
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
MIT
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "poofbg"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python SDK for the Poof background removal API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Poof", email = "support@poof.bg" }
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"background-removal",
|
|
17
|
+
"image-processing",
|
|
18
|
+
"poof",
|
|
19
|
+
"api",
|
|
20
|
+
"sdk",
|
|
21
|
+
]
|
|
22
|
+
classifiers = [
|
|
23
|
+
"Development Status :: 4 - Beta",
|
|
24
|
+
"Intended Audience :: Developers",
|
|
25
|
+
"License :: OSI Approved :: MIT License",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.9",
|
|
29
|
+
"Programming Language :: Python :: 3.10",
|
|
30
|
+
"Programming Language :: Python :: 3.11",
|
|
31
|
+
"Programming Language :: Python :: 3.12",
|
|
32
|
+
"Programming Language :: Python :: 3.13",
|
|
33
|
+
"Topic :: Multimedia :: Graphics",
|
|
34
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
35
|
+
"Typing :: Typed",
|
|
36
|
+
]
|
|
37
|
+
dependencies = [
|
|
38
|
+
"httpx>=0.24.0",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[project.urls]
|
|
42
|
+
Homepage = "https://poof.bg"
|
|
43
|
+
Documentation = "https://docs.poof.bg"
|
|
44
|
+
Repository = "https://github.com/poof-bg/py"
|
|
45
|
+
Issues = "https://github.com/poof-bg/py/issues"
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.wheel]
|
|
48
|
+
packages = ["src/poof"]
|
|
49
|
+
|
|
50
|
+
[tool.hatch.build.targets.sdist]
|
|
51
|
+
include = [
|
|
52
|
+
"/src",
|
|
53
|
+
"/README.md",
|
|
54
|
+
"/LICENSE",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
[tool.ruff]
|
|
58
|
+
target-version = "py39"
|
|
59
|
+
line-length = 88
|
|
60
|
+
|
|
61
|
+
[tool.ruff.lint]
|
|
62
|
+
select = [
|
|
63
|
+
"E", # pycodestyle errors
|
|
64
|
+
"W", # pycodestyle warnings
|
|
65
|
+
"F", # pyflakes
|
|
66
|
+
"I", # isort
|
|
67
|
+
"B", # flake8-bugbear
|
|
68
|
+
"UP", # pyupgrade
|
|
69
|
+
]
|
|
70
|
+
ignore = [
|
|
71
|
+
"E501", # line too long (handled by formatter)
|
|
72
|
+
]
|
|
73
|
+
|
|
74
|
+
[tool.mypy]
|
|
75
|
+
python_version = "3.9"
|
|
76
|
+
strict = true
|
|
77
|
+
warn_return_any = true
|
|
78
|
+
warn_unused_configs = true
|
|
79
|
+
|
|
80
|
+
[tool.pytest.ini_options]
|
|
81
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Poof - Python SDK for the Poof background removal API.
|
|
3
|
+
|
|
4
|
+
Simple, clean API for removing backgrounds from images.
|
|
5
|
+
|
|
6
|
+
Example:
|
|
7
|
+
from poof import Poof
|
|
8
|
+
|
|
9
|
+
client = Poof(api_key="your-api-key")
|
|
10
|
+
result = client.remove_background("image.jpg")
|
|
11
|
+
|
|
12
|
+
# Save the result
|
|
13
|
+
with open("output.png", "wb") as f:
|
|
14
|
+
f.write(result)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import mimetypes
|
|
20
|
+
import os
|
|
21
|
+
from io import IOBase
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
from typing import BinaryIO, Literal, TypedDict, Union
|
|
24
|
+
|
|
25
|
+
import httpx
|
|
26
|
+
|
|
27
|
+
from .exceptions import (
|
|
28
|
+
AuthError,
|
|
29
|
+
PaymentRequiredError,
|
|
30
|
+
PermissionDeniedError,
|
|
31
|
+
PoofError,
|
|
32
|
+
RateLimitError,
|
|
33
|
+
ServerError,
|
|
34
|
+
ValidationError,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
__version__ = "0.1.0"
|
|
38
|
+
__all__ = [
|
|
39
|
+
"Poof",
|
|
40
|
+
"PoofError",
|
|
41
|
+
"AuthError",
|
|
42
|
+
"RateLimitError",
|
|
43
|
+
"PaymentRequiredError",
|
|
44
|
+
"PermissionDeniedError",
|
|
45
|
+
"ValidationError",
|
|
46
|
+
"ServerError",
|
|
47
|
+
"AccountInfo",
|
|
48
|
+
"RemoveBackgroundResult",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
DEFAULT_BASE_URL = "https://api.poof.bg/v1"
|
|
52
|
+
DEFAULT_TIMEOUT = 60.0
|
|
53
|
+
|
|
54
|
+
# Type aliases
|
|
55
|
+
ImageInput = Union[str, Path, bytes, BinaryIO]
|
|
56
|
+
Format = Literal["png", "jpg", "webp"]
|
|
57
|
+
Channels = Literal["rgba", "rgb"]
|
|
58
|
+
Size = Literal["full", "preview", "small", "medium", "large"]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class AccountInfo(TypedDict, total=False):
|
|
62
|
+
"""Account information returned by the /me endpoint."""
|
|
63
|
+
|
|
64
|
+
organizationId: str
|
|
65
|
+
plan: str
|
|
66
|
+
maxCredits: int
|
|
67
|
+
usedCredits: int
|
|
68
|
+
autoRechargeThreshold: int | None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class RemoveBackgroundResult:
|
|
72
|
+
"""Result of a background removal operation.
|
|
73
|
+
|
|
74
|
+
Attributes:
|
|
75
|
+
data: The processed image bytes.
|
|
76
|
+
content_type: MIME type of the image (e.g., "image/png").
|
|
77
|
+
request_id: Unique request identifier for support.
|
|
78
|
+
processing_time_ms: Processing time in milliseconds.
|
|
79
|
+
width: Output image width in pixels.
|
|
80
|
+
height: Output image height in pixels.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
__slots__ = (
|
|
84
|
+
"data",
|
|
85
|
+
"content_type",
|
|
86
|
+
"request_id",
|
|
87
|
+
"processing_time_ms",
|
|
88
|
+
"width",
|
|
89
|
+
"height",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
def __init__(
|
|
93
|
+
self,
|
|
94
|
+
data: bytes,
|
|
95
|
+
content_type: str | None = None,
|
|
96
|
+
request_id: str | None = None,
|
|
97
|
+
processing_time_ms: int | None = None,
|
|
98
|
+
width: int | None = None,
|
|
99
|
+
height: int | None = None,
|
|
100
|
+
) -> None:
|
|
101
|
+
self.data = data
|
|
102
|
+
self.content_type = content_type
|
|
103
|
+
self.request_id = request_id
|
|
104
|
+
self.processing_time_ms = processing_time_ms
|
|
105
|
+
self.width = width
|
|
106
|
+
self.height = height
|
|
107
|
+
|
|
108
|
+
def __bytes__(self) -> bytes:
|
|
109
|
+
"""Return the image data as bytes."""
|
|
110
|
+
return self.data
|
|
111
|
+
|
|
112
|
+
def __len__(self) -> int:
|
|
113
|
+
"""Return the size of the image data in bytes."""
|
|
114
|
+
return len(self.data)
|
|
115
|
+
|
|
116
|
+
def save(self, path: str | Path) -> None:
|
|
117
|
+
"""Save the image to a file.
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
path: Destination file path.
|
|
121
|
+
"""
|
|
122
|
+
Path(path).write_bytes(self.data)
|
|
123
|
+
|
|
124
|
+
def __repr__(self) -> str:
|
|
125
|
+
return (
|
|
126
|
+
f"RemoveBackgroundResult("
|
|
127
|
+
f"size={len(self.data)}, "
|
|
128
|
+
f"content_type={self.content_type!r}, "
|
|
129
|
+
f"dimensions={self.width}x{self.height})"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class Poof:
|
|
134
|
+
"""Client for the Poof background removal API.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
api_key: Your Poof API key. Get one at https://dash.poof.bg
|
|
138
|
+
base_url: API base URL. Defaults to https://api.poof.bg/v1
|
|
139
|
+
timeout: Request timeout in seconds. Defaults to 60.
|
|
140
|
+
httpx_client: Optional custom httpx.Client instance.
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
>>> from poof import Poof
|
|
144
|
+
>>> client = Poof(api_key="your-api-key")
|
|
145
|
+
>>> result = client.remove_background("photo.jpg")
|
|
146
|
+
>>> result.save("output.png")
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def __init__(
|
|
150
|
+
self,
|
|
151
|
+
api_key: str,
|
|
152
|
+
*,
|
|
153
|
+
base_url: str = DEFAULT_BASE_URL,
|
|
154
|
+
timeout: float = DEFAULT_TIMEOUT,
|
|
155
|
+
httpx_client: httpx.Client | None = None,
|
|
156
|
+
) -> None:
|
|
157
|
+
if not api_key:
|
|
158
|
+
raise ValueError("api_key is required")
|
|
159
|
+
|
|
160
|
+
self._api_key = api_key
|
|
161
|
+
self._base_url = base_url.rstrip("/")
|
|
162
|
+
self._timeout = timeout
|
|
163
|
+
self._owns_client = httpx_client is None
|
|
164
|
+
self._client = httpx_client or httpx.Client(timeout=timeout)
|
|
165
|
+
|
|
166
|
+
def __enter__(self) -> Poof:
|
|
167
|
+
return self
|
|
168
|
+
|
|
169
|
+
def __exit__(self, *args: object) -> None:
|
|
170
|
+
self.close()
|
|
171
|
+
|
|
172
|
+
def close(self) -> None:
|
|
173
|
+
"""Close the underlying HTTP client."""
|
|
174
|
+
if self._owns_client:
|
|
175
|
+
self._client.close()
|
|
176
|
+
|
|
177
|
+
def _get_headers(self) -> dict[str, str]:
|
|
178
|
+
"""Get default headers for API requests."""
|
|
179
|
+
return {
|
|
180
|
+
"x-api-key": self._api_key,
|
|
181
|
+
"User-Agent": f"poofbg/{__version__}",
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
def _handle_error(self, response: httpx.Response) -> None:
|
|
185
|
+
"""Handle error responses from the API."""
|
|
186
|
+
try:
|
|
187
|
+
error_data = response.json()
|
|
188
|
+
code = error_data.get("code", "unknown_error")
|
|
189
|
+
message = error_data.get("message", "Unknown error")
|
|
190
|
+
details = error_data.get("details")
|
|
191
|
+
request_id = error_data.get("request_id")
|
|
192
|
+
except Exception:
|
|
193
|
+
code = "unknown_error"
|
|
194
|
+
message = response.text or f"HTTP {response.status_code}"
|
|
195
|
+
details = None
|
|
196
|
+
request_id = response.headers.get("X-Request-ID")
|
|
197
|
+
|
|
198
|
+
error_kwargs = {
|
|
199
|
+
"message": message,
|
|
200
|
+
"code": code,
|
|
201
|
+
"details": details,
|
|
202
|
+
"request_id": request_id,
|
|
203
|
+
"status_code": response.status_code,
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if response.status_code == 401:
|
|
207
|
+
raise AuthError(**error_kwargs)
|
|
208
|
+
elif response.status_code == 402:
|
|
209
|
+
raise PaymentRequiredError(**error_kwargs)
|
|
210
|
+
elif response.status_code == 403:
|
|
211
|
+
raise PermissionDeniedError(**error_kwargs)
|
|
212
|
+
elif response.status_code == 429:
|
|
213
|
+
raise RateLimitError(**error_kwargs)
|
|
214
|
+
elif response.status_code == 400:
|
|
215
|
+
raise ValidationError(**error_kwargs)
|
|
216
|
+
elif response.status_code >= 500:
|
|
217
|
+
raise ServerError(**error_kwargs)
|
|
218
|
+
else:
|
|
219
|
+
raise PoofError(**error_kwargs)
|
|
220
|
+
|
|
221
|
+
def _prepare_image(
|
|
222
|
+
self, image: ImageInput
|
|
223
|
+
) -> tuple[str, bytes, str]:
|
|
224
|
+
"""Prepare image for upload.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Tuple of (filename, data, content_type)
|
|
228
|
+
"""
|
|
229
|
+
if isinstance(image, (str, Path)):
|
|
230
|
+
path = Path(image)
|
|
231
|
+
if not path.exists():
|
|
232
|
+
raise FileNotFoundError(f"Image file not found: {path}")
|
|
233
|
+
filename = path.name
|
|
234
|
+
data = path.read_bytes()
|
|
235
|
+
content_type = mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
|
236
|
+
elif isinstance(image, bytes):
|
|
237
|
+
filename = "image"
|
|
238
|
+
data = image
|
|
239
|
+
content_type = "application/octet-stream"
|
|
240
|
+
elif isinstance(image, IOBase) or hasattr(image, "read"):
|
|
241
|
+
# File-like object
|
|
242
|
+
data = image.read() # type: ignore
|
|
243
|
+
filename = getattr(image, "name", "image")
|
|
244
|
+
if isinstance(filename, (str, Path)):
|
|
245
|
+
filename = Path(filename).name
|
|
246
|
+
else:
|
|
247
|
+
filename = "image"
|
|
248
|
+
content_type = mimetypes.guess_type(filename)[0] or "application/octet-stream"
|
|
249
|
+
else:
|
|
250
|
+
raise TypeError(
|
|
251
|
+
f"image must be a file path, bytes, or file-like object, "
|
|
252
|
+
f"got {type(image).__name__}"
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
return filename, data, content_type
|
|
256
|
+
|
|
257
|
+
def remove_background(
|
|
258
|
+
self,
|
|
259
|
+
image: ImageInput,
|
|
260
|
+
*,
|
|
261
|
+
format: Format | None = None,
|
|
262
|
+
channels: Channels | None = None,
|
|
263
|
+
bg_color: str | None = None,
|
|
264
|
+
size: Size | None = None,
|
|
265
|
+
crop: bool | None = None,
|
|
266
|
+
) -> RemoveBackgroundResult:
|
|
267
|
+
"""Remove background from an image.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
image: Image to process. Can be a file path (str or Path),
|
|
271
|
+
bytes, or a file-like object.
|
|
272
|
+
format: Output format - "png", "jpg", or "webp". Defaults to "png".
|
|
273
|
+
channels: Color channels - "rgba" for transparency, "rgb" for opaque.
|
|
274
|
+
Defaults to "rgba".
|
|
275
|
+
bg_color: Background color when channels="rgb". Can be hex (#ffffff),
|
|
276
|
+
rgb, or color name.
|
|
277
|
+
size: Output size preset - "full", "preview", "small", "medium", "large".
|
|
278
|
+
Defaults to "full".
|
|
279
|
+
crop: Whether to crop the image to the subject bounds.
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
RemoveBackgroundResult containing the processed image and metadata.
|
|
283
|
+
|
|
284
|
+
Raises:
|
|
285
|
+
FileNotFoundError: If image path doesn't exist.
|
|
286
|
+
AuthError: Invalid or missing API key.
|
|
287
|
+
PaymentRequiredError: Insufficient credits.
|
|
288
|
+
RateLimitError: Rate limit exceeded.
|
|
289
|
+
ValidationError: Invalid parameters or image.
|
|
290
|
+
ServerError: Server-side error.
|
|
291
|
+
PoofError: Other API errors.
|
|
292
|
+
|
|
293
|
+
Example:
|
|
294
|
+
>>> result = client.remove_background("photo.jpg", format="webp", crop=True)
|
|
295
|
+
>>> result.save("output.webp")
|
|
296
|
+
"""
|
|
297
|
+
filename, data, content_type = self._prepare_image(image)
|
|
298
|
+
|
|
299
|
+
# Build multipart form data
|
|
300
|
+
files = {"image_file": (filename, data, content_type)}
|
|
301
|
+
form_data: dict[str, str] = {}
|
|
302
|
+
|
|
303
|
+
if format is not None:
|
|
304
|
+
form_data["format"] = format
|
|
305
|
+
if channels is not None:
|
|
306
|
+
form_data["channels"] = channels
|
|
307
|
+
if bg_color is not None:
|
|
308
|
+
form_data["bg_color"] = bg_color
|
|
309
|
+
if size is not None:
|
|
310
|
+
form_data["size"] = size
|
|
311
|
+
if crop is not None:
|
|
312
|
+
form_data["crop"] = str(crop).lower()
|
|
313
|
+
|
|
314
|
+
response = self._client.post(
|
|
315
|
+
f"{self._base_url}/remove",
|
|
316
|
+
headers=self._get_headers(),
|
|
317
|
+
files=files,
|
|
318
|
+
data=form_data if form_data else None,
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
if response.status_code != 200:
|
|
322
|
+
self._handle_error(response)
|
|
323
|
+
|
|
324
|
+
# Extract metadata from headers
|
|
325
|
+
headers = response.headers
|
|
326
|
+
processing_time = headers.get("X-Processing-Time-Ms")
|
|
327
|
+
width = headers.get("X-Image-Width")
|
|
328
|
+
height = headers.get("X-Image-Height")
|
|
329
|
+
|
|
330
|
+
return RemoveBackgroundResult(
|
|
331
|
+
data=response.content,
|
|
332
|
+
content_type=headers.get("Content-Type"),
|
|
333
|
+
request_id=headers.get("X-Request-ID"),
|
|
334
|
+
processing_time_ms=int(processing_time) if processing_time else None,
|
|
335
|
+
width=int(width) if width else None,
|
|
336
|
+
height=int(height) if height else None,
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
def me(self) -> AccountInfo:
|
|
340
|
+
"""Get account information.
|
|
341
|
+
|
|
342
|
+
Returns information about the authenticated account including
|
|
343
|
+
plan details and credit usage.
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
AccountInfo dict with organization details and credit usage.
|
|
347
|
+
|
|
348
|
+
Raises:
|
|
349
|
+
AuthError: Invalid or missing API key.
|
|
350
|
+
PoofError: Other API errors.
|
|
351
|
+
|
|
352
|
+
Example:
|
|
353
|
+
>>> info = client.me()
|
|
354
|
+
>>> print(f"Used {info['usedCredits']} of {info['maxCredits']} credits")
|
|
355
|
+
"""
|
|
356
|
+
response = self._client.get(
|
|
357
|
+
f"{self._base_url}/me",
|
|
358
|
+
headers=self._get_headers(),
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
if response.status_code != 200:
|
|
362
|
+
self._handle_error(response)
|
|
363
|
+
|
|
364
|
+
return response.json()
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Custom exceptions for the Poof SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PoofError(Exception):
|
|
7
|
+
"""Base exception for all Poof API errors.
|
|
8
|
+
|
|
9
|
+
Attributes:
|
|
10
|
+
message: Human-readable error message.
|
|
11
|
+
code: Machine-readable error code.
|
|
12
|
+
details: Additional details or troubleshooting steps.
|
|
13
|
+
request_id: Unique request ID for support.
|
|
14
|
+
status_code: HTTP status code.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(
|
|
18
|
+
self,
|
|
19
|
+
message: str,
|
|
20
|
+
*,
|
|
21
|
+
code: str | None = None,
|
|
22
|
+
details: str | None = None,
|
|
23
|
+
request_id: str | None = None,
|
|
24
|
+
status_code: int | None = None,
|
|
25
|
+
) -> None:
|
|
26
|
+
self.message = message
|
|
27
|
+
self.code = code
|
|
28
|
+
self.details = details
|
|
29
|
+
self.request_id = request_id
|
|
30
|
+
self.status_code = status_code
|
|
31
|
+
super().__init__(message)
|
|
32
|
+
|
|
33
|
+
def __repr__(self) -> str:
|
|
34
|
+
parts = [f"PoofError({self.message!r}"]
|
|
35
|
+
if self.code:
|
|
36
|
+
parts.append(f", code={self.code!r}")
|
|
37
|
+
if self.status_code:
|
|
38
|
+
parts.append(f", status_code={self.status_code}")
|
|
39
|
+
if self.request_id:
|
|
40
|
+
parts.append(f", request_id={self.request_id!r}")
|
|
41
|
+
parts.append(")")
|
|
42
|
+
return "".join(parts)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class AuthError(PoofError):
|
|
46
|
+
"""Raised when authentication fails (401).
|
|
47
|
+
|
|
48
|
+
This typically means the API key is invalid, missing, or revoked.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class PaymentRequiredError(PoofError):
|
|
55
|
+
"""Raised when the account has insufficient credits (402).
|
|
56
|
+
|
|
57
|
+
Upgrade your plan or wait for the next billing cycle.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class PermissionDeniedError(PoofError):
|
|
64
|
+
"""Raised when access is forbidden (403).
|
|
65
|
+
|
|
66
|
+
The account may be suspended or the API key lacks permissions.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class RateLimitError(PoofError):
|
|
73
|
+
"""Raised when rate limit is exceeded (429).
|
|
74
|
+
|
|
75
|
+
Implement exponential backoff and retry.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ValidationError(PoofError):
|
|
82
|
+
"""Raised when request validation fails (400).
|
|
83
|
+
|
|
84
|
+
Check the parameters and image format.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ServerError(PoofError):
|
|
91
|
+
"""Raised when a server-side error occurs (5xx).
|
|
92
|
+
|
|
93
|
+
Please try again later.
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
pass
|
|
File without changes
|