-
-
Notifications
You must be signed in to change notification settings - Fork 8.1k
MNT: Registered 3rd party scales do not need an axis parameter anymore #29358
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| 3rd party scales do not need to have an *axis* parameter anymore | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
|
|
||
| Since matplotlib 3.1 `PR 12831 <https://github.com/matplotlib/matplotlib/pull/12831>`_ | ||
| scale objects should be reusable and therefore independent of any particular Axis. | ||
| Therefore, the use of the *axis* parameter in the ``__init__`` had been discouraged. | ||
| However, having that parameter in the signature was still necessary for API | ||
| backwards-compatibility. This is no longer the case. | ||
|
|
||
| `.register_scale` now accepts scale classes with or without this parameter. | ||
|
|
||
| The *axis* parameter is pending-deprecated. It will be deprecated in matplotlib 3.13, | ||
| and removed in matplotlib 3.15. | ||
|
|
||
| 3rd-party scales are recommended to remove the *axis* parameter now if they can | ||
| afford to restrict compatibility to matplotlib >= 3.11 already. Otherwise, they may | ||
| 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): | |
| For back-compatibility reasons, scales take an `~matplotlib.axis.Axis` | ||
| object as the first argument. | ||
|
|
||
| The current recommendation for `.ScaleBase` subclasses is to have the | ||
| *axis* parameter for API compatibility, but not make use of it. This is | ||
| because we plan to remove this argument to make a scale object usable | ||
| by multiple `~matplotlib.axis.Axis`\es at the same time. | ||
| .. deprecated:: 3.11 | ||
|
|
||
| The *axis* parameter is now optional, i.e. matplotlib is compatible | ||
| with `.ScaleBase` subclasses that do not take an *axis* parameter. | ||
|
|
||
| The *axis* parameter is pending-deprecated. It will be deprecated | ||
| in matplotlib 3.13, and removed in matplotlib 3.15. | ||
|
|
||
| 3rd-party scales are recommended to remove the *axis* parameter now | ||
| if they can afford to restrict compatibility to matplotlib >= 3.11 | ||
| already. Otherwise, they may keep the *axis* parameter and remove it | ||
| in time for matplotlib 3.13. | ||
| """ | ||
|
|
||
| def get_transform(self): | ||
|
|
@@ -801,6 +809,20 @@ def limit_range_for_scale(self, vmin, vmax, minpos): | |
| 'functionlog': FuncScaleLog, | ||
| } | ||
|
|
||
| # caching of signature info | ||
| # For backward compatibility, the built-in scales will keep the *axis* parameter | ||
| # in their constructors until matplotlib 3.15, i.e. as long as the *axis* parameter | ||
| # is still supported. | ||
| _scale_has_axis_parameter = { | ||
| 'linear': True, | ||
| 'log': True, | ||
| 'symlog': True, | ||
| 'asinh': True, | ||
| 'logit': True, | ||
| 'function': True, | ||
| 'functionlog': True, | ||
| } | ||
|
|
||
|
|
||
| def get_scale_names(): | ||
| """Return the names of the available scales.""" | ||
|
|
@@ -817,7 +839,11 @@ def scale_factory(scale, axis, **kwargs): | |
| axis : `~matplotlib.axis.Axis` | ||
| """ | ||
| scale_cls = _api.check_getitem(_scale_mapping, scale=scale) | ||
| return scale_cls(axis, **kwargs) | ||
|
|
||
| if _scale_has_axis_parameter[scale]: | ||
| return scale_cls(axis, **kwargs) | ||
| else: | ||
| return scale_cls(**kwargs) | ||
|
|
||
|
|
||
| if scale_factory.__doc__: | ||
|
|
@@ -836,6 +862,20 @@ def register_scale(scale_class): | |
| """ | ||
| _scale_mapping[scale_class.name] = scale_class | ||
|
|
||
| # migration code to handle the *axis* parameter | ||
| has_axis_parameter = "axis" in inspect.signature(scale_class).parameters | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible that a subclass would name the parameter something other than "axis", in which case the better check here would be to see if there is a single positional only argument instead?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In theory that would be possible. However, (i) it would be deeply confusing and I doubt a reaonsable subclass would do this; and (ii) it is possible that a subclass has defined two positional parameters |
||
| _scale_has_axis_parameter[scale_class.name] = has_axis_parameter | ||
| if has_axis_parameter: | ||
| _api.warn_deprecated( | ||
| "3.11", | ||
| message=f"The scale {scale_class.__qualname__!r} uses an 'axis' parameter " | ||
| "in the constructors. This parameter is pending-deprecated since " | ||
| "matplotlib 3.11. It will be fully deprecated in 3.13 and removed " | ||
| "in 3.15. Starting with 3.11, 'register_scale()' accepts scales " | ||
| "without the *axis* parameter.", | ||
| pending=True, | ||
| ) | ||
|
|
||
|
|
||
| def _get_scale_docs(): | ||
| """ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really need the separate variable, or can we get away with changing
_scale_mappingtodict[str, tuple[bool, ScaleBase]]?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could change
_scale_mappinginstead. I've considered that. In the end, I've chosen the separate variable, because this functionality is only needed during the deprecation period. It's simpler to delete the extra variable rather than rewrite the logic of_scale_mappingback to the previous behavior when the deprecation expires. But one can argument both ways. I don't have a strong opinion, but also don't think it really matters.