Description
Cartopy's axes_grid_basic was broken by Matplotlib 3.6. The relevant call is:
axgr = AxesGrid(fig, 111, axes_class=axes_class,
nrows_ncols=(3, 2),
axes_pad=0.6,
cbar_location='right',
cbar_mode='single',
cbar_pad=0.2,
cbar_size='3%',
label_mode='') # note the empty label_mode
In #23550, we began explicitly checking for the documented values of label_mode
, ('All', 'L', '1'
), which this runs afoul of. It appears the previous code let ''
(or any option not handled) essentially work as a noop for set_label_mode()
.
Based on the output when the example worked, '1'
would be fine behavior-wise, but that currently produces in this example:
/Users/rmay/repos/cartopy/examples/miscellanea/axes_grid_basic.py:48: MatplotlibDeprecationWarning: The resize_event function was deprecated in Matplotlib 3.6 and will be removed two minor releases later. Use callbacks.process('resize_event', ResizeEvent(...)) instead.
fig = plt.figure()
Traceback (most recent call last):
File "/Users/rmay/repos/cartopy/examples/miscellanea/axes_grid_basic.py", line 78, in <module>
main()
File "/Users/rmay/repos/cartopy/examples/miscellanea/axes_grid_basic.py", line 49, in main
axgr = AxesGrid(fig, 111,
File "/Users/rmay/miniconda3/envs/py310/lib/python3.10/site-packages/mpl_toolkits/axes_grid1/axes_grid.py", line 391, in __init__
super().__init__(
File "/Users/rmay/miniconda3/envs/py310/lib/python3.10/site-packages/mpl_toolkits/axes_grid1/axes_grid.py", line 174, in __init__
self.set_label_mode(label_mode)
File "/Users/rmay/miniconda3/envs/py310/lib/python3.10/site-packages/mpl_toolkits/axes_grid1/axes_grid.py", line 295, in set_label_mode
_tick_only(ax, bottom_on=True, left_on=True)
File "/Users/rmay/miniconda3/envs/py310/lib/python3.10/site-packages/mpl_toolkits/axes_grid1/axes_grid.py", line 16, in _tick_only
ax.axis["bottom"].toggle(ticklabels=bottom_off, label=bottom_off)
TypeError: 'method' object is not subscriptable
This is because, while AxesGrid
supposedly supports arbitrary classes by passing axes_class
, there's a huge caveat: the default is mpl_toolkits.axes_grid1.mpl_axes.Axes
(TERRIBLY CONFUSING NAME--it's imported as just Axes
in axis_grid.py
). This is a custom subclass that adds:
@property
def axis(self):
return self._axislines
def clear(self):
# docstring inherited
super().clear()
# Init axis artists.
self._axislines = self.AxisDict(self)
self._axislines.update(
bottom=SimpleAxisArtist(self.xaxis, 1, self.spines["bottom"]),
top=SimpleAxisArtist(self.xaxis, 2, self.spines["top"]),
left=SimpleAxisArtist(self.yaxis, 1, self.spines["left"]),
right=SimpleAxisArtist(self.yaxis, 2, self.spines["right"]))
Options I can think of:
- Restore a noop option for
set_label_mode
- Fix
AxesGrid
to work more generally withaxes_class
--monkey-patch/subclass to add the necessary modifications - Document that
axes_class
needs to have certain behavior - Fix
AxesGrid
to not rely on a property that OVERRIDESmatplotlib.axes.Axes.axis
with a different type that also manually dispatches to the unboundaxis()
method 😱
class AxisDict(dict):
def __init__(self, axes):
self.axes = axes
super().__init__()
def __getitem__(self, k):
if isinstance(k, tuple):
r = SimpleChainedObjects(
# super() within a list comprehension needs explicit args.
[super(Axes.AxisDict, self).__getitem__(k1) for k1 in k])
return r
elif isinstance(k, slice):
if k.start is None and k.stop is None and k.step is None:
return SimpleChainedObjects(list(self.values()))
else:
raise ValueError("Unsupported slice")
else:
return dict.__getitem__(self, k)
def __call__(self, *v, **kwargs):
return maxes.Axes.axis(self.axes, *v, **kwargs)
Anyone have any opinions?