Movatterモバイル変換


[0]ホーム

URL:


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

Propose a less error-prone helper for module-level getattrs.#20857

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
QuLogic merged 2 commits intomatplotlib:masterfromanntzer:module-getattr
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletionslib/matplotlib/__init__.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -176,16 +176,14 @@ def _get_version():
return _version.version


@functools.lru_cache(None)
def __getattr__(name):
if name == "__version__":
return _get_version()
elif name == "__version_info__":
return _parse_to_version_info(__getattr__("__version__"))
elif name == "URL_REGEX": # module-level deprecation.
_api.warn_deprecated("3.5", name=name)
return re.compile(r'^http://|^https://|^ftp://|^file:')
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@_api.caching_module_getattr
class __getattr__:
__version__ = property(lambda self: _get_version())
__version_info__ = property(
lambda self: _parse_to_version_info(self.__version__))
# module-level deprecations
URL_REGEX = _api.deprecated("3.5", obj_type="")(property(
lambda self: re.compile(r'^http://|^https://|^ftp://|^file:')))


def _check_versions():
Expand Down
36 changes: 36 additions & 0 deletionslib/matplotlib/_api/__init__.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@

"""

import functools
import itertools
import re
import sys
Expand DownExpand Up@@ -189,6 +190,41 @@ def check_getitem(_mapping, **kwargs):
.format(v, k, ', '.join(map(repr, mapping)))) from None


def caching_module_getattr(cls):
"""
Helper decorator for implementing module-level ``__getattr__`` as a class.

This decorator must be used at the module toplevel as follows::

@caching_module_getattr
class __getattr__: # The class *must* be named ``__getattr__``.
@property # Only properties are taken into account.
def name(self): ...

The ``__getattr__`` class will be replaced by a ``__getattr__``
function such that trying to access ``name`` on the module will
resolve the corresponding property (which may be decorated e.g. with
``_api.deprecated`` for deprecating module globals). The properties are
all implicitly cached. Moreover, a suitable AttributeError is generated
and raised if no property with the given name exists.
"""

assert cls.__name__ == "__getattr__"
# Don't accidentally export cls dunders.
props = {name: prop for name, prop in vars(cls).items()
if isinstance(prop, property)}
instance = cls()

@functools.lru_cache(None)
def __getattr__(name):
if name in props:
return props[name].__get__(instance)
raise AttributeError(
f"module {cls.__module__!r} has no attribute {name!r}")

return __getattr__


def select_matching_signature(funcs, *args, **kwargs):
"""
Select and call the function that accepts ``*args, **kwargs``.
Expand Down
5 changes: 3 additions & 2 deletionslib/matplotlib/_api/deprecation.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -150,13 +150,14 @@ def finalize(wrapper, new_doc):
return obj

elif isinstance(obj, (property, classproperty)):
obj_type = "attribute"
if obj_type is None:
obj_type = "attribute"
func = None
name = name or obj.fget.__name__
old_doc = obj.__doc__

class _deprecated_property(type(obj)):
def __get__(self, instance, owner):
def __get__(self, instance, owner=None):
if instance is not None or owner is not None \
and isinstance(self, classproperty):
emit_warning()
Expand Down
12 changes: 5 additions & 7 deletionslib/matplotlib/backends/backend_gtk3.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -37,11 +37,11 @@
Gtk.get_major_version(), Gtk.get_minor_version(), Gtk.get_micro_version())


# module-level deprecations.
@functools.lru_cache(None)
def __getattr__(name):
if name == "cursord":
_api.warn_deprecated("3.5", name=name)
@_api.caching_module_getattr# module-level deprecations
class __getattr__:
@_api.deprecated("3.5", obj_type="")
@property
def cursord(self):
try:
new_cursor = functools.partial(
Gdk.Cursor.new_from_name, Gdk.Display.get_default())
Expand All@@ -54,8 +54,6 @@ def __getattr__(name):
}
except TypeError as exc:
return {}
else:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")


# Placeholder
Expand Down
32 changes: 13 additions & 19 deletionslib/matplotlib/backends/backend_wx.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -41,25 +41,19 @@
PIXELS_PER_INCH = 75


# module-level deprecations.
@functools.lru_cache(None)
def __getattr__(name):
if name == "IDLE_DELAY":
_api.warn_deprecated("3.1", name=name)
return 5
elif name == "cursord":
_api.warn_deprecated("3.5", name=name)
return { # deprecated in Matplotlib 3.5.
cursors.MOVE: wx.CURSOR_HAND,
cursors.HAND: wx.CURSOR_HAND,
cursors.POINTER: wx.CURSOR_ARROW,
cursors.SELECT_REGION: wx.CURSOR_CROSS,
cursors.WAIT: wx.CURSOR_WAIT,
cursors.RESIZE_HORIZONTAL: wx.CURSOR_SIZEWE,
cursors.RESIZE_VERTICAL: wx.CURSOR_SIZENS,
}
else:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@_api.caching_module_getattr # module-level deprecations
class __getattr__:
IDLE_DELAY = _api.deprecated("3.1", obj_type="", removal="3.6")(property(
lambda self: 5))
cursord = _api.deprecated("3.5", obj_type="")(property(lambda self: {
cursors.MOVE: wx.CURSOR_HAND,
cursors.HAND: wx.CURSOR_HAND,
cursors.POINTER: wx.CURSOR_ARROW,
cursors.SELECT_REGION: wx.CURSOR_CROSS,
cursors.WAIT: wx.CURSOR_WAIT,
cursors.RESIZE_HORIZONTAL: wx.CURSOR_SIZEWE,
cursors.RESIZE_VERTICAL: wx.CURSOR_SIZENS,
}))


def error_msg_wx(msg, parent=None):
Expand Down
14 changes: 5 additions & 9 deletionslib/matplotlib/cm.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,7 +16,6 @@
"""

from collections.abc import Mapping, MutableMapping
import functools

import numpy as np
from numpy import ma
Expand All@@ -27,14 +26,11 @@
from matplotlib._cm_listed import cmaps as cmaps_listed


# module-level deprecations.
@functools.lru_cache(None)
def __getattr__(name):
if name == "LUTSIZE":
_api.warn_deprecated("3.5", name=name,
alternative="rcParams['image.lut']")
return _LUTSIZE
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@_api.caching_module_getattr # module-level deprecations
class __getattr__:
LUTSIZE = _api.deprecated(
"3.5", obj_type="", alternative="rcParams['image.lut']")(
property(lambda self: _LUTSIZE))


_LUTSIZE = mpl.rcParams['image.lut']
Expand Down
23 changes: 8 additions & 15 deletionslib/matplotlib/colorbar.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,6 @@
"""

import copy
import functools
import logging
import textwrap

Expand DownExpand Up@@ -195,20 +194,14 @@
_colormap_kw_doc))


# module-level deprecations.
@functools.lru_cache(None)
def __getattr__(name):
if name == "colorbar_doc":
_api.warn_deprecated("3.4", name=name)
return docstring.interpd.params["colorbar_doc"]
elif name == "colormap_kw_doc":
_api.warn_deprecated("3.4", name=name)
return _colormap_kw_doc
elif name == "make_axes_kw_doc":
_api.warn_deprecated("3.4", name=name)
return _make_axes_param_doc + _make_axes_other_param_doc
else:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@_api.caching_module_getattr # module-level deprecations
class __getattr__:
colorbar_doc = _api.deprecated("3.4", obj_type="")(property(
lambda self: docstring.interpd.params["colorbar_doc"]))
colorbar_kw_doc = _api.deprecated("3.4", obj_type="")(property(
lambda self: _colormap_kw_doc))
make_axes_kw_doc = _api.deprecated("3.4", obj_type="")(property(
lambda self: _make_axes_param_doc + _make_axes_other_param_doc))


def _set_ticks_on_axis_warn(*args, **kw):
Expand Down
12 changes: 4 additions & 8 deletionslib/matplotlib/style/core.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,7 +12,6 @@
"""

import contextlib
import functools
import logging
import os
from pathlib import Path
Expand All@@ -27,13 +26,10 @@
__all__ = ['use', 'context', 'available', 'library', 'reload_library']


# module-level deprecations.
@functools.lru_cache(None)
def __getattr__(name):
if name == "STYLE_FILE_PATTERN":
_api.warn_deprecated("3.5", name=name)
return re.compile(r'([\S]+).%s$' % STYLE_EXTENSION)
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
@_api.caching_module_getattr # module-level deprecations
class __getattr__:
STYLE_FILE_PATTERN = _api.deprecated("3.5", obj_type="")(property(
lambda self: re.compile(r'([\S]+).%s$' % STYLE_EXTENSION)))


BASE_LIBRARY_PATH = os.path.join(mpl.get_data_path(), 'stylelib')
Expand Down
28 changes: 28 additions & 0 deletionslib/matplotlib/tests/test_getattr.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
from importlib import import_module
from pkgutil import walk_packages

import matplotlib
import pytest

# Get the names of all matplotlib submodules, except for the unit tests.
module_names = [m.name for m in walk_packages(path=matplotlib.__path__,
prefix=f'{matplotlib.__name__}.')
if not m.name.startswith(__package__)]


@pytest.mark.parametrize('module_name', module_names)
@pytest.mark.filterwarnings('ignore::DeprecationWarning')
def test_getattr(module_name):
"""
Test that __getattr__ methods raise AttributeError for unknown keys.
See #20822, #20855.
"""
try:
module = import_module(module_name)
except (ImportError, RuntimeError) as e:
# Skip modules that cannot be imported due to missing dependencies
pytest.skip(f'Cannot import {module_name} due to {e}')

key = 'THIS_SYMBOL_SHOULD_NOT_EXIST'
if hasattr(module, key):
delattr(module, key)

[8]ページ先頭

©2009-2025 Movatter.jp