Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0aff44a

Browse files
authored
Merge pull request #27909 from timhoffm/doc-pyplot-ref
Add a note to pyplot docstrings referencing the corresponding object methods
2 parents e20ce21 + 2cdd078 commit 0aff44a

File tree

3 files changed

+98
-7
lines changed

3 files changed

+98
-7
lines changed

galleries/users_explain/figure/api_interfaces.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ is very flexible, and allows us to customize the objects after they are created,
5858
but before they are displayed.
5959

6060

61+
.. _pyplot_interface:
62+
6163
The implicit "pyplot" interface
6264
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6365

lib/matplotlib/axes/_axes.py

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

4545

46+
def _make_axes_method(func):
47+
"""
48+
Patch the qualname for functions that are directly added to Axes.
49+
50+
Some Axes functionality is defined in functions in other submodules.
51+
These are simply added as attributes to Axes. As a result, their
52+
``__qualname__`` is e.g. only "table" and not "Axes.table". This
53+
function fixes that.
54+
55+
Note that the function itself is patched, so that
56+
``matplotlib.table.table.__qualname__` will also show "Axes.table".
57+
However, since these functions are not intended to be standalone,
58+
this is bearable.
59+
"""
60+
func.__qualname__ = f"Axes.{func.__name__}"
61+
return func
62+
63+
4664
@_docstring.interpd
4765
class Axes(_AxesBase):
4866
"""
@@ -8544,18 +8562,19 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
85448562

85458563
# Methods that are entirely implemented in other modules.
85468564

8547-
table = mtable.table
8565+
table = _make_axes_method(mtable.table)
85488566

85498567
# args can be either Y or y1, y2, ... and all should be replaced
8550-
stackplot = _preprocess_data()(mstack.stackplot)
8568+
stackplot = _preprocess_data()(_make_axes_method(mstack.stackplot))
85518569

85528570
streamplot = _preprocess_data(
8553-
replace_names=["x", "y", "u", "v", "start_points"])(mstream.streamplot)
8571+
replace_names=["x", "y", "u", "v", "start_points"])(
8572+
_make_axes_method(mstream.streamplot))
85548573

8555-
tricontour = mtri.tricontour
8556-
tricontourf = mtri.tricontourf
8557-
tripcolor = mtri.tripcolor
8558-
triplot = mtri.triplot
8574+
tricontour = _make_axes_method(mtri.tricontour)
8575+
tricontourf = _make_axes_method(mtri.tricontourf)
8576+
tripcolor = _make_axes_method(mtri.tripcolor)
8577+
triplot = _make_axes_method(mtri.triplot)
85598578

85608579
def _get_aspect_ratio(self):
85618580
"""

lib/matplotlib/pyplot.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,79 @@ def _copy_docstring_and_deprecators(
179179
method = method.__wrapped__
180180
for decorator in decorators[::-1]:
181181
func = decorator(func)
182+
_add_pyplot_note(func, method)
182183
return func
183184

184185

186+
_NO_PYPLOT_NOTE = [
187+
'FigureBase._gci', # wrapped_func is private
188+
'_AxesBase._sci', # wrapped_func is private
189+
'Artist.findobj', # not a standard pyplot wrapper because it does not operate
190+
# on the current Figure / Axes. Explanation of relation would
191+
# be more complex and is not too important.
192+
]
193+
194+
195+
def _add_pyplot_note(func, wrapped_func):
196+
"""
197+
Add a note to the docstring of *func* that it is a pyplot wrapper.
198+
199+
The note is added to the "Notes" section of the docstring. If that does
200+
not exist, a "Notes" section is created. In numpydoc, the "Notes"
201+
section is the third last possible section, only potentially followed by
202+
"References" and "Examples".
203+
"""
204+
if not func.__doc__:
205+
return # nothing to do
206+
207+
qualname = wrapped_func.__qualname__
208+
if qualname in _NO_PYPLOT_NOTE:
209+
return
210+
211+
wrapped_func_is_method = True
212+
if "." not in qualname:
213+
# method qualnames are prefixed by the class and ".", e.g. "Axes.plot"
214+
wrapped_func_is_method = False
215+
link = f"{wrapped_func.__module__}.{qualname}"
216+
elif qualname.startswith("Axes."): # e.g. "Axes.plot"
217+
link = ".axes." + qualname
218+
elif qualname.startswith("_AxesBase."): # e.g. "_AxesBase.set_xlabel"
219+
link = ".axes.Axes" + qualname[9:]
220+
elif qualname.startswith("Figure."): # e.g. "Figure.figimage"
221+
link = "." + qualname
222+
elif qualname.startswith("FigureBase."): # e.g. "FigureBase.gca"
223+
link = ".Figure" + qualname[10:]
224+
elif qualname.startswith("FigureCanvasBase."): # "FigureBaseCanvas.mpl_connect"
225+
link = "." + qualname
226+
else:
227+
raise RuntimeError(f"Wrapped method from unexpected class: {qualname}")
228+
229+
if wrapped_func_is_method:
230+
message = f"This is the :ref:`pyplot wrapper <pyplot_interface>` for `{link}`."
231+
else:
232+
message = f"This is equivalent to `{link}`."
233+
234+
# Find the correct insert position:
235+
# - either we already have a "Notes" section into which we can insert
236+
# - or we create one before the next present section. Note that in numpydoc, the
237+
# "Notes" section is the third last possible section, only potentially followed
238+
# by "References" and "Examples".
239+
# - or we append a new "Notes" section at the end.
240+
doc = inspect.cleandoc(func.__doc__)
241+
if "\nNotes\n-----" in doc:
242+
before, after = doc.split("\nNotes\n-----", 1)
243+
elif (index := doc.find("\nReferences\n----------")) != -1:
244+
before, after = doc[:index], doc[index:]
245+
elif (index := doc.find("\nExamples\n--------")) != -1:
246+
before, after = doc[:index], doc[index:]
247+
else:
248+
# No "Notes", "References", or "Examples" --> append to the end.
249+
before = doc + "\n"
250+
after = ""
251+
252+
func.__doc__ = f"{before}\nNotes\n-----\n\n.. note::\n\n {message}\n{after}"
253+
254+
185255
## Global ##
186256

187257

0 commit comments

Comments
 (0)