Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 74b2c2d

Browse files
committed
Correctly handle Axes subclasses that override cla
This fixes, e.g., Cartopy, but probably most other third-party packages that will subclass `Axes`.
1 parent 87b801b commit 74b2c2d

File tree

3 files changed

+101
-8
lines changed

3 files changed

+101
-8
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
``AXes`` subclasses should override ``clear`` instead of ``cla``
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
For clarity, `.axes.Axes.clear` is now preferred over `.Axes.cla`. However, for
5+
backwards compatibility, the latter will remain as an alias for the former.
6+
7+
For additional compatibility with third-party libraries, Matplotlib will
8+
continue to call the ``cla`` method of any `~.axes.Axes` subclasses if they
9+
define it. In the future, this will no longer occur, and Matplotlib will only
10+
call the ``clear`` method in `~.axes.Axes` subclasses.
11+
12+
It is recommended to define only the ``clear`` method when on Matplotlib 3.6,
13+
and only ``cla`` for older versions.

lib/matplotlib/axes/_base.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,8 @@ class _AxesBase(martist.Artist):
559559
_shared_axes = {name: cbook.Grouper() for name in _axis_names}
560560
_twinned_axes = cbook.Grouper()
561561

562+
_subclass_uses_cla = False
563+
562564
@property
563565
def _axis_map(self):
564566
"""A mapping of axis names, e.g. 'x', to `Axis` instances."""
@@ -699,6 +701,19 @@ def __init__(self, fig, rect,
699701
rcParams['ytick.major.right']),
700702
which='major')
701703

704+
def __init_subclass__(cls, **kwargs):
705+
parent_uses_cla = super(cls, cls)._subclass_uses_cla
706+
if 'cla' in cls.__dict__:
707+
_api.warn_deprecated(
708+
'3.6',
709+
pending=True,
710+
message=f'Overriding `Axes.cla` in {cls.__qualname__} is '
711+
'pending deprecation in %(since)s and will be fully '
712+
'deprecated for `Axes.clear` in the future. Please report '
713+
f'this to the {cls.__module__!r} author.')
714+
cls._subclass_uses_cla = 'cla' in cls.__dict__ or parent_uses_cla
715+
super().__init_subclass__(**kwargs)
716+
702717
def __getstate__(self):
703718
state = super().__getstate__()
704719
# Prune the sharing & twinning info to only contain the current group.
@@ -1199,7 +1214,7 @@ def sharey(self, other):
11991214
self.set_ylim(y0, y1, emit=False, auto=other.get_autoscaley_on())
12001215
self.yaxis._scale = other.yaxis._scale
12011216

1202-
def clear(self):
1217+
def _clear(self):
12031218
"""Clear the Axes."""
12041219
# Note: this is called by Axes.__init__()
12051220

@@ -1318,6 +1333,24 @@ def clear(self):
13181333

13191334
self.stale = True
13201335

1336+
def clear(self):
1337+
"""Clear the Axes."""
1338+
# Act as an alias, or as the superclass implementation depending on the
1339+
# subclass implementation.
1340+
if self._subclass_uses_cla:
1341+
self.cla()
1342+
else:
1343+
self._clear()
1344+
1345+
def cla(self):
1346+
"""Clear the Axes."""
1347+
# Act as an alias, or as the superclass implementation depending on the
1348+
# subclass implementation.
1349+
if self._subclass_uses_cla:
1350+
self._clear()
1351+
else:
1352+
self.clear()
1353+
13211354
class ArtistList(MutableSequence):
13221355
"""
13231356
A sublist of Axes children based on their type.
@@ -1481,10 +1514,6 @@ def texts(self):
14811514
return self.ArtistList(self, 'texts', 'add_artist',
14821515
valid_types=mtext.Text)
14831516

1484-
def cla(self):
1485-
"""Clear the Axes."""
1486-
self.clear()
1487-
14881517
def get_facecolor(self):
14891518
"""Get the facecolor of the Axes."""
14901519
return self.patch.get_facecolor()

lib/matplotlib/tests/test_axes.py

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -486,10 +486,61 @@ def test_inverted_cla():
486486
plt.close(fig)
487487

488488

489-
def test_cla_not_redefined():
489+
def test_subclass_clear_cla():
490+
# Ensure that subclasses of Axes call cla/clear correctly.
491+
# Note, we cannot use mocking here as we want to be sure that the
492+
# superclass fallback does not recurse.
493+
494+
with pytest.warns(match='Overriding `Axes.cla`'):
495+
class ClaAxes(Axes):
496+
def cla(self):
497+
nonlocal called
498+
called = True
499+
500+
with pytest.warns(match='Overriding `Axes.cla`'):
501+
class ClaSuperAxes(Axes):
502+
def cla(self):
503+
nonlocal called
504+
called = True
505+
super().cla()
506+
507+
class SubClaAxes(ClaAxes):
508+
pass
509+
510+
class ClearAxes(Axes):
511+
def clear(self):
512+
nonlocal called
513+
called = True
514+
515+
class ClearSuperAxes(Axes):
516+
def clear(self):
517+
nonlocal called
518+
called = True
519+
super().clear()
520+
521+
class SubClearAxes(ClearAxes):
522+
pass
523+
524+
fig = Figure()
525+
for axes_class in [ClaAxes, ClaSuperAxes, SubClaAxes,
526+
ClearAxes, ClearSuperAxes, SubClearAxes]:
527+
called = False
528+
ax = axes_class(fig, [0, 0, 1, 1])
529+
# Axes.__init__ has already called clear (which aliases to cla or is in
530+
# the subclass).
531+
assert called
532+
533+
called = False
534+
ax.cla()
535+
assert called
536+
537+
538+
def test_cla_not_redefined_internally():
490539
for klass in Axes.__subclasses__():
491-
# check that cla does not get redefined in our Axes subclasses
492-
assert 'cla' not in klass.__dict__
540+
# Check that cla does not get redefined in our Axes subclasses, except
541+
# for in the above test function.
542+
if 'test_subclass_clear_cla' not in klass.__qualname__:
543+
assert 'cla' not in klass.__dict__
493544

494545

495546
@check_figures_equal(extensions=["png"])

0 commit comments

Comments
 (0)