mirror of
https://github.com/python/cpython.git
synced 2025-10-09 16:34:44 +00:00
gh-104003: Implement PEP 702 (#104004)
Co-authored-by: Hugo van Kemenade <hugovk@users.noreply.github.com> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
This commit is contained in:
parent
4038869423
commit
d4a6229afe
5 changed files with 473 additions and 2 deletions
|
@ -522,6 +522,56 @@ Available Functions
|
||||||
and calls to :func:`simplefilter`.
|
and calls to :func:`simplefilter`.
|
||||||
|
|
||||||
|
|
||||||
|
.. decorator:: deprecated(msg, *, category=DeprecationWarning, stacklevel=1)
|
||||||
|
|
||||||
|
Decorator to indicate that a class, function or overload is deprecated.
|
||||||
|
|
||||||
|
When this decorator is applied to an object,
|
||||||
|
deprecation warnings may be emitted at runtime when the object is used.
|
||||||
|
:term:`static type checkers <static type checker>`
|
||||||
|
will also generate a diagnostic on usage of the deprecated object.
|
||||||
|
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
from warnings import deprecated
|
||||||
|
from typing import overload
|
||||||
|
|
||||||
|
@deprecated("Use B instead")
|
||||||
|
class A:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@deprecated("Use g instead")
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
|
||||||
|
@overload
|
||||||
|
@deprecated("int support is deprecated")
|
||||||
|
def g(x: int) -> int: ...
|
||||||
|
@overload
|
||||||
|
def g(x: str) -> int: ...
|
||||||
|
|
||||||
|
The warning specified by *category* will be emitted at runtime
|
||||||
|
on use of deprecated objects. For functions, that happens on calls;
|
||||||
|
for classes, on instantiation and on creation of subclasses.
|
||||||
|
If the *category* is ``None``, no warning is emitted at runtime.
|
||||||
|
The *stacklevel* determines where the
|
||||||
|
warning is emitted. If it is ``1`` (the default), the warning
|
||||||
|
is emitted at the direct caller of the deprecated object; if it
|
||||||
|
is higher, it is emitted further up the stack.
|
||||||
|
Static type checker behavior is not affected by the *category*
|
||||||
|
and *stacklevel* arguments.
|
||||||
|
|
||||||
|
The deprecation message passed to the decorator is saved in the
|
||||||
|
``__deprecated__`` attribute on the decorated object.
|
||||||
|
If applied to an overload, the decorator
|
||||||
|
must be after the :func:`@overload <typing.overload>` decorator
|
||||||
|
for the attribute to exist on the overload as returned by
|
||||||
|
:func:`typing.get_overloads`.
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
See :pep:`702`.
|
||||||
|
|
||||||
|
|
||||||
Available Context Managers
|
Available Context Managers
|
||||||
--------------------------
|
--------------------------
|
||||||
|
|
||||||
|
|
|
@ -348,6 +348,15 @@ venv
|
||||||
(using ``--without-scm-ignore-files``). (Contributed by Brett Cannon in
|
(using ``--without-scm-ignore-files``). (Contributed by Brett Cannon in
|
||||||
:gh:`108125`.)
|
:gh:`108125`.)
|
||||||
|
|
||||||
|
warnings
|
||||||
|
--------
|
||||||
|
|
||||||
|
* The new :func:`warnings.deprecated` decorator provides a way to communicate
|
||||||
|
deprecations to :term:`static type checkers <static type checker>` and
|
||||||
|
to warn on usage of deprecated classes and functions. A runtime deprecation
|
||||||
|
warning may also be emitted when a decorated function or class is used at runtime.
|
||||||
|
See :pep:`702`. (Contributed by Jelle Zijlstra in :gh:`104003`.)
|
||||||
|
|
||||||
Optimizations
|
Optimizations
|
||||||
=============
|
=============
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ from io import StringIO
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
import textwrap
|
import textwrap
|
||||||
|
import types
|
||||||
|
from typing import overload, get_overloads
|
||||||
import unittest
|
import unittest
|
||||||
from test import support
|
from test import support
|
||||||
from test.support import import_helper
|
from test.support import import_helper
|
||||||
|
@ -16,6 +18,7 @@ from test.test_warnings.data import package_helper
|
||||||
from test.test_warnings.data import stacklevel as warning_tests
|
from test.test_warnings.data import stacklevel as warning_tests
|
||||||
|
|
||||||
import warnings as original_warnings
|
import warnings as original_warnings
|
||||||
|
from warnings import deprecated
|
||||||
|
|
||||||
|
|
||||||
py_warnings = import_helper.import_fresh_module('warnings',
|
py_warnings = import_helper.import_fresh_module('warnings',
|
||||||
|
@ -90,7 +93,7 @@ class PublicAPITests(BaseTest):
|
||||||
self.assertTrue(hasattr(self.module, '__all__'))
|
self.assertTrue(hasattr(self.module, '__all__'))
|
||||||
target_api = ["warn", "warn_explicit", "showwarning",
|
target_api = ["warn", "warn_explicit", "showwarning",
|
||||||
"formatwarning", "filterwarnings", "simplefilter",
|
"formatwarning", "filterwarnings", "simplefilter",
|
||||||
"resetwarnings", "catch_warnings"]
|
"resetwarnings", "catch_warnings", "deprecated"]
|
||||||
self.assertSetEqual(set(self.module.__all__),
|
self.assertSetEqual(set(self.module.__all__),
|
||||||
set(target_api))
|
set(target_api))
|
||||||
|
|
||||||
|
@ -1377,6 +1380,283 @@ a=A()
|
||||||
self.assertTrue(err.startswith(expected), ascii(err))
|
self.assertTrue(err.startswith(expected), ascii(err))
|
||||||
|
|
||||||
|
|
||||||
|
class DeprecatedTests(unittest.TestCase):
|
||||||
|
def test_dunder_deprecated(self):
|
||||||
|
@deprecated("A will go away soon")
|
||||||
|
class A:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(A.__deprecated__, "A will go away soon")
|
||||||
|
self.assertIsInstance(A, type)
|
||||||
|
|
||||||
|
@deprecated("b will go away soon")
|
||||||
|
def b():
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(b.__deprecated__, "b will go away soon")
|
||||||
|
self.assertIsInstance(b, types.FunctionType)
|
||||||
|
|
||||||
|
@overload
|
||||||
|
@deprecated("no more ints")
|
||||||
|
def h(x: int) -> int: ...
|
||||||
|
@overload
|
||||||
|
def h(x: str) -> str: ...
|
||||||
|
def h(x):
|
||||||
|
return x
|
||||||
|
|
||||||
|
overloads = get_overloads(h)
|
||||||
|
self.assertEqual(len(overloads), 2)
|
||||||
|
self.assertEqual(overloads[0].__deprecated__, "no more ints")
|
||||||
|
|
||||||
|
def test_class(self):
|
||||||
|
@deprecated("A will go away soon")
|
||||||
|
class A:
|
||||||
|
pass
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"):
|
||||||
|
A()
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "A will go away soon"):
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
A(42)
|
||||||
|
|
||||||
|
def test_class_with_init(self):
|
||||||
|
@deprecated("HasInit will go away soon")
|
||||||
|
class HasInit:
|
||||||
|
def __init__(self, x):
|
||||||
|
self.x = x
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "HasInit will go away soon"):
|
||||||
|
instance = HasInit(42)
|
||||||
|
self.assertEqual(instance.x, 42)
|
||||||
|
|
||||||
|
def test_class_with_new(self):
|
||||||
|
has_new_called = False
|
||||||
|
|
||||||
|
@deprecated("HasNew will go away soon")
|
||||||
|
class HasNew:
|
||||||
|
def __new__(cls, x):
|
||||||
|
nonlocal has_new_called
|
||||||
|
has_new_called = True
|
||||||
|
return super().__new__(cls)
|
||||||
|
|
||||||
|
def __init__(self, x) -> None:
|
||||||
|
self.x = x
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "HasNew will go away soon"):
|
||||||
|
instance = HasNew(42)
|
||||||
|
self.assertEqual(instance.x, 42)
|
||||||
|
self.assertTrue(has_new_called)
|
||||||
|
|
||||||
|
def test_class_with_inherited_new(self):
|
||||||
|
new_base_called = False
|
||||||
|
|
||||||
|
class NewBase:
|
||||||
|
def __new__(cls, x):
|
||||||
|
nonlocal new_base_called
|
||||||
|
new_base_called = True
|
||||||
|
return super().__new__(cls)
|
||||||
|
|
||||||
|
def __init__(self, x) -> None:
|
||||||
|
self.x = x
|
||||||
|
|
||||||
|
@deprecated("HasInheritedNew will go away soon")
|
||||||
|
class HasInheritedNew(NewBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "HasInheritedNew will go away soon"):
|
||||||
|
instance = HasInheritedNew(42)
|
||||||
|
self.assertEqual(instance.x, 42)
|
||||||
|
self.assertTrue(new_base_called)
|
||||||
|
|
||||||
|
def test_class_with_new_but_no_init(self):
|
||||||
|
new_called = False
|
||||||
|
|
||||||
|
@deprecated("HasNewNoInit will go away soon")
|
||||||
|
class HasNewNoInit:
|
||||||
|
def __new__(cls, x):
|
||||||
|
nonlocal new_called
|
||||||
|
new_called = True
|
||||||
|
obj = super().__new__(cls)
|
||||||
|
obj.x = x
|
||||||
|
return obj
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "HasNewNoInit will go away soon"):
|
||||||
|
instance = HasNewNoInit(42)
|
||||||
|
self.assertEqual(instance.x, 42)
|
||||||
|
self.assertTrue(new_called)
|
||||||
|
|
||||||
|
def test_mixin_class(self):
|
||||||
|
@deprecated("Mixin will go away soon")
|
||||||
|
class Mixin:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Base:
|
||||||
|
def __init__(self, a) -> None:
|
||||||
|
self.a = a
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "Mixin will go away soon"):
|
||||||
|
class Child(Base, Mixin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
instance = Child(42)
|
||||||
|
self.assertEqual(instance.a, 42)
|
||||||
|
|
||||||
|
def test_existing_init_subclass(self):
|
||||||
|
@deprecated("C will go away soon")
|
||||||
|
class C:
|
||||||
|
def __init_subclass__(cls) -> None:
|
||||||
|
cls.inited = True
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"):
|
||||||
|
C()
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"):
|
||||||
|
class D(C):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertTrue(D.inited)
|
||||||
|
self.assertIsInstance(D(), D) # no deprecation
|
||||||
|
|
||||||
|
def test_existing_init_subclass_in_base(self):
|
||||||
|
class Base:
|
||||||
|
def __init_subclass__(cls, x) -> None:
|
||||||
|
cls.inited = x
|
||||||
|
|
||||||
|
@deprecated("C will go away soon")
|
||||||
|
class C(Base, x=42):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(C.inited, 42)
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"):
|
||||||
|
C()
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "C will go away soon"):
|
||||||
|
class D(C, x=3):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertEqual(D.inited, 3)
|
||||||
|
|
||||||
|
def test_init_subclass_has_correct_cls(self):
|
||||||
|
init_subclass_saw = None
|
||||||
|
|
||||||
|
@deprecated("Base will go away soon")
|
||||||
|
class Base:
|
||||||
|
def __init_subclass__(cls) -> None:
|
||||||
|
nonlocal init_subclass_saw
|
||||||
|
init_subclass_saw = cls
|
||||||
|
|
||||||
|
self.assertIsNone(init_subclass_saw)
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "Base will go away soon"):
|
||||||
|
class C(Base):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertIs(init_subclass_saw, C)
|
||||||
|
|
||||||
|
def test_init_subclass_with_explicit_classmethod(self):
|
||||||
|
init_subclass_saw = None
|
||||||
|
|
||||||
|
@deprecated("Base will go away soon")
|
||||||
|
class Base:
|
||||||
|
@classmethod
|
||||||
|
def __init_subclass__(cls) -> None:
|
||||||
|
nonlocal init_subclass_saw
|
||||||
|
init_subclass_saw = cls
|
||||||
|
|
||||||
|
self.assertIsNone(init_subclass_saw)
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "Base will go away soon"):
|
||||||
|
class C(Base):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assertIs(init_subclass_saw, C)
|
||||||
|
|
||||||
|
def test_function(self):
|
||||||
|
@deprecated("b will go away soon")
|
||||||
|
def b():
|
||||||
|
pass
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "b will go away soon"):
|
||||||
|
b()
|
||||||
|
|
||||||
|
def test_method(self):
|
||||||
|
class Capybara:
|
||||||
|
@deprecated("x will go away soon")
|
||||||
|
def x(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
instance = Capybara()
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "x will go away soon"):
|
||||||
|
instance.x()
|
||||||
|
|
||||||
|
def test_property(self):
|
||||||
|
class Capybara:
|
||||||
|
@property
|
||||||
|
@deprecated("x will go away soon")
|
||||||
|
def x(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def no_more_setting(self):
|
||||||
|
return 42
|
||||||
|
|
||||||
|
@no_more_setting.setter
|
||||||
|
@deprecated("no more setting")
|
||||||
|
def no_more_setting(self, value):
|
||||||
|
pass
|
||||||
|
|
||||||
|
instance = Capybara()
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "x will go away soon"):
|
||||||
|
instance.x
|
||||||
|
|
||||||
|
with py_warnings.catch_warnings():
|
||||||
|
py_warnings.simplefilter("error")
|
||||||
|
self.assertEqual(instance.no_more_setting, 42)
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(DeprecationWarning, "no more setting"):
|
||||||
|
instance.no_more_setting = 42
|
||||||
|
|
||||||
|
def test_category(self):
|
||||||
|
@deprecated("c will go away soon", category=RuntimeWarning)
|
||||||
|
def c():
|
||||||
|
pass
|
||||||
|
|
||||||
|
with self.assertWarnsRegex(RuntimeWarning, "c will go away soon"):
|
||||||
|
c()
|
||||||
|
|
||||||
|
def test_turn_off_warnings(self):
|
||||||
|
@deprecated("d will go away soon", category=None)
|
||||||
|
def d():
|
||||||
|
pass
|
||||||
|
|
||||||
|
with py_warnings.catch_warnings():
|
||||||
|
py_warnings.simplefilter("error")
|
||||||
|
d()
|
||||||
|
|
||||||
|
def test_only_strings_allowed(self):
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError,
|
||||||
|
"Expected an object of type str for 'message', not 'type'"
|
||||||
|
):
|
||||||
|
@deprecated
|
||||||
|
class Foo: ...
|
||||||
|
|
||||||
|
with self.assertRaisesRegex(
|
||||||
|
TypeError,
|
||||||
|
"Expected an object of type str for 'message', not 'function'"
|
||||||
|
):
|
||||||
|
@deprecated
|
||||||
|
def foo(): ...
|
||||||
|
|
||||||
|
def test_no_retained_references_to_wrapper_instance(self):
|
||||||
|
@deprecated('depr')
|
||||||
|
def d(): pass
|
||||||
|
|
||||||
|
self.assertFalse(any(
|
||||||
|
isinstance(cell.cell_contents, deprecated) for cell in d.__closure__
|
||||||
|
))
|
||||||
|
|
||||||
def setUpModule():
|
def setUpModule():
|
||||||
py_warnings.onceregistry.clear()
|
py_warnings.onceregistry.clear()
|
||||||
c_warnings.onceregistry.clear()
|
c_warnings.onceregistry.clear()
|
||||||
|
|
131
Lib/warnings.py
131
Lib/warnings.py
|
@ -5,7 +5,7 @@ import sys
|
||||||
|
|
||||||
__all__ = ["warn", "warn_explicit", "showwarning",
|
__all__ = ["warn", "warn_explicit", "showwarning",
|
||||||
"formatwarning", "filterwarnings", "simplefilter",
|
"formatwarning", "filterwarnings", "simplefilter",
|
||||||
"resetwarnings", "catch_warnings"]
|
"resetwarnings", "catch_warnings", "deprecated"]
|
||||||
|
|
||||||
def showwarning(message, category, filename, lineno, file=None, line=None):
|
def showwarning(message, category, filename, lineno, file=None, line=None):
|
||||||
"""Hook to write a warning to a file; replace if you like."""
|
"""Hook to write a warning to a file; replace if you like."""
|
||||||
|
@ -508,6 +508,135 @@ class catch_warnings(object):
|
||||||
self._module._showwarnmsg_impl = self._showwarnmsg_impl
|
self._module._showwarnmsg_impl = self._showwarnmsg_impl
|
||||||
|
|
||||||
|
|
||||||
|
class deprecated:
|
||||||
|
"""Indicate that a class, function or overload is deprecated.
|
||||||
|
|
||||||
|
When this decorator is applied to an object, the type checker
|
||||||
|
will generate a diagnostic on usage of the deprecated object.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
|
@deprecated("Use B instead")
|
||||||
|
class A:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@deprecated("Use g instead")
|
||||||
|
def f():
|
||||||
|
pass
|
||||||
|
|
||||||
|
@overload
|
||||||
|
@deprecated("int support is deprecated")
|
||||||
|
def g(x: int) -> int: ...
|
||||||
|
@overload
|
||||||
|
def g(x: str) -> int: ...
|
||||||
|
|
||||||
|
The warning specified by *category* will be emitted at runtime
|
||||||
|
on use of deprecated objects. For functions, that happens on calls;
|
||||||
|
for classes, on instantiation and on creation of subclasses.
|
||||||
|
If the *category* is ``None``, no warning is emitted at runtime.
|
||||||
|
The *stacklevel* determines where the
|
||||||
|
warning is emitted. If it is ``1`` (the default), the warning
|
||||||
|
is emitted at the direct caller of the deprecated object; if it
|
||||||
|
is higher, it is emitted further up the stack.
|
||||||
|
Static type checker behavior is not affected by the *category*
|
||||||
|
and *stacklevel* arguments.
|
||||||
|
|
||||||
|
The deprecation message passed to the decorator is saved in the
|
||||||
|
``__deprecated__`` attribute on the decorated object.
|
||||||
|
If applied to an overload, the decorator
|
||||||
|
must be after the ``@overload`` decorator for the attribute to
|
||||||
|
exist on the overload as returned by ``get_overloads()``.
|
||||||
|
|
||||||
|
See PEP 702 for details.
|
||||||
|
|
||||||
|
"""
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
message: str,
|
||||||
|
/,
|
||||||
|
*,
|
||||||
|
category: type[Warning] | None = DeprecationWarning,
|
||||||
|
stacklevel: int = 1,
|
||||||
|
) -> None:
|
||||||
|
if not isinstance(message, str):
|
||||||
|
raise TypeError(
|
||||||
|
f"Expected an object of type str for 'message', not {type(message).__name__!r}"
|
||||||
|
)
|
||||||
|
self.message = message
|
||||||
|
self.category = category
|
||||||
|
self.stacklevel = stacklevel
|
||||||
|
|
||||||
|
def __call__(self, arg, /):
|
||||||
|
# Make sure the inner functions created below don't
|
||||||
|
# retain a reference to self.
|
||||||
|
msg = self.message
|
||||||
|
category = self.category
|
||||||
|
stacklevel = self.stacklevel
|
||||||
|
if category is None:
|
||||||
|
arg.__deprecated__ = msg
|
||||||
|
return arg
|
||||||
|
elif isinstance(arg, type):
|
||||||
|
import functools
|
||||||
|
from types import MethodType
|
||||||
|
|
||||||
|
original_new = arg.__new__
|
||||||
|
|
||||||
|
@functools.wraps(original_new)
|
||||||
|
def __new__(cls, *args, **kwargs):
|
||||||
|
if cls is arg:
|
||||||
|
warn(msg, category=category, stacklevel=stacklevel + 1)
|
||||||
|
if original_new is not object.__new__:
|
||||||
|
return original_new(cls, *args, **kwargs)
|
||||||
|
# Mirrors a similar check in object.__new__.
|
||||||
|
elif cls.__init__ is object.__init__ and (args or kwargs):
|
||||||
|
raise TypeError(f"{cls.__name__}() takes no arguments")
|
||||||
|
else:
|
||||||
|
return original_new(cls)
|
||||||
|
|
||||||
|
arg.__new__ = staticmethod(__new__)
|
||||||
|
|
||||||
|
original_init_subclass = arg.__init_subclass__
|
||||||
|
# We need slightly different behavior if __init_subclass__
|
||||||
|
# is a bound method (likely if it was implemented in Python)
|
||||||
|
if isinstance(original_init_subclass, MethodType):
|
||||||
|
original_init_subclass = original_init_subclass.__func__
|
||||||
|
|
||||||
|
@functools.wraps(original_init_subclass)
|
||||||
|
def __init_subclass__(*args, **kwargs):
|
||||||
|
warn(msg, category=category, stacklevel=stacklevel + 1)
|
||||||
|
return original_init_subclass(*args, **kwargs)
|
||||||
|
|
||||||
|
arg.__init_subclass__ = classmethod(__init_subclass__)
|
||||||
|
# Or otherwise, which likely means it's a builtin such as
|
||||||
|
# object's implementation of __init_subclass__.
|
||||||
|
else:
|
||||||
|
@functools.wraps(original_init_subclass)
|
||||||
|
def __init_subclass__(*args, **kwargs):
|
||||||
|
warn(msg, category=category, stacklevel=stacklevel + 1)
|
||||||
|
return original_init_subclass(*args, **kwargs)
|
||||||
|
|
||||||
|
arg.__init_subclass__ = __init_subclass__
|
||||||
|
|
||||||
|
arg.__deprecated__ = __new__.__deprecated__ = msg
|
||||||
|
__init_subclass__.__deprecated__ = msg
|
||||||
|
return arg
|
||||||
|
elif callable(arg):
|
||||||
|
import functools
|
||||||
|
|
||||||
|
@functools.wraps(arg)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
warn(msg, category=category, stacklevel=stacklevel + 1)
|
||||||
|
return arg(*args, **kwargs)
|
||||||
|
|
||||||
|
arg.__deprecated__ = wrapper.__deprecated__ = msg
|
||||||
|
return wrapper
|
||||||
|
else:
|
||||||
|
raise TypeError(
|
||||||
|
"@deprecated decorator with non-None category must be applied to "
|
||||||
|
f"a class or callable, not {arg!r}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
_DEPRECATED_MSG = "{name!r} is deprecated and slated for removal in Python {remove}"
|
_DEPRECATED_MSG = "{name!r} is deprecated and slated for removal in Python {remove}"
|
||||||
|
|
||||||
def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_info):
|
def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_info):
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
Add :func:`warnings.deprecated`, a decorator to mark deprecated functions to
|
||||||
|
static type checkers and to warn on usage of deprecated classes and functions.
|
||||||
|
See :pep:`702`. Patch by Jelle Zijlstra.
|
Loading…
Add table
Add a link
Reference in a new issue