Skip to content

Commit 5b310ae

Browse files
authored
Rename plot to render (#59)
* Renamed plot to render, added plot(x, y) to canvas * Formatting * Raise warning
1 parent edd4c93 commit 5b310ae

19 files changed

Lines changed: 246 additions & 162 deletions

‎README.md‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ canvas.show()
4545

4646
![](README_files/figure-commonmark/cell-3-output-1.png)
4747

48+
Use `canvas.plot(x, y)` to add data directly to a canvas. When you want
49+
to explicitly render an already-built canvas, use `canvas.render(...)`.
50+
The older `canvas.plot(backend=...)` spelling is still supported for
51+
compatibility, but emits a `FutureWarning`.
52+
4853
For Matplotlib-specific customization, pass method calls declaratively.
4954
Figure methods run once and axes methods run for every subplot,
5055
providing access to any Matplotlib API without requiring a maxplotlib
@@ -149,14 +154,14 @@ parts of a mixed canvas, explicitly opt into skipping unsupported
149154
primitives:
150155

151156
``` python
152-
plotly_canvas.plot(backend="plotly", allow_unsupported=True)
157+
plotly_canvas.render(backend="plotly", allow_unsupported=True)
153158
```
154159

155160
Render the same line graph directly in the terminal with the `plotext`
156161
backend:
157162

158163
``` python
159-
terminal_fig = canvas.plot(backend="plotext")
164+
terminal_fig = canvas.render(backend="plotext")
160165
print(terminal_fig.build(keep_colors=False))
161166
```
162167

@@ -270,7 +275,7 @@ canvas.show(backend="plotext")
270275
1.0 1.8 3.2 5.6 10.0
271276
y x
272277

273-
<maxplotlib.backends.plotext.figure.PlotextFigure at 0x110a30550>
278+
<maxplotlib.backends.plotext.figure.PlotextFigure at 0x10ee1ce10>
274279

275280
### Layers
276281

‎README.qmd‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ Plot the figure with the default (matplotlib) backend:
4242
canvas.show()
4343
```
4444

45+
Use `canvas.plot(x, y)` to add data directly to a canvas. When you want to
46+
explicitly render an already-built canvas, use `canvas.render(...)`. The older
47+
`canvas.plot(backend=...)` spelling is still supported for compatibility, but
48+
emits a `FutureWarning`.
49+
4550
For Matplotlib-specific customization, pass method calls declaratively. Figure
4651
methods run once and axes methods run for every subplot, providing access to
4752
any Matplotlib API without requiring a maxplotlib wrapper:
@@ -149,13 +154,13 @@ of a mixed canvas, explicitly opt into skipping unsupported primitives:
149154

150155
```{python}
151156
#| output: false
152-
plotly_canvas.plot(backend="plotly", allow_unsupported=True)
157+
plotly_canvas.render(backend="plotly", allow_unsupported=True)
153158
```
154159

155160
Render the same line graph directly in the terminal with the `plotext` backend:
156161

157162
```{python}
158-
terminal_fig = canvas.plot(backend="plotext")
163+
terminal_fig = canvas.render(backend="plotext")
159164
print(terminal_fig.build(keep_colors=False))
160165
```
161166

‎src/maxplotlib/canvas/canvas.py‎

Lines changed: 57 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
import re
3+
import warnings
34
from dataclasses import dataclass
45
from typing import Mapping
56

@@ -1686,7 +1687,7 @@ def savefig(
16861687
layers = []
16871688
for layer in self.layers:
16881689
layers.append(layer)
1689-
fig, axs = self.plot(
1690+
fig, axs = self._render(
16901691
show=False,
16911692
backend="matplotlib",
16921693
savefig=True,
@@ -1707,7 +1708,7 @@ def savefig(
17071708
savefig_kwargs = {"dpi": self.dpi} if self.dpi is not None else {}
17081709
self._matplotlib_fig.savefig(full_filepath, **savefig_kwargs)
17091710
else:
1710-
fig, axs = self.plot(
1711+
fig, axs = self._render(
17111712
backend="matplotlib",
17121713
savefig=True,
17131714
layers=layers,
@@ -1721,7 +1722,7 @@ def savefig(
17211722
layers = []
17221723
for layer in self.layers:
17231724
layers.append(layer)
1724-
figure = self.plot(
1725+
figure = self._render(
17251726
backend="plotext",
17261727
savefig=False,
17271728
layers=layers,
@@ -1735,7 +1736,7 @@ def savefig(
17351736
full_filepath = filename
17361737
else:
17371738
full_filepath = f"{filename_no_extension}_{layers}.{extension}"
1738-
figure = self.plot(
1739+
figure = self._render(
17391740
backend="plotext",
17401741
savefig=False,
17411742
layers=layers,
@@ -1749,7 +1750,7 @@ def savefig(
17491750
for layer in self.layers:
17501751
layers.append(layer)
17511752
full_filepath = f"{filename_no_extension}_{layers}{extension}"
1752-
fig = self.plot(
1753+
fig = self._render(
17531754
backend="plotly",
17541755
savefig=False,
17551756
layers=layers,
@@ -1763,7 +1764,7 @@ def savefig(
17631764
full_filepath = filename
17641765
else:
17651766
full_filepath = f"{filename_no_extension}_{layers}{extension}"
1766-
fig = self.plot(
1767+
fig = self._render(
17671768
backend="plotly",
17681769
savefig=False,
17691770
layers=layers,
@@ -1776,12 +1777,12 @@ def savefig(
17761777
raise NotImplementedError(
17771778
"Layer-by-layer rendering is not supported for tikzfigure backend"
17781779
)
1779-
fig = self.plot(backend="tikzfigure", savefig=False)
1780+
fig = self._render(backend="tikzfigure", savefig=False)
17801781
fig.savefig(filename)
17811782
if verbose:
17821783
print(f"Saved {filename}")
17831784

1784-
def plot(
1785+
def _render(
17851786
self,
17861787
backend: Backends = "matplotlib",
17871788
savefig: bool = False,
@@ -1844,6 +1845,52 @@ def plot(
18441845
else:
18451846
raise ValueError(f"Invalid backend: {backend}")
18461847

1848+
def plot(self, *args, backend=None, **kwargs):
1849+
"""Add a line, or render when called with backend options.
1850+
1851+
``canvas.plot(x, y, **style)`` is the convenient direct plotting form.
1852+
Rendering is named explicitly by ``canvas.render(...)``; the legacy
1853+
``canvas.plot(backend=...)`` form remains supported.
1854+
"""
1855+
explicit_render = backend is not None or (args and isinstance(args[0], str))
1856+
if args and not isinstance(args[0], str):
1857+
if len(args) < 2:
1858+
raise TypeError("plot(x, y) requires both x and y data")
1859+
if len(args) > 2:
1860+
raise TypeError("plot() accepts only x and y positional data")
1861+
layer = kwargs.pop("layer", 0)
1862+
row = kwargs.pop("row", None)
1863+
col = kwargs.pop("col", None)
1864+
self.add_line(args[0], args[1], layer=layer, row=row, col=col, **kwargs)
1865+
return self
1866+
if args:
1867+
if len(args) > 1:
1868+
raise TypeError(
1869+
"plot() accepts at most one backend positional argument"
1870+
)
1871+
if backend is not None:
1872+
raise TypeError("backend was provided both positionally and by keyword")
1873+
backend = args[0]
1874+
if backend is None:
1875+
backend = "matplotlib"
1876+
if explicit_render:
1877+
warnings.warn(
1878+
"canvas.plot(backend=...) is deprecated; use "
1879+
"canvas.render(backend=...) instead",
1880+
FutureWarning,
1881+
stacklevel=2,
1882+
)
1883+
return self._render(backend=backend, **kwargs)
1884+
1885+
def render(self, *args, **kwargs):
1886+
"""Render the canvas using the selected backend.
1887+
1888+
This is the explicit name for the operation historically exposed as
1889+
``Canvas.plot(backend=...)``. The latter remains available for
1890+
backwards compatibility.
1891+
"""
1892+
return self._render(*args, **kwargs)
1893+
18471894
def show(
18481895
self,
18491896
backend: Backends = "matplotlib",
@@ -1883,7 +1930,7 @@ def show(
18831930
if backend == "matplotlib":
18841931
if verbose:
18851932
print("Generating Matplotlib figure for display...")
1886-
fig, axes = self.plot(
1933+
fig, axes = self._render(
18871934
backend="matplotlib",
18881935
savefig=False,
18891936
layers=layers,
@@ -2621,5 +2668,5 @@ def __str__(self):
26212668
c = Canvas(ncols=2, nrows=2)
26222669
sp = c.add_subplot()
26232670
sp.plot([0, 1, 2, 3], [0, 1, 4, 9], label="Line 1")
2624-
c.plot(backend="matplotlib")
2671+
c.render(backend="matplotlib")
26252672
print("done")

‎src/maxplotlib/tests/test_canvas.py‎

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ def test_canvas_show_uses_ipython_display_in_jupyter(monkeypatch):
376376
monkeypatch.setitem(sys.modules, "IPython", ipython)
377377
monkeypatch.setitem(sys.modules, "IPython.display", ipython_display)
378378
monkeypatch.setattr(plt, "close", lambda value: closed.append(value))
379-
monkeypatch.setattr(Canvas, "plot", lambda *args, **kwargs: (fig, object()))
379+
monkeypatch.setattr(Canvas, "_render", lambda *args, **kwargs: (fig, object()))
380380
monkeypatch.setattr(plt, "show", lambda: pytest.fail("pyplot.show was called"))
381381

382382
canvas = Canvas()
@@ -880,6 +880,39 @@ def test_axis_getters_reflect_configured_state():
880880
assert canvas.get_ymargin() == 0.2
881881

882882

883+
def test_render_is_the_explicit_rendering_alias():
884+
from maxplotlib import Canvas
885+
886+
canvas = Canvas()
887+
canvas.add_line([0, 1], [0, 1])
888+
889+
rendered = canvas.render(backend="plotly")
890+
891+
assert rendered is not None
892+
893+
894+
def test_plot_adds_line_data_when_given_x_and_y():
895+
from maxplotlib import Canvas
896+
897+
canvas = Canvas()
898+
result = canvas.plot([0, 1], [1, 2], color="purple", label="line")
899+
900+
assert result is canvas
901+
assert canvas.render(backend="plotly").data[0].name == "line"
902+
903+
904+
def test_legacy_plot_backend_form_warns():
905+
import pytest
906+
907+
from maxplotlib import Canvas
908+
909+
canvas = Canvas()
910+
canvas.add_line([0, 1], [0, 1])
911+
912+
with pytest.warns(FutureWarning, match=r"canvas\.render"):
913+
canvas.plot(backend="plotly")
914+
915+
883916
def test_matplotlib_postprocess_can_customize_figure_and_axes():
884917
import matplotlib.pyplot as plt
885918
from matplotlib.colors import to_rgba

‎tutorials/tutorial_01.ipynb‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"y = np.sin(x)\n",
8181
"\n",
8282
"canvas = Canvas()\n",
83-
"canvas.add_line(x, y)\n",
83+
"canvas.plot(x, y)\n",
8484
"canvas.show(backend=BACKEND)"
8585
]
8686
},
@@ -101,13 +101,13 @@
101101
"source": [
102102
"canvas = Canvas()\n",
103103
"\n",
104-
"canvas.add_line(\n",
104+
"canvas.plot(\n",
105105
" x, np.sin(x), label=\"sin(x)\", color=\"royalblue\", linestyle=\"solid\", linewidth=2\n",
106106
")\n",
107-
"canvas.add_line(\n",
107+
"canvas.plot(\n",
108108
" x, np.cos(x), label=\"cos(x)\", color=\"tomato\", linestyle=\"dashed\", linewidth=2\n",
109109
")\n",
110-
"canvas.add_line(\n",
110+
"canvas.plot(\n",
111111
" x,\n",
112112
" np.sin(2 * x),\n",
113113
" label=\"sin(2x)\",\n",
@@ -144,8 +144,8 @@
144144
"source": [
145145
"canvas = Canvas(ratio=0.5, fontsize=12)\n",
146146
"\n",
147-
"canvas.add_line(x, np.sin(x), label=\"sin(x)\", color=\"steelblue\")\n",
148-
"canvas.add_line(x, np.cos(x), label=\"cos(x)\", color=\"darkorange\", linestyle=\"dashed\")\n",
147+
"canvas.plot(x, np.sin(x), label=\"sin(x)\", color=\"steelblue\")\n",
148+
"canvas.plot(x, np.cos(x), label=\"cos(x)\", color=\"darkorange\", linestyle=\"dashed\")\n",
149149
"\n",
150150
"canvas.set_xlabel(\"angle (rad)\")\n",
151151
"canvas.set_ylabel(\"amplitude\")\n",
@@ -183,8 +183,8 @@
183183
" legend=True,\n",
184184
")\n",
185185
"\n",
186-
"canvas.add_line(x, np.sin(x), label=\"sin\", color=\"royalblue\")\n",
187-
"canvas.add_line(x, x / (2 * np.pi), label=\"x/2π\", color=\"coral\", linestyle=\"dashed\")\n",
186+
"canvas.plot(x, np.sin(x), label=\"sin\", color=\"royalblue\")\n",
187+
"canvas.plot(x, x / (2 * np.pi), label=\"x/2π\", color=\"coral\", linestyle=\"dashed\")\n",
188188
"\n",
189189
"canvas.show(backend=BACKEND)"
190190
]
@@ -209,7 +209,7 @@
209209
"source": [
210210
"canvas = Canvas(ratio=0.5)\n",
211211
"ax = canvas.add_subplot(xlabel=\"x\", ylabel=\"sin(x)\", grid=True)\n",
212-
"canvas.add_line(x, np.sin(x), color=\"steelblue\")\n",
212+
"canvas.plot(x, np.sin(x), color=\"steelblue\")\n",
213213
"\n",
214214
"canvas.savefig(\"tutorial_01_output.png\")\n",
215215
"print(\"Figure saved to tutorial_01_output.png\")"
@@ -225,7 +225,7 @@
225225
"| Task | Code |\n",
226226
"|---|---|\n",
227227
"| Create a canvas | `canvas = Canvas()` |\n",
228-
"| Add a line | `canvas.add_line(x, y, label=..., color=..., linestyle=...)` |\n",
228+
"| Add a line | `canvas.plot(x, y, label=..., color=..., linestyle=...)` |\n",
229229
"| Optional Matplotlib-style axes | `canvas, ax = Canvas.subplots()` |\n",
230230
"| Labels / title | `canvas.set_xlabel()`, `canvas.set_ylabel()`, `canvas.set_title()` |\n",
231231
"| Legend / grid | `canvas.set_legend(True)`, `canvas.set_grid(True)` |\n",

‎tutorials/tutorial_02.ipynb‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,8 +321,8 @@
321321
"canvas.add_subplot(row=0, col=0, title=\"Left\", xlabel=\"x\", ylabel=\"sin\")\n",
322322
"canvas.add_subplot(row=0, col=1, title=\"Right\", xlabel=\"x\", ylabel=\"cos\")\n",
323323
"\n",
324-
"canvas.add_line(x, np.sin(x), row=0, col=0, color=\"royalblue\", label=\"sin\")\n",
325-
"canvas.add_line(x, np.cos(x), row=0, col=1, color=\"tomato\", label=\"cos\")\n",
324+
"canvas.plot(x, np.sin(x), row=0, col=0, color=\"royalblue\", label=\"sin\")\n",
325+
"canvas.plot(x, np.cos(x), row=0, col=1, color=\"tomato\", label=\"cos\")\n",
326326
"\n",
327327
"canvas.set_legend(True, row=0, col=0)\n",
328328
"canvas.set_legend(True, row=0, col=1)\n",
@@ -346,7 +346,7 @@
346346
"| Get subplot | `canvas.subplot(r, c)` or `canvas[r, c]` |\n",
347347
"| Loop panels | `for row, col, sp in canvas.iter_subplots()` |\n",
348348
"| Figure title | `canvas.suptitle('...')` |\n",
349-
"| Route plot | `canvas.add_line(x, y, row=r, col=c)` |\n",
349+
"| Route plot | `canvas.plot(x, y, row=r, col=c)` |\n",
350350
"\n",
351351
"Next: **Tutorial 03** covers all the available plot types."
352352
]

0 commit comments

Comments
 (0)