smallpict 0.0.1__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.
- smallpict-0.0.1/.github/workflows/ci.yml +36 -0
- smallpict-0.0.1/.github/workflows/release.yml +29 -0
- smallpict-0.0.1/.gitignore +31 -0
- smallpict-0.0.1/CHANGELOG.md +21 -0
- smallpict-0.0.1/LICENSE +21 -0
- smallpict-0.0.1/PKG-INFO +202 -0
- smallpict-0.0.1/README.md +166 -0
- smallpict-0.0.1/SECURITY.md +5 -0
- smallpict-0.0.1/pyproject.toml +67 -0
- smallpict-0.0.1/smallpict/__init__.py +61 -0
- smallpict-0.0.1/smallpict/aclient.py +316 -0
- smallpict-0.0.1/smallpict/client.py +326 -0
- smallpict-0.0.1/smallpict/crypto.py +27 -0
- smallpict-0.0.1/smallpict/errors.py +208 -0
- smallpict-0.0.1/smallpict/models.py +91 -0
- smallpict-0.0.1/smallpict/pil_adapter.py +52 -0
- smallpict-0.0.1/smallpict/py.typed +1 -0
- smallpict-0.0.1/tests/test_async_client.py +101 -0
- smallpict-0.0.1/tests/test_crypto.py +32 -0
- smallpict-0.0.1/tests/test_errors.py +61 -0
- smallpict-0.0.1/tests/test_pil_adapter.py +19 -0
- smallpict-0.0.1/tests/test_sync_client.py +128 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test & Lint (Python ${{ matrix.python-version }})
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
python -m pip install --upgrade pip
|
|
27
|
+
pip install -e ".[dev,pil]"
|
|
28
|
+
|
|
29
|
+
- name: Lint with Ruff
|
|
30
|
+
run: ruff check .
|
|
31
|
+
|
|
32
|
+
- name: Type check with MyPy
|
|
33
|
+
run: mypy smallpict
|
|
34
|
+
|
|
35
|
+
- name: Run Pytest
|
|
36
|
+
run: pytest -v
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
publish:
|
|
11
|
+
name: 📦 Publish to PyPI (OIDC)
|
|
12
|
+
environment: release
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.11"
|
|
22
|
+
- name: Install build tools
|
|
23
|
+
run: pip install hatch
|
|
24
|
+
- name: Build wheel and source dist
|
|
25
|
+
run: hatch build
|
|
26
|
+
- name: Publish to PyPI
|
|
27
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
28
|
+
with:
|
|
29
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.so
|
|
5
|
+
.Python
|
|
6
|
+
build/
|
|
7
|
+
develop-eggs/
|
|
8
|
+
dist/
|
|
9
|
+
downloads/
|
|
10
|
+
eggs/
|
|
11
|
+
.eggs/
|
|
12
|
+
lib/
|
|
13
|
+
lib64/
|
|
14
|
+
parts/
|
|
15
|
+
sdist/
|
|
16
|
+
var/
|
|
17
|
+
wheels/
|
|
18
|
+
*.egg-info/
|
|
19
|
+
.installed.cfg
|
|
20
|
+
*.egg
|
|
21
|
+
.env
|
|
22
|
+
.venv
|
|
23
|
+
env/
|
|
24
|
+
venv/
|
|
25
|
+
ENV/
|
|
26
|
+
.pytest_cache/
|
|
27
|
+
.coverage
|
|
28
|
+
htmlcov/
|
|
29
|
+
.mypy_cache/
|
|
30
|
+
.ruff_cache/
|
|
31
|
+
.DS_Store
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `smallpict` Python SDK will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2026-08-22
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Official Python SDK implementation for SmallPict OpenAPI 3.1.0 API.
|
|
12
|
+
- First-class `SmallPictClient` (sync) and `AsyncSmallPictClient` (async) clients powered by `httpx`.
|
|
13
|
+
- Context manager support (`with` and `async with`).
|
|
14
|
+
- Pydantic models for request options and response payloads.
|
|
15
|
+
- 4 unified core client methods: `optimize()`, `get_quota()`, `purge_cdn()`, and `validate_key()`.
|
|
16
|
+
- Helper `get_job_status()` for polling async conversion tasks.
|
|
17
|
+
- Optional Pillow / PIL integration (`smallpict[pil]`) supporting direct `PIL.Image` input.
|
|
18
|
+
- Automatic secret masking to ensure API keys and signatures never leak in exception strings.
|
|
19
|
+
- Resilient HTTP transport with 30s timeouts, exponential backoff, jitter, and automatic `Idempotency-Key` UUID injection.
|
|
20
|
+
- Optional `fallback_mode=FallbackMode.PASSTHROUGH` for high availability on quota limits.
|
|
21
|
+
- Full type annotations (PEP 561 `py.typed`) and strict `mypy` compatibility.
|
smallpict-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SmallPict Engineering <support@smallpict.app>
|
|
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.
|
smallpict-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: smallpict
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Official Python SDK for SmallPict Image Optimization API
|
|
5
|
+
Project-URL: Homepage, https://smallpict.app
|
|
6
|
+
Project-URL: Repository, https://github.com/tuxnoob/smallpict-python
|
|
7
|
+
Project-URL: Documentation, https://smallpict.app/docs/sdks/python
|
|
8
|
+
Project-URL: Changelog, https://github.com/tuxnoob/smallpict-python/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: SmallPict Engineering <support@smallpict.app>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.8
|
|
25
|
+
Requires-Dist: httpx>=0.24.0
|
|
26
|
+
Requires-Dist: pydantic>=2.0.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: respx>=0.21.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
33
|
+
Provides-Extra: pil
|
|
34
|
+
Requires-Dist: pillow>=9.0.0; extra == 'pil'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# SmallPict Python SDK
|
|
38
|
+
|
|
39
|
+
Official Python client for the [SmallPict Image Optimization API](https://smallpict.app) — high-performance next-gen image transcoding (AVIF, WebP), smart compression, Edge CDN delivery, and cache purging.
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/smallpict/)
|
|
42
|
+
[](https://pypi.org/project/smallpict/)
|
|
43
|
+
[](https://opensource.org/licenses/MIT)
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## ⚡ Features
|
|
48
|
+
|
|
49
|
+
- **🚀 Synchronous & Asynchronous:** First-class sync (`SmallPictClient`) and async (`AsyncSmallPictClient`) clients powered by `httpx`.
|
|
50
|
+
- **🖼️ Optional Pillow / PIL Support:** Optimize `PIL.Image` instances directly via `pip install smallpict[pil]`.
|
|
51
|
+
- **🛡️ Secure HMAC-SHA256 & Bearer Auth:** Tamper-proof payload verification.
|
|
52
|
+
- **✨ 4 Core Unified Methods:** `optimize()`, `get_quota()`, `purge_cdn()`, and `validate_key()`.
|
|
53
|
+
- **🔄 Resilience & Fault Tolerance:** Automatic `Idempotency-Key` UUID injection, 30s timeouts, and exponential backoff with jitter on HTTP 429/5xx.
|
|
54
|
+
- **🔒 Zero-Leak Privacy:** API keys and credentials are automatically redacted from error traces and logs.
|
|
55
|
+
- **🏷️ Fully Typed:** Complete type annotations (PEP 561 `py.typed`) and strict `mypy` compatibility.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 📥 Installation
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Standard installation
|
|
63
|
+
pip install smallpict
|
|
64
|
+
|
|
65
|
+
# With optional Pillow / PIL integration
|
|
66
|
+
pip install "smallpict[pil]"
|
|
67
|
+
|
|
68
|
+
# With poetry
|
|
69
|
+
poetry add smallpict
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 🚀 Quick Start
|
|
75
|
+
|
|
76
|
+
### 1. Synchronous Example
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
import os
|
|
80
|
+
from smallpict import SmallPictClient
|
|
81
|
+
|
|
82
|
+
client = SmallPictClient(
|
|
83
|
+
api_key=os.environ["SMALLPICT_API_KEY"],
|
|
84
|
+
secret_key=os.environ.get("SMALLPICT_SECRET_KEY"),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
with open("hero-banner.png", "rb") as f:
|
|
88
|
+
image_bytes = f.read()
|
|
89
|
+
|
|
90
|
+
result = client.optimize(
|
|
91
|
+
image_bytes,
|
|
92
|
+
filename="hero-banner.png",
|
|
93
|
+
format="avif",
|
|
94
|
+
quality=80,
|
|
95
|
+
max_width=1920,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
print(f"Optimized CDN URL: {result.url}")
|
|
99
|
+
print(f"Original: {result.original_size}B ➔ Compressed: {result.compressed_size}B")
|
|
100
|
+
print(f"Saved: {result.savings_percentage}% ({result.bytes_saved} bytes)")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### 2. Asynchronous Example (FastAPI / Starlette)
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import os
|
|
107
|
+
from fastapi import FastAPI, UploadFile, File
|
|
108
|
+
from smallpict import AsyncSmallPictClient
|
|
109
|
+
|
|
110
|
+
app = FastAPI()
|
|
111
|
+
client = AsyncSmallPictClient(api_key=os.environ["SMALLPICT_API_KEY"])
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@app.post("/upload")
|
|
115
|
+
async def upload_image(file: UploadFile = File(...)):
|
|
116
|
+
contents = await file.read()
|
|
117
|
+
|
|
118
|
+
result = await client.optimize(
|
|
119
|
+
contents,
|
|
120
|
+
filename=file.filename,
|
|
121
|
+
format="auto", # Intelligently selects best format (AVIF/WebP)
|
|
122
|
+
quality=85,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
"url": result.url,
|
|
127
|
+
"format": result.format,
|
|
128
|
+
"saved_percentage": f"{result.savings_percentage}%",
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 3. Celery Background Worker Task
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
import os
|
|
136
|
+
from celery import Celery
|
|
137
|
+
from smallpict import SmallPictClient
|
|
138
|
+
|
|
139
|
+
app = Celery("tasks", broker="redis://localhost:6379/0")
|
|
140
|
+
client = SmallPictClient(api_key=os.environ["SMALLPICT_API_KEY"])
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@app.task
|
|
144
|
+
def process_user_avatar(file_path: str):
|
|
145
|
+
result = client.optimize(
|
|
146
|
+
file_path,
|
|
147
|
+
format="webp",
|
|
148
|
+
quality=80,
|
|
149
|
+
max_width=400,
|
|
150
|
+
max_height=400,
|
|
151
|
+
)
|
|
152
|
+
return {"cdn_url": result.url, "bytes_saved": result.bytes_saved}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 4. Optional Pillow / PIL Integration
|
|
156
|
+
|
|
157
|
+
```python
|
|
158
|
+
from PIL import Image
|
|
159
|
+
from smallpict import SmallPictClient
|
|
160
|
+
|
|
161
|
+
img = Image.open("photo.jpg")
|
|
162
|
+
# Crop or transform with Pillow
|
|
163
|
+
img_cropped = img.crop((0, 0, 800, 600))
|
|
164
|
+
|
|
165
|
+
client = SmallPictClient(api_key="sp_live_...")
|
|
166
|
+
result = client.optimize(img_cropped, format="avif", quality=85)
|
|
167
|
+
print(f"Delivered via CDN: {result.url}")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## 📊 Checking Quota & Invalidating CDN Cache
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from smallpict import SmallPictClient
|
|
176
|
+
|
|
177
|
+
client = SmallPictClient(api_key="sp_live_...")
|
|
178
|
+
|
|
179
|
+
# 1. Real-time quota metrics
|
|
180
|
+
quota = client.get_quota()
|
|
181
|
+
print(f"Plan: {quota.plan}, Used: {quota.quota_percentage}%")
|
|
182
|
+
|
|
183
|
+
# 2. Invalidate CDN cache
|
|
184
|
+
client.purge_cdn(["https://cdn.smallpict.app/opt/hero-banner.avif"])
|
|
185
|
+
print("CDN edge cache invalidated successfully.")
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## 🧪 Testing
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
pytest
|
|
194
|
+
mypy smallpict
|
|
195
|
+
ruff check .
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## 📄 License
|
|
201
|
+
|
|
202
|
+
MIT © [SmallPict Engineering](https://smallpict.app)
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# SmallPict Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python client for the [SmallPict Image Optimization API](https://smallpict.app) — high-performance next-gen image transcoding (AVIF, WebP), smart compression, Edge CDN delivery, and cache purging.
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/smallpict/)
|
|
6
|
+
[](https://pypi.org/project/smallpict/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## ⚡ Features
|
|
12
|
+
|
|
13
|
+
- **🚀 Synchronous & Asynchronous:** First-class sync (`SmallPictClient`) and async (`AsyncSmallPictClient`) clients powered by `httpx`.
|
|
14
|
+
- **🖼️ Optional Pillow / PIL Support:** Optimize `PIL.Image` instances directly via `pip install smallpict[pil]`.
|
|
15
|
+
- **🛡️ Secure HMAC-SHA256 & Bearer Auth:** Tamper-proof payload verification.
|
|
16
|
+
- **✨ 4 Core Unified Methods:** `optimize()`, `get_quota()`, `purge_cdn()`, and `validate_key()`.
|
|
17
|
+
- **🔄 Resilience & Fault Tolerance:** Automatic `Idempotency-Key` UUID injection, 30s timeouts, and exponential backoff with jitter on HTTP 429/5xx.
|
|
18
|
+
- **🔒 Zero-Leak Privacy:** API keys and credentials are automatically redacted from error traces and logs.
|
|
19
|
+
- **🏷️ Fully Typed:** Complete type annotations (PEP 561 `py.typed`) and strict `mypy` compatibility.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 📥 Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Standard installation
|
|
27
|
+
pip install smallpict
|
|
28
|
+
|
|
29
|
+
# With optional Pillow / PIL integration
|
|
30
|
+
pip install "smallpict[pil]"
|
|
31
|
+
|
|
32
|
+
# With poetry
|
|
33
|
+
poetry add smallpict
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 🚀 Quick Start
|
|
39
|
+
|
|
40
|
+
### 1. Synchronous Example
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
import os
|
|
44
|
+
from smallpict import SmallPictClient
|
|
45
|
+
|
|
46
|
+
client = SmallPictClient(
|
|
47
|
+
api_key=os.environ["SMALLPICT_API_KEY"],
|
|
48
|
+
secret_key=os.environ.get("SMALLPICT_SECRET_KEY"),
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
with open("hero-banner.png", "rb") as f:
|
|
52
|
+
image_bytes = f.read()
|
|
53
|
+
|
|
54
|
+
result = client.optimize(
|
|
55
|
+
image_bytes,
|
|
56
|
+
filename="hero-banner.png",
|
|
57
|
+
format="avif",
|
|
58
|
+
quality=80,
|
|
59
|
+
max_width=1920,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
print(f"Optimized CDN URL: {result.url}")
|
|
63
|
+
print(f"Original: {result.original_size}B ➔ Compressed: {result.compressed_size}B")
|
|
64
|
+
print(f"Saved: {result.savings_percentage}% ({result.bytes_saved} bytes)")
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 2. Asynchronous Example (FastAPI / Starlette)
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import os
|
|
71
|
+
from fastapi import FastAPI, UploadFile, File
|
|
72
|
+
from smallpict import AsyncSmallPictClient
|
|
73
|
+
|
|
74
|
+
app = FastAPI()
|
|
75
|
+
client = AsyncSmallPictClient(api_key=os.environ["SMALLPICT_API_KEY"])
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@app.post("/upload")
|
|
79
|
+
async def upload_image(file: UploadFile = File(...)):
|
|
80
|
+
contents = await file.read()
|
|
81
|
+
|
|
82
|
+
result = await client.optimize(
|
|
83
|
+
contents,
|
|
84
|
+
filename=file.filename,
|
|
85
|
+
format="auto", # Intelligently selects best format (AVIF/WebP)
|
|
86
|
+
quality=85,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
"url": result.url,
|
|
91
|
+
"format": result.format,
|
|
92
|
+
"saved_percentage": f"{result.savings_percentage}%",
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### 3. Celery Background Worker Task
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
import os
|
|
100
|
+
from celery import Celery
|
|
101
|
+
from smallpict import SmallPictClient
|
|
102
|
+
|
|
103
|
+
app = Celery("tasks", broker="redis://localhost:6379/0")
|
|
104
|
+
client = SmallPictClient(api_key=os.environ["SMALLPICT_API_KEY"])
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@app.task
|
|
108
|
+
def process_user_avatar(file_path: str):
|
|
109
|
+
result = client.optimize(
|
|
110
|
+
file_path,
|
|
111
|
+
format="webp",
|
|
112
|
+
quality=80,
|
|
113
|
+
max_width=400,
|
|
114
|
+
max_height=400,
|
|
115
|
+
)
|
|
116
|
+
return {"cdn_url": result.url, "bytes_saved": result.bytes_saved}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 4. Optional Pillow / PIL Integration
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from PIL import Image
|
|
123
|
+
from smallpict import SmallPictClient
|
|
124
|
+
|
|
125
|
+
img = Image.open("photo.jpg")
|
|
126
|
+
# Crop or transform with Pillow
|
|
127
|
+
img_cropped = img.crop((0, 0, 800, 600))
|
|
128
|
+
|
|
129
|
+
client = SmallPictClient(api_key="sp_live_...")
|
|
130
|
+
result = client.optimize(img_cropped, format="avif", quality=85)
|
|
131
|
+
print(f"Delivered via CDN: {result.url}")
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 📊 Checking Quota & Invalidating CDN Cache
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from smallpict import SmallPictClient
|
|
140
|
+
|
|
141
|
+
client = SmallPictClient(api_key="sp_live_...")
|
|
142
|
+
|
|
143
|
+
# 1. Real-time quota metrics
|
|
144
|
+
quota = client.get_quota()
|
|
145
|
+
print(f"Plan: {quota.plan}, Used: {quota.quota_percentage}%")
|
|
146
|
+
|
|
147
|
+
# 2. Invalidate CDN cache
|
|
148
|
+
client.purge_cdn(["https://cdn.smallpict.app/opt/hero-banner.avif"])
|
|
149
|
+
print("CDN edge cache invalidated successfully.")
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 🧪 Testing
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
pytest
|
|
158
|
+
mypy smallpict
|
|
159
|
+
ruff check .
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## 📄 License
|
|
165
|
+
|
|
166
|
+
MIT © [SmallPict Engineering](https://smallpict.app)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "smallpict"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Official Python SDK for SmallPict Image Optimization API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.8"
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "SmallPict Engineering", email = "support@smallpict.app" }
|
|
14
|
+
]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 5 - Production/Stable",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.8",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Topic :: Multimedia :: Graphics",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
"Typing :: Typed",
|
|
28
|
+
]
|
|
29
|
+
dependencies = [
|
|
30
|
+
"httpx>=0.24.0",
|
|
31
|
+
"pydantic>=2.0.0",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
pil = [
|
|
36
|
+
"Pillow>=9.0.0",
|
|
37
|
+
]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=7.0.0",
|
|
40
|
+
"pytest-asyncio>=0.21.0",
|
|
41
|
+
"respx>=0.21.0",
|
|
42
|
+
"mypy>=1.0.0",
|
|
43
|
+
"ruff>=0.1.0",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[project.urls]
|
|
47
|
+
Homepage = "https://smallpict.app"
|
|
48
|
+
Repository = "https://github.com/tuxnoob/smallpict-python"
|
|
49
|
+
Documentation = "https://smallpict.app/docs/sdks/python"
|
|
50
|
+
Changelog = "https://github.com/tuxnoob/smallpict-python/blob/main/CHANGELOG.md"
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.wheel]
|
|
53
|
+
packages = ["smallpict"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff]
|
|
56
|
+
line-length = 100
|
|
57
|
+
target-version = "py38"
|
|
58
|
+
|
|
59
|
+
[tool.mypy]
|
|
60
|
+
python_version = "3.8"
|
|
61
|
+
strict = true
|
|
62
|
+
warn_return_any = true
|
|
63
|
+
warn_unused_configs = true
|
|
64
|
+
|
|
65
|
+
[tool.pytest.ini_options]
|
|
66
|
+
asyncio_mode = "auto"
|
|
67
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SmallPict Official Python SDK
|
|
3
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
4
|
+
High-performance cloud image optimization, format transcoding (WebP, AVIF),
|
|
5
|
+
CDN edge invalidation, and real-time quota tracking.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from .client import SmallPictClient
|
|
9
|
+
from .aclient import AsyncSmallPictClient
|
|
10
|
+
from .errors import (
|
|
11
|
+
SmallPictError,
|
|
12
|
+
ValidationError,
|
|
13
|
+
AuthenticationError,
|
|
14
|
+
PermissionDeniedError,
|
|
15
|
+
NotFoundError,
|
|
16
|
+
QuotaExceededError,
|
|
17
|
+
RateLimitError,
|
|
18
|
+
ServerError,
|
|
19
|
+
TimeoutError,
|
|
20
|
+
NetworkError,
|
|
21
|
+
sanitize_message,
|
|
22
|
+
)
|
|
23
|
+
from .models import (
|
|
24
|
+
ImageFormat,
|
|
25
|
+
FitMode,
|
|
26
|
+
FallbackMode,
|
|
27
|
+
PurgeType,
|
|
28
|
+
OptimizeOptions,
|
|
29
|
+
OptimizeResult,
|
|
30
|
+
JobStatusResult,
|
|
31
|
+
QuotaResponse,
|
|
32
|
+
PurgeOptions,
|
|
33
|
+
PurgeResponse,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
__version__ = "0.0.1"
|
|
37
|
+
__all__ = [
|
|
38
|
+
"SmallPictClient",
|
|
39
|
+
"AsyncSmallPictClient",
|
|
40
|
+
"SmallPictError",
|
|
41
|
+
"ValidationError",
|
|
42
|
+
"AuthenticationError",
|
|
43
|
+
"PermissionDeniedError",
|
|
44
|
+
"NotFoundError",
|
|
45
|
+
"QuotaExceededError",
|
|
46
|
+
"RateLimitError",
|
|
47
|
+
"ServerError",
|
|
48
|
+
"TimeoutError",
|
|
49
|
+
"NetworkError",
|
|
50
|
+
"sanitize_message",
|
|
51
|
+
"ImageFormat",
|
|
52
|
+
"FitMode",
|
|
53
|
+
"FallbackMode",
|
|
54
|
+
"PurgeType",
|
|
55
|
+
"OptimizeOptions",
|
|
56
|
+
"OptimizeResult",
|
|
57
|
+
"JobStatusResult",
|
|
58
|
+
"QuotaResponse",
|
|
59
|
+
"PurgeOptions",
|
|
60
|
+
"PurgeResponse",
|
|
61
|
+
]
|