yorillow 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.
- yorillow-1.0.0/LICENSE +21 -0
- yorillow-1.0.0/PKG-INFO +423 -0
- yorillow-1.0.0/README.md +394 -0
- yorillow-1.0.0/pyproject.toml +40 -0
- yorillow-1.0.0/setup.cfg +4 -0
- yorillow-1.0.0/tests/test_render.py +649 -0
- yorillow-1.0.0/yorillow/__init__.py +58 -0
- yorillow-1.0.0/yorillow/__main__.py +160 -0
- yorillow-1.0.0/yorillow/canvas.py +283 -0
- yorillow-1.0.0/yorillow/charts/__init__.py +17 -0
- yorillow-1.0.0/yorillow/charts/area.py +116 -0
- yorillow-1.0.0/yorillow/charts/bar.py +94 -0
- yorillow-1.0.0/yorillow/charts/base.py +51 -0
- yorillow-1.0.0/yorillow/charts/line.py +130 -0
- yorillow-1.0.0/yorillow/charts/pie.py +110 -0
- yorillow-1.0.0/yorillow/charts/scatter.py +75 -0
- yorillow-1.0.0/yorillow/elements.py +118 -0
- yorillow-1.0.0/yorillow/font.py +146 -0
- yorillow-1.0.0/yorillow/render.py +300 -0
- yorillow-1.0.0/yorillow/styles.py +91 -0
- yorillow-1.0.0/yorillow/svg.py +436 -0
- yorillow-1.0.0/yorillow/validate.py +258 -0
- yorillow-1.0.0/yorillow.egg-info/PKG-INFO +423 -0
- yorillow-1.0.0/yorillow.egg-info/SOURCES.txt +26 -0
- yorillow-1.0.0/yorillow.egg-info/dependency_links.txt +1 -0
- yorillow-1.0.0/yorillow.egg-info/entry_points.txt +2 -0
- yorillow-1.0.0/yorillow.egg-info/requires.txt +3 -0
- yorillow-1.0.0/yorillow.egg-info/top_level.txt +1 -0
yorillow-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 yorillow contributors
|
|
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.
|
yorillow-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: yorillow
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Zero-dependency Python chart renderer — works offline, in APIs, and in serverless functions
|
|
5
|
+
Author: yorillow contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/harshi79/yorillow
|
|
8
|
+
Project-URL: Repository, https://github.com/harshi79/yorillow
|
|
9
|
+
Project-URL: Issues, https://github.com/harshi79/yorillow/issues
|
|
10
|
+
Keywords: charts,plotting,lightweight,png,svg,serverless,api
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
21
|
+
Classifier: Framework :: Flask
|
|
22
|
+
Classifier: Framework :: FastAPI
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
License-File: LICENSE
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest; extra == "dev"
|
|
28
|
+
Dynamic: license-file
|
|
29
|
+
|
|
30
|
+
# Yorillow
|
|
31
|
+
|
|
32
|
+
**Lightweight, zero-dependency Python chart renderer that generates PNG and SVG charts entirely in Python.**
|
|
33
|
+
|
|
34
|
+
Runs offline, inside Python applications, behind APIs, in Docker, on VPS servers, or in serverless environments.
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from yorillow import render
|
|
38
|
+
|
|
39
|
+
png = render({
|
|
40
|
+
"type": "line",
|
|
41
|
+
"x": [1, 2, 3, 4],
|
|
42
|
+
"y": [[10, 20, 15, 30]],
|
|
43
|
+
"title": "Sales",
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
with open("sales.png", "wb") as f:
|
|
47
|
+
f.write(png)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
No Pillow. No NumPy. No matplotlib. No Chromium. No Node.js. No browser. No database.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Why Yorillow?
|
|
55
|
+
|
|
56
|
+
| | Yorillow | matplotlib | Chart.js |
|
|
57
|
+
|---|---|---|---|
|
|
58
|
+
| Runtime dependencies | **0** | numpy, Pillow, … | Node/browser |
|
|
59
|
+
| Returns | **bytes directly** | figure objects | HTML/JS |
|
|
60
|
+
| Serverless | **native** | requires setup | needs browser |
|
|
61
|
+
| Import time | **~27 ms** | seconds | N/A |
|
|
62
|
+
| Memory (800×600) | **~1.8 MiB** | ~50 MiB | N/A |
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
python -m pip install yorillow
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Then:
|
|
73
|
+
|
|
74
|
+
```python
|
|
75
|
+
from yorillow import render
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Package name:** `yorillow` (PyPI) / **Import name:** `yorillow` (Python)
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Quick Start
|
|
83
|
+
|
|
84
|
+
### Render to bytes
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
from yorillow import render
|
|
88
|
+
|
|
89
|
+
png = render(config, format="png") # bytes
|
|
90
|
+
svg = render(config, format="svg") # str
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Save to file
|
|
94
|
+
|
|
95
|
+
```python
|
|
96
|
+
from yorillow import render_to_file
|
|
97
|
+
|
|
98
|
+
render_to_file(config, "chart.png")
|
|
99
|
+
render_to_file(config, "chart.svg")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### HTTP response helper
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
from yorillow import render_response
|
|
106
|
+
|
|
107
|
+
resp = render_response(config, format="png")
|
|
108
|
+
# resp["status_code"] → 200
|
|
109
|
+
# resp["content_type"] → "image/png"
|
|
110
|
+
# resp["body"] → bytes
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Offline Usage
|
|
116
|
+
|
|
117
|
+
After installation, rendering requires **no internet, no API key, no external service**.
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from yorillow import render
|
|
121
|
+
|
|
122
|
+
png = render({"type": "line", "x": [1, 2, 3], "y": [[10, 20, 30]]})
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Completely offline installation
|
|
126
|
+
|
|
127
|
+
On an internet-connected machine:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
python -m pip download yorillow
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Transfer the downloaded file to the offline machine, then:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
python -m pip install --no-index --find-links . yorillow
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Since yorillow has zero runtime dependencies, only the yorillow package itself needs to be transferred.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## Chart Types
|
|
144
|
+
|
|
145
|
+
### Line
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
render({
|
|
149
|
+
"type": "line",
|
|
150
|
+
"x": [1, 2, 3, 4, 5],
|
|
151
|
+
"y": [[10, 20, 15, 25, 30], [5, 15, 10, 20, 25]],
|
|
152
|
+
"labels": ["Revenue", "Profit"],
|
|
153
|
+
"title": "Monthly Performance",
|
|
154
|
+
"legend": True,
|
|
155
|
+
"fill": True,
|
|
156
|
+
})
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Bar
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
render({
|
|
163
|
+
"type": "bar",
|
|
164
|
+
"categories": ["Q1", "Q2", "Q3", "Q4"],
|
|
165
|
+
"values": [[120, 150, 170, 200], [90, 130, 140, 180]],
|
|
166
|
+
"labels": ["Product A", "Product B"],
|
|
167
|
+
"stacked": False,
|
|
168
|
+
})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Scatter
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
render({
|
|
175
|
+
"type": "scatter",
|
|
176
|
+
"x": [1.0, 2.5, 3.7, 4.2, 5.8],
|
|
177
|
+
"y": [10.0, 20.5, 15.3, 25.1, 30.0],
|
|
178
|
+
"sizes": [5, 8, 6, 10, 4],
|
|
179
|
+
})
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Pie / Donut
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
render({
|
|
186
|
+
"type": "pie",
|
|
187
|
+
"values": [35, 25, 20, 12, 8],
|
|
188
|
+
"labels": ["Chrome", "Firefox", "Safari", "Edge", "Other"],
|
|
189
|
+
"donut": True,
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Area
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
render({
|
|
197
|
+
"type": "area",
|
|
198
|
+
"x": [1, 2, 3, 4, 5],
|
|
199
|
+
"y": [[10, 20, 15, 25, 30], [5, 10, 8, 12, 15]],
|
|
200
|
+
"labels": ["Downloads", "Installs"],
|
|
201
|
+
"stacked": True,
|
|
202
|
+
})
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
---
|
|
206
|
+
|
|
207
|
+
## Themes
|
|
208
|
+
|
|
209
|
+
Built-in: `default`, `dark`, `minimal`, `neon`
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
render({**config, "theme": "dark"})
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## JSON Configuration
|
|
218
|
+
|
|
219
|
+
Chart configurations are plain JSON — no Python objects required.
|
|
220
|
+
|
|
221
|
+
```json
|
|
222
|
+
{
|
|
223
|
+
"type": "line",
|
|
224
|
+
"x": [1, 2, 3, 4],
|
|
225
|
+
"y": [[10, 20, 15, 30]],
|
|
226
|
+
"title": "Sales"
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
import json
|
|
232
|
+
from yorillow import render
|
|
233
|
+
|
|
234
|
+
with open("chart.json") as f:
|
|
235
|
+
config = json.load(f)
|
|
236
|
+
|
|
237
|
+
png = render(config)
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
This makes Yorillow useful when configuration comes from another program, a database, an API, or another language.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## CLI
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
yorillow chart.json -o chart.png # render PNG
|
|
248
|
+
yorillow chart.json -o chart.svg # render SVG
|
|
249
|
+
cat chart.json | yorillow -o chart.png # stdin
|
|
250
|
+
yorillow chart.json --base64 # JSON with base64 data
|
|
251
|
+
yorillow chart.json --validate-only # validate only
|
|
252
|
+
yorillow chart.json -o out.png --bench # show timing
|
|
253
|
+
yorillow --version
|
|
254
|
+
yorillow --help
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
PowerShell:
|
|
258
|
+
|
|
259
|
+
```powershell
|
|
260
|
+
Get-Content chart.json -Raw | yorillow -o chart.png
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## Build an API
|
|
266
|
+
|
|
267
|
+
Yorillow is the renderer, not the server.
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
Client → JSON → HTTP Server → yorillow.render() → PNG/SVG → HTTP Response
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### stdlib (zero dependencies)
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
python examples/api/simple_http_server.py
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
curl -X POST http://localhost:8080/chart \
|
|
281
|
+
-H "Content-Type: application/json" \
|
|
282
|
+
-d '{"type":"line","x":[1,2,3],"y":[[10,20,30]]}' \
|
|
283
|
+
--output chart.png
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Flask
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
pip install flask yorillow
|
|
290
|
+
python examples/api/flask_app.py
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### FastAPI
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
pip install fastapi uvicorn yorillow
|
|
297
|
+
python -m uvicorn examples.api.fastapi_app:app
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Deploy
|
|
303
|
+
|
|
304
|
+
| Environment | Guide | Status |
|
|
305
|
+
|---|---|---|
|
|
306
|
+
| Local Python | [Offline Usage](#offline-usage) | ✓ Tested |
|
|
307
|
+
| CLI | [CLI](#cli) | ✓ Tested |
|
|
308
|
+
| VPS | [docs/deployment/vps.md](docs/deployment/vps.md) | ✓ Tested |
|
|
309
|
+
| Docker | [docs/deployment/docker.md](docs/deployment/docker.md) | ✓ Tested |
|
|
310
|
+
| Flask | [examples/api/flask_app.py](examples/api/flask_app.py) | Example |
|
|
311
|
+
| FastAPI | [examples/api/fastapi_app.py](examples/api/fastapi_app.py) | Example |
|
|
312
|
+
| Render | [docs/deployment/render.md](docs/deployment/render.md) | Example |
|
|
313
|
+
| Railway | [docs/deployment/railway.md](docs/deployment/railway.md) | Example |
|
|
314
|
+
| Vercel | [docs/deployment/vercel.md](docs/deployment/vercel.md) | Example |
|
|
315
|
+
| Netlify | [docs/deployment/netlify.md](docs/deployment/netlify.md) | Example |
|
|
316
|
+
| AWS Lambda | [docs/deployment/aws-lambda.md](docs/deployment/aws-lambda.md) | Example |
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Serverless Architecture
|
|
321
|
+
|
|
322
|
+
The core renderer is stateless and dependency-free, making it suitable for Python-compatible serverless runtimes.
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
Request → render() → PNG/SVG → Response
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
No persistent process. No database. No filesystem. No browser.
|
|
329
|
+
|
|
330
|
+
See [docs/architecture/serverless.md](docs/architecture/serverless.md).
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## Performance
|
|
335
|
+
|
|
336
|
+
| Metric | Value |
|
|
337
|
+
|---|---|
|
|
338
|
+
| Import | ~27 ms |
|
|
339
|
+
| PNG 800×600 | ~60–90 ms |
|
|
340
|
+
| SVG 800×600 | <0.1 ms |
|
|
341
|
+
| PNG 1920×1080 | ~240 ms |
|
|
342
|
+
| Canvas 800×600 | 1.8 MiB |
|
|
343
|
+
|
|
344
|
+
Run `python benchmarks/bench.py` to reproduce.
|
|
345
|
+
|
|
346
|
+
See [docs/performance.md](docs/performance.md).
|
|
347
|
+
|
|
348
|
+
---
|
|
349
|
+
|
|
350
|
+
## Security
|
|
351
|
+
|
|
352
|
+
Yorillow validates all inputs: max dimensions, data point limits, NaN/Infinity rejection, type checking.
|
|
353
|
+
|
|
354
|
+
When exposing publicly, add authentication, rate limiting, and request size limits at the HTTP layer.
|
|
355
|
+
|
|
356
|
+
See [docs/security.md](docs/security.md).
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## API Reference
|
|
361
|
+
|
|
362
|
+
| Function | Returns | Description |
|
|
363
|
+
|---|---|---|
|
|
364
|
+
| `render(config, format, encoding)` | `bytes`/`str` | Stateless chart renderer |
|
|
365
|
+
| `render_to_file(config, path)` | `None` | Render and save to file |
|
|
366
|
+
| `render_response(config, format)` | `dict` | HTTP response helper |
|
|
367
|
+
|
|
368
|
+
| Class | Description |
|
|
369
|
+
|---|---|
|
|
370
|
+
| `Canvas` | Low-level drawing surface |
|
|
371
|
+
| `LineChart` | Line chart |
|
|
372
|
+
| `BarChart` | Bar chart |
|
|
373
|
+
| `ScatterChart` | Scatter plot |
|
|
374
|
+
| `PieChart` | Pie / donut chart |
|
|
375
|
+
| `AreaChart` | Area chart |
|
|
376
|
+
|
|
377
|
+
| Exception | Description |
|
|
378
|
+
|---|---|
|
|
379
|
+
| `YorillowError` | Base exception |
|
|
380
|
+
| `ValidationError` | Invalid input |
|
|
381
|
+
| `RenderError` | Internal error |
|
|
382
|
+
|
|
383
|
+
Full reference: [docs/api/](docs/api/)
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Examples
|
|
388
|
+
|
|
389
|
+
| Example | Description |
|
|
390
|
+
|---|---|
|
|
391
|
+
| [examples/python/basic_png.py](examples/python/basic_png.py) | Basic PNG |
|
|
392
|
+
| [examples/python/basic_svg.py](examples/python/basic_svg.py) | Basic SVG |
|
|
393
|
+
| [examples/python/multiple_series.py](examples/python/multiple_series.py) | Multiple series |
|
|
394
|
+
| [examples/python/themes.py](examples/python/themes.py) | Theme demo |
|
|
395
|
+
| [examples/python/save_to_file.py](examples/python/save_to_file.py) | Save to file |
|
|
396
|
+
| [examples/python/json_config.py](examples/python/json_config.py) | JSON config |
|
|
397
|
+
| [examples/api/simple_http_server.py](examples/api/simple_http_server.py) | stdlib HTTP API |
|
|
398
|
+
| [examples/api/flask_app.py](examples/api/flask_app.py) | Flask API |
|
|
399
|
+
| [examples/api/fastapi_app.py](examples/api/fastapi_app.py) | FastAPI API |
|
|
400
|
+
| [examples/client/remote_client.py](examples/client/remote_client.py) | Python HTTP client |
|
|
401
|
+
| [examples/serverless/](examples/serverless/) | Serverless handlers |
|
|
402
|
+
| [examples/docker/](examples/docker/) | Docker |
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
## Development
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
git clone https://github.com/harshi79/yorillow.git
|
|
410
|
+
cd yorillow
|
|
411
|
+
python -m venv .venv
|
|
412
|
+
source .venv/bin/activate
|
|
413
|
+
pip install -e ".[dev]"
|
|
414
|
+
python -m pytest tests/ -v
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## License
|
|
422
|
+
|
|
423
|
+
[MIT](LICENSE)
|