_docstring.py 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import inspect
  2. from . import _api
  3. class Substitution:
  4. """
  5. A decorator that performs %-substitution on an object's docstring.
  6. This decorator should be robust even if ``obj.__doc__`` is None (for
  7. example, if -OO was passed to the interpreter).
  8. Usage: construct a docstring.Substitution with a sequence or dictionary
  9. suitable for performing substitution; then decorate a suitable function
  10. with the constructed object, e.g.::
  11. sub_author_name = Substitution(author='Jason')
  12. @sub_author_name
  13. def some_function(x):
  14. "%(author)s wrote this function"
  15. # note that some_function.__doc__ is now "Jason wrote this function"
  16. One can also use positional arguments::
  17. sub_first_last_names = Substitution('Edgar Allen', 'Poe')
  18. @sub_first_last_names
  19. def some_function(x):
  20. "%s %s wrote the Raven"
  21. """
  22. def __init__(self, *args, **kwargs):
  23. if args and kwargs:
  24. raise TypeError("Only positional or keyword args are allowed")
  25. self.params = args or kwargs
  26. def __call__(self, func):
  27. if func.__doc__:
  28. func.__doc__ = inspect.cleandoc(func.__doc__) % self.params
  29. return func
  30. def update(self, *args, **kwargs):
  31. """
  32. Update ``self.params`` (which must be a dict) with the supplied args.
  33. """
  34. self.params.update(*args, **kwargs)
  35. class _ArtistKwdocLoader(dict):
  36. def __missing__(self, key):
  37. if not key.endswith(":kwdoc"):
  38. raise KeyError(key)
  39. name = key[:-len(":kwdoc")]
  40. from matplotlib.artist import Artist, kwdoc
  41. try:
  42. cls, = [cls for cls in _api.recursive_subclasses(Artist)
  43. if cls.__name__ == name]
  44. except ValueError as e:
  45. raise KeyError(key) from e
  46. return self.setdefault(key, kwdoc(cls))
  47. class _ArtistPropertiesSubstitution(Substitution):
  48. """
  49. A `.Substitution` with two additional features:
  50. - Substitutions of the form ``%(classname:kwdoc)s`` (ending with the
  51. literal ":kwdoc" suffix) trigger lookup of an Artist subclass with the
  52. given *classname*, and are substituted with the `.kwdoc` of that class.
  53. - Decorating a class triggers substitution both on the class docstring and
  54. on the class' ``__init__`` docstring (which is a commonly required
  55. pattern for Artist subclasses).
  56. """
  57. def __init__(self):
  58. self.params = _ArtistKwdocLoader()
  59. def __call__(self, obj):
  60. super().__call__(obj)
  61. if isinstance(obj, type) and obj.__init__ != object.__init__:
  62. self(obj.__init__)
  63. return obj
  64. def copy(source):
  65. """Copy a docstring from another source function (if present)."""
  66. def do_copy(target):
  67. if source.__doc__:
  68. target.__doc__ = source.__doc__
  69. return target
  70. return do_copy
  71. # Create a decorator that will house the various docstring snippets reused
  72. # throughout Matplotlib.
  73. dedent_interpd = interpd = _ArtistPropertiesSubstitution()