Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 2dd7d9f

Browse files
timhoffmQuLogic
andauthored
MNT: Registered 3rd party scales do not need an axis parameter anymore (matplotlib#29358)
* MNT: Registered 3rd party scales do not need an axis parameter anymore First step of matplotlib#29349. * Update doc/api/next_api_changes/deprecations/29358-TH.rst Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com> --------- Co-authored-by: Elliott Sales de Andrade <quantum.analyst@gmail.com>
1 parent 949ad9a commit 2dd7d9f

File tree

3 files changed

+139
-6
lines changed

3 files changed

+139
-6
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
3rd party scales do not need to have an *axis* parameter anymore
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
Since matplotlib 3.1 `PR 12831 <https://github.com/matplotlib/matplotlib/pull/12831>`_
5+
scale objects should be reusable and therefore independent of any particular Axis.
6+
Therefore, the use of the *axis* parameter in the ``__init__`` had been discouraged.
7+
However, having that parameter in the signature was still necessary for API
8+
backwards-compatibility. This is no longer the case.
9+
10+
`.register_scale` now accepts scale classes with or without this parameter.
11+
12+
The *axis* parameter is pending-deprecated. It will be deprecated in matplotlib 3.13,
13+
and removed in matplotlib 3.15.
14+
15+
3rd-party scales are recommended to remove the *axis* parameter now if they can
16+
afford to restrict compatibility to matplotlib >= 3.11 already. Otherwise, they may
17+
keep the *axis* parameter and remove it in time for matplotlib 3.13.

‎lib/matplotlib/scale.py

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,18 @@ def __init__(self, axis):
7777
For back-compatibility reasons, scales take an `~matplotlib.axis.Axis`
7878
object as the first argument.
7979
80-
The current recommendation for `.ScaleBase` subclasses is to have the
81-
*axis* parameter for API compatibility, but not make use of it. This is
82-
because we plan to remove this argument to make a scale object usable
83-
by multiple `~matplotlib.axis.Axis`\es at the same time.
80+
.. deprecated:: 3.11
81+
82+
The *axis* parameter is now optional, i.e. matplotlib is compatible
83+
with `.ScaleBase` subclasses that do not take an *axis* parameter.
84+
85+
The *axis* parameter is pending-deprecated. It will be deprecated
86+
in matplotlib 3.13, and removed in matplotlib 3.15.
87+
88+
3rd-party scales are recommended to remove the *axis* parameter now
89+
if they can afford to restrict compatibility to matplotlib >= 3.11
90+
already. Otherwise, they may keep the *axis* parameter and remove it
91+
in time for matplotlib 3.13.
8492
"""
8593

8694
def get_transform(self):
@@ -801,6 +809,20 @@ def limit_range_for_scale(self, vmin, vmax, minpos):
801809
'functionlog': FuncScaleLog,
802810
}
803811

812+
# caching of signature info
813+
# For backward compatibility, the built-in scales will keep the *axis* parameter
814+
# in their constructors until matplotlib 3.15, i.e. as long as the *axis* parameter
815+
# is still supported.
816+
_scale_has_axis_parameter = {
817+
'linear': True,
818+
'log': True,
819+
'symlog': True,
820+
'asinh': True,
821+
'logit': True,
822+
'function': True,
823+
'functionlog': True,
824+
}
825+
804826

805827
def get_scale_names():
806828
"""Return the names of the available scales."""
@@ -817,7 +839,11 @@ def scale_factory(scale, axis, **kwargs):
817839
axis : `~matplotlib.axis.Axis`
818840
"""
819841
scale_cls = _api.check_getitem(_scale_mapping, scale=scale)
820-
return scale_cls(axis, **kwargs)
842+
843+
if _scale_has_axis_parameter[scale]:
844+
return scale_cls(axis, **kwargs)
845+
else:
846+
return scale_cls(**kwargs)
821847

822848

823849
if scale_factory.__doc__:
@@ -836,6 +862,20 @@ def register_scale(scale_class):
836862
"""
837863
_scale_mapping[scale_class.name] = scale_class
838864

865+
# migration code to handle the *axis* parameter
866+
has_axis_parameter = "axis" in inspect.signature(scale_class).parameters
867+
_scale_has_axis_parameter[scale_class.name] = has_axis_parameter
868+
if has_axis_parameter:
869+
_api.warn_deprecated(
870+
"3.11",
871+
message=f"The scale {scale_class.__qualname__!r} uses an 'axis' parameter "
872+
"in the constructors. This parameter is pending-deprecated since "
873+
"matplotlib 3.11. It will be fully deprecated in 3.13 and removed "
874+
"in 3.15. Starting with 3.11, 'register_scale()' accepts scales "
875+
"without the *axis* parameter.",
876+
pending=True,
877+
)
878+
839879

840880
def _get_scale_docs():
841881
"""

‎lib/matplotlib/tests/test_scale.py

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,12 @@
66
LogTransform, InvertedLogTransform,
77
SymmetricalLogTransform)
88
import matplotlib.scale as mscale
9-
from matplotlib.ticker import AsinhLocator, LogFormatterSciNotation
9+
from matplotlib.ticker import (
10+
AsinhLocator, AutoLocator, LogFormatterSciNotation,
11+
NullFormatter, NullLocator, ScalarFormatter
12+
)
1013
from matplotlib.testing.decorators import check_figures_equal, image_comparison
14+
from matplotlib.transforms import IdentityTransform
1115

1216
import numpy as np
1317
from numpy.testing import assert_allclose
@@ -295,3 +299,75 @@ def test_bad_scale(self):
295299
AsinhScale(axis=None, linear_width=-1)
296300
s0 = AsinhScale(axis=None, )
297301
s1 = AsinhScale(axis=None, linear_width=3.0)
302+
303+
304+
def test_custom_scale_without_axis():
305+
"""
306+
Test that one can register and use custom scales that don't take an *axis* param.
307+
"""
308+
class CustomTransform(IdentityTransform):
309+
pass
310+
311+
class CustomScale(mscale.ScaleBase):
312+
name = "custom"
313+
314+
# Important: __init__ has no *axis* parameter
315+
def __init__(self):
316+
self._transform = CustomTransform()
317+
318+
def get_transform(self):
319+
return self._transform
320+
321+
def set_default_locators_and_formatters(self, axis):
322+
axis.set_major_locator(AutoLocator())
323+
axis.set_major_formatter(ScalarFormatter())
324+
axis.set_minor_locator(NullLocator())
325+
axis.set_minor_formatter(NullFormatter())
326+
327+
try:
328+
mscale.register_scale(CustomScale)
329+
fig, ax = plt.subplots()
330+
ax.set_xscale('custom')
331+
assert isinstance(ax.xaxis.get_transform(), CustomTransform)
332+
finally:
333+
# cleanup - there's no public unregister_scale()
334+
del mscale._scale_mapping["custom"]
335+
del mscale._scale_has_axis_parameter["custom"]
336+
337+
338+
def test_custom_scale_with_axis():
339+
"""
340+
Test that one can still register and use custom scales with an *axis*
341+
parameter, but that registering issues a pending-deprecation warning.
342+
"""
343+
class CustomTransform(IdentityTransform):
344+
pass
345+
346+
class CustomScale(mscale.ScaleBase):
347+
name = "custom"
348+
349+
# Important: __init__ still has the *axis* parameter
350+
def __init__(self, axis):
351+
self._transform = CustomTransform()
352+
353+
def get_transform(self):
354+
return self._transform
355+
356+
def set_default_locators_and_formatters(self, axis):
357+
axis.set_major_locator(AutoLocator())
358+
axis.set_major_formatter(ScalarFormatter())
359+
axis.set_minor_locator(NullLocator())
360+
axis.set_minor_formatter(NullFormatter())
361+
362+
try:
363+
with pytest.warns(
364+
PendingDeprecationWarning,
365+
match=r"'axis' parameter .* is pending-deprecated"):
366+
mscale.register_scale(CustomScale)
367+
fig, ax = plt.subplots()
368+
ax.set_xscale('custom')
369+
assert isinstance(ax.xaxis.get_transform(), CustomTransform)
370+
finally:
371+
# cleanup - there's no public unregister_scale()
372+
del mscale._scale_mapping["custom"]
373+
del mscale._scale_has_axis_parameter["custom"]

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /