-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Add a note to pyplot docstrings referencing the corresponding object methods #27909
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -179,9 +179,79 @@ def _copy_docstring_and_deprecators( | |
method = method.__wrapped__ | ||
for decorator in decorators[::-1]: | ||
func = decorator(func) | ||
_add_pyplot_note(func, method) | ||
return func | ||
|
||
|
||
_NO_PYPLOT_NOTE = [ | ||
'FigureBase._gci', # wrapped_func is private | ||
'_AxesBase._sci', # wrapped_func is private | ||
'Artist.findobj', # not a standard pyplot wrapper because it does not operate | ||
# on the current Figure / Axes. Explanation of relation would | ||
# be more complex and is not too important. | ||
] | ||
|
||
|
||
def _add_pyplot_note(func, wrapped_func): | ||
""" | ||
Add a note to the docstring of *func* that it is a pyplot wrapper. | ||
|
||
The note is added to the "Notes" section of the docstring. If that does | ||
not exist, a "Notes" section is created. In numpydoc, the "Notes" | ||
section is the third last possible section, only potentially followed by | ||
"References" and "Examples". | ||
""" | ||
if not func.__doc__: | ||
return # nothing to do | ||
|
||
qualname = wrapped_func.__qualname__ | ||
if qualname in _NO_PYPLOT_NOTE: | ||
return | ||
|
||
wrapped_func_is_method = True | ||
if "." not in qualname: | ||
# method qualnames are prefixed by the class and ".", e.g. "Axes.plot" | ||
wrapped_func_is_method = False | ||
link = f"{wrapped_func.__module__}.{qualname}" | ||
elif qualname.startswith("Axes."): # e.g. "Axes.plot" | ||
link = ".axes." + qualname | ||
elif qualname.startswith("_AxesBase."): # e.g. "_AxesBase.set_xlabel" | ||
link = ".axes.Axes" + qualname[9:] | ||
elif qualname.startswith("Figure."): # e.g. "Figure.figimage" | ||
link = "." + qualname | ||
elif qualname.startswith("FigureBase."): # e.g. "FigureBase.gca" | ||
link = ".Figure" + qualname[10:] | ||
elif qualname.startswith("FigureCanvasBase."): # "FigureBaseCanvas.mpl_connect" | ||
link = "." + qualname | ||
else: | ||
raise RuntimeError(f"Wrapped method from unexpected class: {qualname}") | ||
|
||
if wrapped_func_is_method: | ||
message = f"This is the :ref:`pyplot wrapper <pyplot_interface>` for `{link}`." | ||
else: | ||
message = f"This is equivalent to `{link}`." | ||
|
||
# Find the correct insert position: | ||
# - either we already have a "Notes" section into which we can insert | ||
# - or we create one before the next present section. Note that in numpydoc, the | ||
# "Notes" section is the third last possible section, only potentially followed | ||
# by "References" and "Examples". | ||
# - or we append a new "Notes" section at the end. | ||
doc = inspect.cleandoc(func.__doc__) | ||
if "\nNotes\n-----" in doc: | ||
before, after = doc.split("\nNotes\n-----", 1) | ||
elif (index := doc.find("\nReferences\n----------")) != -1: | ||
before, after = doc[:index], doc[index:] | ||
elif (index := doc.find("\nExamples\n--------")) != -1: | ||
before, after = doc[:index], doc[index:] | ||
else: | ||
# No "Notes", "References", or "Examples" --> append to the end. | ||
before = doc + "\n" | ||
after = "" | ||
|
||
func.__doc__ = f"{before}\nNotes\n-----\n\n.. note::\n\n {message}\n{after}" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you know examples of each type (Notes/no Notes/no Refs/etc) that we could check here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For the four branch aternative to detect the insert position: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per https://numpydoc.readthedocs.io/en/latest/format.html "See Also" should come before "Notes" and we also have to check for "Examples"? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but it looks like its rendering in the correct order in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like something in the html pipeline is fixing the order as
but |
||
|
||
|
||
## Global ## | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, since you've wrapped all of them with this function, you could return a new function from here and not modify the original.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the scope of this PR, I don't care too much how this is implemented, I just need a correct
__qualname__
and changing is good enough.Yes, you could create a new function, but that's an additional indirection. While negligible from a performance perspective, it shows up e.g. in stacktraces. I'm not even sure that this way of adding functionality to classes reasonable style and what other edge-case consequences that has. Alternatively, one could make the original functions private. But that's all for a different PR.