Skip to content

Commit 24c5048

Browse files
authored
MNT: Cleanup docstring substitution mechanisms (matplotlib#28795)
* MNT: Make Substitution and _ArtistPropertiesSubstitution independent They do not have relevant functional overlap. Thus, it's simpler to keep them completely separated. * Rename _ArtistPropertiesSubstitution.update() to register() While it's internally a dict.update. The logical process is a registration to make the names publically available. Also, remove the possibility to pass a dict. Passing kwargs is enough and we can simplify the API to only one type of usage.
1 parent 6404c95 commit 24c5048

15 files changed

+56
-37
lines changed

lib/matplotlib/_docstring.py

+32-16
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,6 @@ def __call__(self, func):
6868
func.__doc__ = inspect.cleandoc(func.__doc__) % self.params
6969
return func
7070

71-
def update(self, *args, **kwargs):
72-
"""
73-
Update ``self.params`` (which must be a dict) with the supplied args.
74-
"""
75-
self.params.update(*args, **kwargs)
76-
7771

7872
class _ArtistKwdocLoader(dict):
7973
def __missing__(self, key):
@@ -89,23 +83,45 @@ def __missing__(self, key):
8983
return self.setdefault(key, kwdoc(cls))
9084

9185

92-
class _ArtistPropertiesSubstitution(Substitution):
86+
class _ArtistPropertiesSubstitution:
9387
"""
94-
A `.Substitution` with two additional features:
95-
96-
- Substitutions of the form ``%(classname:kwdoc)s`` (ending with the
97-
literal ":kwdoc" suffix) trigger lookup of an Artist subclass with the
98-
given *classname*, and are substituted with the `.kwdoc` of that class.
99-
- Decorating a class triggers substitution both on the class docstring and
100-
on the class' ``__init__`` docstring (which is a commonly required
101-
pattern for Artist subclasses).
88+
A class to substitute formatted placeholders in docstrings.
89+
90+
This is realized in a single instance ``_docstring.interpd``.
91+
92+
Use `~._ArtistPropertiesSubstition.register` to define placeholders and
93+
their substitution, e.g. ``_docstring.interpd.register(name="some value")``.
94+
95+
Use this as a decorator to apply the substitution::
96+
97+
@_docstring.interpd
98+
def some_func():
99+
'''Replace %(name)s.'''
100+
101+
Decorating a class triggers substitution both on the class docstring and
102+
on the class' ``__init__`` docstring (which is a commonly required
103+
pattern for Artist subclasses).
104+
105+
Substitutions of the form ``%(classname:kwdoc)s`` (ending with the
106+
literal ":kwdoc" suffix) trigger lookup of an Artist subclass with the
107+
given *classname*, and are substituted with the `.kwdoc` of that class.
102108
"""
103109

104110
def __init__(self):
105111
self.params = _ArtistKwdocLoader()
106112

113+
def register(self, **kwargs):
114+
"""
115+
Register substitutions.
116+
117+
``_docstring.interpd.register(name="some value")`` makes "name" available
118+
as a named parameter that will be replaced by "some value".
119+
"""
120+
self.params.update(**kwargs)
121+
107122
def __call__(self, obj):
108-
super().__call__(obj)
123+
if obj.__doc__:
124+
obj.__doc__ = inspect.cleandoc(obj.__doc__) % self.params
109125
if isinstance(obj, type) and obj.__init__ != object.__init__:
110126
self(obj.__init__)
111127
return obj

lib/matplotlib/_docstring.pyi

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ class _ArtistKwdocLoader(dict[str, str]):
2121
def __missing__(self, key: str) -> str: ...
2222

2323

24-
class _ArtistPropertiesSubstitution(Substitution):
24+
class _ArtistPropertiesSubstitution:
2525
def __init__(self) -> None: ...
26+
def register(self, **kwargs) -> None: ...
2627
def __call__(self, obj: _T) -> _T: ...
2728

2829

lib/matplotlib/_enums.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -181,5 +181,7 @@ def demo():
181181
+ ", ".join([f"'{cs.name}'" for cs in CapStyle]) \
182182
+ "}"
183183

184-
_docstring.interpd.update({'JoinStyle': JoinStyle.input_description,
185-
'CapStyle': CapStyle.input_description})
184+
_docstring.interpd.register(
185+
JoinStyle=JoinStyle.input_description,
186+
CapStyle=CapStyle.input_description,
187+
)

lib/matplotlib/axes/_secondary_axes.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -319,4 +319,4 @@ def set_color(self, color):
319319
**kwargs : `~matplotlib.axes.Axes` properties.
320320
Other miscellaneous Axes parameters.
321321
'''
322-
_docstring.interpd.update(_secax_docstring=_secax_docstring)
322+
_docstring.interpd.register(_secax_docstring=_secax_docstring)

lib/matplotlib/cm.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ def _format_cursor_data_override(self, data):
651651

652652

653653
# The docstrings here must be generic enough to apply to all relevant methods.
654-
mpl._docstring.interpd.update(
654+
mpl._docstring.interpd.register(
655655
cmap_doc="""\
656656
cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap`
657657
The Colormap instance or registered colormap name used to map scalar data

lib/matplotlib/colorbar.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
_log = logging.getLogger(__name__)
2828

29-
_docstring.interpd.update(
29+
_docstring.interpd.register(
3030
_make_axes_kw_doc="""
3131
location : None or {'left', 'right', 'top', 'bottom'}
3232
The location, relative to the parent Axes, where the colorbar Axes

lib/matplotlib/contour.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,7 @@ def _find_closest_point_on_path(xys, p):
536536
return (d2s[imin], projs[imin], (imin, imin+1))
537537

538538

539-
_docstring.interpd.update(contour_set_attributes=r"""
539+
_docstring.interpd.register(contour_set_attributes=r"""
540540
Attributes
541541
----------
542542
ax : `~matplotlib.axes.Axes`
@@ -1450,7 +1450,7 @@ def _initialize_x_y(self, z):
14501450
return np.meshgrid(x, y)
14511451

14521452

1453-
_docstring.interpd.update(contour_doc="""
1453+
_docstring.interpd.register(contour_doc="""
14541454
`.contour` and `.contourf` draw contour lines and filled contours,
14551455
respectively. Except as noted, function signatures and return values
14561456
are the same for both versions.

lib/matplotlib/legend.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
305305
_loc_doc_base.format(parent='axes', default=':rc:`legend.loc`',
306306
best=_loc_doc_best, outside='') +
307307
_legend_kw_doc_base)
308-
_docstring.interpd.update(_legend_kw_axes=_legend_kw_axes_st)
308+
_docstring.interpd.register(_legend_kw_axes=_legend_kw_axes_st)
309309

310310
_outside_doc = """
311311
If a figure is using the constrained layout manager, the string codes
@@ -323,20 +323,20 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
323323
_loc_doc_base.format(parent='figure', default="'upper right'",
324324
best='', outside=_outside_doc) +
325325
_legend_kw_doc_base)
326-
_docstring.interpd.update(_legend_kw_figure=_legend_kw_figure_st)
326+
_docstring.interpd.register(_legend_kw_figure=_legend_kw_figure_st)
327327

328328
_legend_kw_both_st = (
329329
_loc_doc_base.format(parent='axes/figure',
330330
default=":rc:`legend.loc` for Axes, 'upper right' for Figure",
331331
best=_loc_doc_best, outside=_outside_doc) +
332332
_legend_kw_doc_base)
333-
_docstring.interpd.update(_legend_kw_doc=_legend_kw_both_st)
333+
_docstring.interpd.register(_legend_kw_doc=_legend_kw_both_st)
334334

335335
_legend_kw_set_loc_st = (
336336
_loc_doc_base.format(parent='axes/figure',
337337
default=":rc:`legend.loc` for Axes, 'upper right' for Figure",
338338
best=_loc_doc_best, outside=_outside_doc))
339-
_docstring.interpd.update(_legend_kw_set_loc_doc=_legend_kw_set_loc_st)
339+
_docstring.interpd.register(_legend_kw_set_loc_doc=_legend_kw_set_loc_st)
340340

341341

342342
class Legend(Artist):

lib/matplotlib/mlab.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ def _single_spectrum_helper(
400400

401401

402402
# Split out these keyword docs so that they can be used elsewhere
403-
_docstring.interpd.update(
403+
_docstring.interpd.register(
404404
Spectral="""\
405405
Fs : float, default: 2
406406
The sampling frequency (samples per time unit). It is used to calculate

lib/matplotlib/patches.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1552,7 +1552,7 @@ def _make_verts(self):
15521552
]
15531553

15541554

1555-
_docstring.interpd.update(
1555+
_docstring.interpd.register(
15561556
FancyArrow="\n".join(
15571557
(inspect.getdoc(FancyArrow.__init__) or "").splitlines()[2:]))
15581558

@@ -2290,7 +2290,7 @@ def __init_subclass__(cls):
22902290
# - %(BoxStyle:table_and_accepts)s
22912291
# - %(ConnectionStyle:table_and_accepts)s
22922292
# - %(ArrowStyle:table_and_accepts)s
2293-
_docstring.interpd.update({
2293+
_docstring.interpd.register(**{
22942294
f"{cls.__name__}:table": cls.pprint_styles(),
22952295
f"{cls.__name__}:table_and_accepts": (
22962296
cls.pprint_styles()

lib/matplotlib/projections/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,4 @@ def get_projection_class(projection=None):
123123

124124

125125
get_projection_names = projection_registry.get_projection_names
126-
_docstring.interpd.update(projection_names=get_projection_names())
126+
_docstring.interpd.register(projection_names=get_projection_names())

lib/matplotlib/quiver.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@
230230
of the head in forward direction so that the arrow head looks broken.
231231
""" % _docstring.interpd.params
232232

233-
_docstring.interpd.update(quiver_doc=_quiver_doc)
233+
_docstring.interpd.register(quiver_doc=_quiver_doc)
234234

235235

236236
class QuiverKey(martist.Artist):
@@ -865,7 +865,7 @@ def _h_arrows(self, length):
865865
%(PolyCollection:kwdoc)s
866866
""" % _docstring.interpd.params
867867

868-
_docstring.interpd.update(barbs_doc=_barbs_doc)
868+
_docstring.interpd.register(barbs_doc=_barbs_doc)
869869

870870

871871
class Barbs(mcollections.PolyCollection):

lib/matplotlib/scale.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ def _get_scale_docs():
742742
return "\n".join(docs)
743743

744744

745-
_docstring.interpd.update(
745+
_docstring.interpd.register(
746746
scale_type='{%s}' % ', '.join([repr(x) for x in get_scale_names()]),
747747
scale_docs=_get_scale_docs().rstrip(),
748748
)

lib/matplotlib/text.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2029,4 +2029,4 @@ def get_tightbbox(self, renderer=None):
20292029
return super().get_tightbbox(renderer)
20302030

20312031

2032-
_docstring.interpd.update(Annotation=Annotation.__init__.__doc__)
2032+
_docstring.interpd.register(Annotation=Annotation.__init__.__doc__)

lib/matplotlib/tri/_tricontour.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def _contour_args(self, args, kwargs):
7979
return (tri, z)
8080

8181

82-
_docstring.interpd.update(_tricontour_doc="""
82+
_docstring.interpd.register(_tricontour_doc="""
8383
Draw contour %%(type)s on an unstructured triangular grid.
8484
8585
Call signatures::

0 commit comments

Comments
 (0)