8000 MNT: Cleanup docstring substitution mechanisms by timhoffm · Pull Request #28795 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

MNT: Cleanup docstring substitution mechanisms #28795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 32 additions & 16 deletions lib/matplotlib/_docstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@ def __call__(self, func):
func.__doc__ = inspect.cleandoc(func.__doc__) % self.params
return func

def update(self, *args, **kwargs):
"""
Update ``self.params`` (which must be a dict) with the supplied args.
"""
self.params.update(*args, **kwargs)


class _ArtistKwdocLoader(dict):
def __missing__(self, key):
Expand All @@ -89,23 +83,45 @@ def __missing__(self, key):
return self.setdefault(key, kwdoc(cls))


class _ArtistPropertiesSubstitution(Substitution):
class _ArtistPropertiesSubstitution:
"""
A `.Substitution` with two additional features:

- Substitutions of the form ``%(classname:kwdoc)s`` (ending with the
literal ":kwdoc" suffix) trigger lookup of an Artist subclass with the
given *classname*, and are substituted with the `.kwdoc` of that class.
- Decorating a class triggers substitution both on the class docstring and
on the class' ``__init__`` docstring (which is a commonly required
pattern for Artist subclasses).
A class to substitute formatted placeholders in docstrings.

This is realized in a single instance ``_docstring.interpd``.

Use `~._ArtistPropertiesSubstition.register` to define placeholders and
their substitution, e.g. ``_docstring.interpd.register(name="some value")``.

Use this as a decorator to apply the substitution::

@_docstring.interpd
def some_func():
'''Replace %(name)s.'''

Decorating a class triggers substitution both on the class docstring and
on the class' ``__init__`` docstring (which is a commonly required
pattern for Artist subclasses).

Substitutions of the form ``%(classname:kwdoc)s`` (ending with the
literal ":kwdoc" suffix) trigger lookup of an Artist subclass with the
given *classname*, and are substituted with the `.kwdoc` of that class.
"""

def __init__(self):
self.params = _ArtistKwdocLoader()

def register(self, **kwargs):
"""
Register substitutions.

``_docstring.interpd.register(name="some value")`` makes "name" available
as a named parameter that will be replaced by "some value".
"""
self.params.update(**kwargs)

def __call__(self, obj):
super().__call__(obj)
if obj.__doc__:
obj.__doc__ = inspect.cleandoc(obj.__doc__) % self.params
if isinstance(obj, type) and obj.__init__ != object.__init__:
self(obj.__init__)
return obj
Expand Down
3 changes: 2 additions & 1 deletion lib/matplotlib/_docstring.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ class _ArtistKwdocLoader(dict[str, str]):
def __missing__(self, key: str) -> str: ...


class _ArtistPropertiesSubstitution(Substitution):
class _ArtistPropertiesSubstitution:
def __init__(self) -> None: ...
def register(self, **kwargs) -> None: ...
def __call__(self, obj: _T) -> _T: ...


Expand Down
6 changes: 4 additions & 2 deletions lib/matplotlib/_enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,5 +181,7 @@ def demo():
+ ", ".join([f"'{cs.name}'" for cs in CapStyle]) \
+ "}"

_docstring.interpd.update({'JoinStyle': JoinStyle.input_description,
'CapStyle': CapStyle.input_description})
_docstring.interpd.register(
JoinStyle=JoinStyle.input_description,
CapStyle=CapStyle.input_description,
)
2 changes: 1 addition & 1 deletion lib/matplotlib/axes/_secondary_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,4 +319,4 @@ def set_color(self, color):
**kwargs : `~matplotlib.axes.Axes` properties.
Other miscellaneous Axes parameters.
'''
_docstring.interpd.update(_secax_docstring=_secax_docstring)
_docstring.interpd.register(_secax_docstring=_secax_docstring)
2 changes: 1 addition & 1 deletion lib/matplotlib/cm.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ def _format_cursor_data_override(self, data):


# The docstrings here must be generic enough to apply to all relevant methods.
mpl._docstring.interpd.update(
mpl._docstring.interpd.register(
cmap_doc="""\
cmap : str or `~matplotlib.colors.Colormap`, default: :rc:`image.cmap`
The Colormap instance or registered colormap name used to map scalar data
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@

_log = logging.getLogger(__name__)

_docstring.interpd.update(
_docstring.interpd.register(
_make_axes_kw_doc="""
location : None or {'left', 'right', 'top', 'bottom'}
The location, relative to the parent Axes, where the colorbar Axes
Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/contour.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ def _find_closest_point_on_path(xys, p):
return (d2s[imin], projs[imin], (imin, imin+1))


_docstring.interpd.update(contour_set_attributes=r"""
_docstring.interpd.register(contour_set_attributes=r"""
Attributes
----------
ax : `~matplotlib.axes.Axes`
Expand Down Expand Up @@ -1445,7 +1445,7 @@ def _initialize_x_y(self, z):
return np.meshgrid(x, y)


_docstring.interpd.update(contour_doc="""
_docstring.interpd.register(contour_doc="""
`.contour` and `.contourf` draw contour lines and filled contours,
respectively. Except as noted, function signatures and return values
are the same for both versions.
Expand Down
8 changes: 4 additions & 4 deletions lib/matplotlib/legend.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ def _update_bbox_to_anchor(self, loc_in_canvas):
_loc_doc_base.format(parent='axes', default=':rc:`legend.loc`',
best=_loc_doc_best, outside='') +
_legend_kw_doc_base)
_docstring.interpd.update(_legend_kw_axes=_legend_kw_axes_st)
_docstring.interpd.register(_legend_kw_axes=_legend_kw_axes_st)

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

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

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


class Legend(Artist):
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/mlab.py
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ def _single_spectrum_helper(


# Split out these keyword docs so that they can be used elsewhere
_docstring.interpd.update(
_docstring.interpd.register(
Spectral="""\
Fs : float, default: 2
The sampling frequency (samples per time unit). It is used to calculate
Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -1552,7 +1552,7 @@ def _make_verts(self):
]


_docstring.interpd.update(
_docstring.interpd.register(
FancyArrow="\n".join(
(inspect.getdoc(FancyArrow.__init__) or "").splitlines()[2:]))

Expand Down Expand Up @@ -2290,7 +2290,7 @@ def __init_subclass__(cls):
# - %(BoxStyle:table_and_accepts)s
# - %(ConnectionStyle:table_and_accepts)s
# - %(ArrowStyle:table_and_accepts)s
_docstring.interpd.update({
_docstring.interpd.register(**{
f"{cls.__name__}:table": cls.pprint_styles(),
f"{cls.__name__}:table_and_accepts": (
cls.pprint_styles()
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/projections/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,4 @@ def get_projection_class(projection=None):


get_projection_names = projection_registry.get_projection_names
_docstring.interpd.update(projection_names=get_projection_names())
_docstring.interpd.register(projection_names=get_projection_names())
4 changes: 2 additions & 2 deletions lib/matplotlib/quiver.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@
of the head in forward direction so that the arrow head looks broken.
""" % _docstring.interpd.params

_docstring.interpd.update(quiver_doc=_quiver_doc)
_docstring.interpd.register(quiver_doc=_quiver_doc)


class QuiverKey(martist.Artist):
Expand Down Expand Up @@ -865,7 +865,7 @@ def _h_arrows(self, length):
%(PolyCollection:kwdoc)s
""" % _docstring.interpd.params

_docstring.interpd.update(barbs_doc=_barbs_doc)
_docstring.interpd.register(barbs_doc=_barbs_doc)


class Barbs(mcollections.PolyCollection):
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/scale.py
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ def _get_scale_docs():
return "\n".join(docs)


_docstring.interpd.update(
_docstring.interpd.register(
scale_type='{%s}' % ', '.join([repr(x) for x in get_scale_names()]),
scale_docs=_get_scale_docs().rstrip(),
)
2 changes: 1 addition & 1 deletion lib/matplotlib/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -2030,4 +2030,4 @@ def get_tightbbox(self, renderer=None):
return super().get_tightbbox(renderer)


_docstring.interpd.update(Annotation=Annotation.__init__.__doc__)
_docstring.interpd.register(Annotation=Annotation.__init__.__doc__)
2 changes: 1 addition & 1 deletion lib/matplotlib/tri/_tricontour.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def _contour_args(self, args, kwargs):
return (tri, z)


_docstring.interpd.update(_tricontour_doc="""
_docstring.interpd.register(_tricontour_doc="""
Draw contour %%(type)s on an unstructured triangular grid.

Call signatures::
Expand Down
Loading
0