diff --git a/doc/api/next_api_changes/removals/19033-AL.rst b/doc/api/next_api_changes/removals/19033-AL.rst new file mode 100644 index 000000000000..258950c1b86a --- /dev/null +++ b/doc/api/next_api_changes/removals/19033-AL.rst @@ -0,0 +1,6 @@ +The private ``matplotlib.axes._subplots._subplot_classes`` dict has been removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Support for passing ``None`` to ``subplot_class_factory`` has been removed +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Explicitly pass in the base `~matplotlib.axes.Axes` class instead. diff --git a/doc/missing-references.json b/doc/missing-references.json index 5a5d6237a549..6cc524e98b59 100644 --- a/doc/missing-references.json +++ b/doc/missing-references.json @@ -20,7 +20,6 @@ "doc/users/prev_whats_new/whats_new_3.1.0.rst:335" ], "cbar_axes": [ - "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid.__init__:45", "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:45", "lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:45" ], @@ -79,39 +78,35 @@ ], "matplotlib.axes.Axes.lines": [ "doc/tutorials/intermediate/artists.rst:100", - "doc/tutorials/intermediate/artists.rst:440" + "doc/tutorials/intermediate/artists.rst:442" ], "matplotlib.axes.Axes.patch": [ "doc/api/prev_api_changes/api_changes_0.98.x.rst:89", - "doc/tutorials/intermediate/artists.rst:186", - "doc/tutorials/intermediate/artists.rst:424" + "doc/tutorials/intermediate/artists.rst:187", + "doc/tutorials/intermediate/artists.rst:426" ], "matplotlib.axes.Axes.patches": [ - "doc/tutorials/intermediate/artists.rst:463" + "doc/tutorials/intermediate/artists.rst:465" ], "matplotlib.axes.Axes.transAxes": [ - "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredDirectionArrows.__init__:8", "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredDirectionArrows:8" ], "matplotlib.axes.Axes.transData": [ - "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredAuxTransformBox.__init__:11", "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredAuxTransformBox:11", - "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredEllipse.__init__:8", "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredEllipse:8", - "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar.__init__:8", "lib/mpl_toolkits/axes_grid1/anchored_artists.py:docstring of mpl_toolkits.axes_grid1.anchored_artists.AnchoredSizeBar:8" ], "matplotlib.axes.Axes.viewLim": [ "doc/api/prev_api_changes/api_changes_0.99.x.rst:23" ], "matplotlib.axes.Axes.xaxis": [ - "doc/tutorials/intermediate/artists.rst:608" + "doc/tutorials/intermediate/artists.rst:610" ], "matplotlib.axes.Axes.yaxis": [ - "doc/tutorials/intermediate/artists.rst:608" + "doc/tutorials/intermediate/artists.rst:610" ], "matplotlib.axis.Axis.label": [ - "doc/tutorials/intermediate/artists.rst:655" + "doc/tutorials/intermediate/artists.rst:657" ], "matplotlib.cm.ScalarMappable.callbacksSM": [ "doc/api/prev_api_changes/api_changes_0.98.0.rst:10" @@ -124,11 +119,11 @@ ], "matplotlib.figure.Figure.patch": [ "doc/api/prev_api_changes/api_changes_0.98.x.rst:89", - "doc/tutorials/intermediate/artists.rst:186", - "doc/tutorials/intermediate/artists.rst:319" + "doc/tutorials/intermediate/artists.rst:187", + "doc/tutorials/intermediate/artists.rst:320" ], "matplotlib.figure.Figure.transFigure": [ - "doc/tutorials/intermediate/artists.rst:368" + "doc/tutorials/intermediate/artists.rst:369" ], "max": [ "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.Bbox.p1:4" @@ -256,15 +251,6 @@ "doc/tutorials/intermediate/artists.rst:44", "doc/tutorials/intermediate/artists.rst:67" ], - "matplotlib.axes._axes.Axes": [ - "doc/api/artist_api.rst:189", - "doc/api/axes_api.rst:609", - "lib/matplotlib/projections/geo.py:docstring of matplotlib.projections.geo.GeoAxes:1", - "lib/matplotlib/projections/polar.py:docstring of matplotlib.projections.polar.PolarAxes:1", - "lib/mpl_toolkits/axes_grid1/mpl_axes.py:docstring of mpl_toolkits.axes_grid1.mpl_axes.Axes:1", - "lib/mpl_toolkits/axisartist/axislines.py:docstring of mpl_toolkits.axisartist.axislines.Axes:1", - "lib/mpl_toolkits/mplot3d/axes3d.py:docstring of mpl_toolkits.mplot3d.axes3d.Axes3D:1" - ], "matplotlib.axes._base._AxesBase": [ "doc/api/artist_api.rst:189", "doc/api/axes_api.rst:609", @@ -428,16 +414,6 @@ "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.BlendedAffine2D:1", "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.BlendedGenericTransform:1" ], - "matplotlib.tri.trifinder.TriFinder": [ - "lib/matplotlib/tri/trifinder.py:docstring of matplotlib.tri.trifinder.TrapezoidMapTriFinder:1" - ], - "matplotlib.tri.triinterpolate.TriInterpolator": [ - "lib/matplotlib/tri/triinterpolate.py:docstring of matplotlib.tri.triinterpolate.CubicTriInterpolator:1", - "lib/matplotlib/tri/triinterpolate.py:docstring of matplotlib.tri.triinterpolate.LinearTriInterpolator:1" - ], - "matplotlib.tri.trirefine.TriRefiner": [ - "lib/matplotlib/tri/trirefine.py:docstring of matplotlib.tri.trirefine.UniformTriRefiner:1" - ], "matplotlib.widgets._SelectorWidget": [ "lib/matplotlib/widgets.py:docstring of matplotlib.widgets.LassoSelector:1", "lib/matplotlib/widgets.py:docstring of matplotlib.widgets.PolygonSelector:1", @@ -485,7 +461,7 @@ "lib/mpl_toolkits/axisartist/axislines.py:docstring of mpl_toolkits.axisartist.axislines.AxisArtistHelper.Fixed:1", "lib/mpl_toolkits/axisartist/axislines.py:docstring of mpl_toolkits.axisartist.axislines.AxisArtistHelper.Floating:1" ], - "mpl_toolkits.axisartist.floating_axes.Floating AxesHostAxes": [ + "mpl_toolkits.axisartist.floating_axes.FloatingAxesHostAxes": [ "<unknown>:1", "doc/api/_as_gen/mpl_toolkits.axisartist.floating_axes.rst:31:<autosummary>:1" ], @@ -498,11 +474,11 @@ }, "py:data": { "matplotlib.axes.Axes.transAxes": [ - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.legend:219", - "lib/matplotlib/figure.py:docstring of matplotlib.figure.FigureBase.legend:220", - "lib/matplotlib/legend.py:docstring of matplotlib.legend.Legend:179", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.figlegend:220", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.legend:219" + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.legend:222", + "lib/matplotlib/figure.py:docstring of matplotlib.figure.FigureBase.legend:223", + "lib/matplotlib/legend.py:docstring of matplotlib.legend.Legend:182", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.figlegend:223", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.legend:222" ] }, "py:func": { @@ -541,7 +517,6 @@ ], "_iter_collection": [ "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.RendererBase.draw_path_collection:12", - "lib/matplotlib/backends/backend_agg.py:docstring of matplotlib.backends.backend_agg.RendererAgg.draw_path_collection:12", "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf.draw_path_collection:12", "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS.draw_path_collection:12", "lib/matplotlib/backends/backend_svg.py:docstring of matplotlib.backends.backend_svg.RendererSVG.draw_path_collection:12", @@ -549,7 +524,6 @@ ], "_iter_collection_raw_paths": [ "lib/matplotlib/backend_bases.py:docstring of matplotlib.backend_bases.RendererBase.draw_path_collection:12", - "lib/matplotlib/backends/backend_agg.py:docstring of matplotlib.backends.backend_agg.RendererAgg.draw_path_collection:12", "lib/matplotlib/backends/backend_pdf.py:docstring of matplotlib.backends.backend_pdf.RendererPdf.draw_path_collection:12", "lib/matplotlib/backends/backend_ps.py:docstring of matplotlib.backends.backend_ps.RendererPS.draw_path_collection:12", "lib/matplotlib/backends/backend_svg.py:docstring of matplotlib.backends.backend_svg.RendererSVG.draw_path_collection:12", @@ -574,24 +548,22 @@ "lib/matplotlib/transforms.py:docstring of matplotlib.transforms.Affine2DBase:13" ], "matplotlib.collections._CollectionWithSizes.set_sizes": [ - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.barbs:172", - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.broken_barh:82", - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.fill_between:112", - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.fill_betweenx:112", - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.hexbin:168", - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.pcolor:165", - "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.quiver:202", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.barbs:172", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.broken_barh:82", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.fill_between:112", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.fill_betweenx:112", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.hexbin:168", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.pcolor:165", - "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.quiver:202", - "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Barbs.__init__:176", - "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Barbs:206", - "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Quiver.__init__:206", - "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Quiver:239" + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.barbs:171", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.broken_barh:81", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.fill_between:111", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.fill_betweenx:111", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.hexbin:167", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.pcolor:164", + "lib/matplotlib/axes/_axes.py:docstring of matplotlib.axes._axes.Axes.quiver:201", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.barbs:171", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.broken_barh:81", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.fill_between:111", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.fill_betweenx:111", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.hexbin:167", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.pcolor:164", + "lib/matplotlib/pyplot.py:docstring of matplotlib.pyplot.quiver:201", + "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Barbs:205", + "lib/matplotlib/quiver.py:docstring of matplotlib.quiver.Quiver:238" ], "matplotlib.dates.DateFormatter.__call__": [ "doc/users/prev_whats_new/whats_new_1.5.rst:497" @@ -727,7 +699,6 @@ "doc/users/event_handling.rst:179" ], "Size.from_any": [ - "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid.__init__:57", "lib/mpl_toolkits/axes_grid1/axes_grid.py:docstring of mpl_toolkits.axes_grid1.axes_grid.ImageGrid:57", "lib/mpl_toolkits/axisartist/axes_grid.py:docstring of mpl_toolkits.axisartist.axes_grid.ImageGrid:57" ], diff --git a/lib/matplotlib/axes/_subplots.py b/lib/matplotlib/axes/_subplots.py index 5e8d3822fa53..beefc077f703 100644 --- a/lib/matplotlib/axes/_subplots.py +++ b/lib/matplotlib/axes/_subplots.py @@ -1,6 +1,4 @@ -import functools - -from matplotlib import _api +from matplotlib import _api, cbook from matplotlib.axes._axes import Axes from matplotlib.gridspec import GridSpec, SubplotSpec @@ -36,15 +34,6 @@ def __init__(self, fig, *args, **kwargs): # This will also update the axes position. self.set_subplotspec(SubplotSpec._from_subplot_args(fig, args)) - def __reduce__(self): - # get the first axes class which does not inherit from a subplotbase - axes_class = next( - c for c in type(self).__mro__ - if issubclass(c, Axes) and not issubclass(c, SubplotBase)) - return (_picklable_subplot_class_constructor, - (axes_class,), - self.__getstate__()) - @_api.deprecated( "3.4", alternative="get_subplotspec", addendum="(get_subplotspec returns a SubplotSpec instance.)") @@ -169,53 +158,6 @@ def _make_twin_axes(self, *args, **kwargs): return twin -# this here to support cartopy which was using a private part of the -# API to register their Axes subclasses. - -# In 3.1 this should be changed to a dict subclass that warns on use -# In 3.3 to a dict subclass that raises a useful exception on use -# In 3.4 should be removed - -# The slow timeline is to give cartopy enough time to get several -# release out before we break them. -_subplot_classes = {} - - -@functools.lru_cache(None) -def subplot_class_factory(axes_class=None): - """ - Make a new class that inherits from `.SubplotBase` and the - given axes_class (which is assumed to be a subclass of `.axes.Axes`). - This is perhaps a little bit roundabout to make a new class on - the fly like this, but it means that a new Subplot class does - not have to be created for every type of Axes. - """ - if axes_class is None: - _api.warn_deprecated( - "3.3", message="Support for passing None to subplot_class_factory " - "is deprecated since %(since)s; explicitly pass the default Axes " - "class instead. This will become an error %(removal)s.") - axes_class = Axes - try: - # Avoid creating two different instances of GeoAxesSubplot... - # Only a temporary backcompat fix. This should be removed in - # 3.4 - return next(cls for cls in SubplotBase.__subclasses__() - if cls.__bases__ == (SubplotBase, axes_class)) - except StopIteration: - return type("%sSubplot" % axes_class.__name__, - (SubplotBase, axes_class), - {'_axes_class': axes_class}) - - +subplot_class_factory = cbook._make_class_factory( + SubplotBase, "{}Subplot", "_axes_class") Subplot = subplot_class_factory(Axes) # Provided for backward compatibility. - - -def _picklable_subplot_class_constructor(axes_class): - """ - Stub factory that returns an empty instance of the appropriate subplot - class when called with an axes class. This is purely to allow pickling of - Axes and Subplots. - """ - subplot_class = subplot_class_factory(axes_class) - return subplot_class.__new__(subplot_class) diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index d334d48eafb2..4f080f8e6fe1 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -2188,3 +2188,53 @@ def _unikey_or_keysym_to_mplkey(unikey, keysym): "next": "pagedown", # Used by tk. }.get(key, key) return key + + +@functools.lru_cache(None) +def _make_class_factory(mixin_class, fmt, attr_name=None): + """ + Return a function that creates picklable classes inheriting from a mixin. + + After :: + + factory = _make_class_factory(FooMixin, fmt, attr_name) + FooAxes = factory(Axes) + + ``Foo`` is a class that inherits from ``FooMixin`` and ``Axes`` and **is + picklable** (picklability is what differentiates this from a plain call to + `type`). Its ``__name__`` is set to ``fmt.format(Axes.__name__)`` and the + base class is stored in the ``attr_name`` attribute, if not None. + + Moreover, the return value of ``factory`` is memoized: calls with the same + ``Axes`` class always return the same subclass. + """ + + @functools.lru_cache(None) + def class_factory(axes_class): + # The parameter is named "axes_class" for backcompat but is really just + # a base class; no axes semantics are used. + base_class = axes_class + + class subcls(mixin_class, base_class): + # Better approximation than __module__ = "matplotlib.cbook". + __module__ = mixin_class.__module__ + + def __reduce__(self): + return (_picklable_class_constructor, + (mixin_class, fmt, attr_name, base_class), + self.__getstate__()) + + subcls.__name__ = subcls.__qualname__ = fmt.format(base_class.__name__) + if attr_name is not None: + setattr(subcls, attr_name, base_class) + return subcls + + class_factory.__module__ = mixin_class.__module__ + return class_factory + + +def _picklable_class_constructor(mixin_class, fmt, attr_name, base_class): + """Internal helper for _make_class_factory.""" + factory = _make_class_factory(mixin_class, fmt, attr_name) + cls = factory(base_class) + return cls.__new__(cls) diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 3736888fdde5..184e513d5a69 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -6340,21 +6340,6 @@ def test_spines_properbbox_after_zoom(): np.testing.assert_allclose(bb.get_points(), bb2.get_points(), rtol=1e-6) -def test_cartopy_backcompat(): - - class Dummy(matplotlib.axes.Axes): - ... - - class DummySubplot(matplotlib.axes.SubplotBase, Dummy): - _axes_class = Dummy - - matplotlib.axes._subplots._subplot_classes[Dummy] = DummySubplot - - FactoryDummySubplot = matplotlib.axes.subplot_class_factory(Dummy) - - assert DummySubplot is FactoryDummySubplot - - def test_gettightbbox_ignore_nan(): fig, ax = plt.subplots() remove_ticks_and_titles(fig) diff --git a/lib/matplotlib/tests/test_pickle.py b/lib/matplotlib/tests/test_pickle.py index 5c169cb23303..c8b9260bc291 100644 --- a/lib/matplotlib/tests/test_pickle.py +++ b/lib/matplotlib/tests/test_pickle.py @@ -10,6 +10,7 @@ import matplotlib.pyplot as plt import matplotlib.transforms as mtransforms import matplotlib.figure as mfigure +from mpl_toolkits.axes_grid1 import parasite_axes def test_simple(): @@ -212,3 +213,8 @@ def test_unpickle_canvas(): out.seek(0) fig2 = pickle.load(out) assert fig2.canvas is not None + + +def test_mpl_toolkits(): + ax = parasite_axes.host_axes([0, 0, 1, 1]) + assert type(pickle.loads(pickle.dumps(ax))) == parasite_axes.HostAxes diff --git a/lib/mpl_toolkits/axes_grid1/parasite_axes.py b/lib/mpl_toolkits/axes_grid1/parasite_axes.py index 7aba5d9efa19..a140be9ef260 100644 --- a/lib/mpl_toolkits/axes_grid1/parasite_axes.py +++ b/lib/mpl_toolkits/axes_grid1/parasite_axes.py @@ -1,6 +1,6 @@ import functools -from matplotlib import _api +from matplotlib import _api, cbook import matplotlib.artist as martist import matplotlib.image as mimage import matplotlib.transforms as mtransforms @@ -95,12 +95,8 @@ def apply_aspect(self, position=None): # end of aux_transform support -@functools.lru_cache(None) -def parasite_axes_class_factory(axes_class): - return type("%sParasite" % axes_class.__name__, - (ParasiteAxesBase, axes_class), {}) - - +parasite_axes_class_factory = cbook._make_class_factory( + ParasiteAxesBase, "{}Parasite") ParasiteAxes = parasite_axes_class_factory(Axes) @@ -277,7 +273,7 @@ def _add_twin_axes(self, axes_class, **kwargs): *kwargs* are forwarded to the parasite axes constructor. """ if axes_class is None: - axes_class = self._get_base_axes() + axes_class = self._base_axes_class ax = parasite_axes_class_factory(axes_class)(self, **kwargs) self.parasites.append(ax) ax._remove_method = self._remove_any_twin @@ -304,11 +300,10 @@ def get_tightbbox(self, renderer, call_axes_locator=True, return Bbox.union([b for b in bbs if b.width != 0 or b.height != 0]) -@functools.lru_cache(None) -def host_axes_class_factory(axes_class): - return type("%sHostAxes" % axes_class.__name__, - (HostAxesBase, axes_class), - {'_get_base_axes': lambda self: axes_class}) +host_axes_class_factory = cbook._make_class_factory( + HostAxesBase, "{}HostAxes", "_base_axes_class") +HostAxes = host_axes_class_factory(Axes) +SubplotHost = subplot_class_factory(HostAxes) def host_subplot_class_factory(axes_class): @@ -317,10 +312,6 @@ def host_subplot_class_factory(axes_class): return subplot_host_class -HostAxes = host_axes_class_factory(Axes) -SubplotHost = subplot_class_factory(HostAxes) - - def host_axes(*args, axes_class=Axes, figure=None, **kwargs): """ Create axes that can act as a hosts to parasitic axes. diff --git a/lib/mpl_toolkits/axisartist/floating_axes.py b/lib/mpl_toolkits/axisartist/floating_axes.py index 32fdedad0d27..ac146729b8d0 100644 --- a/lib/mpl_toolkits/axisartist/floating_axes.py +++ b/lib/mpl_toolkits/axisartist/floating_axes.py @@ -5,10 +5,9 @@ # TODO : # see if tick_iterator method can be simplified by reusing the parent method. -import functools - import numpy as np +from matplotlib import cbook import matplotlib.patches as mpatches from matplotlib.path import Path import matplotlib.axes as maxes @@ -351,12 +350,8 @@ def adjust_axes_lim(self): self.set_ylim(ymin-dy, ymax+dy) -@functools.lru_cache(None) -def floatingaxes_class_factory(axes_class): - return type("Floating %s" % axes_class.__name__, - (FloatingAxesBase, axes_class), {}) - - +floatingaxes_class_factory = cbook._make_class_factory( + FloatingAxesBase, "Floating{}") FloatingAxes = floatingaxes_class_factory( host_axes_class_factory(axislines.Axes)) FloatingSubplot = maxes.subplot_class_factory(FloatingAxes)