10000 Auto-generate required kwdoc entries into docstring.interpd. · matplotlib/matplotlib@379cb38 · GitHub
[go: up one dir, main page]

Skip to content

Commit 379cb38

Browse files
committed
Auto-generate required kwdoc entries into docstring.interpd.
This does two things: - Replace Foo_kwdoc docstring substitutions by auto-generated Foo:kwdoc substitutions, removing the need to explicitly call `docstring.interpd.update(Foo_kwdoc=artist.kwdoc(Foo))` for each artist. - Decorating a class with `docstring.interpd` will now also perform interpolation over the docstring of the class' `__init__`, making it possible to list properties here as well, instead of having to manually call `docstring.interpd` after the class has been defined. The new behavior is also explained in documenting_mpl.rst. I think it's slightly more magical, but also easier to explain...
1 parent f5880d2 commit 379cb38

File tree

18 files changed

+128
-138
lines changed

18 files changed

+128
-138
lines changed

doc/devel/documenting_mpl.rst

Lines changed: 15 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -658,26 +658,14 @@ are:
658658
2. as automated as possible so that as properties change, the docs
659659
are updated automatically.
660660

661-
The function `matplotlib.artist.kwdoc` and the decorator
662-
``matplotlib.docstring.dedent_interpd`` facilitate this. They combine Python
663-
string interpolation in the docstring with the Matplotlib artist introspection
664-
facility that underlies ``setp`` and ``getp``. The ``kwdoc`` function gives
665-
the list of properties as a docstring. In order to use this in another
666-
docstring, first update the ``matplotlib.docstring.interpd`` object, as seen in
667-
this example from `matplotlib.lines`:
668-
669-
.. code-block:: python
670-
671-
# in lines.py
672-
docstring.interpd.update(Line2D_kwdoc=artist.kwdoc(Line2D))
673-
674-
Then in any function accepting `~.Line2D` pass-through ``kwargs``, e.g.,
675-
`matplotlib.axes.Axes.plot`:
661+
The ``@docstring.interpd`` decorator implements this. Any function accepting
662+
`.Line2D` pass-through ``kwargs``, e.g., `matplotlib.axes.Axes.plot`, can list
663+
a summary of the `.Line2D` properties, as follows:
676664

677665
.. code-block:: python
678666
679667
# in axes.py
680-
@docstring.dedent_interpd
668+
@docstring.interpd
681669
def plot(self, *args, **kwargs):
682670
"""
683671
Some stuff omitted
@@ -702,17 +690,19 @@ Then in any function accepting `~.Line2D` pass-through ``kwargs``, e.g.,
702690
703691
Here is a list of available `.Line2D` properties:
704692
705-
%(Line2D_kwdoc)s
706-
693+
%(Line2D:kwdoc)s
707694
"""
708695
709-
Note there is a problem for `~matplotlib.artist.Artist` ``__init__`` methods,
710-
e.g., `matplotlib.patches.Patch.__init__`, which supports ``Patch`` ``kwargs``,
711-
since the artist inspector cannot work until the class is fully defined and
712-
we can't modify the ``Patch.__init__.__doc__`` docstring outside the class
713-
definition. There are some some manual hacks in this case, violating the
714-
"single entry point" requirement above -- see the ``docstring.interpd.update``
715-
calls in `matplotlib.patches`.
696+
The ``%(Line2D:kwdoc)`` syntax makes ``interpd`` lookup an `.Artist` subclass
697+
named ``Line2D``, and call `.artist.kwdoc` on that class. `.artist.kwdoc`
698+
introspects the subclass and summarizes its properties as a substring, which
699+
gets interpolated into the docstring.
700+
701+
Note that this scheme does not work for decorating an Artist's ``__init__``, as
702+
the subclass and its properties are not defined yet at that point. Instead,
703+
``@docstring.interpd`` can be used to decorate the class itself -- at that
704+
point, `.kwdoc` can list the properties and interpolate them into
705+
``__init__.__doc__``.
716706

717707

718708
Inheriting docstrings

lib/matplotlib/artist.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,6 +1684,3 @@ def kwdoc(artist):
16841684
return ('\n'.join(ai.pprint_setters_rest(leadingspace=4))
16851685
if mpl.rcParams['docstring.hardcopy'] else
16861686
'Properties:\n' + '\n'.join(ai.pprint_setters(leadingspace=4)))
1687-
1688-
1689-
docstring.interpd.update(Artist_kwdoc=kwdoc(Artist))

lib/matplotlib/axes/_axes.py

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
# All the other methods should go in the _AxesBase class.
4444

4545

46+
@docstring.interpd
4647
class Axes(_AxesBase):
4748
"""
4849
The `Axes` contains most of the figure elements: `~.axis.Axis`,
@@ -397,7 +398,7 @@ def indicate_inset(self, bounds, inset_ax=None, *, transform=None,
397398
**kwargs
398399
Other keyword arguments are passed on to the `.Rectangle` patch:
399400
400-
%(Rectangle_kwdoc)s
401+
%(Rectangle:kwdoc)s
401402
402403
Returns
403404
-------
@@ -609,7 +610,7 @@ def text(self, x, y, s, fontdict=None, **kwargs):
609610
**kwargs : `~matplotlib.text.Text` properties.
610611
Other miscellaneous text parameters.
611612
612-
%(Text_kwdoc)s
613+
%(Text:kwdoc)s
613614
614615
Examples
615616
--------
@@ -686,7 +687,7 @@ def axhline(self, y=0, xmin=0, xmax=1, **kwargs):
686687
Valid keyword arguments are `.Line2D` properties, with the
687688
exception of 'transform':
688689
689-
%(Line2D_kwdoc)s
690+
%(Line2D:kwdoc)s
690691
691692
See Also
692693
--------
@@ -753,7 +754,7 @@ def axvline(self, x=0, ymin=0, ymax=1, **kwargs):
753754
Valid keyword arguments are `.Line2D` properties, with the
754755
exception of 'transform':
755756
756-
%(Line2D_kwdoc)s
757+
%(Line2D:kwdoc)s
757758
758759
See Also
759760
--------
@@ -837,7 +838,7 @@ def axline(self, xy1, xy2=None, *, slope=None, **kwargs):
837838
**kwargs
838839
Valid kwargs are `.Line2D` properties
839840
840-
%(Line2D_kwdoc)s
841+
%(Line2D:kwdoc)s
841842
842843
See Also
843844
--------
@@ -905,7 +906,7 @@ def axhspan(self, ymin, ymax, xmin=0, xmax=1, **kwargs):
905906
----------------
906907
**kwargs : `~matplotlib.patches.Polygon` properties
907908
908-
%(Polygon_kwdoc)s
909+
%(Polygon:kwdoc)s
909910
910911
See Also
911912
--------
@@ -953,7 +954,7 @@ def axvspan(self, xmin, xmax, ymin=0, ymax=1, **kwargs):
953954
----------------
954955
**kwargs : `~matplotlib.patches.Polygon` properties
955956
956-
%(Polygon_kwdoc)s
957+
%(Polygon:kwdoc)s
957958
958959
See Also
959960
--------
@@ -1506,7 +1507,7 @@ def plot(self, *args, scalex=True, scaley=True, data=None, **kwargs):
15061507
15071508
Here is a list of available `.Line2D` properties:
15081509
1509-
%(Line2D_kwdoc)s
1510+
%(Line2D:kwdoc)s
15101511
15111512
See Also
15121513
--------
@@ -1651,7 +1652,7 @@ def plot_date(self, x, y, fmt='o', tz=None, xdate=True, ydate=False,
16511652
**kwargs
16521653
Keyword arguments control the `.Line2D` properties:
16531654
1654-
%(Line2D_kwdoc)s
1655+
%(Line2D:kwdoc)s
16551656
16561657
See Also
16571658
--------
@@ -2222,7 +2223,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
22222223
22232224
**kwargs : `.Rectangle` properties
22242225
2225-
%(Rectangle_kwdoc)s
2226+
%(Rectangle:kwdoc)s
22262227
22272228
See Also
22282229
--------
@@ -2497,7 +2498,7 @@ def barh(self, y, width, height=0.8, left=None, *, align="center",
24972498
24982499
**kwargs : `.Rectangle` properties
24992500
2500-
%(Rectangle_kwdoc)s
2501+
%(Rectangle:kwdoc)s
25012502
25022503
See Also
25032504
--------
@@ -2685,7 +2686,7 @@ def broken_barh(self, xranges, yrange, **kwargs):
26852686
26862687
Supported keywords:
26872688
2688-
%(BrokenBarHCollection_kwdoc)s
2689+
%(BrokenBarHCollection:kwdoc)s
26892690
"""
26902691
# process the unit information
26912692
if len(xranges):
@@ -3276,7 +3277,7 @@ def errorbar(self, x, y, yerr=None, xerr=None,
32763277
32773278
Valid kwargs for the marker properties are `.Line2D` properties:
32783279
3279-
%(Line2D_kwdoc)s
3280+
%(Line2D:kwdoc)s
32803281
"""
32813282
kwargs = cbook.normalize_kwargs(kwargs, mlines.Line2D)
32823283
# anything that comes in as 'None', drop so the default thing
@@ -4728,7 +4729,7 @@ def reduce_C_function(C: array) -> float
47284729
**kwargs : `~matplotlib.collections.PolyCollection` properties
47294730
All other keyword arguments are passed on to `.PolyCollection`:
47304731
4731-
%(PolyCollection_kwdoc)s
4732+
%(PolyCollection:kwdoc)s
47324733
47334734
"""
47344735
self._process_unit_info([("x", x), ("y", y)], kwargs, convert=False)
@@ -5237,7 +5238,7 @@ def _fill_between_x_or_y(
52375238
All other keyword arguments are passed on to `.PolyCollection`.
52385239
They control the `.Polygon` properties:
52395240
5240-
%(PolyCollection_kwdoc)s
5241+
%(PolyCollection:kwdoc)s
52415242
52425243
See Also
52435244
--------
@@ -5843,7 +5844,7 @@ def pcolor(self, *args, shading=None, alpha=None, norm=None, cmap=None,
58435844
Additionally, the following arguments are allowed. They are passed
58445845
along to the `~matplotlib.collections.PolyCollection` constructor:
58455846
5846-
%(PolyCollection_kwdoc)s
5847+
%(PolyCollection:kwdoc)s
58475848
58485849
See Also
58495850
--------
@@ -6088,7 +6089,7 @@ def pcolormesh(self, *args, alpha=None, norm=None, cmap=None, vmin=None,
60886089
Additionally, the following arguments are allowed. They are passed
60896090
along to the `~matplotlib.collections.QuadMesh` constructor:
60906091
6091-
%(QuadMesh_kwdoc)s
6092+
%(QuadMesh:kwdoc)s
60926093
60936094
See Also
60946095
--------
@@ -7129,7 +7130,7 @@ def psd(self, x, NFFT=None, Fs=None, Fc=None, detrend=None,
71297130
**kwargs
71307131
Keyword arguments control the `.Line2D` properties:
71317132
7132-
%(Line2D_kwdoc)s
7133+
%(Line2D:kwdoc)s
71337134
71347135
See Also
71357136
--------
@@ -7242,7 +7243,7 @@ def csd(self, x, y, NFFT=None, Fs=None, Fc=None, detrend=None,
72427243
**kwargs
72437244
Keyword arguments control the `.Line2D` properties:
72447245
7245-
%(Line2D_kwdoc)s
7246+
%(Line2D:kwdoc)s
72467247
72477248
See Also
72487249
--------
@@ -7332,7 +7333,7 @@ def magnitude_spectrum(self, x, Fs=None, Fc=None, window=None,
73327333
**kwargs
73337334
Keyword arguments control the `.Line2D` properties:
73347335
7335-
%(Line2D_kwdoc)s
7336+
%(Line2D:kwdoc)s
73367337
73377338
See Also
73387339
--------
@@ -7409,7 +7410,7 @@ def angle_spectrum(self, x, Fs=None, Fc=None, window=None,
74097410
**kwargs
74107411
Keyword arguments control the `.Line2D` properties:
74117412
7412-
%(Line2D_kwdoc)s
7413+
%(Line2D:kwdoc)s
74137414
74147415
See Also
74157416
--------
@@ -7475,7 +7476,7 @@ def phase_spectrum(self, x, Fs=None, Fc=None, window=None,
74757476
**kwargs
74767477
Keyword arguments control the `.Line2D` properties:
74777478
7478-
%(Line2D_kwdoc)s
7479+
%(Line2D:kwdoc)s
74797480
74807481
See Also
74817482
--------
@@ -7542,7 +7543,7 @@ def cohere(self, x, y, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none,
75427543
**kwargs
75437544
Keyword arguments control the `.Line2D` properties:
75447545
7545-
%(Line2D_kwdoc)s
7546+
%(Line2D:kwdoc)s
75467547
75477548
References
75487549
----------
@@ -7792,7 +7793,7 @@ def spy(self, Z, precision=0, marker=None, markersize=None,
77927793
For the marker style, you can pass any `.Line2D` property except
77937794
for *linestyle*:
77947795
7795-
%(Line2D_kwdoc)s
7796+
%(Line2D:kwdoc)s
77967797
"""
77977798
if marker is None and markersize is None and hasattr(Z, 'tocoo'):
77987799
marker = 's'

lib/matplotlib/axes/_base.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ def __init__(self, fig, rect,
591591
**kwargs
592592
Other optional keyword arguments:
593593
594-
%(Axes_kwdoc)s
594+
%(Axes:kwdoc)s
595595
596596
Returns
597597
-------
@@ -3223,7 +3223,7 @@ def grid(self, b=None, which='major', axis='both', **kwargs):
32233223
32243224
Valid keyword arguments are:
32253225
3226-
%(Line2D_kwdoc)s
3226+
%(Line2D:kwdoc)s
32273227
32283228
Notes
32293229
-----

lib/matplotlib/axes/_subplots.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,9 +220,3 @@ class when called with an axes class. This is purely to allow pickling of
220220
"""
221221
subplot_class = subplot_class_factory(axes_class)
222222
return subplot_class.__new__(subplot_class)
223-
224-
225-
docstring.interpd.update(Axes_kwdoc=martist.kwdoc(Axes))
226-
docstring.dedent_interpd(Axes.__init__)
227-
228-
docstring.interpd.update(Subplot_kwdoc=martist.kwdoc(Axes))

lib/matplotlib/collections.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,12 +2132,3 @@ def draw(self, renderer):
21322132
gc.restore()
21332133
renderer.close_group(self.__class__.__name__)
21342134
self.stale = False
2135-
2136-
2137-
_artist_kwdoc = artist.kwdoc(Collection)
2138-
for k in ('QuadMesh', 'TriMesh', 'PolyCollection', 'BrokenBarHCollection',
2139-
'RegularPolyCollection', 'PathCollection',
2140-
'StarPolygonCollection', 'PatchCollection',
2141-
'CircleCollection', 'Collection',):
2142-
docstring.interpd.update({f'{k}_kwdoc': _artist_kwdoc})
2143-
docstring.interpd.update(LineCollection_kwdoc=artist.kwdoc(LineCollection))

lib/matplotlib/docstring.py

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def some_function(x):
3333
def __init__(self, *args, **kwargs):
3434
if args and kwargs:
3535
raise TypeError("Only positional or keyword args are allowed")
36-
self.params = args or kwargs
36+
self.params = params = args or kwargs
3737

3838
def __call__(self, func):
3939
if func.__doc__:
@@ -62,6 +62,48 @@ def from_params(cls, params):
6262
return result
6363

6464

65+
def _recursive_subclasses(cls):
66+
yield cls
67+
for subcls in cls.__subclasses__():
68+
yield from _recursive_subclasses(subcls)
69+
70+
71+
class _ArtistKwdocLoader(dict):
72+
def __missing__(self, key):
73+
if not key.endswith(":kwdoc"):
74+
raise KeyError(key)
75+
name = key[:-len(":kwdoc")]
76+
from matplotlib.artist import Artist, kwdoc
77+
try:
78+
cls, = [cls for cls in _recursive_subclasses(Artist)
79+
if cls.__name__ == name]
80+
except ValueError as e:
81+
raise KeyError(key) from e
82+
return self.setdefault(key, kwdoc(cls))
83+
84+
85+
class _ArtistPropertiesSubstitution(Substitution):
86+
"""
87+
A `.Substitution` with two additional features:
88+
89+
- Substitutions of the form ``%(classname:kwdoc)s`` (ending with the
90+
literal ":kwdoc" suffix) trigger lookup of an Artist subclass with the
91+
given *classname*, and are substituted with the `.kwdoc` of that class.
92+
- Decorating a class triggers substitution both on the class docstring and
93+
on the class' ``__init__`` docstring (which is a commonly required
94+
pattern for Artist subclasses).
95+
"""
96+
97+
def __init__(self):
98+
self.params = _ArtistKwdocLoader()
99+
100+
def __call__(self, obj):
101+
super().__call__(obj)
102+
if isinstance(obj, type) and obj.__init__ != object.__init__:
103+
self(obj.__init__)
104+
return obj
105+
106+
65107
def copy(source):
66108
"""Copy a docstring from another source function (if present)."""
67109
def do_copy(target):
@@ -73,5 +115,4 @@ def do_copy(target):
73115

74116
# Create a decorator that will house the various docstring snippets reused
75117
# throughout Matplotlib.
76-
interpd = Substitution()
77-
dedent_interpd = interpd
118+
dedent_interpd = interpd = _ArtistPropertiesSubstitution()

0 commit comments

Comments
 (0)
0