| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 949ad9a commit 2dd7d9f
3 files changed
| Original file line number | Diff line number | Diff 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. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -77,10 +77,18 @@ def __init__(self, axis): | |||
| 77 | 77 | For back-compatibility reasons, scales take an `~matplotlib.axis.Axis` | |
| 78 | 78 | object as the first argument. | |
| 79 | 79 | ||
| 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. | ||
| 84 | 92 | """ | |
| 85 | 93 | ||
| 86 | 94 | def get_transform(self): | |
@@ -801,6 +809,20 @@ def limit_range_for_scale(self, vmin, vmax, minpos): | |||
| 801 | 809 | 'functionlog': FuncScaleLog, | |
| 802 | 810 | } | |
| 803 | 811 | ||
| 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 | + | ||
| 804 | 826 | ||
| 805 | 827 | def get_scale_names(): | |
| 806 | 828 | """Return the names of the available scales.""" | |
@@ -817,7 +839,11 @@ def scale_factory(scale, axis, **kwargs): | |||
| 817 | 839 | axis : `~matplotlib.axis.Axis` | |
| 818 | 840 | """ | |
| 819 | 841 | 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) | ||
| 821 | 847 | ||
| 822 | 848 | ||
| 823 | 849 | if scale_factory.__doc__: | |
@@ -836,6 +862,20 @@ def register_scale(scale_class): | |||
| 836 | 862 | """ | |
| 837 | 863 | _scale_mapping[scale_class.name] = scale_class | |
| 838 | 864 | ||
| 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 | + | ||
| 839 | 879 | ||
| 840 | 880 | def _get_scale_docs(): | |
| 841 | 881 | """ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,8 +6,12 @@ | |||
| 6 | 6 | LogTransform, InvertedLogTransform, | |
| 7 | 7 | SymmetricalLogTransform) | |
| 8 | 8 | 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 | + ) | ||
| 10 | 13 | from matplotlib.testing.decorators import check_figures_equal, image_comparison | |
| 14 | + from matplotlib.transforms import IdentityTransform | ||
| 11 | 15 | ||
| 12 | 16 | import numpy as np | |
| 13 | 17 | from numpy.testing import assert_allclose | |
@@ -295,3 +299,75 @@ def test_bad_scale(self): | |||
| 295 | 299 | AsinhScale(axis=None, linear_width=-1) | |
| 296 | 300 | s0 = AsinhScale(axis=None, ) | |
| 297 | 301 | 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"] | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments