1import inspect
2
3from . import _api
4
5
6def kwarg_doc(text):
7 """
8 Decorator for defining the kwdoc documentation of artist properties.
9
10 This decorator can be applied to artist property setter methods.
11 The given text is stored in a private attribute ``_kwarg_doc`` on
12 the method. It is used to overwrite auto-generated documentation
13 in the *kwdoc list* for artists. The kwdoc list is used to document
14 ``**kwargs`` when they are properties of an artist. See e.g. the
15 ``**kwargs`` section in `.Axes.text`.
16
17 The text should contain the supported types, as well as the default
18 value if applicable, e.g.:
19
20 @_docstring.kwarg_doc("bool, default: :rc:`text.usetex`")
21 def set_usetex(self, usetex):
22
23 See Also
24 --------
25 matplotlib.artist.kwdoc
26
27 """
28 def decorator(func):
29 func._kwarg_doc = text
30 return func
31 return decorator
32
33
34class Substitution:
35 """
36 A decorator that performs %-substitution on an object's docstring.
37
38 This decorator should be robust even if ``obj.__doc__`` is None (for
39 example, if -OO was passed to the interpreter).
40
41 Usage: construct a docstring.Substitution with a sequence or dictionary
42 suitable for performing substitution; then decorate a suitable function
43 with the constructed object, e.g.::
44
45 sub_author_name = Substitution(author='Jason')
46
47 @sub_author_name
48 def some_function(x):
49 "%(author)s wrote this function"
50
51 # note that some_function.__doc__ is now "Jason wrote this function"
52
53 One can also use positional arguments::
54
55 sub_first_last_names = Substitution('Edgar Allen', 'Poe')
56
57 @sub_first_last_names
58 def some_function(x):
59 "%s %s wrote the Raven"
60 """
61 def __init__(self, *args, **kwargs):
62 if args and kwargs:
63 raise TypeError("Only positional or keyword args are allowed")
64 self.params = args or kwargs
65
66 def __call__(self, func):
67 if func.__doc__:
68 func.__doc__ = inspect.cleandoc(func.__doc__) % self.params
69 return func
70
71 def update(self, *args, **kwargs):
72 """
73 Update ``self.params`` (which must be a dict) with the supplied args.
74 """
75 self.params.update(*args, **kwargs)
76
77
78class _ArtistKwdocLoader(dict):
79 def __missing__(self, key):
80 if not key.endswith(":kwdoc"):
81 raise KeyError(key)
82 name = key[:-len(":kwdoc")]
83 from matplotlib.artist import Artist, kwdoc
84 try:
85 cls, = [cls for cls in _api.recursive_subclasses(Artist)
86 if cls.__name__ == name]
87 except ValueError as e:
88 raise KeyError(key) from e
89 return self.setdefault(key, kwdoc(cls))
90
91
92class _ArtistPropertiesSubstitution(Substitution):
93 """
94 A `.Substitution` with two additional features:
95
96 - Substitutions of the form ``%(classname:kwdoc)s`` (ending with the
97 literal ":kwdoc" suffix) trigger lookup of an Artist subclass with the
98 given *classname*, and are substituted with the `.kwdoc` of that class.
99 - Decorating a class triggers substitution both on the class docstring and
100 on the class' ``__init__`` docstring (which is a commonly required
101 pattern for Artist subclasses).
102 """
103
104 def __init__(self):
105 self.params = _ArtistKwdocLoader()
106
107 def __call__(self, obj):
108 super().__call__(obj)
109 if isinstance(obj, type) and obj.__init__ != object.__init__:
110 self(obj.__init__)
111 return obj
112
113
114def copy(source):
115 """Copy a docstring from another source function (if present)."""
116 def do_copy(target):
117 if source.__doc__:
118 target.__doc__ = source.__doc__
119 return target
120 return do_copy
121
122
123# Create a decorator that will house the various docstring snippets reused
124# throughout Matplotlib.
125dedent_interpd = interpd = _ArtistPropertiesSubstitution()