plotlet 0.2.0__tar.gz → 0.4.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.
Files changed (42) hide show
  1. {plotlet-0.2.0/src/plotlet.egg-info → plotlet-0.4.0}/PKG-INFO +22 -16
  2. {plotlet-0.2.0 → plotlet-0.4.0}/README.md +19 -14
  3. {plotlet-0.2.0 → plotlet-0.4.0}/pyproject.toml +3 -2
  4. plotlet-0.4.0/src/plotlet/__init__.py +33 -0
  5. plotlet-0.4.0/src/plotlet/artists.py +503 -0
  6. plotlet-0.4.0/src/plotlet/builtin_artists.py +570 -0
  7. plotlet-0.4.0/src/plotlet/chart.py +718 -0
  8. plotlet-0.4.0/src/plotlet/colormaps.py +113 -0
  9. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/core.py +451 -277
  10. plotlet-0.4.0/src/plotlet/dendrogram.py +197 -0
  11. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/layout.py +340 -89
  12. plotlet-0.4.0/src/plotlet/layout_diagram.py +237 -0
  13. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/legend.py +29 -25
  14. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/registry.py +24 -1
  15. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/spec.json +13 -4
  16. {plotlet-0.2.0 → plotlet-0.4.0/src/plotlet.egg-info}/PKG-INFO +22 -16
  17. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet.egg-info/SOURCES.txt +3 -1
  18. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet.egg-info/requires.txt +1 -0
  19. plotlet-0.4.0/tests/test_chart.py +564 -0
  20. plotlet-0.4.0/tests/test_layout_diagram.py +93 -0
  21. {plotlet-0.2.0 → plotlet-0.4.0}/tests/test_legend.py +34 -20
  22. plotlet-0.4.0/tests/test_subplots.py +385 -0
  23. {plotlet-0.2.0 → plotlet-0.4.0}/tests/test_units.py +49 -39
  24. plotlet-0.2.0/src/plotlet/__init__.py +0 -29
  25. plotlet-0.2.0/src/plotlet/artists.py +0 -314
  26. plotlet-0.2.0/src/plotlet/builtin_artists.py +0 -254
  27. plotlet-0.2.0/src/plotlet/chart.py +0 -367
  28. plotlet-0.2.0/src/plotlet/colormaps.py +0 -50
  29. plotlet-0.2.0/tests/test_chart.py +0 -319
  30. plotlet-0.2.0/tests/test_old.py +0 -112
  31. plotlet-0.2.0/tests/test_subplots.py +0 -327
  32. {plotlet-0.2.0 → plotlet-0.4.0}/LICENSE +0 -0
  33. {plotlet-0.2.0 → plotlet-0.4.0}/setup.cfg +0 -0
  34. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/_cm_data.py +0 -0
  35. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/_png.py +0 -0
  36. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/_spec.py +0 -0
  37. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/colors.py +0 -0
  38. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/font.py +0 -0
  39. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/fonts/DejaVuSans.ttf +0 -0
  40. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet/scales.py +0 -0
  41. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet.egg-info/dependency_links.txt +0 -0
  42. {plotlet-0.2.0 → plotlet-0.4.0}/src/plotlet.egg-info/top_level.txt +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plotlet
3
- Version: 0.2.0
4
- Summary: Small, hackable Python library that emits matplotlib-style SVG plots.
3
+ Version: 0.4.0
4
+ Summary: Python library for SVG plots, with multi-panel composition and an extension API for custom plot types.
5
5
  Author: gitbamboo42
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/gitbamboo42/plotlet
@@ -22,6 +22,7 @@ Requires-Python: >=3.10
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  Requires-Dist: fonttools>=4.0
25
+ Requires-Dist: scipy>=1.10
25
26
  Provides-Extra: dev
26
27
  Requires-Dist: jupyter; extra == "dev"
27
28
  Requires-Dist: nbconvert; extra == "dev"
@@ -29,13 +30,13 @@ Dynamic: license-file
29
30
 
30
31
  # plotlet
31
32
 
32
- A small, hackable Python library that emits matplotlib-style SVG plots.
33
+ A Python library for SVG plots — with multi-panel composition, shared-axis layouts, and an extension API for custom plot types.
33
34
 
34
- ## Why
35
+ ## What it's for
35
36
 
36
- matplotlib is the right tool when you want the kitchen sink. plotlet's niche is **custom plot types** — genome tracks, Manhattan plots, phylogenetic trees, anything matplotlib's extension API makes painful. The whole library has a deliberately tiny, exposed core: adding a new plot type is a 3-step recipe, not an architecture project.
37
+ plotlet is built for **multi-panel scientific figures with custom plot types** — genome tracks, spike rasters, climate stacks, Manhattan plots, phylogenetic trees. The core ships ~5 standard plots plus multi-panel composition (`|`, `/`, `share_x()`).
37
38
 
38
- It's a **scaffold, not a feature catalog**: the core ships ~5 standard plots and the infrastructure for extending. Custom plot types live in your own project (or [`cookbook/`](cookbook/)), not upstream. See [docs/PHILOSOPHY.md](docs/PHILOSOPHY.md) for the full framing.
39
+ Custom plot types are a 3-step recipe (`record`, `xdomain`/`ydomain`, `draw`) and live in your own project (or [`cookbook/`](cookbook/)) rather than upstream. See [docs/PHILOSOPHY.md](docs/PHILOSOPHY.md) for the full framing.
39
40
 
40
41
  ```python
41
42
  import plotlet as pt
@@ -59,12 +60,13 @@ pip install plotlet
59
60
 
60
61
  ## Properties
61
62
 
62
- - **Lightweight.** `fonttools` for font handling. numpy / pandas / polars inputs work transparently if you have them.
63
+ - **Minimal dependencies.** `fonttools` for font handling. numpy / pandas / polars inputs work transparently if you have them.
63
64
  - **Static SVG output.** No interactivity, no animation. Same script → byte-identical SVG.
64
65
  - **Cross-machine reproducible.** Bundled DejaVu Sans + text-as-paths means rendering is identical on Linux, macOS, Windows, headless CI.
65
- - **Jupyter-native.** `Figure._repr_html_` auto-renders the last expression in a cell.
66
- - **Tiny output.** Each plot is ~50 KB SVG, self-contained.
67
- - **Compositional.** Multi-panel layouts via `|`, `/`, `pt.grid`; share scales with `share_x=` / `share_y=`; layout-level legend with `pt.legend()` covering both discrete swatches and continuous gradients (the colorbar).
66
+ - **Jupyter-native.** `Chart._repr_html_` auto-renders the last expression in a cell.
67
+ - **Compact output.** Each plot is ~50 KB SVG, self-contained.
68
+ - **Compositional.** Multi-panel layouts via `|`, `/`, `pt.grid`; share scales with `(a | b).share_x()` or `pt.grid(..., share_x="col")`; layout-level legend with `pt.legend()` covering both discrete swatches and continuous gradients (the colorbar).
69
+ - **AI-readable.** Every figure ships `data-plotlet-*` attributes describing plot type, axes, scales, ranges, and series labels — readable in one XML parse, no glyph-path OCR. Schema: [docs/AI_ATTRS.md](docs/AI_ATTRS.md).
68
70
 
69
71
  ## API
70
72
 
@@ -74,7 +76,7 @@ pip install plotlet
74
76
 
75
77
  Pass at construction (`pt.chart(data, title=..., grid=True, ...)`) or as chained setters (`c.title(...)`, etc.):
76
78
 
77
- `title`, `xlabel`, `ylabel`, `xlim=(a, b)`, `ylim=(a, b)`, `xscale="linear"|"log"|"category"` (chained: `c.xscale("category", order=[...], padding=0)`), `yscale=...`, `grid=True/False`, `legend=True/False`, `data_width`, `data_height` (the data region — preferred), or `canvas_width`, `canvas_height` (the full SVG canvas — mutually exclusive with the data form). Sizes accept bare pixels (`400`) or unit-suffixed strings (`"4in"`, `"10cm"`, `"100mm"`, `"72pt"`).
79
+ `title`, `xlabel`, `ylabel`, `xlim=(a, b)`, `ylim=(a, b)`, `xscale="linear"|"log"|"category"` (chained: `c.xscale("category", order=[...], padding=0)`), `yscale=...`, `grid=True/False`, `legend=True/False`, `data_width`, `data_height` (the data region — the figure canvas grows to fit titles, tick labels, and axis labels). Sizes accept bare pixels (`400`) or unit-suffixed strings (`"4in"`, `"10cm"`, `"100mm"`, `"72pt"`). To fit a composition into a target SVG canvas, chain `.fit(canvas_width=…, canvas_height=…)` after composing — it rescales data regions while keeping fonts, spines, and margins at their absolute pixel sizes.
78
80
 
79
81
  String-valued data on either axis (`scatter(["a","b","c"], ...)`, `bar`, …) auto-switches to a categorical scale, alphabetical by default. `padding=0` makes category bands contiguous (heatmap-track look).
80
82
 
@@ -92,11 +94,14 @@ Tick customization: `c.xticks([0, 5, 10], ["A","B","C"], rotation=45, fontsize=1
92
94
  | `.axhline(y, **opts)` / `.axvline(x, **opts)` | `color`, `linewidth`, `linestyle`, `alpha`, `label`, axes-fraction `xmin`/`xmax` (or `ymin`/`ymax`) |
93
95
  | `.axhspan(ymin, ymax, **opts)` / `.axvspan(xmin, xmax, **opts)` | `color`, `alpha`, `label`, axes-fraction `xmin`/`xmax` (or `ymin`/`ymax`) |
94
96
  | `.imshow(data, **opts)` | `cmap` (any matplotlib name, default `"viridis"`), `vmin`, `vmax`, `extent=(left, right, bottom, top)` |
97
+ | `.heatmap(df, **opts)` | `cmap`, `vmin`, `vmax`, `norm`, `center`, `xticklabels`, `yticklabels`, `legend` |
95
98
 
96
99
  `hue=<col>` (on `.line` / `.scatter`) splits into one call per unique value with auto-labels and tab10 colors. Reference lines and spans default to black; spans use `alpha=0.2`. They're drawn outside the data color cycle and don't participate in autoscaling — they're decorations on the frame, not data.
97
100
 
98
101
  `.imshow(data)` renders a 2-D array as a colored grid. Small grids (`nrows × ncols ≤ 10000`) emit one `<rect>` per cell and stay vector-clean at any zoom; larger grids encode as a single base64 PNG and quantize to 256 levels. Image row 0 is rendered at the top of its rectangle; the y axis stays Cartesian (small at bottom). All ~180 matplotlib colormaps are vendored — see `pt.list_colormaps()`.
99
102
 
103
+ `.heatmap(df)` is the DataFrame-aware companion to `.imshow`. A pandas DataFrame's `index` becomes the row tick labels and `columns` becomes the column tick labels; row 0 sits at the top. For a plain 2-D array, default labels are integer indices — pass `xticklabels=` / `yticklabels=` to override. Cells render at integer + 0.5 centers on a linear axis, which lines up with scipy's dendrogram leaf positions so a top/left dendrogram pairs cleanly via `share_x` / `share_y`.
104
+
100
105
  ### Subplots
101
106
 
102
107
  Compose multi-panel layouts with operators on `Chart`:
@@ -110,10 +115,11 @@ pt.grid([[a, b], # 2-D grid; cells may be `None`
110
115
  [c, d]])
111
116
 
112
117
  # Share x or y across panels — collapses the gap between them
113
- # and forces both onto the source's scale.
118
+ # and unions data ranges; the first leaf in reading order anchors
119
+ # the scale, others are aspect-scaled to match.
114
120
  top = pt.chart()
115
- main = pt.chart(share_x=top)
116
- top / main # vertically stacked, x-axis joined
121
+ main = pt.chart()
122
+ (top / main).share_x() # vertically stacked, x-axis joined
117
123
 
118
124
  # Layout-level legend (covers colorbar and discrete swatches in
119
125
  # one constructor — geometry follows from the source's color mapping).
@@ -147,7 +153,7 @@ c.write_html("plot.html") # standalone HTML
147
153
 
148
154
  ## Adding a new plot type
149
155
 
150
- plotlet's central hackability claim: a custom plot type is a 3-step recipe (~50–100 lines) that gets axes, scales, legend, grid, and composability for free. The recommended home is your own project, or [`cookbook/`](cookbook/) as reference. Full guide: [docs/EXTENDING.md](docs/EXTENDING.md).
156
+ plotlet is designed so that adding a new plot type is a 3-step recipe (~50–100 lines) that gets axes, scales, legend, grid, and composability for free. The recommended home is your own project, or [`cookbook/`](cookbook/) as reference. Full guide: [docs/EXTENDING.md](docs/EXTENDING.md).
151
157
 
152
158
  ## Testing
153
159
 
@@ -161,7 +167,7 @@ python tests/test_subplots.py # subplot baselines + composition invarian
161
167
  ## Non-goals
162
168
 
163
169
  - No interactivity (hover, zoom, click). Static rendering is the point.
164
- - Not competing with matplotlib on standard plots; matplotlib is bigger and battle-tested.
170
+ - Not aiming for full coverage of standard statistical plots — those needs are well-served elsewhere.
165
171
  - Not a 3D plotter, not a dashboard tool.
166
172
  - Not a feature catalog — new plot types belong in user projects or `cookbook/`, not in the core.
167
173
 
@@ -1,12 +1,12 @@
1
1
  # plotlet
2
2
 
3
- A small, hackable Python library that emits matplotlib-style SVG plots.
3
+ A Python library for SVG plots — with multi-panel composition, shared-axis layouts, and an extension API for custom plot types.
4
4
 
5
- ## Why
5
+ ## What it's for
6
6
 
7
- matplotlib is the right tool when you want the kitchen sink. plotlet's niche is **custom plot types** — genome tracks, Manhattan plots, phylogenetic trees, anything matplotlib's extension API makes painful. The whole library has a deliberately tiny, exposed core: adding a new plot type is a 3-step recipe, not an architecture project.
7
+ plotlet is built for **multi-panel scientific figures with custom plot types** — genome tracks, spike rasters, climate stacks, Manhattan plots, phylogenetic trees. The core ships ~5 standard plots plus multi-panel composition (`|`, `/`, `share_x()`).
8
8
 
9
- It's a **scaffold, not a feature catalog**: the core ships ~5 standard plots and the infrastructure for extending. Custom plot types live in your own project (or [`cookbook/`](cookbook/)), not upstream. See [docs/PHILOSOPHY.md](docs/PHILOSOPHY.md) for the full framing.
9
+ Custom plot types are a 3-step recipe (`record`, `xdomain`/`ydomain`, `draw`) and live in your own project (or [`cookbook/`](cookbook/)) rather than upstream. See [docs/PHILOSOPHY.md](docs/PHILOSOPHY.md) for the full framing.
10
10
 
11
11
  ```python
12
12
  import plotlet as pt
@@ -30,12 +30,13 @@ pip install plotlet
30
30
 
31
31
  ## Properties
32
32
 
33
- - **Lightweight.** `fonttools` for font handling. numpy / pandas / polars inputs work transparently if you have them.
33
+ - **Minimal dependencies.** `fonttools` for font handling. numpy / pandas / polars inputs work transparently if you have them.
34
34
  - **Static SVG output.** No interactivity, no animation. Same script → byte-identical SVG.
35
35
  - **Cross-machine reproducible.** Bundled DejaVu Sans + text-as-paths means rendering is identical on Linux, macOS, Windows, headless CI.
36
- - **Jupyter-native.** `Figure._repr_html_` auto-renders the last expression in a cell.
37
- - **Tiny output.** Each plot is ~50 KB SVG, self-contained.
38
- - **Compositional.** Multi-panel layouts via `|`, `/`, `pt.grid`; share scales with `share_x=` / `share_y=`; layout-level legend with `pt.legend()` covering both discrete swatches and continuous gradients (the colorbar).
36
+ - **Jupyter-native.** `Chart._repr_html_` auto-renders the last expression in a cell.
37
+ - **Compact output.** Each plot is ~50 KB SVG, self-contained.
38
+ - **Compositional.** Multi-panel layouts via `|`, `/`, `pt.grid`; share scales with `(a | b).share_x()` or `pt.grid(..., share_x="col")`; layout-level legend with `pt.legend()` covering both discrete swatches and continuous gradients (the colorbar).
39
+ - **AI-readable.** Every figure ships `data-plotlet-*` attributes describing plot type, axes, scales, ranges, and series labels — readable in one XML parse, no glyph-path OCR. Schema: [docs/AI_ATTRS.md](docs/AI_ATTRS.md).
39
40
 
40
41
  ## API
41
42
 
@@ -45,7 +46,7 @@ pip install plotlet
45
46
 
46
47
  Pass at construction (`pt.chart(data, title=..., grid=True, ...)`) or as chained setters (`c.title(...)`, etc.):
47
48
 
48
- `title`, `xlabel`, `ylabel`, `xlim=(a, b)`, `ylim=(a, b)`, `xscale="linear"|"log"|"category"` (chained: `c.xscale("category", order=[...], padding=0)`), `yscale=...`, `grid=True/False`, `legend=True/False`, `data_width`, `data_height` (the data region — preferred), or `canvas_width`, `canvas_height` (the full SVG canvas — mutually exclusive with the data form). Sizes accept bare pixels (`400`) or unit-suffixed strings (`"4in"`, `"10cm"`, `"100mm"`, `"72pt"`).
49
+ `title`, `xlabel`, `ylabel`, `xlim=(a, b)`, `ylim=(a, b)`, `xscale="linear"|"log"|"category"` (chained: `c.xscale("category", order=[...], padding=0)`), `yscale=...`, `grid=True/False`, `legend=True/False`, `data_width`, `data_height` (the data region — the figure canvas grows to fit titles, tick labels, and axis labels). Sizes accept bare pixels (`400`) or unit-suffixed strings (`"4in"`, `"10cm"`, `"100mm"`, `"72pt"`). To fit a composition into a target SVG canvas, chain `.fit(canvas_width=…, canvas_height=…)` after composing — it rescales data regions while keeping fonts, spines, and margins at their absolute pixel sizes.
49
50
 
50
51
  String-valued data on either axis (`scatter(["a","b","c"], ...)`, `bar`, …) auto-switches to a categorical scale, alphabetical by default. `padding=0` makes category bands contiguous (heatmap-track look).
51
52
 
@@ -63,11 +64,14 @@ Tick customization: `c.xticks([0, 5, 10], ["A","B","C"], rotation=45, fontsize=1
63
64
  | `.axhline(y, **opts)` / `.axvline(x, **opts)` | `color`, `linewidth`, `linestyle`, `alpha`, `label`, axes-fraction `xmin`/`xmax` (or `ymin`/`ymax`) |
64
65
  | `.axhspan(ymin, ymax, **opts)` / `.axvspan(xmin, xmax, **opts)` | `color`, `alpha`, `label`, axes-fraction `xmin`/`xmax` (or `ymin`/`ymax`) |
65
66
  | `.imshow(data, **opts)` | `cmap` (any matplotlib name, default `"viridis"`), `vmin`, `vmax`, `extent=(left, right, bottom, top)` |
67
+ | `.heatmap(df, **opts)` | `cmap`, `vmin`, `vmax`, `norm`, `center`, `xticklabels`, `yticklabels`, `legend` |
66
68
 
67
69
  `hue=<col>` (on `.line` / `.scatter`) splits into one call per unique value with auto-labels and tab10 colors. Reference lines and spans default to black; spans use `alpha=0.2`. They're drawn outside the data color cycle and don't participate in autoscaling — they're decorations on the frame, not data.
68
70
 
69
71
  `.imshow(data)` renders a 2-D array as a colored grid. Small grids (`nrows × ncols ≤ 10000`) emit one `<rect>` per cell and stay vector-clean at any zoom; larger grids encode as a single base64 PNG and quantize to 256 levels. Image row 0 is rendered at the top of its rectangle; the y axis stays Cartesian (small at bottom). All ~180 matplotlib colormaps are vendored — see `pt.list_colormaps()`.
70
72
 
73
+ `.heatmap(df)` is the DataFrame-aware companion to `.imshow`. A pandas DataFrame's `index` becomes the row tick labels and `columns` becomes the column tick labels; row 0 sits at the top. For a plain 2-D array, default labels are integer indices — pass `xticklabels=` / `yticklabels=` to override. Cells render at integer + 0.5 centers on a linear axis, which lines up with scipy's dendrogram leaf positions so a top/left dendrogram pairs cleanly via `share_x` / `share_y`.
74
+
71
75
  ### Subplots
72
76
 
73
77
  Compose multi-panel layouts with operators on `Chart`:
@@ -81,10 +85,11 @@ pt.grid([[a, b], # 2-D grid; cells may be `None`
81
85
  [c, d]])
82
86
 
83
87
  # Share x or y across panels — collapses the gap between them
84
- # and forces both onto the source's scale.
88
+ # and unions data ranges; the first leaf in reading order anchors
89
+ # the scale, others are aspect-scaled to match.
85
90
  top = pt.chart()
86
- main = pt.chart(share_x=top)
87
- top / main # vertically stacked, x-axis joined
91
+ main = pt.chart()
92
+ (top / main).share_x() # vertically stacked, x-axis joined
88
93
 
89
94
  # Layout-level legend (covers colorbar and discrete swatches in
90
95
  # one constructor — geometry follows from the source's color mapping).
@@ -118,7 +123,7 @@ c.write_html("plot.html") # standalone HTML
118
123
 
119
124
  ## Adding a new plot type
120
125
 
121
- plotlet's central hackability claim: a custom plot type is a 3-step recipe (~50–100 lines) that gets axes, scales, legend, grid, and composability for free. The recommended home is your own project, or [`cookbook/`](cookbook/) as reference. Full guide: [docs/EXTENDING.md](docs/EXTENDING.md).
126
+ plotlet is designed so that adding a new plot type is a 3-step recipe (~50–100 lines) that gets axes, scales, legend, grid, and composability for free. The recommended home is your own project, or [`cookbook/`](cookbook/) as reference. Full guide: [docs/EXTENDING.md](docs/EXTENDING.md).
122
127
 
123
128
  ## Testing
124
129
 
@@ -132,7 +137,7 @@ python tests/test_subplots.py # subplot baselines + composition invarian
132
137
  ## Non-goals
133
138
 
134
139
  - No interactivity (hover, zoom, click). Static rendering is the point.
135
- - Not competing with matplotlib on standard plots; matplotlib is bigger and battle-tested.
140
+ - Not aiming for full coverage of standard statistical plots — those needs are well-served elsewhere.
136
141
  - Not a 3D plotter, not a dashboard tool.
137
142
  - Not a feature catalog — new plot types belong in user projects or `cookbook/`, not in the core.
138
143
 
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "plotlet"
7
- version = "0.2.0"
8
- description = "Small, hackable Python library that emits matplotlib-style SVG plots."
7
+ version = "0.4.0"
8
+ description = "Python library for SVG plots, with multi-panel composition and an extension API for custom plot types."
9
9
  readme = "README.md"
10
10
  license = "MIT"
11
11
  license-files = ["LICENSE"]
@@ -36,6 +36,7 @@ classifiers = [
36
36
  ]
37
37
  dependencies = [
38
38
  "fonttools>=4.0",
39
+ "scipy>=1.10",
39
40
  ]
40
41
 
41
42
  [project.optional-dependencies]
@@ -0,0 +1,33 @@
1
+ """plotlet — pure-Python SVG renderer, matplotlib-flavored.
2
+
3
+ import plotlet as pt
4
+ c = pt.chart(df, title="...", xlabel="x", ylabel="y", legend=True, grid=True)
5
+ c.line(x="time", y="value", hue="series")
6
+ c # auto-renders in Jupyter
7
+
8
+ For non-tabular use, the chained form works directly on the same Chart:
9
+
10
+ c = pt.chart()
11
+ c.plot([1, 2, 3], [1, 4, 9], label="squares")
12
+ c.title("Hello").legend().grid(True)
13
+ c
14
+ """
15
+ from ._spec import SPEC
16
+ from .colors import TAB10, colors
17
+ from .colormaps import colormap, list_colormaps
18
+ from .chart import Chart, chart
19
+ from .layout import grid
20
+ from .legend import legend
21
+ from .registry import ArtistSpec, add_artist
22
+ from .layout_diagram import layout_diagram
23
+
24
+ __all__ = ["chart", "Chart", "SPEC", "TAB10", "colors",
25
+ "colormap", "list_colormaps", "grid", "legend",
26
+ "ArtistSpec", "add_artist", "layout_diagram"]
27
+
28
+ # Single source of truth: pyproject.toml. importlib.metadata reads it at
29
+ # runtime from the installed package metadata (works for `pip install` and
30
+ # `pip install -e .` alike). Migrating to setuptools_scm later doesn't
31
+ # touch this code — it just changes how pyproject.toml's version is set.
32
+ from importlib.metadata import version as _pkg_version
33
+ __version__ = _pkg_version("plotlet")