mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Update warnings from 3.13.5 (#6019)
This commit is contained in:
1 parent
ba22ad2c0c
commit
9995cc60b5
6 files changed
+2197
-28
No files matched your search
Vendored
+1983
File diff suppressed because it is too large.
Load diff
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
import unittest
|
||||
|
||||
unittest.main('test.test_warnings')
|
||||
@@ -0,0 +1,3 @@
|
||||
import warnings
|
||||
|
||||
warnings.warn('module-level warning', DeprecationWarning, stacklevel=2)
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
# helper to the helper for testing skip_file_prefixes.
|
||||
|
||||
import os
|
||||
|
||||
package_path = os.path.dirname(__file__)
|
||||
|
||||
def inner_api(message, *, stacklevel, warnings_module):
|
||||
warnings_module.warn(
|
||||
message, stacklevel=stacklevel,
|
||||
skip_file_prefixes=(package_path,))
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Helper module for testing stacklevel and skip_file_prefixes arguments
|
||||
# of warnings.warn()
|
||||
|
||||
import warnings
|
||||
from test.test_warnings.data import package_helper
|
||||
|
||||
def outer(message, stacklevel=1):
|
||||
inner(message, stacklevel)
|
||||
|
||||
def inner(message, stacklevel=1):
|
||||
warnings.warn(message, stacklevel=stacklevel)
|
||||
|
||||
def package(message, *, stacklevel):
|
||||
package_helper.inner_api(message, stacklevel=stacklevel,
|
||||
warnings_module=warnings)
|
||||
Vendored
+183
-28
@@ -5,7 +5,7 @@ import sys
|
||||
|
||||
__all__ = ["warn", "warn_explicit", "showwarning",
|
||||
"formatwarning", "filterwarnings", "simplefilter",
|
||||
"resetwarnings", "catch_warnings"]
|
||||
"resetwarnings", "catch_warnings", "deprecated"]
|
||||
|
||||
def showwarning(message, category, filename, lineno, file=None, line=None):
|
||||
"""Hook to write a warning to a file; replace if you like."""
|
||||
@@ -58,15 +58,16 @@ def _formatwarnmsg_impl(msg):
|
||||
# catch Exception, not only ImportError and RecursionError.
|
||||
except Exception:
|
||||
# don't suggest to enable tracemalloc if it's not available
|
||||
tracing = True
|
||||
suggest_tracemalloc = False
|
||||
tb = None
|
||||
else:
|
||||
tracing = tracemalloc.is_tracing()
|
||||
try:
|
||||
suggest_tracemalloc = not tracemalloc.is_tracing()
|
||||
tb = tracemalloc.get_object_traceback(msg.source)
|
||||
except Exception:
|
||||
# When a warning is logged during Python shutdown, tracemalloc
|
||||
# and the import machinery don't work anymore
|
||||
suggest_tracemalloc = False
|
||||
tb = None
|
||||
|
||||
if tb is not None:
|
||||
@@ -85,7 +86,7 @@ def _formatwarnmsg_impl(msg):
|
||||
if line:
|
||||
line = line.strip()
|
||||
s += ' %s\n' % line
|
||||
elif not tracing:
|
||||
elif suggest_tracemalloc:
|
||||
s += (f'{category}: Enable tracemalloc to get the object '
|
||||
f'allocation traceback\n')
|
||||
return s
|
||||
@@ -139,14 +140,18 @@ def filterwarnings(action, message="", category=Warning, module="", lineno=0,
|
||||
'lineno' -- an integer line number, 0 matches all warnings
|
||||
'append' -- if true, append to the list of filters
|
||||
"""
|
||||
assert action in ("error", "ignore", "always", "default", "module",
|
||||
"once"), "invalid action: %r" % (action,)
|
||||
assert isinstance(message, str), "message must be a string"
|
||||
assert isinstance(category, type), "category must be a class"
|
||||
assert issubclass(category, Warning), "category must be a Warning subclass"
|
||||
assert isinstance(module, str), "module must be a string"
|
||||
assert isinstance(lineno, int) and lineno >= 0, \
|
||||
"lineno must be an int >= 0"
|
||||
if action not in {"error", "ignore", "always", "default", "module", "once"}:
|
||||
raise ValueError(f"invalid action: {action!r}")
|
||||
if not isinstance(message, str):
|
||||
raise TypeError("message must be a string")
|
||||
if not isinstance(category, type) or not issubclass(category, Warning):
|
||||
raise TypeError("category must be a Warning subclass")
|
||||
if not isinstance(module, str):
|
||||
raise TypeError("module must be a string")
|
||||
if not isinstance(lineno, int):
|
||||
raise TypeError("lineno must be an int")
|
||||
if lineno < 0:
|
||||
raise ValueError("lineno must be an int >= 0")
|
||||
|
||||
if message or module:
|
||||
import re
|
||||
@@ -172,10 +177,12 @@ def simplefilter(action, category=Warning, lineno=0, append=False):
|
||||
'lineno' -- an integer line number, 0 matches all warnings
|
||||
'append' -- if true, append to the list of filters
|
||||
"""
|
||||
assert action in ("error", "ignore", "always", "default", "module",
|
||||
"once"), "invalid action: %r" % (action,)
|
||||
assert isinstance(lineno, int) and lineno >= 0, \
|
||||
"lineno must be an int >= 0"
|
||||
if action not in {"error", "ignore", "always", "default", "module", "once"}:
|
||||
raise ValueError(f"invalid action: {action!r}")
|
||||
if not isinstance(lineno, int):
|
||||
raise TypeError("lineno must be an int")
|
||||
if lineno < 0:
|
||||
raise ValueError("lineno must be an int >= 0")
|
||||
_add_filter(action, None, category, None, lineno, append=append)
|
||||
|
||||
def _add_filter(*item, append):
|
||||
@@ -269,22 +276,32 @@ def _getcategory(category):
|
||||
return cat
|
||||
|
||||
|
||||
def _is_internal_frame(frame):
|
||||
"""Signal whether the frame is an internal CPython implementation detail."""
|
||||
filename = frame.f_code.co_filename
|
||||
def _is_internal_filename(filename):
|
||||
return 'importlib' in filename and '_bootstrap' in filename
|
||||
|
||||
|
||||
def _next_external_frame(frame):
|
||||
"""Find the next frame that doesn't involve CPython internals."""
|
||||
def _is_filename_to_skip(filename, skip_file_prefixes):
|
||||
return any(filename.startswith(prefix) for prefix in skip_file_prefixes)
|
||||
|
||||
|
||||
def _is_internal_frame(frame):
|
||||
"""Signal whether the frame is an internal CPython implementation detail."""
|
||||
return _is_internal_filename(frame.f_code.co_filename)
|
||||
|
||||
|
||||
def _next_external_frame(frame, skip_file_prefixes):
|
||||
"""Find the next frame that doesn't involve Python or user internals."""
|
||||
frame = frame.f_back
|
||||
while frame is not None and _is_internal_frame(frame):
|
||||
while frame is not None and (
|
||||
_is_internal_filename(filename := frame.f_code.co_filename) or
|
||||
_is_filename_to_skip(filename, skip_file_prefixes)):
|
||||
frame = frame.f_back
|
||||
return frame
|
||||
|
||||
|
||||
# Code typically replaced by _warnings
|
||||
def warn(message, category=None, stacklevel=1, source=None):
|
||||
def warn(message, category=None, stacklevel=1, source=None,
|
||||
*, skip_file_prefixes=()):
|
||||
"""Issue a warning, or maybe ignore it or raise an exception."""
|
||||
# Check if message is already a Warning object
|
||||
if isinstance(message, Warning):
|
||||
@@ -295,6 +312,11 @@ def warn(message, category=None, stacklevel=1, source=None):
|
||||
if not (isinstance(category, type) and issubclass(category, Warning)):
|
||||
raise TypeError("category must be a Warning subclass, "
|
||||
"not '{:s}'".format(type(category).__name__))
|
||||
if not isinstance(skip_file_prefixes, tuple):
|
||||
# The C version demands a tuple for implementation performance.
|
||||
raise TypeError('skip_file_prefixes must be a tuple of strs.')
|
||||
if skip_file_prefixes:
|
||||
stacklevel = max(2, stacklevel)
|
||||
# Get context information
|
||||
try:
|
||||
if stacklevel <= 1 or _is_internal_frame(sys._getframe(1)):
|
||||
@@ -305,13 +327,13 @@ def warn(message, category=None, stacklevel=1, source=None):
|
||||
frame = sys._getframe(1)
|
||||
# Look for one frame less since the above line starts us off.
|
||||
for x in range(stacklevel-1):
|
||||
frame = _next_external_frame(frame)
|
||||
frame = _next_external_frame(frame, skip_file_prefixes)
|
||||
if frame is None:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
globals = sys.__dict__
|
||||
filename = "sys"
|
||||
lineno = 1
|
||||
filename = "<sys>"
|
||||
lineno = 0
|
||||
else:
|
||||
globals = frame.f_globals
|
||||
filename = frame.f_code.co_filename
|
||||
@@ -391,7 +413,7 @@ def warn_explicit(message, category, filename, lineno,
|
||||
"Unrecognized action (%r) in warnings.filters:\n %s" %
|
||||
(action, item))
|
||||
# Print message and context
|
||||
msg = WarningMessage(message, category, filename, lineno, source)
|
||||
msg = WarningMessage(message, category, filename, lineno, source=source)
|
||||
_showwarnmsg(msg)
|
||||
|
||||
|
||||
@@ -493,6 +515,139 @@ class catch_warnings(object):
|
||||
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
|
||||
import inspect
|
||||
|
||||
@functools.wraps(arg)
|
||||
def wrapper(*args, **kwargs):
|
||||
warn(msg, category=category, stacklevel=stacklevel + 1)
|
||||
return arg(*args, **kwargs)
|
||||
|
||||
if inspect.iscoroutinefunction(arg):
|
||||
wrapper = inspect.markcoroutinefunction(wrapper)
|
||||
|
||||
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}"
|
||||
|
||||
def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_info):
|
||||
@@ -502,7 +657,7 @@ def _deprecated(name, message=_DEPRECATED_MSG, *, remove, _version=sys.version_i
|
||||
the current Python version or the same version but past the alpha.
|
||||
|
||||
The *message* argument is formatted with *name* and *remove* as a Python
|
||||
version (e.g. "3.11").
|
||||
version tuple (e.g. (3, 11)).
|
||||
|
||||
"""
|
||||
remove_formatted = f"{remove[0]}.{remove[1]}"
|
||||
|
||||
Reference in new issue
Block a user