remove venv
This commit is contained in:
parent
056387013d
commit
0680c7594e
13999 changed files with 0 additions and 2895688 deletions
|
|
@ -1,391 +0,0 @@
|
|||
"""
|
||||
Helper functions for managing the Matplotlib API.
|
||||
|
||||
This documentation is only relevant for Matplotlib developers, not for users.
|
||||
|
||||
.. warning::
|
||||
|
||||
This module and its submodules are for internal use only. Do not use them
|
||||
in your own code. We may change the API at any time with no warning.
|
||||
|
||||
"""
|
||||
|
||||
import functools
|
||||
import itertools
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
from .deprecation import ( # noqa: F401
|
||||
deprecated, warn_deprecated,
|
||||
rename_parameter, delete_parameter, make_keyword_only,
|
||||
deprecate_method_override, deprecate_privatize_attribute,
|
||||
suppress_matplotlib_deprecation_warning,
|
||||
MatplotlibDeprecationWarning)
|
||||
|
||||
|
||||
class classproperty:
|
||||
"""
|
||||
Like `property`, but also triggers on access via the class, and it is the
|
||||
*class* that's passed as argument.
|
||||
|
||||
Examples
|
||||
--------
|
||||
::
|
||||
|
||||
class C:
|
||||
@classproperty
|
||||
def foo(cls):
|
||||
return cls.__name__
|
||||
|
||||
assert C.foo == "C"
|
||||
"""
|
||||
|
||||
def __init__(self, fget, fset=None, fdel=None, doc=None):
|
||||
self._fget = fget
|
||||
if fset is not None or fdel is not None:
|
||||
raise ValueError('classproperty only implements fget.')
|
||||
self.fset = fset
|
||||
self.fdel = fdel
|
||||
# docs are ignored for now
|
||||
self._doc = doc
|
||||
|
||||
def __get__(self, instance, owner):
|
||||
return self._fget(owner)
|
||||
|
||||
@property
|
||||
def fget(self):
|
||||
return self._fget
|
||||
|
||||
|
||||
# In the following check_foo() functions, the first parameter is positional-only to make
|
||||
# e.g. `_api.check_isinstance([...], types=foo)` work.
|
||||
|
||||
def check_isinstance(types, /, **kwargs):
|
||||
"""
|
||||
For each *key, value* pair in *kwargs*, check that *value* is an instance
|
||||
of one of *types*; if not, raise an appropriate TypeError.
|
||||
|
||||
As a special case, a ``None`` entry in *types* is treated as NoneType.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> _api.check_isinstance((SomeClass, None), arg=arg)
|
||||
"""
|
||||
none_type = type(None)
|
||||
types = ((types,) if isinstance(types, type) else
|
||||
(none_type,) if types is None else
|
||||
tuple(none_type if tp is None else tp for tp in types))
|
||||
|
||||
def type_name(tp):
|
||||
return ("None" if tp is none_type
|
||||
else tp.__qualname__ if tp.__module__ == "builtins"
|
||||
else f"{tp.__module__}.{tp.__qualname__}")
|
||||
|
||||
for k, v in kwargs.items():
|
||||
if not isinstance(v, types):
|
||||
names = [*map(type_name, types)]
|
||||
if "None" in names: # Move it to the end for better wording.
|
||||
names.remove("None")
|
||||
names.append("None")
|
||||
raise TypeError(
|
||||
"{!r} must be an instance of {}, not a {}".format(
|
||||
k,
|
||||
", ".join(names[:-1]) + " or " + names[-1]
|
||||
if len(names) > 1 else names[0],
|
||||
type_name(type(v))))
|
||||
|
||||
|
||||
def check_in_list(values, /, *, _print_supported_values=True, **kwargs):
|
||||
"""
|
||||
For each *key, value* pair in *kwargs*, check that *value* is in *values*;
|
||||
if not, raise an appropriate ValueError.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
values : iterable
|
||||
Sequence of values to check on.
|
||||
_print_supported_values : bool, default: True
|
||||
Whether to print *values* when raising ValueError.
|
||||
**kwargs : dict
|
||||
*key, value* pairs as keyword arguments to find in *values*.
|
||||
|
||||
Raises
|
||||
------
|
||||
ValueError
|
||||
If any *value* in *kwargs* is not found in *values*.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> _api.check_in_list(["foo", "bar"], arg=arg, other_arg=other_arg)
|
||||
"""
|
||||
if not kwargs:
|
||||
raise TypeError("No argument to check!")
|
||||
for key, val in kwargs.items():
|
||||
if val not in values:
|
||||
msg = f"{val!r} is not a valid value for {key}"
|
||||
if _print_supported_values:
|
||||
msg += f"; supported values are {', '.join(map(repr, values))}"
|
||||
raise ValueError(msg)
|
||||
|
||||
|
||||
def check_shape(shape, /, **kwargs):
|
||||
"""
|
||||
For each *key, value* pair in *kwargs*, check that *value* has the shape *shape*;
|
||||
if not, raise an appropriate ValueError.
|
||||
|
||||
*None* in the shape is treated as a "free" size that can have any length.
|
||||
e.g. (None, 2) -> (N, 2)
|
||||
|
||||
The values checked must be numpy arrays.
|
||||
|
||||
Examples
|
||||
--------
|
||||
To check for (N, 2) shaped arrays
|
||||
|
||||
>>> _api.check_shape((None, 2), arg=arg, other_arg=other_arg)
|
||||
"""
|
||||
for k, v in kwargs.items():
|
||||
data_shape = v.shape
|
||||
|
||||
if (len(data_shape) != len(shape)
|
||||
or any(s != t and t is not None for s, t in zip(data_shape, shape))):
|
||||
dim_labels = iter(itertools.chain(
|
||||
'NMLKJIH',
|
||||
(f"D{i}" for i in itertools.count())))
|
||||
text_shape = ", ".join([str(n) if n is not None else next(dim_labels)
|
||||
for n in shape[::-1]][::-1])
|
||||
if len(shape) == 1:
|
||||
text_shape += ","
|
||||
|
||||
raise ValueError(
|
||||
f"{k!r} must be {len(shape)}D with shape ({text_shape}), "
|
||||
f"but your input has shape {v.shape}"
|
||||
)
|
||||
|
||||
|
||||
def check_getitem(mapping, /, **kwargs):
|
||||
"""
|
||||
*kwargs* must consist of a single *key, value* pair. If *key* is in
|
||||
*mapping*, return ``mapping[value]``; else, raise an appropriate
|
||||
ValueError.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> _api.check_getitem({"foo": "bar"}, arg=arg)
|
||||
"""
|
||||
if len(kwargs) != 1:
|
||||
raise ValueError("check_getitem takes a single keyword argument")
|
||||
(k, v), = kwargs.items()
|
||||
try:
|
||||
return mapping[v]
|
||||
except KeyError:
|
||||
raise ValueError(
|
||||
f"{v!r} is not a valid value for {k}; supported values are "
|
||||
f"{', '.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.cache
|
||||
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 define_aliases(alias_d, cls=None):
|
||||
"""
|
||||
Class decorator for defining property aliases.
|
||||
|
||||
Use as ::
|
||||
|
||||
@_api.define_aliases({"property": ["alias", ...], ...})
|
||||
class C: ...
|
||||
|
||||
For each property, if the corresponding ``get_property`` is defined in the
|
||||
class so far, an alias named ``get_alias`` will be defined; the same will
|
||||
be done for setters. If neither the getter nor the setter exists, an
|
||||
exception will be raised.
|
||||
|
||||
The alias map is stored as the ``_alias_map`` attribute on the class and
|
||||
can be used by `.normalize_kwargs` (which assumes that higher priority
|
||||
aliases come last).
|
||||
"""
|
||||
if cls is None: # Return the actual class decorator.
|
||||
return functools.partial(define_aliases, alias_d)
|
||||
|
||||
def make_alias(name): # Enforce a closure over *name*.
|
||||
@functools.wraps(getattr(cls, name))
|
||||
def method(self, *args, **kwargs):
|
||||
return getattr(self, name)(*args, **kwargs)
|
||||
return method
|
||||
|
||||
for prop, aliases in alias_d.items():
|
||||
exists = False
|
||||
for prefix in ["get_", "set_"]:
|
||||
if prefix + prop in vars(cls):
|
||||
exists = True
|
||||
for alias in aliases:
|
||||
method = make_alias(prefix + prop)
|
||||
method.__name__ = prefix + alias
|
||||
method.__doc__ = f"Alias for `{prefix + prop}`."
|
||||
setattr(cls, prefix + alias, method)
|
||||
if not exists:
|
||||
raise ValueError(
|
||||
f"Neither getter nor setter exists for {prop!r}")
|
||||
|
||||
def get_aliased_and_aliases(d):
|
||||
return {*d, *(alias for aliases in d.values() for alias in aliases)}
|
||||
|
||||
preexisting_aliases = getattr(cls, "_alias_map", {})
|
||||
conflicting = (get_aliased_and_aliases(preexisting_aliases)
|
||||
& get_aliased_and_aliases(alias_d))
|
||||
if conflicting:
|
||||
# Need to decide on conflict resolution policy.
|
||||
raise NotImplementedError(
|
||||
f"Parent class already defines conflicting aliases: {conflicting}")
|
||||
cls._alias_map = {**preexisting_aliases, **alias_d}
|
||||
return cls
|
||||
|
||||
|
||||
def select_matching_signature(funcs, *args, **kwargs):
|
||||
"""
|
||||
Select and call the function that accepts ``*args, **kwargs``.
|
||||
|
||||
*funcs* is a list of functions which should not raise any exception (other
|
||||
than `TypeError` if the arguments passed do not match their signature).
|
||||
|
||||
`select_matching_signature` tries to call each of the functions in *funcs*
|
||||
with ``*args, **kwargs`` (in the order in which they are given). Calls
|
||||
that fail with a `TypeError` are silently skipped. As soon as a call
|
||||
succeeds, `select_matching_signature` returns its return value. If no
|
||||
function accepts ``*args, **kwargs``, then the `TypeError` raised by the
|
||||
last failing call is re-raised.
|
||||
|
||||
Callers should normally make sure that any ``*args, **kwargs`` can only
|
||||
bind a single *func* (to avoid any ambiguity), although this is not checked
|
||||
by `select_matching_signature`.
|
||||
|
||||
Notes
|
||||
-----
|
||||
`select_matching_signature` is intended to help implementing
|
||||
signature-overloaded functions. In general, such functions should be
|
||||
avoided, except for back-compatibility concerns. A typical use pattern is
|
||||
::
|
||||
|
||||
def my_func(*args, **kwargs):
|
||||
params = select_matching_signature(
|
||||
[lambda old1, old2: locals(), lambda new: locals()],
|
||||
*args, **kwargs)
|
||||
if "old1" in params:
|
||||
warn_deprecated(...)
|
||||
old1, old2 = params.values() # note that locals() is ordered.
|
||||
else:
|
||||
new, = params.values()
|
||||
# do things with params
|
||||
|
||||
which allows *my_func* to be called either with two parameters (*old1* and
|
||||
*old2*) or a single one (*new*). Note that the new signature is given
|
||||
last, so that callers get a `TypeError` corresponding to the new signature
|
||||
if the arguments they passed in do not match any signature.
|
||||
"""
|
||||
# Rather than relying on locals() ordering, one could have just used func's
|
||||
# signature (``bound = inspect.signature(func).bind(*args, **kwargs);
|
||||
# bound.apply_defaults(); return bound``) but that is significantly slower.
|
||||
for i, func in enumerate(funcs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except TypeError:
|
||||
if i == len(funcs) - 1:
|
||||
raise
|
||||
|
||||
|
||||
def nargs_error(name, takes, given):
|
||||
"""Generate a TypeError to be raised by function calls with wrong arity."""
|
||||
return TypeError(f"{name}() takes {takes} positional arguments but "
|
||||
f"{given} were given")
|
||||
|
||||
|
||||
def kwarg_error(name, kw):
|
||||
"""
|
||||
Generate a TypeError to be raised by function calls with wrong kwarg.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
The name of the calling function.
|
||||
kw : str or Iterable[str]
|
||||
Either the invalid keyword argument name, or an iterable yielding
|
||||
invalid keyword arguments (e.g., a ``kwargs`` dict).
|
||||
"""
|
||||
if not isinstance(kw, str):
|
||||
kw = next(iter(kw))
|
||||
return TypeError(f"{name}() got an unexpected keyword argument '{kw}'")
|
||||
|
||||
|
||||
def recursive_subclasses(cls):
|
||||
"""Yield *cls* and direct and indirect subclasses of *cls*."""
|
||||
yield cls
|
||||
for subcls in cls.__subclasses__():
|
||||
yield from recursive_subclasses(subcls)
|
||||
|
||||
|
||||
def warn_external(message, category=None):
|
||||
"""
|
||||
`warnings.warn` wrapper that sets *stacklevel* to "outside Matplotlib".
|
||||
|
||||
The original emitter of the warning can be obtained by patching this
|
||||
function back to `warnings.warn`, i.e. ``_api.warn_external =
|
||||
warnings.warn`` (or ``functools.partial(warnings.warn, stacklevel=2)``,
|
||||
etc.).
|
||||
"""
|
||||
kwargs = {}
|
||||
if sys.version_info[:2] >= (3, 12):
|
||||
# Go to Python's `site-packages` or `lib` from an editable install.
|
||||
basedir = pathlib.Path(__file__).parents[2]
|
||||
kwargs['skip_file_prefixes'] = (str(basedir / 'matplotlib'),
|
||||
str(basedir / 'mpl_toolkits'))
|
||||
else:
|
||||
frame = sys._getframe()
|
||||
for stacklevel in itertools.count(1):
|
||||
if frame is None:
|
||||
# when called in embedded context may hit frame is None
|
||||
kwargs['stacklevel'] = stacklevel
|
||||
break
|
||||
if not re.match(r"\A(matplotlib|mpl_toolkits)(\Z|\.(?!tests\.))",
|
||||
# Work around sphinx-gallery not setting __name__.
|
||||
frame.f_globals.get("__name__", "")):
|
||||
kwargs['stacklevel'] = stacklevel
|
||||
break
|
||||
frame = frame.f_back
|
||||
# preemptively break reference cycle between locals and the frame
|
||||
del frame
|
||||
warnings.warn(message, category, **kwargs)
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
from collections.abc import Callable, Generator, Iterable, Mapping, Sequence
|
||||
from typing import Any, TypeVar, overload
|
||||
from typing_extensions import Self # < Py 3.11
|
||||
|
||||
from numpy.typing import NDArray
|
||||
|
||||
from .deprecation import ( # noqa: F401, re-exported API
|
||||
deprecated as deprecated,
|
||||
warn_deprecated as warn_deprecated,
|
||||
rename_parameter as rename_parameter,
|
||||
delete_parameter as delete_parameter,
|
||||
make_keyword_only as make_keyword_only,
|
||||
deprecate_method_override as deprecate_method_override,
|
||||
deprecate_privatize_attribute as deprecate_privatize_attribute,
|
||||
suppress_matplotlib_deprecation_warning as suppress_matplotlib_deprecation_warning,
|
||||
MatplotlibDeprecationWarning as MatplotlibDeprecationWarning,
|
||||
)
|
||||
|
||||
_T = TypeVar("_T")
|
||||
|
||||
class classproperty(Any):
|
||||
def __init__(
|
||||
self,
|
||||
fget: Callable[[_T], Any],
|
||||
fset: None = ...,
|
||||
fdel: None = ...,
|
||||
doc: str | None = None,
|
||||
): ...
|
||||
@overload
|
||||
def __get__(self, instance: None, owner: None) -> Self: ...
|
||||
@overload
|
||||
def __get__(self, instance: object, owner: type[object]) -> Any: ...
|
||||
@property
|
||||
def fget(self) -> Callable[[_T], Any]: ...
|
||||
|
||||
def check_isinstance(
|
||||
types: type | tuple[type | None, ...], /, **kwargs: Any
|
||||
) -> None: ...
|
||||
def check_in_list(
|
||||
values: Sequence[Any], /, *, _print_supported_values: bool = ..., **kwargs: Any
|
||||
) -> None: ...
|
||||
def check_shape(shape: tuple[int | None, ...], /, **kwargs: NDArray) -> None: ...
|
||||
def check_getitem(mapping: Mapping[Any, Any], /, **kwargs: Any) -> Any: ...
|
||||
def caching_module_getattr(cls: type) -> Callable[[str], Any]: ...
|
||||
@overload
|
||||
def define_aliases(
|
||||
alias_d: dict[str, list[str]], cls: None = ...
|
||||
) -> Callable[[type[_T]], type[_T]]: ...
|
||||
@overload
|
||||
def define_aliases(alias_d: dict[str, list[str]], cls: type[_T]) -> type[_T]: ...
|
||||
def select_matching_signature(
|
||||
funcs: list[Callable], *args: Any, **kwargs: Any
|
||||
) -> Any: ...
|
||||
def nargs_error(name: str, takes: int | str, given: int) -> TypeError: ...
|
||||
def kwarg_error(name: str, kw: str | Iterable[str]) -> TypeError: ...
|
||||
def recursive_subclasses(cls: type) -> Generator[type, None, None]: ...
|
||||
def warn_external(
|
||||
message: str | Warning, category: type[Warning] | None = ...
|
||||
) -> None: ...
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -1,509 +0,0 @@
|
|||
"""
|
||||
Helper functions for deprecating parts of the Matplotlib API.
|
||||
|
||||
This documentation is only relevant for Matplotlib developers, not for users.
|
||||
|
||||
.. warning::
|
||||
|
||||
This module is for internal use only. Do not use it in your own code.
|
||||
We may change the API at any time with no warning.
|
||||
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import functools
|
||||
import inspect
|
||||
import math
|
||||
import warnings
|
||||
|
||||
|
||||
class MatplotlibDeprecationWarning(DeprecationWarning):
|
||||
"""A class for issuing deprecation warnings for Matplotlib users."""
|
||||
|
||||
|
||||
def _generate_deprecation_warning(
|
||||
since, message='', name='', alternative='', pending=False, obj_type='',
|
||||
addendum='', *, removal=''):
|
||||
if pending:
|
||||
if removal:
|
||||
raise ValueError("A pending deprecation cannot have a scheduled removal")
|
||||
elif removal == '':
|
||||
macro, meso, *_ = since.split('.')
|
||||
removal = f'{macro}.{int(meso) + 2}'
|
||||
if not message:
|
||||
message = (
|
||||
("The %(name)s %(obj_type)s" if obj_type else "%(name)s") +
|
||||
(" will be deprecated in a future version" if pending else
|
||||
(" was deprecated in Matplotlib %(since)s" +
|
||||
(" and will be removed in %(removal)s" if removal else ""))) +
|
||||
"." +
|
||||
(" Use %(alternative)s instead." if alternative else "") +
|
||||
(" %(addendum)s" if addendum else ""))
|
||||
warning_cls = PendingDeprecationWarning if pending else MatplotlibDeprecationWarning
|
||||
return warning_cls(message % dict(
|
||||
func=name, name=name, obj_type=obj_type, since=since, removal=removal,
|
||||
alternative=alternative, addendum=addendum))
|
||||
|
||||
|
||||
def warn_deprecated(
|
||||
since, *, message='', name='', alternative='', pending=False,
|
||||
obj_type='', addendum='', removal=''):
|
||||
"""
|
||||
Display a standardized deprecation.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
since : str
|
||||
The release at which this API became deprecated.
|
||||
message : str, optional
|
||||
Override the default deprecation message. The ``%(since)s``,
|
||||
``%(name)s``, ``%(alternative)s``, ``%(obj_type)s``, ``%(addendum)s``,
|
||||
and ``%(removal)s`` format specifiers will be replaced by the values
|
||||
of the respective arguments passed to this function.
|
||||
name : str, optional
|
||||
The name of the deprecated object.
|
||||
alternative : str, optional
|
||||
An alternative API that the user may use in place of the deprecated
|
||||
API. The deprecation warning will tell the user about this alternative
|
||||
if provided.
|
||||
pending : bool, optional
|
||||
If True, uses a PendingDeprecationWarning instead of a
|
||||
DeprecationWarning. Cannot be used together with *removal*.
|
||||
obj_type : str, optional
|
||||
The object type being deprecated.
|
||||
addendum : str, optional
|
||||
Additional text appended directly to the final message.
|
||||
removal : str, optional
|
||||
The expected removal version. With the default (an empty string), a
|
||||
removal version is automatically computed from *since*. Set to other
|
||||
Falsy values to not schedule a removal date. Cannot be used together
|
||||
with *pending*.
|
||||
|
||||
Examples
|
||||
--------
|
||||
::
|
||||
|
||||
# To warn of the deprecation of "matplotlib.name_of_module"
|
||||
warn_deprecated('1.4.0', name='matplotlib.name_of_module',
|
||||
obj_type='module')
|
||||
"""
|
||||
warning = _generate_deprecation_warning(
|
||||
since, message, name, alternative, pending, obj_type, addendum,
|
||||
removal=removal)
|
||||
from . import warn_external
|
||||
warn_external(warning, category=MatplotlibDeprecationWarning)
|
||||
|
||||
|
||||
def deprecated(since, *, message='', name='', alternative='', pending=False,
|
||||
obj_type=None, addendum='', removal=''):
|
||||
"""
|
||||
Decorator to mark a function, a class, or a property as deprecated.
|
||||
|
||||
When deprecating a classmethod, a staticmethod, or a property, the
|
||||
``@deprecated`` decorator should go *under* ``@classmethod`` and
|
||||
``@staticmethod`` (i.e., `deprecated` should directly decorate the
|
||||
underlying callable), but *over* ``@property``.
|
||||
|
||||
When deprecating a class ``C`` intended to be used as a base class in a
|
||||
multiple inheritance hierarchy, ``C`` *must* define an ``__init__`` method
|
||||
(if ``C`` instead inherited its ``__init__`` from its own base class, then
|
||||
``@deprecated`` would mess up ``__init__`` inheritance when installing its
|
||||
own (deprecation-emitting) ``C.__init__``).
|
||||
|
||||
Parameters are the same as for `warn_deprecated`, except that *obj_type*
|
||||
defaults to 'class' if decorating a class, 'attribute' if decorating a
|
||||
property, and 'function' otherwise.
|
||||
|
||||
Examples
|
||||
--------
|
||||
::
|
||||
|
||||
@deprecated('1.4.0')
|
||||
def the_function_to_deprecate():
|
||||
pass
|
||||
"""
|
||||
|
||||
def deprecate(obj, message=message, name=name, alternative=alternative,
|
||||
pending=pending, obj_type=obj_type, addendum=addendum):
|
||||
from matplotlib._api import classproperty
|
||||
|
||||
if isinstance(obj, type):
|
||||
if obj_type is None:
|
||||
obj_type = "class"
|
||||
func = obj.__init__
|
||||
name = name or obj.__name__
|
||||
old_doc = obj.__doc__
|
||||
|
||||
def finalize(wrapper, new_doc):
|
||||
try:
|
||||
obj.__doc__ = new_doc
|
||||
except AttributeError: # Can't set on some extension objects.
|
||||
pass
|
||||
obj.__init__ = functools.wraps(obj.__init__)(wrapper)
|
||||
return obj
|
||||
|
||||
elif isinstance(obj, (property, classproperty)):
|
||||
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=None):
|
||||
if instance is not None or owner is not None \
|
||||
and isinstance(self, classproperty):
|
||||
emit_warning()
|
||||
return super().__get__(instance, owner)
|
||||
|
||||
def __set__(self, instance, value):
|
||||
if instance is not None:
|
||||
emit_warning()
|
||||
return super().__set__(instance, value)
|
||||
|
||||
def __delete__(self, instance):
|
||||
if instance is not None:
|
||||
emit_warning()
|
||||
return super().__delete__(instance)
|
||||
|
||||
def __set_name__(self, owner, set_name):
|
||||
nonlocal name
|
||||
if name == "<lambda>":
|
||||
name = set_name
|
||||
|
||||
def finalize(_, new_doc):
|
||||
return _deprecated_property(
|
||||
fget=obj.fget, fset=obj.fset, fdel=obj.fdel, doc=new_doc)
|
||||
|
||||
else:
|
||||
if obj_type is None:
|
||||
obj_type = "function"
|
||||
func = obj
|
||||
name = name or obj.__name__
|
||||
old_doc = func.__doc__
|
||||
|
||||
def finalize(wrapper, new_doc):
|
||||
wrapper = functools.wraps(func)(wrapper)
|
||||
wrapper.__doc__ = new_doc
|
||||
return wrapper
|
||||
|
||||
def emit_warning():
|
||||
warn_deprecated(
|
||||
since, message=message, name=name, alternative=alternative,
|
||||
pending=pending, obj_type=obj_type, addendum=addendum,
|
||||
removal=removal)
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
emit_warning()
|
||||
return func(*args, **kwargs)
|
||||
|
||||
old_doc = inspect.cleandoc(old_doc or '').strip('\n')
|
||||
|
||||
notes_header = '\nNotes\n-----'
|
||||
second_arg = ' '.join([t.strip() for t in
|
||||
(message, f"Use {alternative} instead."
|
||||
if alternative else "", addendum) if t])
|
||||
new_doc = (f"[*Deprecated*] {old_doc}\n"
|
||||
f"{notes_header if notes_header not in old_doc else ''}\n"
|
||||
f".. deprecated:: {since}\n"
|
||||
f" {second_arg}")
|
||||
|
||||
if not old_doc:
|
||||
# This is to prevent a spurious 'unexpected unindent' warning from
|
||||
# docutils when the original docstring was blank.
|
||||
new_doc += r'\ '
|
||||
|
||||
return finalize(wrapper, new_doc)
|
||||
|
||||
return deprecate
|
||||
|
||||
|
||||
class deprecate_privatize_attribute:
|
||||
"""
|
||||
Helper to deprecate public access to an attribute (or method).
|
||||
|
||||
This helper should only be used at class scope, as follows::
|
||||
|
||||
class Foo:
|
||||
attr = _deprecate_privatize_attribute(*args, **kwargs)
|
||||
|
||||
where *all* parameters are forwarded to `deprecated`. This form makes
|
||||
``attr`` a property which forwards read and write access to ``self._attr``
|
||||
(same name but with a leading underscore), with a deprecation warning.
|
||||
Note that the attribute name is derived from *the name this helper is
|
||||
assigned to*. This helper also works for deprecating methods.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.deprecator = deprecated(*args, **kwargs)
|
||||
|
||||
def __set_name__(self, owner, name):
|
||||
setattr(owner, name, self.deprecator(
|
||||
property(lambda self: getattr(self, f"_{name}"),
|
||||
lambda self, value: setattr(self, f"_{name}", value)),
|
||||
name=name))
|
||||
|
||||
|
||||
# Used by _copy_docstring_and_deprecators to redecorate pyplot wrappers and
|
||||
# boilerplate.py to retrieve original signatures. It may seem natural to store
|
||||
# this information as an attribute on the wrapper, but if the wrapper gets
|
||||
# itself functools.wraps()ed, then such attributes are silently propagated to
|
||||
# the outer wrapper, which is not desired.
|
||||
DECORATORS = {}
|
||||
|
||||
|
||||
def rename_parameter(since, old, new, func=None):
|
||||
"""
|
||||
Decorator indicating that parameter *old* of *func* is renamed to *new*.
|
||||
|
||||
The actual implementation of *func* should use *new*, not *old*. If *old*
|
||||
is passed to *func*, a DeprecationWarning is emitted, and its value is
|
||||
used, even if *new* is also passed by keyword (this is to simplify pyplot
|
||||
wrapper functions, which always pass *new* explicitly to the Axes method).
|
||||
If *new* is also passed but positionally, a TypeError will be raised by the
|
||||
underlying function during argument binding.
|
||||
|
||||
Examples
|
||||
--------
|
||||
::
|
||||
|
||||
@_api.rename_parameter("3.1", "bad_name", "good_name")
|
||||
def func(good_name): ...
|
||||
"""
|
||||
|
||||
decorator = functools.partial(rename_parameter, since, old, new)
|
||||
|
||||
if func is None:
|
||||
return decorator
|
||||
|
||||
signature = inspect.signature(func)
|
||||
assert old not in signature.parameters, (
|
||||
f"Matplotlib internal error: {old!r} cannot be a parameter for "
|
||||
f"{func.__name__}()")
|
||||
assert new in signature.parameters, (
|
||||
f"Matplotlib internal error: {new!r} must be a parameter for "
|
||||
f"{func.__name__}()")
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
if old in kwargs:
|
||||
warn_deprecated(
|
||||
since, message=f"The {old!r} parameter of {func.__name__}() "
|
||||
f"has been renamed {new!r} since Matplotlib {since}; support "
|
||||
f"for the old name will be dropped in %(removal)s.")
|
||||
kwargs[new] = kwargs.pop(old)
|
||||
return func(*args, **kwargs)
|
||||
|
||||
# wrapper() must keep the same documented signature as func(): if we
|
||||
# instead made both *old* and *new* appear in wrapper()'s signature, they
|
||||
# would both show up in the pyplot function for an Axes method as well and
|
||||
# pyplot would explicitly pass both arguments to the Axes method.
|
||||
|
||||
DECORATORS[wrapper] = decorator
|
||||
return wrapper
|
||||
|
||||
|
||||
class _deprecated_parameter_class:
|
||||
def __repr__(self):
|
||||
return "<deprecated parameter>"
|
||||
|
||||
|
||||
_deprecated_parameter = _deprecated_parameter_class()
|
||||
|
||||
|
||||
def delete_parameter(since, name, func=None, **kwargs):
|
||||
"""
|
||||
Decorator indicating that parameter *name* of *func* is being deprecated.
|
||||
|
||||
The actual implementation of *func* should keep the *name* parameter in its
|
||||
signature, or accept a ``**kwargs`` argument (through which *name* would be
|
||||
passed).
|
||||
|
||||
Parameters that come after the deprecated parameter effectively become
|
||||
keyword-only (as they cannot be passed positionally without triggering the
|
||||
DeprecationWarning on the deprecated parameter), and should be marked as
|
||||
such after the deprecation period has passed and the deprecated parameter
|
||||
is removed.
|
||||
|
||||
Parameters other than *since*, *name*, and *func* are keyword-only and
|
||||
forwarded to `.warn_deprecated`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
::
|
||||
|
||||
@_api.delete_parameter("3.1", "unused")
|
||||
def func(used_arg, other_arg, unused, more_args): ...
|
||||
"""
|
||||
|
||||
decorator = functools.partial(delete_parameter, since, name, **kwargs)
|
||||
|
||||
if func is None:
|
||||
return decorator
|
||||
|
||||
signature = inspect.signature(func)
|
||||
# Name of `**kwargs` parameter of the decorated function, typically
|
||||
# "kwargs" if such a parameter exists, or None if the decorated function
|
||||
# doesn't accept `**kwargs`.
|
||||
kwargs_name = next((param.name for param in signature.parameters.values()
|
||||
if param.kind == inspect.Parameter.VAR_KEYWORD), None)
|
||||
if name in signature.parameters:
|
||||
kind = signature.parameters[name].kind
|
||||
is_varargs = kind is inspect.Parameter.VAR_POSITIONAL
|
||||
is_varkwargs = kind is inspect.Parameter.VAR_KEYWORD
|
||||
if not is_varargs and not is_varkwargs:
|
||||
name_idx = (
|
||||
# Deprecated parameter can't be passed positionally.
|
||||
math.inf if kind is inspect.Parameter.KEYWORD_ONLY
|
||||
# If call site has no more than this number of parameters, the
|
||||
# deprecated parameter can't have been passed positionally.
|
||||
else [*signature.parameters].index(name))
|
||||
func.__signature__ = signature = signature.replace(parameters=[
|
||||
param.replace(default=_deprecated_parameter)
|
||||
if param.name == name else param
|
||||
for param in signature.parameters.values()])
|
||||
else:
|
||||
name_idx = -1 # Deprecated parameter can always have been passed.
|
||||
else:
|
||||
is_varargs = is_varkwargs = False
|
||||
# Deprecated parameter can't be passed positionally.
|
||||
name_idx = math.inf
|
||||
assert kwargs_name, (
|
||||
f"Matplotlib internal error: {name!r} must be a parameter for "
|
||||
f"{func.__name__}()")
|
||||
|
||||
addendum = kwargs.pop('addendum', None)
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*inner_args, **inner_kwargs):
|
||||
if len(inner_args) <= name_idx and name not in inner_kwargs:
|
||||
# Early return in the simple, non-deprecated case (much faster than
|
||||
# calling bind()).
|
||||
return func(*inner_args, **inner_kwargs)
|
||||
arguments = signature.bind(*inner_args, **inner_kwargs).arguments
|
||||
if is_varargs and arguments.get(name):
|
||||
warn_deprecated(
|
||||
since, message=f"Additional positional arguments to "
|
||||
f"{func.__name__}() are deprecated since %(since)s and "
|
||||
f"support for them will be removed in %(removal)s.")
|
||||
elif is_varkwargs and arguments.get(name):
|
||||
warn_deprecated(
|
||||
since, message=f"Additional keyword arguments to "
|
||||
f"{func.__name__}() are deprecated since %(since)s and "
|
||||
f"support for them will be removed in %(removal)s.")
|
||||
# We cannot just check `name not in arguments` because the pyplot
|
||||
# wrappers always pass all arguments explicitly.
|
||||
elif any(name in d and d[name] != _deprecated_parameter
|
||||
for d in [arguments, arguments.get(kwargs_name, {})]):
|
||||
deprecation_addendum = (
|
||||
f"If any parameter follows {name!r}, they should be passed as "
|
||||
f"keyword, not positionally.")
|
||||
warn_deprecated(
|
||||
since,
|
||||
name=repr(name),
|
||||
obj_type=f"parameter of {func.__name__}()",
|
||||
addendum=(addendum + " " + deprecation_addendum) if addendum
|
||||
else deprecation_addendum,
|
||||
**kwargs)
|
||||
return func(*inner_args, **inner_kwargs)
|
||||
|
||||
DECORATORS[wrapper] = decorator
|
||||
return wrapper
|
||||
|
||||
|
||||
def make_keyword_only(since, name, func=None):
|
||||
"""
|
||||
Decorator indicating that passing parameter *name* (or any of the following
|
||||
ones) positionally to *func* is being deprecated.
|
||||
|
||||
When used on a method that has a pyplot wrapper, this should be the
|
||||
outermost decorator, so that :file:`boilerplate.py` can access the original
|
||||
signature.
|
||||
"""
|
||||
|
||||
decorator = functools.partial(make_keyword_only, since, name)
|
||||
|
||||
if func is None:
|
||||
return decorator
|
||||
|
||||
signature = inspect.signature(func)
|
||||
POK = inspect.Parameter.POSITIONAL_OR_KEYWORD
|
||||
KWO = inspect.Parameter.KEYWORD_ONLY
|
||||
assert (name in signature.parameters
|
||||
and signature.parameters[name].kind == POK), (
|
||||
f"Matplotlib internal error: {name!r} must be a positional-or-keyword "
|
||||
f"parameter for {func.__name__}(). If this error happens on a function with a "
|
||||
f"pyplot wrapper, make sure make_keyword_only() is the outermost decorator.")
|
||||
names = [*signature.parameters]
|
||||
name_idx = names.index(name)
|
||||
kwonly = [name for name in names[name_idx:]
|
||||
if signature.parameters[name].kind == POK]
|
||||
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
# Don't use signature.bind here, as it would fail when stacked with
|
||||
# rename_parameter and an "old" argument name is passed in
|
||||
# (signature.bind would fail, but the actual call would succeed).
|
||||
if len(args) > name_idx:
|
||||
warn_deprecated(
|
||||
since, message="Passing the %(name)s %(obj_type)s "
|
||||
"positionally is deprecated since Matplotlib %(since)s; the "
|
||||
"parameter will become keyword-only in %(removal)s.",
|
||||
name=name, obj_type=f"parameter of {func.__name__}()")
|
||||
return func(*args, **kwargs)
|
||||
|
||||
# Don't modify *func*'s signature, as boilerplate.py needs it.
|
||||
wrapper.__signature__ = signature.replace(parameters=[
|
||||
param.replace(kind=KWO) if param.name in kwonly else param
|
||||
for param in signature.parameters.values()])
|
||||
DECORATORS[wrapper] = decorator
|
||||
return wrapper
|
||||
|
||||
|
||||
def deprecate_method_override(method, obj, *, allow_empty=False, **kwargs):
|
||||
"""
|
||||
Return ``obj.method`` with a deprecation if it was overridden, else None.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
method
|
||||
An unbound method, i.e. an expression of the form
|
||||
``Class.method_name``. Remember that within the body of a method, one
|
||||
can always use ``__class__`` to refer to the class that is currently
|
||||
being defined.
|
||||
obj
|
||||
Either an object of the class where *method* is defined, or a subclass
|
||||
of that class.
|
||||
allow_empty : bool, default: False
|
||||
Whether to allow overrides by "empty" methods without emitting a
|
||||
warning.
|
||||
**kwargs
|
||||
Additional parameters passed to `warn_deprecated` to generate the
|
||||
deprecation warning; must at least include the "since" key.
|
||||
"""
|
||||
|
||||
def empty(): pass
|
||||
def empty_with_docstring(): """doc"""
|
||||
|
||||
name = method.__name__
|
||||
bound_child = getattr(obj, name)
|
||||
bound_base = (
|
||||
method # If obj is a class, then we need to use unbound methods.
|
||||
if isinstance(bound_child, type(empty)) and isinstance(obj, type)
|
||||
else method.__get__(obj))
|
||||
if (bound_child != bound_base
|
||||
and (not allow_empty
|
||||
or (getattr(getattr(bound_child, "__code__", None),
|
||||
"co_code", None)
|
||||
not in [empty.__code__.co_code,
|
||||
empty_with_docstring.__code__.co_code]))):
|
||||
warn_deprecated(**{"name": name, "obj_type": "method", **kwargs})
|
||||
return bound_child
|
||||
return None
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def suppress_matplotlib_deprecation_warning():
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", MatplotlibDeprecationWarning)
|
||||
yield
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
from collections.abc import Callable
|
||||
import contextlib
|
||||
from typing import Any, Literal, ParamSpec, TypedDict, TypeVar, overload
|
||||
from typing_extensions import (
|
||||
Unpack, # < Py 3.11
|
||||
)
|
||||
|
||||
_P = ParamSpec("_P")
|
||||
_R = TypeVar("_R")
|
||||
_T = TypeVar("_T")
|
||||
|
||||
class MatplotlibDeprecationWarning(DeprecationWarning): ...
|
||||
|
||||
class DeprecationKwargs(TypedDict, total=False):
|
||||
message: str
|
||||
alternative: str
|
||||
pending: bool
|
||||
obj_type: str
|
||||
addendum: str
|
||||
removal: str | Literal[False]
|
||||
|
||||
class NamedDeprecationKwargs(DeprecationKwargs, total=False):
|
||||
name: str
|
||||
|
||||
def warn_deprecated(since: str, **kwargs: Unpack[NamedDeprecationKwargs]) -> None: ...
|
||||
def deprecated(
|
||||
since: str, **kwargs: Unpack[NamedDeprecationKwargs]
|
||||
) -> Callable[[_T], _T]: ...
|
||||
|
||||
class deprecate_privatize_attribute(Any):
|
||||
def __init__(self, since: str, **kwargs: Unpack[NamedDeprecationKwargs]): ...
|
||||
def __set_name__(self, owner: type[object], name: str) -> None: ...
|
||||
|
||||
DECORATORS: dict[Callable, Callable] = ...
|
||||
|
||||
@overload
|
||||
def rename_parameter(
|
||||
since: str, old: str, new: str, func: None = ...
|
||||
) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: ...
|
||||
@overload
|
||||
def rename_parameter(
|
||||
since: str, old: str, new: str, func: Callable[_P, _R]
|
||||
) -> Callable[_P, _R]: ...
|
||||
|
||||
class _deprecated_parameter_class: ...
|
||||
|
||||
_deprecated_parameter: _deprecated_parameter_class
|
||||
|
||||
@overload
|
||||
def delete_parameter(
|
||||
since: str, name: str, func: None = ..., **kwargs: Unpack[DeprecationKwargs]
|
||||
) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: ...
|
||||
@overload
|
||||
def delete_parameter(
|
||||
since: str, name: str, func: Callable[_P, _R], **kwargs: Unpack[DeprecationKwargs]
|
||||
) -> Callable[_P, _R]: ...
|
||||
@overload
|
||||
def make_keyword_only(
|
||||
since: str, name: str, func: None = ...
|
||||
) -> Callable[[Callable[_P, _R]], Callable[_P, _R]]: ...
|
||||
@overload
|
||||
def make_keyword_only(
|
||||
since: str, name: str, func: Callable[_P, _R]
|
||||
) -> Callable[_P, _R]: ...
|
||||
def deprecate_method_override(
|
||||
method: Callable[_P, _R],
|
||||
obj: object | type,
|
||||
*,
|
||||
allow_empty: bool = ...,
|
||||
since: str,
|
||||
**kwargs: Unpack[NamedDeprecationKwargs]
|
||||
) -> Callable[_P, _R]: ...
|
||||
def suppress_matplotlib_deprecation_warning() -> (
|
||||
contextlib.AbstractContextManager[None]
|
||||
): ...
|
||||
Loading…
Add table
Add a link
Reference in a new issue