FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

PEP 702: Runtime warnings by JelleZijlstra · Pull Request #112 · python/typing_extensions · GitHub

Repository navigation

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .py  (2) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
120 changes: 119 additions & 1 deletion src/test_typing_extensions.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from typing_extensions import NamedTuple
from typing_extensions import override, deprecated
from _typed_dict_test_helper import Foo, FooGeneric
import warnings

# Flags used to mark tests that only apply after a specific
# version of the typing module.
Expand Down Expand Up @@ -203,7 +204,7 @@ def static_method_bad_order():


class DeprecatedTests(BaseTestCase):
def test_deprecated(self):
def test_dunder_deprecated(self):
@deprecated("A will go away soon")
class A:
pass
Expand All @@ -230,6 +231,123 @@ def h(x):
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.assertRaises(TypeError):
A(42)

@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)

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)
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_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 warnings.catch_warnings():
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 warnings.catch_warnings():
warnings.simplefilter("error")
d()


class AnyTests(BaseTestCase):
def test_can_subclass(self):
Expand Down
44 changes: 41 additions & 3 deletions src/typing_extensions.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import sys
import types as _types
import typing
import warnings


__all__ = [
Expand Down Expand Up @@ -2135,7 +2136,12 @@ def method(self) -> None:
else:
_T = typing.TypeVar("_T")

def deprecated(__msg: str) -> typing.Callable[[_T], _T]:
def deprecated(
__msg: str,
*,
category: typing.Optional[typing.Type[Warning]] = DeprecationWarning,
stacklevel: int = 1,
) -> typing.Callable[[_T], _T]:
"""Indicate that a class, function or overload is deprecated.

Usage:
Expand Down Expand Up @@ -2167,8 +2173,40 @@ def g(x: str) -> int: ...

"""
def decorator(__arg: _T) -> _T:
__arg.__deprecated__ = __msg
return __arg
if category is None:
__arg.__deprecated__ = __msg
return __arg
elif isinstance(__arg, type):
original_new = __arg.__new__
has_init = __arg.__init__ is not object.__init__

@functools.wraps(original_new)
def __new__(cls, *args, **kwargs):
warnings.warn(__msg, category=category, stacklevel=stacklevel + 1)
# Mirrors a similar check in object.__new__.
if not has_init and (args or kwargs):
raise TypeError(f"{cls.__name__}() takes no arguments")
if original_new is not object.__new__:
return original_new(cls, *args, **kwargs)
else:
return original_new(cls)

__arg.__new__ = staticmethod(__new__)
__arg.__deprecated__ = __new__.__deprecated__ = __msg
return __arg
elif callable(__arg):
@functools.wraps(__arg)
def wrapper(*args, **kwargs):
warnings.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}"
)

return decorator

Expand Down

Back | FazBrowse Home | New Git URL