plotnine 0.14.5__py3-none-any.whl → 0.15.0a2__py3-none-any.whl

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 (92) hide show
  1. plotnine/__init__.py +31 -37
  2. plotnine/_mpl/gridspec.py +265 -0
  3. plotnine/_mpl/layout_manager/__init__.py +6 -0
  4. plotnine/_mpl/layout_manager/_engine.py +87 -0
  5. plotnine/_mpl/layout_manager/_layout_items.py +957 -0
  6. plotnine/_mpl/layout_manager/_layout_tree.py +905 -0
  7. plotnine/_mpl/layout_manager/_spaces.py +1154 -0
  8. plotnine/_mpl/patches.py +70 -34
  9. plotnine/_mpl/text.py +159 -37
  10. plotnine/_mpl/utils.py +78 -10
  11. plotnine/_utils/__init__.py +35 -9
  12. plotnine/_utils/dev.py +45 -27
  13. plotnine/_utils/yippie.py +115 -0
  14. plotnine/animation.py +1 -1
  15. plotnine/coords/coord.py +3 -3
  16. plotnine/coords/coord_trans.py +1 -1
  17. plotnine/data/__init__.py +43 -8
  18. plotnine/data/anscombe-quartet.csv +45 -0
  19. plotnine/doctools.py +2 -2
  20. plotnine/facets/facet.py +34 -43
  21. plotnine/facets/facet_grid.py +14 -6
  22. plotnine/facets/facet_wrap.py +3 -5
  23. plotnine/facets/strips.py +20 -33
  24. plotnine/geoms/annotate.py +3 -3
  25. plotnine/geoms/annotation_logticks.py +2 -0
  26. plotnine/geoms/annotation_stripes.py +2 -0
  27. plotnine/geoms/geom.py +3 -3
  28. plotnine/geoms/geom_bar.py +10 -2
  29. plotnine/geoms/geom_col.py +6 -0
  30. plotnine/geoms/geom_crossbar.py +2 -3
  31. plotnine/geoms/geom_path.py +2 -2
  32. plotnine/geoms/geom_violin.py +24 -7
  33. plotnine/ggplot.py +95 -66
  34. plotnine/guides/guide.py +19 -20
  35. plotnine/guides/guide_colorbar.py +6 -6
  36. plotnine/guides/guide_legend.py +15 -16
  37. plotnine/guides/guides.py +8 -8
  38. plotnine/helpers.py +49 -0
  39. plotnine/iapi.py +33 -7
  40. plotnine/labels.py +8 -3
  41. plotnine/layer.py +4 -4
  42. plotnine/mapping/_env.py +2 -2
  43. plotnine/mapping/_eval_environment.py +85 -0
  44. plotnine/mapping/aes.py +14 -30
  45. plotnine/mapping/evaluation.py +7 -65
  46. plotnine/options.py +14 -7
  47. plotnine/plot_composition/__init__.py +10 -0
  48. plotnine/plot_composition/_compose.py +462 -0
  49. plotnine/plot_composition/_plotspec.py +50 -0
  50. plotnine/plot_composition/_spacer.py +32 -0
  51. plotnine/positions/position_dodge.py +1 -1
  52. plotnine/positions/position_dodge2.py +1 -1
  53. plotnine/positions/position_stack.py +1 -2
  54. plotnine/qplot.py +1 -2
  55. plotnine/scales/__init__.py +0 -6
  56. plotnine/scales/limits.py +7 -7
  57. plotnine/scales/scale.py +4 -4
  58. plotnine/scales/scale_continuous.py +2 -1
  59. plotnine/scales/scale_identity.py +10 -2
  60. plotnine/scales/scale_manual.py +6 -2
  61. plotnine/stats/binning.py +5 -2
  62. plotnine/stats/smoothers.py +3 -5
  63. plotnine/stats/stat.py +3 -3
  64. plotnine/stats/stat_bindot.py +1 -3
  65. plotnine/stats/stat_density.py +2 -2
  66. plotnine/stats/stat_qq_line.py +1 -1
  67. plotnine/stats/stat_sina.py +34 -1
  68. plotnine/themes/elements/__init__.py +3 -0
  69. plotnine/themes/elements/element_text.py +35 -24
  70. plotnine/themes/elements/margin.py +137 -61
  71. plotnine/themes/targets.py +3 -1
  72. plotnine/themes/theme.py +21 -7
  73. plotnine/themes/theme_538.py +0 -1
  74. plotnine/themes/theme_bw.py +0 -1
  75. plotnine/themes/theme_dark.py +0 -1
  76. plotnine/themes/theme_gray.py +32 -34
  77. plotnine/themes/theme_light.py +1 -1
  78. plotnine/themes/theme_matplotlib.py +28 -31
  79. plotnine/themes/theme_seaborn.py +36 -36
  80. plotnine/themes/theme_void.py +25 -27
  81. plotnine/themes/theme_xkcd.py +0 -1
  82. plotnine/themes/themeable.py +369 -169
  83. plotnine/typing.py +3 -3
  84. plotnine/watermark.py +3 -3
  85. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/METADATA +8 -5
  86. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/RECORD +89 -78
  87. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/WHEEL +1 -1
  88. plotnine/_mpl/_plot_side_space.py +0 -888
  89. plotnine/_mpl/_plotnine_tight_layout.py +0 -293
  90. plotnine/_mpl/layout_engine.py +0 -110
  91. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info/licenses}/LICENSE +0 -0
  92. {plotnine-0.14.5.dist-info → plotnine-0.15.0a2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,462 @@
1
+ from __future__ import annotations
2
+
3
+ import abc
4
+ from copy import deepcopy
5
+ from dataclasses import dataclass
6
+ from io import BytesIO
7
+ from typing import TYPE_CHECKING
8
+
9
+ from .._utils.ipython import (
10
+ get_display_function,
11
+ get_ipython,
12
+ )
13
+ from ..options import get_option
14
+ from ._plotspec import plotspec
15
+
16
+ if TYPE_CHECKING:
17
+ from pathlib import Path
18
+ from typing import Generator, Iterator, Self
19
+
20
+ from matplotlib.figure import Figure
21
+
22
+ from plotnine._mpl.gridspec import p9GridSpec
23
+ from plotnine._utils.ipython import FigureFormat
24
+ from plotnine.ggplot import PlotAddable, ggplot
25
+
26
+
27
+ class Compose:
28
+ """
29
+ Arrange two or more plots
30
+
31
+ Parameters
32
+ ----------
33
+ operands:
34
+ The objects to be put together (composed).
35
+ """
36
+
37
+ def __init__(self, operands: list[ggplot | Compose]):
38
+ self.operands = operands
39
+
40
+ # These are created in the _create_figure method
41
+ self.figure: Figure
42
+ self.plotspecs: list[plotspec]
43
+ self.gridspec: p9GridSpec
44
+
45
+ @abc.abstractmethod
46
+ def __or__(self, rhs: ggplot | Compose) -> Compose:
47
+ """
48
+ Add rhs as a column
49
+ """
50
+
51
+ @abc.abstractmethod
52
+ def __truediv__(self, rhs: ggplot | Compose) -> Compose:
53
+ """
54
+ Add rhs as a row
55
+ """
56
+
57
+ def __add__(self, rhs: ggplot | Compose | PlotAddable) -> Compose:
58
+ """
59
+ Add rhs to the composition
60
+
61
+ Parameters
62
+ ----------
63
+ rhs:
64
+ What to add to the composition
65
+ """
66
+ from plotnine import ggplot
67
+
68
+ if not isinstance(rhs, (ggplot, Compose)):
69
+ cmp = deepcopy(self)
70
+ cmp.last_plot = cmp.last_plot + rhs
71
+ return cmp
72
+ return self.__class__([*self, rhs])
73
+
74
+ def __sub__(self, rhs: ggplot | Compose) -> Compose:
75
+ """
76
+ Add the rhs besides the composition
77
+
78
+ Parameters
79
+ ----------
80
+ rhs:
81
+ What to place besides the composition
82
+ """
83
+ return self.__class__([self, rhs])
84
+
85
+ def __and__(self, rhs: PlotAddable) -> Compose:
86
+ """
87
+ Add rhs to all plots in the composition
88
+
89
+ Parameters
90
+ ----------
91
+ rhs:
92
+ What to add.
93
+ """
94
+ self = deepcopy(self)
95
+
96
+ def add_other(op: Compose):
97
+ for item in op:
98
+ if isinstance(item, Compose):
99
+ add_other(item)
100
+ else:
101
+ item += rhs
102
+
103
+ add_other(self)
104
+ return self
105
+
106
+ def __mul__(self, rhs: PlotAddable) -> Compose:
107
+ """
108
+ Add rhs to the outermost nesting level of the composition
109
+
110
+ Parameters
111
+ ----------
112
+ rhs:
113
+ What to add.
114
+ """
115
+ from plotnine import ggplot
116
+
117
+ self = deepcopy(self)
118
+
119
+ for item in self:
120
+ if isinstance(item, ggplot):
121
+ item += rhs
122
+ return self
123
+
124
+ def __len__(self) -> int:
125
+ """
126
+ Number of operand
127
+ """
128
+ return len(self.operands)
129
+
130
+ def __iter__(self) -> Iterator[ggplot | Compose]:
131
+ """
132
+ Return an iterable of all the operands
133
+ """
134
+ return iter(self.operands)
135
+
136
+ def _ipython_display_(self):
137
+ """
138
+ Display plot in the output of the cell
139
+ """
140
+ return self._display()
141
+
142
+ @property
143
+ def nrow(self) -> int:
144
+ """
145
+ Number of rows in the composition
146
+ """
147
+ return 0
148
+
149
+ @property
150
+ def ncol(self) -> int:
151
+ """
152
+ Number of cols in the composition
153
+ """
154
+ return 0
155
+
156
+ @property
157
+ def last_plot(self) -> ggplot:
158
+ """
159
+ Last plot added to the composition
160
+ """
161
+ from plotnine import ggplot
162
+
163
+ last_operand = self.operands[-1]
164
+ if isinstance(last_operand, ggplot):
165
+ return last_operand
166
+ else:
167
+ return last_operand.last_plot
168
+
169
+ @last_plot.setter
170
+ def last_plot(self, plot: ggplot):
171
+ """
172
+ Replace the last plot in the composition
173
+ """
174
+ from plotnine import ggplot
175
+
176
+ last_operand = self.operands[-1]
177
+ if isinstance(last_operand, ggplot):
178
+ self.operands[-1] = plot
179
+ else:
180
+ last_operand.last_plot = plot
181
+
182
+ def __deepcopy__(self, memo):
183
+ """
184
+ Deep copy without copying the figure
185
+ """
186
+ cls = self.__class__
187
+ result = cls.__new__(cls)
188
+ memo[id(self)] = result
189
+ old = self.__dict__
190
+ new = result.__dict__
191
+
192
+ shallow = {"figure", "gridsspec", "__copy"}
193
+ for key, item in old.items():
194
+ if key in shallow:
195
+ new[key] = item
196
+ memo[id(new[key])] = new[key]
197
+ else:
198
+ new[key] = deepcopy(item, memo)
199
+
200
+ old["__copy"] = result
201
+
202
+ return result
203
+
204
+ def _to_retina(self):
205
+ from plotnine import ggplot
206
+
207
+ for item in self:
208
+ if isinstance(item, ggplot):
209
+ item.theme = item.theme.to_retina()
210
+ else:
211
+ item._to_retina()
212
+
213
+ def _create_gridspec(self, figure, nest_into):
214
+ """
215
+ Create the gridspec for this composition
216
+ """
217
+ from plotnine._mpl.gridspec import p9GridSpec
218
+
219
+ self.gridspec = p9GridSpec(
220
+ self.nrow, self.ncol, figure, nest_into=nest_into
221
+ )
222
+
223
+ def _create_figure(self):
224
+ import matplotlib.pyplot as plt
225
+
226
+ from plotnine import ggplot
227
+ from plotnine._mpl.gridspec import p9GridSpec
228
+
229
+ def _make_plotspecs(
230
+ cmp: Compose, parent_gridspec: p9GridSpec | None
231
+ ) -> Generator[plotspec]:
232
+ """
233
+ Return the plot specification for each subplot in the composition
234
+ """
235
+ # This gridspec contains a composition group e.g.
236
+ # (p2 | p3) of p1 | (p2 | p3)
237
+ ss_or_none = parent_gridspec[0] if parent_gridspec else None
238
+ cmp._create_gridspec(self.figure, ss_or_none)
239
+
240
+ # Each subplot in the composition will contain one of:
241
+ # 1. A plot
242
+ # 2. A plot composition
243
+ # 3. Nothing
244
+ # Iterating over the gridspec yields the SubplotSpecs for each
245
+ # "subplot" in the grid. The SubplotSpec is the handle that
246
+ # allows us to set it up for a plot or to nest another gridspec
247
+ # in it.
248
+ for item, subplot_spec in zip(cmp, cmp.gridspec): # pyright: ignore[reportArgumentType]
249
+ if isinstance(item, ggplot):
250
+ yield plotspec(
251
+ item,
252
+ self.figure,
253
+ cmp.gridspec,
254
+ subplot_spec,
255
+ p9GridSpec(1, 1, self.figure, nest_into=subplot_spec),
256
+ )
257
+ elif item:
258
+ yield from _make_plotspecs(
259
+ item,
260
+ p9GridSpec(1, 1, self.figure, nest_into=subplot_spec),
261
+ )
262
+
263
+ self.figure = plt.figure()
264
+ self.plotspecs = list(_make_plotspecs(self, None))
265
+
266
+ def _display(self):
267
+ """
268
+ Display plot in the cells output
269
+
270
+ This function is called for its side-effects.
271
+
272
+ It draws the plot to an io buffer then uses ipython display
273
+ methods to show the result.
274
+ """
275
+ ip = get_ipython()
276
+ format: FigureFormat = get_option(
277
+ "figure_format"
278
+ ) or ip.config.InlineBackend.get("figure_format", "retina")
279
+
280
+ if format == "retina":
281
+ self = deepcopy(self)
282
+ self._to_retina()
283
+
284
+ buf = BytesIO()
285
+ self.save(buf, "png" if format == "retina" else format)
286
+ figure_size_px = self.last_plot.theme._figure_size_px
287
+ display_func = get_display_function(format, figure_size_px)
288
+ display_func(buf.getvalue())
289
+
290
+ def draw(self, *, show: bool = False) -> Figure:
291
+ """
292
+ Render the composed plots
293
+
294
+ Parameters
295
+ ----------
296
+ show :
297
+ Whether to show the plot.
298
+
299
+ Returns
300
+ -------
301
+ :
302
+ Matplotlib figure
303
+ """
304
+ from .._mpl.layout_manager import PlotnineCompositionLayoutEngine
305
+
306
+ with plot_composition_context(self, show):
307
+ self._create_figure()
308
+ figure = self.figure
309
+
310
+ for ps in self.plotspecs:
311
+ ps.plot.draw()
312
+
313
+ self.figure.set_layout_engine(
314
+ PlotnineCompositionLayoutEngine(self)
315
+ )
316
+ return figure
317
+
318
+ def save(self, filename: str | Path | BytesIO, format: str | None = None):
319
+ """
320
+ Save a Compose object as an image file
321
+
322
+ Parameters
323
+ ----------
324
+ filename :
325
+ File name to write the plot to. If not specified, a name
326
+ format :
327
+ Image format to use, automatically extract from
328
+ file name extension.
329
+ """
330
+ figure = self.draw()
331
+ figure.savefig(filename, format=format)
332
+
333
+
334
+ @dataclass
335
+ class plot_composition_context:
336
+ cmp: Compose
337
+ show: bool
338
+
339
+ def __post_init__(self):
340
+ import matplotlib as mpl
341
+
342
+ # The dpi is needed when the figure is created, either as
343
+ # a parameter to plt.figure() or an rcParam.
344
+ # https://github.com/matplotlib/matplotlib/issues/24644
345
+ # When drawing the Composition, the dpi themeable is infective
346
+ # because it sets the rcParam after this figure is created.
347
+ rcParams = {"figure.dpi": self.cmp.last_plot.theme.getp("dpi")}
348
+ self._rc_context = mpl.rc_context(rcParams)
349
+
350
+ def __enter__(self) -> Self:
351
+ """
352
+ Enclose in matplolib & pandas environments
353
+ """
354
+ self._rc_context.__enter__()
355
+ return self
356
+
357
+ def __exit__(self, exc_type, exc_value, exc_traceback):
358
+ import matplotlib.pyplot as plt
359
+
360
+ if exc_type is None:
361
+ if self.show:
362
+ plt.show()
363
+ else:
364
+ plt.close(self.cmp.figure)
365
+ else:
366
+ # There is an exception, close any figure
367
+ if hasattr(self.cmp, "figure"):
368
+ plt.close(self.cmp.figure)
369
+
370
+ self._rc_context.__exit__(exc_type, exc_value, exc_traceback)
371
+
372
+
373
+ class OR(Compose):
374
+ """
375
+ Compose by adding a column
376
+ """
377
+
378
+ @property
379
+ def nrow(self) -> int:
380
+ return 1
381
+
382
+ @property
383
+ def ncol(self) -> int:
384
+ return len(self)
385
+
386
+ def __or__(self, rhs: ggplot | Compose) -> Compose:
387
+ """
388
+ Add rhs as a column
389
+ """
390
+ # This is adjacent or i.e. (OR | rhs) so we collapse the
391
+ # operands into a single operation
392
+ return OR([*self, rhs])
393
+
394
+ def __truediv__(self, rhs: ggplot | Compose) -> Compose:
395
+ """
396
+ Add rhs as a row
397
+ """
398
+ return DIV([self, rhs])
399
+
400
+
401
+ class DIV(Compose):
402
+ """
403
+ Compose by adding a row
404
+ """
405
+
406
+ @property
407
+ def nrow(self) -> int:
408
+ return len(self)
409
+
410
+ @property
411
+ def ncol(self) -> int:
412
+ return 1
413
+
414
+ def __truediv__(self, rhs: ggplot | Compose) -> Compose:
415
+ """
416
+ Add rhs as a row
417
+ """
418
+ # This is an adjacent div i.e. (DIV | rhs) so we collapse the
419
+ # operands into a single operation
420
+ return DIV([*self, rhs])
421
+
422
+ def __or__(self, rhs: ggplot | Compose) -> Compose:
423
+ """
424
+ Add rhs as a column
425
+ """
426
+ return OR([self, rhs])
427
+
428
+
429
+ class ADD(Compose):
430
+ """
431
+ Compose by adding
432
+ """
433
+
434
+ @property
435
+ def nrow(self) -> int:
436
+ from plotnine.facets.facet_wrap import wrap_dims
437
+
438
+ return wrap_dims(len(self))[0]
439
+
440
+ @property
441
+ def ncol(self) -> int:
442
+ from plotnine.facets.facet_wrap import wrap_dims
443
+
444
+ return wrap_dims(len(self))[1]
445
+
446
+ def __or__(self, rhs: ggplot | Compose) -> Compose:
447
+ """
448
+ Add rhs as a column
449
+ """
450
+ return OR([self, rhs])
451
+
452
+ def __truediv__(self, rhs: ggplot | Compose) -> Compose:
453
+ """
454
+ Add rhs as a row
455
+ """
456
+ return DIV([self, rhs])
457
+
458
+ def __sub__(self, rhs: ggplot | Compose) -> Compose:
459
+ """
460
+ Add rhs as a column
461
+ """
462
+ return OR([self, rhs])
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from matplotlib.figure import Figure
8
+ from matplotlib.gridspec import SubplotSpec
9
+
10
+ from plotnine._mpl.gridspec import p9GridSpec
11
+ from plotnine.ggplot import ggplot
12
+
13
+
14
+ @dataclass
15
+ class plotspec:
16
+ """
17
+ Plot Specification
18
+ """
19
+
20
+ plot: ggplot
21
+ """
22
+ Plot
23
+ """
24
+
25
+ figure: Figure
26
+ """
27
+ Figure in which the draw the plot
28
+ """
29
+
30
+ composition_gridspec: p9GridSpec
31
+ """
32
+ The gridspec of the innermost composition group that contains the plot
33
+ """
34
+
35
+ subplotspec: SubplotSpec
36
+ """
37
+ The subplotspec that contains the plot
38
+
39
+ This is the subplot within the composition gridspec and it will
40
+ contain the plot's gridspec.
41
+ """
42
+
43
+ plot_gridspec: p9GridSpec
44
+ """
45
+ The gridspec in which the plot is drawn
46
+ """
47
+
48
+ def __post_init__(self):
49
+ self.plot.figure = self.figure
50
+ self.plot._gridspec = self.plot_gridspec
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ from copy import deepcopy
4
+
5
+ from plotnine import element_rect, ggplot, theme, theme_void
6
+
7
+
8
+ class spacer(ggplot):
9
+ """
10
+ An empty plot
11
+ """
12
+
13
+ def __init__(self):
14
+ super().__init__()
15
+ self.theme = theme_void()
16
+
17
+ def __add__(self, rhs) -> spacer: # pyright: ignore[reportIncompatibleMethodOverride]
18
+ """
19
+ Add to spacer
20
+
21
+ All added objects are no ops except the plot_background,
22
+ i.e.:
23
+
24
+ theme(plot_background=element_rect(fill="red"))
25
+ """
26
+ self = deepcopy(self)
27
+ if isinstance(rhs, theme):
28
+ fill = rhs.getp(("plot_background", "facecolor"))
29
+ self.theme += theme(
30
+ plot_background=element_rect(fill=fill),
31
+ )
32
+ return self
@@ -61,7 +61,7 @@ class position_dodge(position):
61
61
  and ("xmax" not in data)
62
62
  and (self.params["width"] is None)
63
63
  ):
64
- msg = "Width not defined. " "Set with `position_dodge(width = ?)`"
64
+ msg = "Width not defined. Set with `position_dodge(width = ?)`"
65
65
  raise PlotnineError(msg)
66
66
 
67
67
  params = copy(self.params)
@@ -64,7 +64,7 @@ class position_dodge2(position_dodge):
64
64
  and ("xmax" not in data)
65
65
  and (self.params["width"] is None)
66
66
  ):
67
- msg = "Width not defined. " "Set with `position_dodge2(width = ?)`"
67
+ msg = "Width not defined. Set with `position_dodge2(width = ?)`"
68
68
  raise PlotnineError(msg)
69
69
 
70
70
  params = copy(self.params)
@@ -39,8 +39,7 @@ class position_stack(position):
39
39
  if "ymax" in data:
40
40
  if any((data["ymin"] != 0) & (data["ymax"] != 0)):
41
41
  warn(
42
- "Stacking not well defined when not "
43
- "anchored on the axis.",
42
+ "Stacking not well defined when not anchored on the axis.",
44
43
  PlotnineWarning,
45
44
  )
46
45
  var = "ymax"
plotnine/qplot.py CHANGED
@@ -180,8 +180,7 @@ def qplot(
180
180
  return "wrap"
181
181
 
182
182
  warn(
183
- "Could not determine the type of faceting, "
184
- "therefore no faceting.",
183
+ "Could not determine the type of faceting, therefore no faceting.",
185
184
  PlotnineWarning,
186
185
  )
187
186
  return "null"
@@ -79,7 +79,6 @@ from .scale_identity import (
79
79
  # linetype
80
80
  from .scale_linetype import (
81
81
  scale_linetype,
82
- scale_linetype_continuous,
83
82
  scale_linetype_discrete,
84
83
  )
85
84
 
@@ -97,7 +96,6 @@ from .scale_manual import (
97
96
  # shape
98
97
  from .scale_shape import (
99
98
  scale_shape,
100
- scale_shape_continuous,
101
99
  scale_shape_discrete,
102
100
  )
103
101
 
@@ -116,7 +114,6 @@ from .scale_size import (
116
114
  from .scale_stroke import (
117
115
  scale_stroke,
118
116
  scale_stroke_continuous,
119
- scale_stroke_discrete,
120
117
  )
121
118
 
122
119
  # xy position and transforms
@@ -198,11 +195,9 @@ __all__ = (
198
195
  # linetype
199
196
  "scale_linetype",
200
197
  "scale_linetype_discrete",
201
- "scale_linetype_continuous",
202
198
  # shape
203
199
  "scale_shape",
204
200
  "scale_shape_discrete",
205
- "scale_shape_continuous",
206
201
  # size
207
202
  "scale_size",
208
203
  "scale_size_area",
@@ -214,7 +209,6 @@ __all__ = (
214
209
  # stroke
215
210
  "scale_stroke",
216
211
  "scale_stroke_continuous",
217
- "scale_stroke_discrete",
218
212
  # identity
219
213
  "scale_alpha_identity",
220
214
  "scale_color_identity",
plotnine/scales/limits.py CHANGED
@@ -78,10 +78,10 @@ class _lim:
78
78
  self.aesthetic, series, limits=self.limits, trans=self.trans
79
79
  )
80
80
 
81
- def __radd__(self, plot):
82
- scale = self.get_scale(plot)
83
- plot.scales.append(scale)
84
- return plot
81
+ def __radd__(self, other):
82
+ scale = self.get_scale(other)
83
+ other.scales.append(scale)
84
+ return other
85
85
 
86
86
 
87
87
  class xlim(_lim):
@@ -194,7 +194,7 @@ class lims:
194
194
  def __init__(self, **kwargs):
195
195
  self._kwargs = kwargs
196
196
 
197
- def __radd__(self, plot):
197
+ def __radd__(self, other):
198
198
  """
199
199
  Add limits to ggplot object
200
200
  """
@@ -206,9 +206,9 @@ class lims:
206
206
  msg = "Cannot change limits for '{}'"
207
207
  raise PlotnineError(msg) from e
208
208
 
209
- plot += klass(value)
209
+ other += klass(value)
210
210
 
211
- return plot
211
+ return other
212
212
 
213
213
 
214
214
  def expand_limits(**kwargs):
plotnine/scales/scale.py CHANGED
@@ -148,12 +148,12 @@ class scale(
148
148
  self.aesthetics if self.aesthetics else self._aesthetics
149
149
  )
150
150
 
151
- def __radd__(self, plot):
151
+ def __radd__(self, other):
152
152
  """
153
153
  Add this scale to ggplot object
154
154
  """
155
- plot.scales.append(copy(self))
156
- return plot
155
+ other.scales.append(copy(self))
156
+ return other
157
157
 
158
158
  def map(self, x, limits=None):
159
159
  """
@@ -243,7 +243,7 @@ class scale(
243
243
  if not (exp := self.expand):
244
244
  m1, m2 = mult if isinstance(mult, (tuple, list)) else (mult, mult)
245
245
  a1, a2 = cast(
246
- tuple[float, float],
246
+ "tuple[float, float]",
247
247
  (add if isinstance(add, (tuple, list)) else (add, add)),
248
248
  )
249
249
  exp = (m1, a1, m2, a2)
@@ -520,11 +520,12 @@ class scale_continuous(
520
520
  # When user sets breaks and labels of equal size,
521
521
  # but the limits exclude some of the breaks.
522
522
  # We remove the corresponding labels
523
- from collections.abc import Sized
523
+ from collections.abc import Iterable, Sized
524
524
 
525
525
  labels = self.labels
526
526
  if (
527
527
  len(labels) != len(breaks)
528
+ and isinstance(self.breaks, Iterable)
528
529
  and isinstance(self.breaks, Sized)
529
530
  and len(labels) == len(self.breaks)
530
531
  ):