|
3 | 3 | The base axes class used for all ProPlot figures. |
4 | 4 | """ |
5 | 5 | import copy |
| 6 | +import functools |
6 | 7 | import re |
| 8 | +import types |
7 | 9 | from numbers import Integral, Number |
8 | 10 |
|
9 | 11 | import matplotlib.axes as maxes |
|
57 | 59 | } |
58 | 60 |
|
59 | 61 |
|
60 | | -docstring.snippets['axes.other'] = """ |
| 62 | +_format_other_docstring = """ |
61 | 63 | rc_kw : dict, optional |
62 | 64 | Dictionary containing `~proplot.config.rc` settings applied to |
63 | 65 | this axes using `~proplot.config.RcConfigurator.context`. |
|
66 | 68 | and used to update axes `~proplot.config.rc` settings. For example, |
67 | 69 | ``abcstyle='A.'`` modifies the :rcraw:`abc.style` setting. |
68 | 70 | """ |
| 71 | +docstring.snippets['axes.format_other'] = _format_other_docstring |
69 | 72 |
|
70 | | -docstring.snippets['axes.patch_kw'] = """ |
| 73 | +_patch_kw_docstring = """ |
71 | 74 | patch_kw : dict-like, optional |
72 | 75 | Keyword arguments used to update the background patch. This can |
73 | 76 | be used e.g. to apply background hatching with ``patch_kw={'hatch': 'xxx'}``. |
74 | 77 | """ |
| 78 | +docstring.snippets['axes.patch_kw'] = _patch_kw_docstring |
75 | 79 |
|
76 | | -docstring.snippets['axes.proj'] = """ |
77 | | -The map projection specification(s). If ``'cartesian'`` (the default), a |
78 | | -`~proplot.axes.CartesianAxes` is created. If ``'polar'``, a |
79 | | -`~proplot.axes.PolarAxes` is created. Otherwise, the argument is |
80 | | -interpreted by `~proplot.constructor.Proj`, and the result is used |
81 | | -to make a `~proplot.axes.GeoAxes` (in this case the argument can be |
82 | | -a `cartopy.crs.Projection` instance, a `~mpl_toolkits.basemap.Basemap` |
83 | | -instance, or a projection name listed in :ref:`this table <proj_table>`). |
84 | | -""" |
85 | | - |
86 | | -docstring.snippets['axes.inset'] = """ |
| 80 | +_inset_docstring = """ |
87 | 81 | Return an inset `CartesianAxes`. This is similar to the builtin |
88 | 82 | `~matplotlib.axes.Axes.inset_axes` but includes some extra options. |
89 | 83 |
|
|
129 | 123 | ---------------- |
130 | 124 | **kwargs |
131 | 125 | Passed to `CartesianAxes`. |
132 | | -""" % docstring.snippets |
| 126 | +""" |
| 127 | +docstring.snippets['axes.inset'] = _inset_docstring |
133 | 128 |
|
134 | | -docstring.snippets['axes.panel'] = """ |
| 129 | +_panel_docstring = """ |
135 | 130 | Return a panel drawn along the edge of this axes. |
136 | 131 |
|
137 | 132 | Parameters |
|
166 | 161 | `~proplot.axes.CartesianAxes` |
167 | 162 | The panel axes. |
168 | 163 | """ |
| 164 | +docstring.snippets['axes.panel'] = _panel_docstring |
| 165 | + |
| 166 | + |
| 167 | +class _MetaAxes(type): |
| 168 | + """ |
| 169 | + Redirect internal plotting calls to native matplotlib methods. |
| 170 | + """ |
| 171 | + # NOTE: This obviates the need for basemap-specific redirection to mpl.axes.Axes. |
| 172 | + def __new__(cls, name, bases, dct_orig): |
| 173 | + dct = dct_orig.copy() |
| 174 | + for attr, func_proplot in tuple(dct_orig.items()): |
| 175 | + if attr[:1] == '_': |
| 176 | + continue |
| 177 | + func_matplotlib = getattr(maxes.Axes, attr, None) |
| 178 | + if not isinstance(func_proplot, types.FunctionType): |
| 179 | + continue |
| 180 | + if not isinstance(func_matplotlib, types.FunctionType): |
| 181 | + continue |
| 182 | + dct[attr] = functools.wraps(func_proplot)( |
| 183 | + lambda self, *args, _func_proplot=func_proplot, _func_matplotlib=func_matplotlib, **kwargs: # noqa: E501 |
| 184 | + _func_matplotlib(self, *args, **kwargs) |
| 185 | + if getattr(self, '_internal_call', None) else |
| 186 | + _func_proplot(self, *args, **kwargs) |
| 187 | + ) |
| 188 | + return super().__new__(cls, name, bases, dct) |
169 | 189 |
|
170 | 190 |
|
171 | | -class Axes(maxes.Axes): |
| 191 | +class Axes(maxes.Axes, metaclass=_MetaAxes): |
172 | 192 | """ |
173 | 193 | Lowest-level axes subclass. Handles titles and axis |
174 | 194 | sharing. Adds several new methods and overrides existing ones. |
|
0 commit comments