Helper to generate Axes methods wrapping Axis methods. After :: get_foo = _axis_method_wrapper("xaxis", "get_bar") (in the body of a class) ``get_foo`` is a method that forwards it arguments to the ``get_bar`` method of the ``xaxis`` attribute, and gets its signature
| 34 | |
| 35 | |
| 36 | class _axis_method_wrapper: |
| 37 | """ |
| 38 | Helper to generate Axes methods wrapping Axis methods. |
| 39 | |
| 40 | After :: |
| 41 | |
| 42 | get_foo = _axis_method_wrapper("xaxis", "get_bar") |
| 43 | |
| 44 | (in the body of a class) ``get_foo`` is a method that forwards it arguments |
| 45 | to the ``get_bar`` method of the ``xaxis`` attribute, and gets its |
| 46 | signature and docstring from ``Axis.get_bar``. |
| 47 | |
| 48 | The docstring of ``get_foo`` is built by replacing "this Axis" by "the |
| 49 | {attr_name}" (i.e., "the xaxis", "the yaxis") in the wrapped method's |
| 50 | dedented docstring; additional replacements can be given in *doc_sub*. |
| 51 | """ |
| 52 | |
| 53 | def __init__(self, attr_name, method_name, *, doc_sub=None): |
| 54 | self.attr_name = attr_name |
| 55 | self.method_name = method_name |
| 56 | # Immediately put the docstring in ``self.__doc__`` so that docstring |
| 57 | # manipulations within the class body work as expected. |
| 58 | doc = inspect.getdoc(getattr(maxis.Axis, method_name)) |
| 59 | self._missing_subs = [] |
| 60 | if doc: |
| 61 | doc_sub = {"this Axis": f"the {self.attr_name}", **(doc_sub or {})} |
| 62 | for k, v in doc_sub.items(): |
| 63 | if k not in doc: # Delay raising error until we know qualname. |
| 64 | self._missing_subs.append(k) |
| 65 | doc = doc.replace(k, v) |
| 66 | self.__doc__ = doc |
| 67 | |
| 68 | def __set_name__(self, owner, name): |
| 69 | # This is called at the end of the class body as |
| 70 | # ``self.__set_name__(cls, name_under_which_self_is_assigned)``; we |
| 71 | # rely on that to give the wrapper the correct __name__/__qualname__. |
| 72 | get_method = attrgetter(f"{self.attr_name}.{self.method_name}") |
| 73 | |
| 74 | def wrapper(self, *args, **kwargs): |
| 75 | return get_method(self)(*args, **kwargs) |
| 76 | |
| 77 | wrapper.__module__ = owner.__module__ |
| 78 | wrapper.__name__ = name |
| 79 | wrapper.__qualname__ = f"{owner.__qualname__}.{name}" |
| 80 | wrapper.__doc__ = self.__doc__ |
| 81 | # Manually copy the signature instead of using functools.wraps because |
| 82 | # displaying the Axis method source when asking for the Axes method |
| 83 | # source would be confusing. |
| 84 | wrapper.__signature__ = inspect.signature( |
| 85 | getattr(maxis.Axis, self.method_name)) |
| 86 | |
| 87 | if self._missing_subs: |
| 88 | raise ValueError( |
| 89 | "The definition of {} expected that the docstring of Axis.{} " |
| 90 | "contains {!r} as substrings".format( |
| 91 | wrapper.__qualname__, self.method_name, |
| 92 | ", ".join(map(repr, self._missing_subs)))) |
| 93 |