Update 2025-04-24_11:44:19

This commit is contained in:
oib
2025-04-24 11:44:23 +02:00
commit e748c737f4
3408 changed files with 717481 additions and 0 deletions

View File

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
"""
Deprecated Library
==================
Python ``@deprecated`` decorator to deprecate old python classes, functions or methods.
"""
__version__ = "1.2.18"
__author__ = u"Laurent LAPORTE <laurent.laporte.pro@gmail.com>"
__date__ = "2025-01-27"
__credits__ = "(c) Laurent LAPORTE"
from deprecated.classic import deprecated

View File

@ -0,0 +1,301 @@
# -*- coding: utf-8 -*-
"""
Classic deprecation warning
===========================
Classic ``@deprecated`` decorator to deprecate old python classes, functions or methods.
.. _The Warnings Filter: https://docs.python.org/3/library/warnings.html#the-warnings-filter
"""
import functools
import inspect
import platform
import warnings
import wrapt
try:
# If the C extension for wrapt was compiled and wrapt/_wrappers.pyd exists, then the
# stack level that should be passed to warnings.warn should be 2. However, if using
# a pure python wrapt, an extra stacklevel is required.
import wrapt._wrappers
_routine_stacklevel = 2
_class_stacklevel = 2
except ImportError:
_routine_stacklevel = 3
if platform.python_implementation() == "PyPy":
_class_stacklevel = 2
else:
_class_stacklevel = 3
string_types = (type(b''), type(u''))
class ClassicAdapter(wrapt.AdapterFactory):
"""
Classic adapter -- *for advanced usage only*
This adapter is used to get the deprecation message according to the wrapped object type:
class, function, standard method, static method, or class method.
This is the base class of the :class:`~deprecated.sphinx.SphinxAdapter` class
which is used to update the wrapped object docstring.
You can also inherit this class to change the deprecation message.
In the following example, we change the message into "The ... is deprecated.":
.. code-block:: python
import inspect
from deprecated.classic import ClassicAdapter
from deprecated.classic import deprecated
class MyClassicAdapter(ClassicAdapter):
def get_deprecated_msg(self, wrapped, instance):
if instance is None:
if inspect.isclass(wrapped):
fmt = "The class {name} is deprecated."
else:
fmt = "The function {name} is deprecated."
else:
if inspect.isclass(instance):
fmt = "The class method {name} is deprecated."
else:
fmt = "The method {name} is deprecated."
if self.reason:
fmt += " ({reason})"
if self.version:
fmt += " -- Deprecated since version {version}."
return fmt.format(name=wrapped.__name__,
reason=self.reason or "",
version=self.version or "")
Then, you can use your ``MyClassicAdapter`` class like this in your source code:
.. code-block:: python
@deprecated(reason="use another function", adapter_cls=MyClassicAdapter)
def some_old_function(x, y):
return x + y
"""
def __init__(self, reason="", version="", action=None, category=DeprecationWarning, extra_stacklevel=0):
"""
Construct a wrapper adapter.
:type reason: str
:param reason:
Reason message which documents the deprecation in your library (can be omitted).
:type version: str
:param version:
Version of your project which deprecates this feature.
If you follow the `Semantic Versioning <https://semver.org/>`_,
the version number has the format "MAJOR.MINOR.PATCH".
:type action: Literal["default", "error", "ignore", "always", "module", "once"]
:param action:
A warning filter used to activate or not the deprecation warning.
Can be one of "error", "ignore", "always", "default", "module", or "once".
If ``None`` or empty, the global filtering mechanism is used.
See: `The Warnings Filter`_ in the Python documentation.
:type category: Type[Warning]
:param category:
The warning category to use for the deprecation warning.
By default, the category class is :class:`~DeprecationWarning`,
you can inherit this class to define your own deprecation warning category.
:type extra_stacklevel: int
:param extra_stacklevel:
Number of additional stack levels to consider instrumentation rather than user code.
With the default value of 0, the warning refers to where the class was instantiated
or the function was called.
.. versionchanged:: 1.2.15
Add the *extra_stacklevel* parameter.
"""
self.reason = reason or ""
self.version = version or ""
self.action = action
self.category = category
self.extra_stacklevel = extra_stacklevel
super(ClassicAdapter, self).__init__()
def get_deprecated_msg(self, wrapped, instance):
"""
Get the deprecation warning message for the user.
:param wrapped: Wrapped class or function.
:param instance: The object to which the wrapped function was bound when it was called.
:return: The warning message.
"""
if instance is None:
if inspect.isclass(wrapped):
fmt = "Call to deprecated class {name}."
else:
fmt = "Call to deprecated function (or staticmethod) {name}."
else:
if inspect.isclass(instance):
fmt = "Call to deprecated class method {name}."
else:
fmt = "Call to deprecated method {name}."
if self.reason:
fmt += " ({reason})"
if self.version:
fmt += " -- Deprecated since version {version}."
return fmt.format(name=wrapped.__name__, reason=self.reason or "", version=self.version or "")
def __call__(self, wrapped):
"""
Decorate your class or function.
:param wrapped: Wrapped class or function.
:return: the decorated class or function.
.. versionchanged:: 1.2.4
Don't pass arguments to :meth:`object.__new__` (other than *cls*).
.. versionchanged:: 1.2.8
The warning filter is not set if the *action* parameter is ``None`` or empty.
"""
if inspect.isclass(wrapped):
old_new1 = wrapped.__new__
def wrapped_cls(cls, *args, **kwargs):
msg = self.get_deprecated_msg(wrapped, None)
stacklevel = _class_stacklevel + self.extra_stacklevel
if self.action:
with warnings.catch_warnings():
warnings.simplefilter(self.action, self.category)
warnings.warn(msg, category=self.category, stacklevel=stacklevel)
else:
warnings.warn(msg, category=self.category, stacklevel=stacklevel)
if old_new1 is object.__new__:
return old_new1(cls)
# actually, we don't know the real signature of *old_new1*
return old_new1(cls, *args, **kwargs)
wrapped.__new__ = staticmethod(wrapped_cls)
elif inspect.isroutine(wrapped):
@wrapt.decorator
def wrapper_function(wrapped_, instance_, args_, kwargs_):
msg = self.get_deprecated_msg(wrapped_, instance_)
stacklevel = _routine_stacklevel + self.extra_stacklevel
if self.action:
with warnings.catch_warnings():
warnings.simplefilter(self.action, self.category)
warnings.warn(msg, category=self.category, stacklevel=stacklevel)
else:
warnings.warn(msg, category=self.category, stacklevel=stacklevel)
return wrapped_(*args_, **kwargs_)
return wrapper_function(wrapped)
else:
raise TypeError(repr(type(wrapped)))
return wrapped
def deprecated(*args, **kwargs):
"""
This is a decorator which can be used to mark functions
as deprecated. It will result in a warning being emitted
when the function is used.
**Classic usage:**
To use this, decorate your deprecated function with **@deprecated** decorator:
.. code-block:: python
from deprecated import deprecated
@deprecated
def some_old_function(x, y):
return x + y
You can also decorate a class or a method:
.. code-block:: python
from deprecated import deprecated
class SomeClass(object):
@deprecated
def some_old_method(self, x, y):
return x + y
@deprecated
class SomeOldClass(object):
pass
You can give a *reason* message to help the developer to choose another function/class,
and a *version* number to specify the starting version number of the deprecation.
.. code-block:: python
from deprecated import deprecated
@deprecated(reason="use another function", version='1.2.0')
def some_old_function(x, y):
return x + y
The *category* keyword argument allow you to specify the deprecation warning class of your choice.
By default, :exc:`DeprecationWarning` is used, but you can choose :exc:`FutureWarning`,
:exc:`PendingDeprecationWarning` or a custom subclass.
.. code-block:: python
from deprecated import deprecated
@deprecated(category=PendingDeprecationWarning)
def some_old_function(x, y):
return x + y
The *action* keyword argument allow you to locally change the warning filtering.
*action* can be one of "error", "ignore", "always", "default", "module", or "once".
If ``None``, empty or missing, the global filtering mechanism is used.
See: `The Warnings Filter`_ in the Python documentation.
.. code-block:: python
from deprecated import deprecated
@deprecated(action="error")
def some_old_function(x, y):
return x + y
The *extra_stacklevel* keyword argument allows you to specify additional stack levels
to consider instrumentation rather than user code. With the default value of 0, the
warning refers to where the class was instantiated or the function was called.
"""
if args and isinstance(args[0], string_types):
kwargs['reason'] = args[0]
args = args[1:]
if args and not callable(args[0]):
raise TypeError(repr(type(args[0])))
if args:
adapter_cls = kwargs.pop('adapter_cls', ClassicAdapter)
adapter = adapter_cls(**kwargs)
wrapped = args[0]
return adapter(wrapped)
return functools.partial(deprecated, **kwargs)

View File

@ -0,0 +1,281 @@
# coding: utf-8
"""
Sphinx directive integration
============================
We usually need to document the life-cycle of functions and classes:
when they are created, modified or deprecated.
To do that, `Sphinx <http://www.sphinx-doc.org>`_ has a set
of `Paragraph-level markups <http://www.sphinx-doc.org/en/stable/markup/para.html>`_:
- ``versionadded``: to document the version of the project which added the described feature to the library,
- ``versionchanged``: to document changes of a feature,
- ``deprecated``: to document a deprecated feature.
The purpose of this module is to defined decorators which adds this Sphinx directives
to the docstring of your function and classes.
Of course, the ``@deprecated`` decorator will emit a deprecation warning
when the function/method is called or the class is constructed.
"""
import re
import textwrap
from deprecated.classic import ClassicAdapter
from deprecated.classic import deprecated as _classic_deprecated
class SphinxAdapter(ClassicAdapter):
"""
Sphinx adapter -- *for advanced usage only*
This adapter override the :class:`~deprecated.classic.ClassicAdapter`
in order to add the Sphinx directives to the end of the function/class docstring.
Such a directive is a `Paragraph-level markup <http://www.sphinx-doc.org/en/stable/markup/para.html>`_
- The directive can be one of "versionadded", "versionchanged" or "deprecated".
- The version number is added if provided.
- The reason message is obviously added in the directive block if not empty.
"""
def __init__(
self,
directive,
reason="",
version="",
action=None,
category=DeprecationWarning,
extra_stacklevel=0,
line_length=70,
):
"""
Construct a wrapper adapter.
:type directive: str
:param directive:
Sphinx directive: can be one of "versionadded", "versionchanged" or "deprecated".
:type reason: str
:param reason:
Reason message which documents the deprecation in your library (can be omitted).
:type version: str
:param version:
Version of your project which deprecates this feature.
If you follow the `Semantic Versioning <https://semver.org/>`_,
the version number has the format "MAJOR.MINOR.PATCH".
:type action: Literal["default", "error", "ignore", "always", "module", "once"]
:param action:
A warning filter used to activate or not the deprecation warning.
Can be one of "error", "ignore", "always", "default", "module", or "once".
If ``None`` or empty, the global filtering mechanism is used.
See: `The Warnings Filter`_ in the Python documentation.
:type category: Type[Warning]
:param category:
The warning category to use for the deprecation warning.
By default, the category class is :class:`~DeprecationWarning`,
you can inherit this class to define your own deprecation warning category.
:type extra_stacklevel: int
:param extra_stacklevel:
Number of additional stack levels to consider instrumentation rather than user code.
With the default value of 0, the warning refers to where the class was instantiated
or the function was called.
:type line_length: int
:param line_length:
Max line length of the directive text. If non nul, a long text is wrapped in several lines.
.. versionchanged:: 1.2.15
Add the *extra_stacklevel* parameter.
"""
if not version:
# https://github.com/laurent-laporte-pro/deprecated/issues/40
raise ValueError("'version' argument is required in Sphinx directives")
self.directive = directive
self.line_length = line_length
super(SphinxAdapter, self).__init__(
reason=reason, version=version, action=action, category=category, extra_stacklevel=extra_stacklevel
)
def __call__(self, wrapped):
"""
Add the Sphinx directive to your class or function.
:param wrapped: Wrapped class or function.
:return: the decorated class or function.
"""
# -- build the directive division
fmt = ".. {directive}:: {version}" if self.version else ".. {directive}::"
div_lines = [fmt.format(directive=self.directive, version=self.version)]
width = self.line_length - 3 if self.line_length > 3 else 2**16
reason = textwrap.dedent(self.reason).strip()
for paragraph in reason.splitlines():
if paragraph:
div_lines.extend(
textwrap.fill(
paragraph,
width=width,
initial_indent=" ",
subsequent_indent=" ",
).splitlines()
)
else:
div_lines.append("")
# -- get the docstring, normalize the trailing newlines
# keep a consistent behaviour if the docstring starts with newline or directly on the first one
docstring = wrapped.__doc__ or ""
lines = docstring.splitlines(True) or [""]
docstring = textwrap.dedent("".join(lines[1:])) if len(lines) > 1 else ""
docstring = lines[0] + docstring
if docstring:
# An empty line must separate the original docstring and the directive.
docstring = re.sub(r"\n+$", "", docstring, flags=re.DOTALL) + "\n\n"
else:
# Avoid "Explicit markup ends without a blank line" when the decorated function has no docstring
docstring = "\n"
# -- append the directive division to the docstring
docstring += "".join("{}\n".format(line) for line in div_lines)
wrapped.__doc__ = docstring
if self.directive in {"versionadded", "versionchanged"}:
return wrapped
return super(SphinxAdapter, self).__call__(wrapped)
def get_deprecated_msg(self, wrapped, instance):
"""
Get the deprecation warning message (without Sphinx cross-referencing syntax) for the user.
:param wrapped: Wrapped class or function.
:param instance: The object to which the wrapped function was bound when it was called.
:return: The warning message.
.. versionadded:: 1.2.12
Strip Sphinx cross-referencing syntax from warning message.
"""
msg = super(SphinxAdapter, self).get_deprecated_msg(wrapped, instance)
# Strip Sphinx cross-reference syntax (like ":function:", ":py:func:" and ":py:meth:")
# Possible values are ":role:`foo`", ":domain:role:`foo`"
# where ``role`` and ``domain`` should match "[a-zA-Z]+"
msg = re.sub(r"(?: : [a-zA-Z]+ )? : [a-zA-Z]+ : (`[^`]*`)", r"\1", msg, flags=re.X)
return msg
def versionadded(reason="", version="", line_length=70):
"""
This decorator can be used to insert a "versionadded" directive
in your function/class docstring in order to document the
version of the project which adds this new functionality in your library.
:param str reason:
Reason message which documents the addition in your library (can be omitted).
:param str version:
Version of your project which adds this feature.
If you follow the `Semantic Versioning <https://semver.org/>`_,
the version number has the format "MAJOR.MINOR.PATCH", and,
in the case of a new functionality, the "PATCH" component should be "0".
:type line_length: int
:param line_length:
Max line length of the directive text. If non nul, a long text is wrapped in several lines.
:return: the decorated function.
"""
adapter = SphinxAdapter(
'versionadded',
reason=reason,
version=version,
line_length=line_length,
)
return adapter
def versionchanged(reason="", version="", line_length=70):
"""
This decorator can be used to insert a "versionchanged" directive
in your function/class docstring in order to document the
version of the project which modifies this functionality in your library.
:param str reason:
Reason message which documents the modification in your library (can be omitted).
:param str version:
Version of your project which modifies this feature.
If you follow the `Semantic Versioning <https://semver.org/>`_,
the version number has the format "MAJOR.MINOR.PATCH".
:type line_length: int
:param line_length:
Max line length of the directive text. If non nul, a long text is wrapped in several lines.
:return: the decorated function.
"""
adapter = SphinxAdapter(
'versionchanged',
reason=reason,
version=version,
line_length=line_length,
)
return adapter
def deprecated(reason="", version="", line_length=70, **kwargs):
"""
This decorator can be used to insert a "deprecated" directive
in your function/class docstring in order to document the
version of the project which deprecates this functionality in your library.
:param str reason:
Reason message which documents the deprecation in your library (can be omitted).
:param str version:
Version of your project which deprecates this feature.
If you follow the `Semantic Versioning <https://semver.org/>`_,
the version number has the format "MAJOR.MINOR.PATCH".
:type line_length: int
:param line_length:
Max line length of the directive text. If non nul, a long text is wrapped in several lines.
Keyword arguments can be:
- "action":
A warning filter used to activate or not the deprecation warning.
Can be one of "error", "ignore", "always", "default", "module", or "once".
If ``None``, empty or missing, the global filtering mechanism is used.
- "category":
The warning category to use for the deprecation warning.
By default, the category class is :class:`~DeprecationWarning`,
you can inherit this class to define your own deprecation warning category.
- "extra_stacklevel":
Number of additional stack levels to consider instrumentation rather than user code.
With the default value of 0, the warning refers to where the class was instantiated
or the function was called.
:return: a decorator used to deprecate a function.
.. versionchanged:: 1.2.13
Change the signature of the decorator to reflect the valid use cases.
.. versionchanged:: 1.2.15
Add the *extra_stacklevel* parameter.
"""
directive = kwargs.pop('directive', 'deprecated')
adapter_cls = kwargs.pop('adapter_cls', SphinxAdapter)
kwargs["reason"] = reason
kwargs["version"] = version
kwargs["line_length"] = line_length
return _classic_deprecated(directive=directive, adapter_cls=adapter_cls, **kwargs)