-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Refactoring: Removing axis parameter from scales #29988
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
base: main
Are you sure you want to change the base?
Conversation
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.
Thank you for opening your first PR into Matplotlib!
If you have not heard from us in a week or so, please leave a new comment below and that should bring it to our attention. Most of our reviewers are volunteers and sometimes things fall through the cracks.
You can also join us on gitter for real-time discussion.
For details on testing, writing docs, and our review process, please see the developer guide
We strive to be a welcoming and open project. Please follow our Code of Conduct.
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.
Thanks for the PR. This is a bit tricky to get right in a backward-compatible and migration-easy way. I think we need to do it slightly differently.
Updated my refactor based on the feedbacks received
Apologies for the sequence of commits — I wanted to ensure each feedback point was properly addressed and tested incrementally. Please let me know if this is the appropriate way to validate ruff, mypy-stubtest, and CI tests in general, or if you'd prefer a cleaner commit history or a different workflow. I changed back the deprecation warning function, and changed the decorator as well. However, in the decorator i had many errors when trying to check "if args and isinstance(args[0], ScaleBase):", so instead of using ScaleBase as a reference i changed to "if args and isinstance(args[0], mpl.axis.Axis):". Does it make sense? Let me know if anything else needs to be refined or if the structure of the changes could be improved further. Thanks again for your time and guidance throughout the review! |
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.
Apologies for the sequence of commits — I wanted to ensure each feedback point was properly addressed and tested incrementally. Please let me know if this is the appropriate way to validate ruff, mypy-stubtest, and CI tests in general, or if you'd prefer a cleaner commit history or a different workflow.
It does not matter for small PRs. We'll typically review the full diff and squash-merge.
However, in the decorator i had many errors when trying to check "if args and isinstance(args[0], ScaleBase):", so instead of using ScaleBase as a reference i changed to "if args and isinstance(args[0], mpl.axis.Axis):". Does it make sense?
Yes. Sorry my bad for the incorrect suggestion.
Let me know if anything else needs to be refined or if the structure of the changes could be improved further. Thanks again for your time and guidance throughout the review!
You should add a test that the decorated scales can be created with and without axis passed. - Either test on one of the scales, or write an explicit test for the decorator.
@@ -103,14 +104,58 @@ def limit_range_for_scale(self, vmin, vmax, minpos): | ||
return vmin, vmax | ||
|
||
|
||
def handle_axis_parameter(init_func): |
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.
def handle_axis_parameter(init_func): | |
def _make_axis_parameter_optional(init_func): |
Let's be very explicit about the naming, and keep it internal. I don't expect that possible downstream Child classes will need to support optional axis parameters.
@@ -103,14 +104,58 @@ def limit_range_for_scale(self, vmin, vmax, minpos): | ||
return vmin, vmax | ||
|
||
|
||
def handle_axis_parameter(init_func): | ||
""" | ||
Decorator to handle the optional *axis* parameter in scale constructors. |
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.
Decorator to handle the optional *axis* parameter in scale constructors. | |
Decorator to allow leaving out the *axis* parameter in scale constructors. |
previously required an *axis* parameter. It allows constructors to work | ||
seamlessly with or without the *axis* parameter. |
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.
previously required an *axis* parameter. It allows constructors to work | |
seamlessly with or without the *axis* parameter. | |
previously required an *axis* parameter. It allows constructors to be | |
callerd with or without the *axis* parameter. | |
For simplicity, this does not handle the case when *axis* is passed as a keyword. Howver, | |
scanning GitHub, there's no evidence that that is used anywhere. |
@@ -56,12 +57,12 @@ class LogScale(ScaleBase): | ||
name: str | ||
subs: Iterable[int] | None | ||
def __init__( | ||
self, | ||
axis: Axis | None, | ||
self: LogScale, |
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.
You don't type self
. It's implicitly understood by typecheckers.
self, | ||
axis: Axis | None, | ||
self: LogScale, | ||
axis: Union[Axis, None] = None, |
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.
axis: Union[Axis, None] = None, | |
axis: Axis | None = ..., |
Axis | None
is equivalent to Union[Axis, None]
, but the preferred spelling. You don't repeat default values in stub files. Instead, you place an ellipsis.
class LinearScale(ScaleBase): | ||
""" | ||
The default linear scale. | ||
""" | ||
|
||
name = 'linear' | ||
|
||
def __init__(self, axis): | ||
@handle_axis_parameter | ||
def __init__(self, axis=None): |
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.
Is there an advantage in making axis
optional here? That should be handled be handled by the decorator. If there's no particular reason, then I'd prefer to keep the signature formally unchanged for now.
except TypeError as e: | ||
if 'unexpected keyword argument' in str(e) or 'positional argument' in str(e): | ||
return scale_cls(**kwargs) | ||
raise |
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.
Are you sure this is safe? Basing logic on the exception message only can be brittle. Minimally, I'd want an explicit comment on what we catch here. But I'd rather split this out into a separte PR as we need tests.
This PR addresses issue #29349 by refactoring built-in scale classes to make the axis parameter optional. The goal is to support future removal of axis while maintaining backward compatibility with current usage.
To achieve this, a decorator was introduced to allow constructors to accept or ignore axis. This was applied across all built-in scales. The scale_factory() function was updated to handle scale classes that do or do not use axis, and register_scale() now issues a deprecation warning if the constructor still expects it.
These changes prepare the codebase for eventual deprecation of axis in scale initializers, while avoiding any breaking changes in current usage.
My only doubt is which initializers for the scales should have the parameter as "axis = None". If all of them should have it, then i have to change the way my decorator works because axis could change position in parameters index