-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Deprecate globals using module-level __getattr__
.
#20733
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
Conversation
except TypeError as exc: | ||
cursord = {} # deprecated in Matplotlib 3.5. | ||
|
||
@functools.lru_cache(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.
@functools.lru_cache(None) | |
# module-level depredations | |
@functools.lru_cache(None) |
Since we don't make a dedicated function which could give the code segment a name, let's at least use a comment as a standard convention for explaining what this is for. - Also for the other uses of this pattern.
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.
How would you want that to be for matplotlib/__init__.py
, which also uses __getattr__
for __version__
?
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.
For simplicity, you can leave that without a comment. Part of the intent of the comment is to easily see that the whole construct serves the deprecation and can be removed when expiring. That's not the case for the matplotlib.__init__.py
__getattr__
.
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.
Actually, I just put the comment in the if
block.
This PR is mostly just to propose a pattern for defining module-level `__getattr__`s for deprecating globals. Relying on lru_cache rather than defining variables as `global` (see change in `__init__.py`) avoids having to repeat *twice* the variable name, and allows immediately returning its value without assigning it. It means later accesses will be very slightly slower (because they'll still go through the lru_cache layer), but that should hopefully be negligible, and will mostly concern deprecated variables anyways. One *could* consider adding a helper API in `_api.deprecation` that just provides the decoration with `@lru_cache` and generates the AttributeError message when needed, but that seems rather overkill. The change in deprecation.py allows one to skip `obj_name` and get a message like "foo is deprecated..." rather than "The foo is deprecated...".
PR matplotlib#20733 added module-level `__getattr__` functions to several modules. All of the functions with the exception of the one in `matplotlib.style.core` had a terminal `raise AttributeError` to handle unmatched attributes. The omission in `matplotlib.style.core` was probably unintentional; it results in confusing and buggy behavior such as: ```pycon >>> import matplotlib.style.core >>> if hasattr(matplotlib.style.core, '__warningregistry__'): ... del matplotlib.style.core.__warningregistry__ ... Traceback (most recent call last): File "<stdin>", line 2, in <module> AttributeError: __warningregistry__ ``` This causes problems in the unit tests for astropy affiliated packages. See astropy/astropy#12038.
This PR is mostly just to propose a pattern for defining module-level
__getattr__
s for deprecating globals.Relying on lru_cache rather than defining variables as
global
(seechange in
__init__.py
) avoids having to repeat twice the variablename, and allows immediately returning its value without assigning it.
It means later accesses will be very slightly slower (because they'll
still go through the lru_cache layer), but that should hopefully be
negligible, and will mostly concern deprecated variables anyways.
One could consider adding a helper API in
_api.deprecation
that justprovides the decoration with
@lru_cache
and generates theAttributeError message when needed, but that seems rather overkill.
(See #20620 (comment), in response to which this was written:
I don't have any "magic" idea this time :-), I now think the #10735
isn't really worth it.)
The change in deprecation.py allows one to skip
obj_name
and get amessage like "foo is deprecated..." rather than "The foo is
deprecated...".
PR Summary
PR Checklist
pytest
passes).flake8
on changed files to check).flake8-docstrings
and runflake8 --docstring-convention=all
).doc/users/next_whats_new/
(follow instructions in README.rst there).doc/api/next_api_changes/
(follow instructions in README.rst there).