2
2
import contextlib
3
3
from functools import wraps
4
4
import inspect
5
+ from inspect import Signature , Parameter
5
6
import logging
6
7
from numbers import Number
7
8
import re
10
11
import numpy as np
11
12
12
13
import matplotlib as mpl
13
- from . import _api , cbook
14
+ from . import _api , cbook , docstring
14
15
from .path import Path
15
16
from .transforms import (Bbox , IdentityTransform , Transform , TransformedBbox ,
16
17
TransformedPatchPath , TransformedPath )
@@ -84,6 +85,12 @@ def _stale_axes_callback(self, val):
84
85
_XYPair = namedtuple ("_XYPair" , "x y" )
85
86
86
87
88
+ class _Unset :
89
+ def __repr__ (self ):
90
+ return "<UNSET>"
91
+ UNSET = _Unset ()
92
+
93
+
87
94
class Artist :
88
95
"""
89
96
Abstract base class for objects that render into a FigureCanvas.
@@ -93,6 +100,56 @@ class Artist:
93
100
94
101
zorder = 0
95
102
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
+ def set (self , ** kwargs ):
116
+ Artist .set (self , ** kwargs )
117
+
118
+ cls .set = set
119
+ cls .set .__qualname__ = f"{ cls .__qualname__ } .set"
120
+ cls ._update_set_signature_and_docstring ()
121
+
122
+ _PROPERTIES_EXCLUDED_FROM_SET = [
123
+ 'navigate_mode' , # not a user-facing function
124
+ 'figure' , # changing the figure is such a profound operation
125
+ # that we don't want this in set()
126
+ '3d_properties' , # cannot be used as a keyword do to leading digit
127
+ ]
128
+
129
+ @classmethod
130
+ def _update_set_signature_and_docstring (cls ):
131
+ """
132
+ Update the signature of the set function to list all properties
133
+ as keyword arguments.
134
+
135
+ Property aliases are not listed in the signature for brevity, but
136
+ are still accepted as keyword arguments.
137
+ """
138
+ cls .set .__signature__ = Signature (
139
+ [Parameter ("self" , Parameter .POSITIONAL_OR_KEYWORD ),
140
+ * [Parameter (prop , Parameter .KEYWORD_ONLY , default = UNSET )
141
+ for prop in ArtistInspector (cls ).get_setters ()
142
+ if prop not in Artist ._PROPERTIES_EXCLUDED_FROM_SET ]])
143
+ cls .set ._autogenerated_signature = True
144
+
145
+ cls .set .__doc__ = "\n " .join ([
146
+ "Set multiple properties at once." ,
147
+ "" ,
148
+ "Supported properties are:" ,
149
+ "" ,
150
+ f"%({ cls .__name__ } :kwdoc)s" ])
151
+ docstring .dedent_interpd (cls .set )
152
+
96
153
def __init__ (self ):
97
154
self ._stale = True
98
155
self .stale_callback = None
@@ -1096,7 +1153,9 @@ def properties(self):
1096
1153
return ArtistInspector (self ).properties ()
1097
1154
1098
1155
def set (self , ** kwargs ):
1099
- """A property batch setter. Pass *kwargs* to set properties."""
1156
+ # docstring and signature are auto-generated via
1157
+ # Artist._update_set_signature_and_docstring() at the end of the
1158
+ # module.
1100
1159
kwargs = cbook .normalize_kwargs (kwargs , self )
1101
1160
return self .update (kwargs )
1102
1161
@@ -1656,3 +1715,7 @@ def kwdoc(artist):
1656
1715
return ('\n ' .join (ai .pprint_setters_rest (leadingspace = 4 ))
1657
1716
if mpl .rcParams ['docstring.hardcopy' ] else
1658
1717
'Properties:\n ' + '\n ' .join (ai .pprint_setters (leadingspace = 4 )))
1718
+
1719
+ # We defer this to the end of them module, because it needs ArtistInspector
1720
+ # to be defined.
1721
+ Artist ._update_set_signature_and_docstring ()
0 commit comments