Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 51c38d4

Browse filesBrowse files
committed
Better signature and docstring for Artist.set
1 parent 831a13c commit 51c38d4
Copy full SHA for 51c38d4

File tree

Expand file treeCollapse file tree

3 files changed

+94
-1
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+94
-1
lines changed

‎doc/api/axes_api.rst

Copy file name to clipboardExpand all lines: doc/api/axes_api.rst
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,3 +606,4 @@ Other
606606
Axes.get_default_bbox_extra_artists
607607
Axes.get_transformed_clip_path_and_affine
608608
Axes.has_data
609+
Axes.set

‎lib/matplotlib/artist.py

Copy file name to clipboardExpand all lines: lib/matplotlib/artist.py
+59-1Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import contextlib
33
from functools import wraps
44
import inspect
5+
from inspect import Signature, Parameter
56
import logging
67
from numbers import Number
78
import re
@@ -84,6 +85,12 @@ def _stale_axes_callback(self, val):
8485
_XYPair = namedtuple("_XYPair", "x y")
8586

8687

88+
class _Unset:
89+
def __repr__(self):
90+
return "<UNSET>"
91+
_UNSET = _Unset()
92+
93+
8794
class Artist:
8895
"""
8996
Abstract base class for objects that render into a FigureCanvas.
@@ -93,6 +100,51 @@ class Artist:
93100

94101
zorder = 0
95102

103+
def __init_subclass__(cls):
104+
# Inject custom set() methods into the subclass with signature and
105+
# docstring based on the subclasses' properties.
106+
107+
if not hasattr(cls.set, '_autogenerated_signature'):
108+
# Don't overwrite cls.set if the subclass or one of its parents
109+
# has defined a set method set itself.
110+
# If there was no explicit definition, cls.set is inherited from
111+
# the hierarchy of auto-generated set methods, which hold the
112+
# flag _autogenerated_signature.
113+
return
114+
115+
cls.set = lambda self, **kwargs: Artist.set(self, **kwargs)
116+
cls.set.__name__ = "set"
117+
cls.set.__qualname__ = f"{cls.__qualname__}.set"
118+
cls._update_set_signature_and_docstring()
119+
120+
_PROPERTIES_EXCLUDED_FROM_SET = [
121+
'navigate_mode', # not a user-facing function
122+
'figure', # changing the figure is such a profound operation
123+
# that we don't want this in set()
124+
'3d_properties', # cannot be used as a keyword due to leading digit
125+
]
126+
127+
@classmethod
128+
def _update_set_signature_and_docstring(cls):
129+
"""
130+
Update the signature of the set function to list all properties
131+
as keyword arguments.
132+
133+
Property aliases are not listed in the signature for brevity, but
134+
are still accepted as keyword arguments.
135+
"""
136+
cls.set.__signature__ = Signature(
137+
[Parameter("self", Parameter.POSITIONAL_OR_KEYWORD),
138+
*[Parameter(prop, Parameter.KEYWORD_ONLY, default=_UNSET)
139+
for prop in ArtistInspector(cls).get_setters()
140+
if prop not in Artist._PROPERTIES_EXCLUDED_FROM_SET]])
141+
cls.set._autogenerated_signature = True
142+
143+
cls.set.__doc__ = (
144+
"Set multiple properties at once.\n\n"
145+
"Supported properties are\n\n"
146+
+ kwdoc(cls))
147+
96148
def __init__(self):
97149
self._stale = True
98150
self.stale_callback = None
@@ -1096,7 +1148,9 @@ def properties(self):
10961148
return ArtistInspector(self).properties()
10971149

10981150
def set(self, **kwargs):
1099-
"""A property batch setter. Pass *kwargs* to set properties."""
1151+
# docstring and signature are auto-generated via
1152+
# Artist._update_set_signature_and_docstring() at the end of the
1153+
# module.
11001154
kwargs = cbook.normalize_kwargs(kwargs, self)
11011155
return self.update(kwargs)
11021156

@@ -1656,3 +1710,7 @@ def kwdoc(artist):
16561710
return ('\n'.join(ai.pprint_setters_rest(leadingspace=4))
16571711
if mpl.rcParams['docstring.hardcopy'] else
16581712
'Properties:\n' + '\n'.join(ai.pprint_setters(leadingspace=4)))
1713+
1714+
# We defer this to the end of them module, because it needs ArtistInspector
1715+
# to be defined.
1716+
Artist._update_set_signature_and_docstring()

‎lib/matplotlib/tests/test_artist.py

Copy file name to clipboardExpand all lines: lib/matplotlib/tests/test_artist.py
+34Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,3 +340,37 @@ def func(artist):
340340
art.remove_callback(oid)
341341
art.pchanged() # must not call the callback anymore
342342
assert func.counter == 2
343+
344+
345+
def test_set_signature():
346+
"""Test autogenerated ``set()`` for Artist subclasses."""
347+
class MyArtist1(martist.Artist):
348+
def set_myparam1(self, val):
349+
pass
350+
351+
assert hasattr(MyArtist1.set, '_autogenerated_signature')
352+
assert 'myparam1' in MyArtist1.set.__doc__
353+
354+
class MyArtist2(MyArtist1):
355+
def set_myparam2(self, val):
356+
pass
357+
358+
assert hasattr(MyArtist2.set, '_autogenerated_signature')
359+
assert 'myparam1' in MyArtist2.set.__doc__
360+
assert 'myparam2' in MyArtist2.set.__doc__
361+
362+
363+
def test_set_is_overwritten():
364+
"""set() defined in Artist subclasses should not be overwritten."""
365+
class MyArtist3(martist.Artist):
366+
367+
def set(self, **kwargs):
368+
"""Not overwritten."""
369+
370+
assert not hasattr(MyArtist3.set, '_autogenerated_signature')
371+
assert MyArtist3.set.__doc__ == "Not overwritten."
372+
373+
class MyArtist4(MyArtist3):
374+
pass
375+
376+
assert MyArtist4.set is MyArtist3.set

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.