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,51 @@ 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
+ # docstring based on the subclasses' properties.
107
+
108
+ if 'set' in cls .__dict__ :
109
+ return # don't overwrite set if the subclass defines set itself.
110
+
111
+ def set (self , ** kwargs ):
112
+ Artist .set (self , ** kwargs )
113
+
114
+ cls .set = set
115
+ cls ._update_set_signature_and_docstring ()
116
+
117
+ _PROPERTIES_EXCLUDED_FROM_SET = [
118
+ 'navigate_mode' , # not a user-facing function
119
+ 'figure' , # changing the figure is such a profound operation
120
+ # that we don't want this in set()
121
+ '3d_properties' , # cannot be used as a keyword do to leading digit
122
+ ]
123
+
124
+ @classmethod
125
+ def _update_set_signature_and_docstring (cls ):
126
+ """
127
+ Update the signature of the set function to list all properties
128
+ as keyword arguments.
129
+
130
+ Property aliases are not listed in the signature for brevity, but
131
+ are still accepted as keyword arguments.
132
+ """
133
+ cls .set .__signature__ = Signature (
134
+ [Parameter ("self" , Parameter .POSITIONAL_OR_KEYWORD ),
135
+ * [Parameter (prop , Parameter .KEYWORD_ONLY , default = UNSET )
136
+ for prop in ArtistInspector (cls ).get_setters ()
137
+ if prop not in Artist ._PROPERTIES_EXCLUDED_FROM_SET ]])
138
+
139
+ cls .set .__doc__ = "\n " .join ([
140
+ "Set multiple properties at once." ,
141
+ "" ,
142
+ "Supported properties are:" ,
143
+ "" ,
144
+ f"%({ cls .__name__ } :kwdoc)s" ])
145
+ docstring .dedent_interpd (cls .set )
146
+
147
+
96
148
def __init__ (self ):
97
149
self ._stale = True
98
150
self .stale_callback = None
@@ -1096,7 +1148,9 @@ def properties(self):
1096
1148
return ArtistInspector (self ).properties ()
1097
1149
1098
1150
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.
1100
1154
kwargs = cbook .normalize_kwargs (kwargs , self )
1101
1155
return self .update (kwargs )
1102
1156
@@ -1656,3 +1710,7 @@ def kwdoc(artist):
1656
1710
return ('\n ' .join (ai .pprint_setters_rest (leadingspace = 4 ))
1657
1711
if mpl .rcParams ['docstring.hardcopy' ] else
1658
1712
'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 ()
0 commit comments