mirror of
https://github.com/python/cpython.git
synced 2024-12-12 03:04:15 +08:00
gh-90953: Emit deprecation warnings for ast
features deprecated in Python 3.8 (#104199)
`ast.Num`, `ast.Str`, `ast.Bytes`, `ast.Ellipsis` and `ast.NameConstant` now all emit deprecation warnings on import, access, instantation or `isinstance()` checks. Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
263abd333d
commit
376137f6ec
@ -844,6 +844,19 @@ Pending Removal in Python 3.14
|
||||
use :func:`importlib.util.find_spec` instead.
|
||||
(Contributed by Nikita Sobolev in :gh:`97850`.)
|
||||
|
||||
* The following :mod:`ast` features have been deprecated in documentation since
|
||||
Python 3.8, now cause a :exc:`DeprecationWarning` to be emitted at runtime
|
||||
when they are accessed or used, and will be removed in Python 3.14:
|
||||
|
||||
* :class:`!ast.Num`
|
||||
* :class:`!ast.Str`
|
||||
* :class:`!ast.Bytes`
|
||||
* :class:`!ast.NameConstant`
|
||||
* :class:`!ast.Ellipsis`
|
||||
|
||||
Use :class:`ast.Constant` instead.
|
||||
(Contributed by Serhiy Storchaka in :gh:`90953`.)
|
||||
|
||||
Pending Removal in Future Versions
|
||||
----------------------------------
|
||||
|
||||
|
82
Lib/ast.py
82
Lib/ast.py
@ -294,9 +294,7 @@ def get_docstring(node, clean=True):
|
||||
if not(node.body and isinstance(node.body[0], Expr)):
|
||||
return None
|
||||
node = node.body[0].value
|
||||
if isinstance(node, Str):
|
||||
text = node.s
|
||||
elif isinstance(node, Constant) and isinstance(node.value, str):
|
||||
if isinstance(node, Constant) and isinstance(node.value, str):
|
||||
text = node.value
|
||||
else:
|
||||
return None
|
||||
@ -499,20 +497,52 @@ class NodeTransformer(NodeVisitor):
|
||||
return node
|
||||
|
||||
|
||||
_DEPRECATED_VALUE_ALIAS_MESSAGE = (
|
||||
"{name} is deprecated and will be removed in Python {remove}; use value instead"
|
||||
)
|
||||
_DEPRECATED_CLASS_MESSAGE = (
|
||||
"{name} is deprecated and will be removed in Python {remove}; "
|
||||
"use ast.Constant instead"
|
||||
)
|
||||
|
||||
|
||||
# If the ast module is loaded more than once, only add deprecated methods once
|
||||
if not hasattr(Constant, 'n'):
|
||||
# The following code is for backward compatibility.
|
||||
# It will be removed in future.
|
||||
|
||||
def _getter(self):
|
||||
def _n_getter(self):
|
||||
"""Deprecated. Use value instead."""
|
||||
import warnings
|
||||
warnings._deprecated(
|
||||
"Attribute n", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
|
||||
)
|
||||
return self.value
|
||||
|
||||
def _setter(self, value):
|
||||
def _n_setter(self, value):
|
||||
import warnings
|
||||
warnings._deprecated(
|
||||
"Attribute n", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
|
||||
)
|
||||
self.value = value
|
||||
|
||||
Constant.n = property(_getter, _setter)
|
||||
Constant.s = property(_getter, _setter)
|
||||
def _s_getter(self):
|
||||
"""Deprecated. Use value instead."""
|
||||
import warnings
|
||||
warnings._deprecated(
|
||||
"Attribute s", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
|
||||
)
|
||||
return self.value
|
||||
|
||||
def _s_setter(self, value):
|
||||
import warnings
|
||||
warnings._deprecated(
|
||||
"Attribute s", message=_DEPRECATED_VALUE_ALIAS_MESSAGE, remove=(3, 14)
|
||||
)
|
||||
self.value = value
|
||||
|
||||
Constant.n = property(_n_getter, _n_setter)
|
||||
Constant.s = property(_s_getter, _s_setter)
|
||||
|
||||
class _ABC(type):
|
||||
|
||||
@ -520,6 +550,13 @@ class _ABC(type):
|
||||
cls.__doc__ = """Deprecated AST node class. Use ast.Constant instead"""
|
||||
|
||||
def __instancecheck__(cls, inst):
|
||||
if cls in _const_types:
|
||||
import warnings
|
||||
warnings._deprecated(
|
||||
f"ast.{cls.__qualname__}",
|
||||
message=_DEPRECATED_CLASS_MESSAGE,
|
||||
remove=(3, 14)
|
||||
)
|
||||
if not isinstance(inst, Constant):
|
||||
return False
|
||||
if cls in _const_types:
|
||||
@ -543,6 +580,10 @@ def _new(cls, *args, **kwargs):
|
||||
if pos < len(args):
|
||||
raise TypeError(f"{cls.__name__} got multiple values for argument {key!r}")
|
||||
if cls in _const_types:
|
||||
import warnings
|
||||
warnings._deprecated(
|
||||
f"ast.{cls.__qualname__}", message=_DEPRECATED_CLASS_MESSAGE, remove=(3, 14)
|
||||
)
|
||||
return Constant(*args, **kwargs)
|
||||
return Constant.__new__(cls, *args, **kwargs)
|
||||
|
||||
@ -565,10 +606,19 @@ class Ellipsis(Constant, metaclass=_ABC):
|
||||
_fields = ()
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if cls is Ellipsis:
|
||||
if cls is _ast_Ellipsis:
|
||||
import warnings
|
||||
warnings._deprecated(
|
||||
"ast.Ellipsis", message=_DEPRECATED_CLASS_MESSAGE, remove=(3, 14)
|
||||
)
|
||||
return Constant(..., *args, **kwargs)
|
||||
return Constant.__new__(cls, *args, **kwargs)
|
||||
|
||||
# Keep another reference to Ellipsis in the global namespace
|
||||
# so it can be referenced in Ellipsis.__new__
|
||||
# (The original "Ellipsis" name is removed from the global namespace later on)
|
||||
_ast_Ellipsis = Ellipsis
|
||||
|
||||
_const_types = {
|
||||
Num: (int, float, complex),
|
||||
Str: (str,),
|
||||
@ -1699,6 +1749,22 @@ def unparse(ast_obj):
|
||||
return unparser.visit(ast_obj)
|
||||
|
||||
|
||||
_deprecated_globals = {
|
||||
name: globals().pop(name)
|
||||
for name in ('Num', 'Str', 'Bytes', 'NameConstant', 'Ellipsis')
|
||||
}
|
||||
|
||||
def __getattr__(name):
|
||||
if name in _deprecated_globals:
|
||||
globals()[name] = value = _deprecated_globals[name]
|
||||
import warnings
|
||||
warnings._deprecated(
|
||||
f"ast.{name}", message=_DEPRECATED_CLASS_MESSAGE, remove=(3, 14)
|
||||
)
|
||||
return value
|
||||
raise AttributeError(f"module 'ast' has no attribute '{name}'")
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
|
@ -8,9 +8,11 @@ import types
|
||||
import unittest
|
||||
import warnings
|
||||
import weakref
|
||||
from functools import partial
|
||||
from textwrap import dedent
|
||||
|
||||
from test import support
|
||||
from test.support.import_helper import import_fresh_module
|
||||
from test.support import os_helper, script_helper
|
||||
from test.support.ast_helper import ASTTestMixin
|
||||
|
||||
@ -267,6 +269,7 @@ eval_tests = [
|
||||
# excepthandler, arguments, keywords, alias
|
||||
|
||||
class AST_Tests(unittest.TestCase):
|
||||
maxDiff = None
|
||||
|
||||
def _is_ast_node(self, name, node):
|
||||
if not isinstance(node, type):
|
||||
@ -435,16 +438,42 @@ class AST_Tests(unittest.TestCase):
|
||||
self.assertTrue(issubclass(ast.comprehension, ast.AST))
|
||||
self.assertTrue(issubclass(ast.Gt, ast.AST))
|
||||
|
||||
def test_import_deprecated(self):
|
||||
ast = import_fresh_module('ast')
|
||||
depr_regex = (
|
||||
r'ast\.{} is deprecated and will be removed in Python 3.14; '
|
||||
r'use ast\.Constant instead'
|
||||
)
|
||||
for name in 'Num', 'Str', 'Bytes', 'NameConstant', 'Ellipsis':
|
||||
with self.assertWarnsRegex(DeprecationWarning, depr_regex.format(name)):
|
||||
getattr(ast, name)
|
||||
|
||||
def test_field_attr_existence_deprecated(self):
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||
from ast import Num, Str, Bytes, NameConstant, Ellipsis
|
||||
|
||||
for name in ('Num', 'Str', 'Bytes', 'NameConstant', 'Ellipsis'):
|
||||
item = getattr(ast, name)
|
||||
if self._is_ast_node(name, item):
|
||||
with self.subTest(item):
|
||||
with self.assertWarns(DeprecationWarning):
|
||||
x = item()
|
||||
if isinstance(x, ast.AST):
|
||||
self.assertIs(type(x._fields), tuple)
|
||||
|
||||
def test_field_attr_existence(self):
|
||||
for name, item in ast.__dict__.items():
|
||||
# These emit DeprecationWarnings
|
||||
if name in {'Num', 'Str', 'Bytes', 'NameConstant', 'Ellipsis'}:
|
||||
continue
|
||||
# constructor has a different signature
|
||||
if name == 'Index':
|
||||
continue
|
||||
if self._is_ast_node(name, item):
|
||||
if name == 'Index':
|
||||
# Index(value) just returns value now.
|
||||
# The argument is required.
|
||||
continue
|
||||
x = item()
|
||||
if isinstance(x, ast.AST):
|
||||
self.assertEqual(type(x._fields), tuple)
|
||||
self.assertIs(type(x._fields), tuple)
|
||||
|
||||
def test_arguments(self):
|
||||
x = ast.arguments()
|
||||
@ -459,25 +488,108 @@ class AST_Tests(unittest.TestCase):
|
||||
self.assertEqual(x.args, 2)
|
||||
self.assertEqual(x.vararg, 3)
|
||||
|
||||
def test_field_attr_writable(self):
|
||||
x = ast.Num()
|
||||
def test_field_attr_writable_deprecated(self):
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||
x = ast.Num()
|
||||
# We can assign to _fields
|
||||
x._fields = 666
|
||||
self.assertEqual(x._fields, 666)
|
||||
|
||||
def test_field_attr_writable(self):
|
||||
x = ast.Constant()
|
||||
# We can assign to _fields
|
||||
x._fields = 666
|
||||
self.assertEqual(x._fields, 666)
|
||||
|
||||
def test_classattrs_deprecated(self):
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||
from ast import Num, Str, Bytes, NameConstant, Ellipsis
|
||||
|
||||
with warnings.catch_warnings(record=True) as wlog:
|
||||
warnings.filterwarnings('always', '', DeprecationWarning)
|
||||
x = ast.Num()
|
||||
self.assertEqual(x._fields, ('value', 'kind'))
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
x.value
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
x.n
|
||||
|
||||
x = ast.Num(42)
|
||||
self.assertEqual(x.value, 42)
|
||||
self.assertEqual(x.n, 42)
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
x.lineno
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
x.foobar
|
||||
|
||||
x = ast.Num(lineno=2)
|
||||
self.assertEqual(x.lineno, 2)
|
||||
|
||||
x = ast.Num(42, lineno=0)
|
||||
self.assertEqual(x.lineno, 0)
|
||||
self.assertEqual(x._fields, ('value', 'kind'))
|
||||
self.assertEqual(x.value, 42)
|
||||
self.assertEqual(x.n, 42)
|
||||
|
||||
self.assertRaises(TypeError, ast.Num, 1, None, 2)
|
||||
self.assertRaises(TypeError, ast.Num, 1, None, 2, lineno=0)
|
||||
|
||||
# Arbitrary keyword arguments are supported
|
||||
self.assertEqual(ast.Num(1, foo='bar').foo, 'bar')
|
||||
|
||||
with self.assertRaisesRegex(TypeError, "Num got multiple values for argument 'n'"):
|
||||
ast.Num(1, n=2)
|
||||
|
||||
self.assertEqual(ast.Num(42).n, 42)
|
||||
self.assertEqual(ast.Num(4.25).n, 4.25)
|
||||
self.assertEqual(ast.Num(4.25j).n, 4.25j)
|
||||
self.assertEqual(ast.Str('42').s, '42')
|
||||
self.assertEqual(ast.Bytes(b'42').s, b'42')
|
||||
self.assertIs(ast.NameConstant(True).value, True)
|
||||
self.assertIs(ast.NameConstant(False).value, False)
|
||||
self.assertIs(ast.NameConstant(None).value, None)
|
||||
|
||||
self.assertEqual([str(w.message) for w in wlog], [
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'ast.Str is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'Attribute s is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'ast.Bytes is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'Attribute s is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'ast.NameConstant is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.NameConstant is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.NameConstant is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
])
|
||||
|
||||
def test_classattrs(self):
|
||||
x = ast.Num()
|
||||
x = ast.Constant()
|
||||
self.assertEqual(x._fields, ('value', 'kind'))
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
x.value
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
x.n
|
||||
|
||||
x = ast.Num(42)
|
||||
x = ast.Constant(42)
|
||||
self.assertEqual(x.value, 42)
|
||||
self.assertEqual(x.n, 42)
|
||||
|
||||
with self.assertRaises(AttributeError):
|
||||
x.lineno
|
||||
@ -485,36 +597,23 @@ class AST_Tests(unittest.TestCase):
|
||||
with self.assertRaises(AttributeError):
|
||||
x.foobar
|
||||
|
||||
x = ast.Num(lineno=2)
|
||||
x = ast.Constant(lineno=2)
|
||||
self.assertEqual(x.lineno, 2)
|
||||
|
||||
x = ast.Num(42, lineno=0)
|
||||
x = ast.Constant(42, lineno=0)
|
||||
self.assertEqual(x.lineno, 0)
|
||||
self.assertEqual(x._fields, ('value', 'kind'))
|
||||
self.assertEqual(x.value, 42)
|
||||
self.assertEqual(x.n, 42)
|
||||
|
||||
self.assertRaises(TypeError, ast.Num, 1, None, 2)
|
||||
self.assertRaises(TypeError, ast.Num, 1, None, 2, lineno=0)
|
||||
self.assertRaises(TypeError, ast.Constant, 1, None, 2)
|
||||
self.assertRaises(TypeError, ast.Constant, 1, None, 2, lineno=0)
|
||||
|
||||
# Arbitrary keyword arguments are supported
|
||||
self.assertEqual(ast.Constant(1, foo='bar').foo, 'bar')
|
||||
self.assertEqual(ast.Num(1, foo='bar').foo, 'bar')
|
||||
|
||||
with self.assertRaisesRegex(TypeError, "Num got multiple values for argument 'n'"):
|
||||
ast.Num(1, n=2)
|
||||
with self.assertRaisesRegex(TypeError, "Constant got multiple values for argument 'value'"):
|
||||
ast.Constant(1, value=2)
|
||||
|
||||
self.assertEqual(ast.Num(42).n, 42)
|
||||
self.assertEqual(ast.Num(4.25).n, 4.25)
|
||||
self.assertEqual(ast.Num(4.25j).n, 4.25j)
|
||||
self.assertEqual(ast.Str('42').s, '42')
|
||||
self.assertEqual(ast.Bytes(b'42').s, b'42')
|
||||
self.assertIs(ast.NameConstant(True).value, True)
|
||||
self.assertIs(ast.NameConstant(False).value, False)
|
||||
self.assertIs(ast.NameConstant(None).value, None)
|
||||
|
||||
self.assertEqual(ast.Constant(42).value, 42)
|
||||
self.assertEqual(ast.Constant(4.25).value, 4.25)
|
||||
self.assertEqual(ast.Constant(4.25j).value, 4.25j)
|
||||
@ -526,85 +625,211 @@ class AST_Tests(unittest.TestCase):
|
||||
self.assertIs(ast.Constant(...).value, ...)
|
||||
|
||||
def test_realtype(self):
|
||||
self.assertEqual(type(ast.Num(42)), ast.Constant)
|
||||
self.assertEqual(type(ast.Num(4.25)), ast.Constant)
|
||||
self.assertEqual(type(ast.Num(4.25j)), ast.Constant)
|
||||
self.assertEqual(type(ast.Str('42')), ast.Constant)
|
||||
self.assertEqual(type(ast.Bytes(b'42')), ast.Constant)
|
||||
self.assertEqual(type(ast.NameConstant(True)), ast.Constant)
|
||||
self.assertEqual(type(ast.NameConstant(False)), ast.Constant)
|
||||
self.assertEqual(type(ast.NameConstant(None)), ast.Constant)
|
||||
self.assertEqual(type(ast.Ellipsis()), ast.Constant)
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||
from ast import Num, Str, Bytes, NameConstant, Ellipsis
|
||||
|
||||
with warnings.catch_warnings(record=True) as wlog:
|
||||
warnings.filterwarnings('always', '', DeprecationWarning)
|
||||
self.assertIs(type(ast.Num(42)), ast.Constant)
|
||||
self.assertIs(type(ast.Num(4.25)), ast.Constant)
|
||||
self.assertIs(type(ast.Num(4.25j)), ast.Constant)
|
||||
self.assertIs(type(ast.Str('42')), ast.Constant)
|
||||
self.assertIs(type(ast.Bytes(b'42')), ast.Constant)
|
||||
self.assertIs(type(ast.NameConstant(True)), ast.Constant)
|
||||
self.assertIs(type(ast.NameConstant(False)), ast.Constant)
|
||||
self.assertIs(type(ast.NameConstant(None)), ast.Constant)
|
||||
self.assertIs(type(ast.Ellipsis()), ast.Constant)
|
||||
|
||||
self.assertEqual([str(w.message) for w in wlog], [
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Str is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Bytes is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.NameConstant is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.NameConstant is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.NameConstant is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Ellipsis is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
])
|
||||
|
||||
def test_isinstance(self):
|
||||
self.assertTrue(isinstance(ast.Num(42), ast.Num))
|
||||
self.assertTrue(isinstance(ast.Num(4.2), ast.Num))
|
||||
self.assertTrue(isinstance(ast.Num(4.2j), ast.Num))
|
||||
self.assertTrue(isinstance(ast.Str('42'), ast.Str))
|
||||
self.assertTrue(isinstance(ast.Bytes(b'42'), ast.Bytes))
|
||||
self.assertTrue(isinstance(ast.NameConstant(True), ast.NameConstant))
|
||||
self.assertTrue(isinstance(ast.NameConstant(False), ast.NameConstant))
|
||||
self.assertTrue(isinstance(ast.NameConstant(None), ast.NameConstant))
|
||||
self.assertTrue(isinstance(ast.Ellipsis(), ast.Ellipsis))
|
||||
from ast import Constant
|
||||
|
||||
self.assertTrue(isinstance(ast.Constant(42), ast.Num))
|
||||
self.assertTrue(isinstance(ast.Constant(4.2), ast.Num))
|
||||
self.assertTrue(isinstance(ast.Constant(4.2j), ast.Num))
|
||||
self.assertTrue(isinstance(ast.Constant('42'), ast.Str))
|
||||
self.assertTrue(isinstance(ast.Constant(b'42'), ast.Bytes))
|
||||
self.assertTrue(isinstance(ast.Constant(True), ast.NameConstant))
|
||||
self.assertTrue(isinstance(ast.Constant(False), ast.NameConstant))
|
||||
self.assertTrue(isinstance(ast.Constant(None), ast.NameConstant))
|
||||
self.assertTrue(isinstance(ast.Constant(...), ast.Ellipsis))
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||
from ast import Num, Str, Bytes, NameConstant, Ellipsis
|
||||
|
||||
self.assertFalse(isinstance(ast.Str('42'), ast.Num))
|
||||
self.assertFalse(isinstance(ast.Num(42), ast.Str))
|
||||
self.assertFalse(isinstance(ast.Str('42'), ast.Bytes))
|
||||
self.assertFalse(isinstance(ast.Num(42), ast.NameConstant))
|
||||
self.assertFalse(isinstance(ast.Num(42), ast.Ellipsis))
|
||||
self.assertFalse(isinstance(ast.NameConstant(True), ast.Num))
|
||||
self.assertFalse(isinstance(ast.NameConstant(False), ast.Num))
|
||||
cls_depr_msg = (
|
||||
'ast.{} is deprecated and will be removed in Python 3.14; '
|
||||
'use ast.Constant instead'
|
||||
)
|
||||
|
||||
self.assertFalse(isinstance(ast.Constant('42'), ast.Num))
|
||||
self.assertFalse(isinstance(ast.Constant(42), ast.Str))
|
||||
self.assertFalse(isinstance(ast.Constant('42'), ast.Bytes))
|
||||
self.assertFalse(isinstance(ast.Constant(42), ast.NameConstant))
|
||||
self.assertFalse(isinstance(ast.Constant(42), ast.Ellipsis))
|
||||
self.assertFalse(isinstance(ast.Constant(True), ast.Num))
|
||||
self.assertFalse(isinstance(ast.Constant(False), ast.Num))
|
||||
assertNumDeprecated = partial(
|
||||
self.assertWarnsRegex, DeprecationWarning, cls_depr_msg.format("Num")
|
||||
)
|
||||
assertStrDeprecated = partial(
|
||||
self.assertWarnsRegex, DeprecationWarning, cls_depr_msg.format("Str")
|
||||
)
|
||||
assertBytesDeprecated = partial(
|
||||
self.assertWarnsRegex, DeprecationWarning, cls_depr_msg.format("Bytes")
|
||||
)
|
||||
assertNameConstantDeprecated = partial(
|
||||
self.assertWarnsRegex,
|
||||
DeprecationWarning,
|
||||
cls_depr_msg.format("NameConstant")
|
||||
)
|
||||
assertEllipsisDeprecated = partial(
|
||||
self.assertWarnsRegex, DeprecationWarning, cls_depr_msg.format("Ellipsis")
|
||||
)
|
||||
|
||||
self.assertFalse(isinstance(ast.Constant(), ast.Num))
|
||||
self.assertFalse(isinstance(ast.Constant(), ast.Str))
|
||||
self.assertFalse(isinstance(ast.Constant(), ast.Bytes))
|
||||
self.assertFalse(isinstance(ast.Constant(), ast.NameConstant))
|
||||
self.assertFalse(isinstance(ast.Constant(), ast.Ellipsis))
|
||||
for arg in 42, 4.2, 4.2j:
|
||||
with self.subTest(arg=arg):
|
||||
with assertNumDeprecated():
|
||||
n = Num(arg)
|
||||
with assertNumDeprecated():
|
||||
self.assertIsInstance(n, Num)
|
||||
|
||||
with assertStrDeprecated():
|
||||
s = Str('42')
|
||||
with assertStrDeprecated():
|
||||
self.assertIsInstance(s, Str)
|
||||
|
||||
with assertBytesDeprecated():
|
||||
b = Bytes(b'42')
|
||||
with assertBytesDeprecated():
|
||||
self.assertIsInstance(b, Bytes)
|
||||
|
||||
for arg in True, False, None:
|
||||
with self.subTest(arg=arg):
|
||||
with assertNameConstantDeprecated():
|
||||
n = NameConstant(arg)
|
||||
with assertNameConstantDeprecated():
|
||||
self.assertIsInstance(n, NameConstant)
|
||||
|
||||
with assertEllipsisDeprecated():
|
||||
e = Ellipsis()
|
||||
with assertEllipsisDeprecated():
|
||||
self.assertIsInstance(e, Ellipsis)
|
||||
|
||||
for arg in 42, 4.2, 4.2j:
|
||||
with self.subTest(arg=arg):
|
||||
with assertNumDeprecated():
|
||||
self.assertIsInstance(Constant(arg), Num)
|
||||
|
||||
with assertStrDeprecated():
|
||||
self.assertIsInstance(Constant('42'), Str)
|
||||
|
||||
with assertBytesDeprecated():
|
||||
self.assertIsInstance(Constant(b'42'), Bytes)
|
||||
|
||||
for arg in True, False, None:
|
||||
with self.subTest(arg=arg):
|
||||
with assertNameConstantDeprecated():
|
||||
self.assertIsInstance(Constant(arg), NameConstant)
|
||||
|
||||
with assertEllipsisDeprecated():
|
||||
self.assertIsInstance(Constant(...), Ellipsis)
|
||||
|
||||
with assertStrDeprecated():
|
||||
s = Str('42')
|
||||
assertNumDeprecated(self.assertNotIsInstance, s, Num)
|
||||
assertBytesDeprecated(self.assertNotIsInstance, s, Bytes)
|
||||
|
||||
with assertNumDeprecated():
|
||||
n = Num(42)
|
||||
assertStrDeprecated(self.assertNotIsInstance, n, Str)
|
||||
assertNameConstantDeprecated(self.assertNotIsInstance, n, NameConstant)
|
||||
assertEllipsisDeprecated(self.assertNotIsInstance, n, Ellipsis)
|
||||
|
||||
with assertNameConstantDeprecated():
|
||||
n = NameConstant(True)
|
||||
with assertNumDeprecated():
|
||||
self.assertNotIsInstance(n, Num)
|
||||
|
||||
with assertNameConstantDeprecated():
|
||||
n = NameConstant(False)
|
||||
with assertNumDeprecated():
|
||||
self.assertNotIsInstance(n, Num)
|
||||
|
||||
for arg in '42', True, False:
|
||||
with self.subTest(arg=arg):
|
||||
with assertNumDeprecated():
|
||||
self.assertNotIsInstance(Constant(arg), Num)
|
||||
|
||||
assertStrDeprecated(self.assertNotIsInstance, Constant(42), Str)
|
||||
assertBytesDeprecated(self.assertNotIsInstance, Constant('42'), Bytes)
|
||||
assertNameConstantDeprecated(self.assertNotIsInstance, Constant(42), NameConstant)
|
||||
assertEllipsisDeprecated(self.assertNotIsInstance, Constant(42), Ellipsis)
|
||||
assertNumDeprecated(self.assertNotIsInstance, Constant(), Num)
|
||||
assertStrDeprecated(self.assertNotIsInstance, Constant(), Str)
|
||||
assertBytesDeprecated(self.assertNotIsInstance, Constant(), Bytes)
|
||||
assertNameConstantDeprecated(self.assertNotIsInstance, Constant(), NameConstant)
|
||||
assertEllipsisDeprecated(self.assertNotIsInstance, Constant(), Ellipsis)
|
||||
|
||||
class S(str): pass
|
||||
self.assertTrue(isinstance(ast.Constant(S('42')), ast.Str))
|
||||
self.assertFalse(isinstance(ast.Constant(S('42')), ast.Num))
|
||||
with assertStrDeprecated():
|
||||
self.assertIsInstance(Constant(S('42')), Str)
|
||||
with assertNumDeprecated():
|
||||
self.assertNotIsInstance(Constant(S('42')), Num)
|
||||
|
||||
def test_subclasses(self):
|
||||
class N(ast.Num):
|
||||
def test_constant_subclasses_deprecated(self):
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||
from ast import Num
|
||||
|
||||
with warnings.catch_warnings(record=True) as wlog:
|
||||
warnings.filterwarnings('always', '', DeprecationWarning)
|
||||
class N(ast.Num):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.z = 'spam'
|
||||
class N2(ast.Num):
|
||||
pass
|
||||
|
||||
n = N(42)
|
||||
self.assertEqual(n.n, 42)
|
||||
self.assertEqual(n.z, 'spam')
|
||||
self.assertIs(type(n), N)
|
||||
self.assertIsInstance(n, N)
|
||||
self.assertIsInstance(n, ast.Num)
|
||||
self.assertNotIsInstance(n, N2)
|
||||
self.assertNotIsInstance(ast.Num(42), N)
|
||||
n = N(n=42)
|
||||
self.assertEqual(n.n, 42)
|
||||
self.assertIs(type(n), N)
|
||||
|
||||
self.assertEqual([str(w.message) for w in wlog], [
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
])
|
||||
|
||||
def test_constant_subclasses(self):
|
||||
class N(ast.Constant):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.z = 'spam'
|
||||
class N2(ast.Num):
|
||||
class N2(ast.Constant):
|
||||
pass
|
||||
|
||||
n = N(42)
|
||||
self.assertEqual(n.n, 42)
|
||||
self.assertEqual(n.value, 42)
|
||||
self.assertEqual(n.z, 'spam')
|
||||
self.assertEqual(type(n), N)
|
||||
self.assertTrue(isinstance(n, N))
|
||||
self.assertTrue(isinstance(n, ast.Num))
|
||||
self.assertTrue(isinstance(n, ast.Constant))
|
||||
self.assertFalse(isinstance(n, N2))
|
||||
self.assertFalse(isinstance(ast.Num(42), N))
|
||||
n = N(n=42)
|
||||
self.assertEqual(n.n, 42)
|
||||
self.assertFalse(isinstance(ast.Constant(42), N))
|
||||
n = N(value=42)
|
||||
self.assertEqual(n.value, 42)
|
||||
self.assertEqual(type(n), N)
|
||||
|
||||
def test_module(self):
|
||||
body = [ast.Num(42)]
|
||||
body = [ast.Constant(42)]
|
||||
x = ast.Module(body, [])
|
||||
self.assertEqual(x.body, body)
|
||||
|
||||
@ -617,8 +842,8 @@ class AST_Tests(unittest.TestCase):
|
||||
x.foobarbaz = 5
|
||||
self.assertEqual(x.foobarbaz, 5)
|
||||
|
||||
n1 = ast.Num(1)
|
||||
n3 = ast.Num(3)
|
||||
n1 = ast.Constant(1)
|
||||
n3 = ast.Constant(3)
|
||||
addop = ast.Add()
|
||||
x = ast.BinOp(n1, addop, n3)
|
||||
self.assertEqual(x.left, n1)
|
||||
@ -987,7 +1212,7 @@ Module(
|
||||
|
||||
def test_copy_location(self):
|
||||
src = ast.parse('1 + 1', mode='eval')
|
||||
src.body.right = ast.copy_location(ast.Num(2), src.body.right)
|
||||
src.body.right = ast.copy_location(ast.Constant(2), src.body.right)
|
||||
self.assertEqual(ast.dump(src, include_attributes=True),
|
||||
'Expression(body=BinOp(left=Constant(value=1, lineno=1, col_offset=0, '
|
||||
'end_lineno=1, end_col_offset=1), op=Add(), right=Constant(value=2, '
|
||||
@ -1004,7 +1229,7 @@ Module(
|
||||
def test_fix_missing_locations(self):
|
||||
src = ast.parse('write("spam")')
|
||||
src.body.append(ast.Expr(ast.Call(ast.Name('spam', ast.Load()),
|
||||
[ast.Str('eggs')], [])))
|
||||
[ast.Constant('eggs')], [])))
|
||||
self.assertEqual(src, ast.fix_missing_locations(src))
|
||||
self.maxDiff = None
|
||||
self.assertEqual(ast.dump(src, include_attributes=True),
|
||||
@ -1317,9 +1542,9 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
check(arguments(args=args), "must have Load context")
|
||||
check(arguments(posonlyargs=args), "must have Load context")
|
||||
check(arguments(kwonlyargs=args), "must have Load context")
|
||||
check(arguments(defaults=[ast.Num(3)]),
|
||||
check(arguments(defaults=[ast.Constant(3)]),
|
||||
"more positional defaults than args")
|
||||
check(arguments(kw_defaults=[ast.Num(4)]),
|
||||
check(arguments(kw_defaults=[ast.Constant(4)]),
|
||||
"length of kwonlyargs is not the same as kw_defaults")
|
||||
args = [ast.arg("x", ast.Name("x", ast.Load()))]
|
||||
check(arguments(args=args, defaults=[ast.Name("x", ast.Store())]),
|
||||
@ -1372,9 +1597,9 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
"must have Del context")
|
||||
|
||||
def test_assign(self):
|
||||
self.stmt(ast.Assign([], ast.Num(3)), "empty targets on Assign")
|
||||
self.stmt(ast.Assign([None], ast.Num(3)), "None disallowed")
|
||||
self.stmt(ast.Assign([ast.Name("x", ast.Load())], ast.Num(3)),
|
||||
self.stmt(ast.Assign([], ast.Constant(3)), "empty targets on Assign")
|
||||
self.stmt(ast.Assign([None], ast.Constant(3)), "None disallowed")
|
||||
self.stmt(ast.Assign([ast.Name("x", ast.Load())], ast.Constant(3)),
|
||||
"must have Store context")
|
||||
self.stmt(ast.Assign([ast.Name("x", ast.Store())],
|
||||
ast.Name("y", ast.Store())),
|
||||
@ -1402,39 +1627,39 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
self.stmt(ast.For(x, y, [p], [e]), "must have Load context")
|
||||
|
||||
def test_while(self):
|
||||
self.stmt(ast.While(ast.Num(3), [], []), "empty body on While")
|
||||
self.stmt(ast.While(ast.Constant(3), [], []), "empty body on While")
|
||||
self.stmt(ast.While(ast.Name("x", ast.Store()), [ast.Pass()], []),
|
||||
"must have Load context")
|
||||
self.stmt(ast.While(ast.Num(3), [ast.Pass()],
|
||||
self.stmt(ast.While(ast.Constant(3), [ast.Pass()],
|
||||
[ast.Expr(ast.Name("x", ast.Store()))]),
|
||||
"must have Load context")
|
||||
|
||||
def test_if(self):
|
||||
self.stmt(ast.If(ast.Num(3), [], []), "empty body on If")
|
||||
self.stmt(ast.If(ast.Constant(3), [], []), "empty body on If")
|
||||
i = ast.If(ast.Name("x", ast.Store()), [ast.Pass()], [])
|
||||
self.stmt(i, "must have Load context")
|
||||
i = ast.If(ast.Num(3), [ast.Expr(ast.Name("x", ast.Store()))], [])
|
||||
i = ast.If(ast.Constant(3), [ast.Expr(ast.Name("x", ast.Store()))], [])
|
||||
self.stmt(i, "must have Load context")
|
||||
i = ast.If(ast.Num(3), [ast.Pass()],
|
||||
i = ast.If(ast.Constant(3), [ast.Pass()],
|
||||
[ast.Expr(ast.Name("x", ast.Store()))])
|
||||
self.stmt(i, "must have Load context")
|
||||
|
||||
def test_with(self):
|
||||
p = ast.Pass()
|
||||
self.stmt(ast.With([], [p]), "empty items on With")
|
||||
i = ast.withitem(ast.Num(3), None)
|
||||
i = ast.withitem(ast.Constant(3), None)
|
||||
self.stmt(ast.With([i], []), "empty body on With")
|
||||
i = ast.withitem(ast.Name("x", ast.Store()), None)
|
||||
self.stmt(ast.With([i], [p]), "must have Load context")
|
||||
i = ast.withitem(ast.Num(3), ast.Name("x", ast.Load()))
|
||||
i = ast.withitem(ast.Constant(3), ast.Name("x", ast.Load()))
|
||||
self.stmt(ast.With([i], [p]), "must have Store context")
|
||||
|
||||
def test_raise(self):
|
||||
r = ast.Raise(None, ast.Num(3))
|
||||
r = ast.Raise(None, ast.Constant(3))
|
||||
self.stmt(r, "Raise with cause but no exception")
|
||||
r = ast.Raise(ast.Name("x", ast.Store()), None)
|
||||
self.stmt(r, "must have Load context")
|
||||
r = ast.Raise(ast.Num(4), ast.Name("x", ast.Store()))
|
||||
r = ast.Raise(ast.Constant(4), ast.Name("x", ast.Store()))
|
||||
self.stmt(r, "must have Load context")
|
||||
|
||||
def test_try(self):
|
||||
@ -1505,11 +1730,11 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
def test_boolop(self):
|
||||
b = ast.BoolOp(ast.And(), [])
|
||||
self.expr(b, "less than 2 values")
|
||||
b = ast.BoolOp(ast.And(), [ast.Num(3)])
|
||||
b = ast.BoolOp(ast.And(), [ast.Constant(3)])
|
||||
self.expr(b, "less than 2 values")
|
||||
b = ast.BoolOp(ast.And(), [ast.Num(4), None])
|
||||
b = ast.BoolOp(ast.And(), [ast.Constant(4), None])
|
||||
self.expr(b, "None disallowed")
|
||||
b = ast.BoolOp(ast.And(), [ast.Num(4), ast.Name("x", ast.Store())])
|
||||
b = ast.BoolOp(ast.And(), [ast.Constant(4), ast.Name("x", ast.Store())])
|
||||
self.expr(b, "must have Load context")
|
||||
|
||||
def test_unaryop(self):
|
||||
@ -1597,11 +1822,11 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
left = ast.Name("x", ast.Load())
|
||||
comp = ast.Compare(left, [ast.In()], [])
|
||||
self.expr(comp, "no comparators")
|
||||
comp = ast.Compare(left, [ast.In()], [ast.Num(4), ast.Num(5)])
|
||||
comp = ast.Compare(left, [ast.In()], [ast.Constant(4), ast.Constant(5)])
|
||||
self.expr(comp, "different number of comparators and operands")
|
||||
comp = ast.Compare(ast.Num("blah"), [ast.In()], [left])
|
||||
comp = ast.Compare(ast.Constant("blah"), [ast.In()], [left])
|
||||
self.expr(comp)
|
||||
comp = ast.Compare(left, [ast.In()], [ast.Num("blah")])
|
||||
comp = ast.Compare(left, [ast.In()], [ast.Constant("blah")])
|
||||
self.expr(comp)
|
||||
|
||||
def test_call(self):
|
||||
@ -1617,23 +1842,37 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
self.expr(call, "must have Load context")
|
||||
|
||||
def test_num(self):
|
||||
class subint(int):
|
||||
pass
|
||||
class subfloat(float):
|
||||
pass
|
||||
class subcomplex(complex):
|
||||
pass
|
||||
for obj in "0", "hello":
|
||||
self.expr(ast.Num(obj))
|
||||
for obj in subint(), subfloat(), subcomplex():
|
||||
self.expr(ast.Num(obj), "invalid type", exc=TypeError)
|
||||
with warnings.catch_warnings(record=True) as wlog:
|
||||
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||
from ast import Num
|
||||
|
||||
with warnings.catch_warnings(record=True) as wlog:
|
||||
warnings.filterwarnings('always', '', DeprecationWarning)
|
||||
class subint(int):
|
||||
pass
|
||||
class subfloat(float):
|
||||
pass
|
||||
class subcomplex(complex):
|
||||
pass
|
||||
for obj in "0", "hello":
|
||||
self.expr(ast.Num(obj))
|
||||
for obj in subint(), subfloat(), subcomplex():
|
||||
self.expr(ast.Num(obj), "invalid type", exc=TypeError)
|
||||
|
||||
self.assertEqual([str(w.message) for w in wlog], [
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
'ast.Num is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
])
|
||||
|
||||
def test_attribute(self):
|
||||
attr = ast.Attribute(ast.Name("x", ast.Store()), "y", ast.Load())
|
||||
self.expr(attr, "must have Load context")
|
||||
|
||||
def test_subscript(self):
|
||||
sub = ast.Subscript(ast.Name("x", ast.Store()), ast.Num(3),
|
||||
sub = ast.Subscript(ast.Name("x", ast.Store()), ast.Constant(3),
|
||||
ast.Load())
|
||||
self.expr(sub, "must have Load context")
|
||||
x = ast.Name("x", ast.Load())
|
||||
@ -1653,7 +1892,7 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
def test_starred(self):
|
||||
left = ast.List([ast.Starred(ast.Name("x", ast.Load()), ast.Store())],
|
||||
ast.Store())
|
||||
assign = ast.Assign([left], ast.Num(4))
|
||||
assign = ast.Assign([left], ast.Constant(4))
|
||||
self.stmt(assign, "must have Store context")
|
||||
|
||||
def _sequence(self, fac):
|
||||
@ -1668,7 +1907,17 @@ class ASTValidatorTests(unittest.TestCase):
|
||||
self._sequence(ast.Tuple)
|
||||
|
||||
def test_nameconstant(self):
|
||||
self.expr(ast.NameConstant(4))
|
||||
with warnings.catch_warnings(record=True) as wlog:
|
||||
warnings.filterwarnings('ignore', '', DeprecationWarning)
|
||||
from ast import NameConstant
|
||||
|
||||
with warnings.catch_warnings(record=True) as wlog:
|
||||
warnings.filterwarnings('always', '', DeprecationWarning)
|
||||
self.expr(ast.NameConstant(4))
|
||||
|
||||
self.assertEqual([str(w.message) for w in wlog], [
|
||||
'ast.NameConstant is deprecated and will be removed in Python 3.14; use ast.Constant instead',
|
||||
])
|
||||
|
||||
def test_stdlib_validates(self):
|
||||
stdlib = os.path.dirname(ast.__file__)
|
||||
@ -2357,10 +2606,15 @@ class BaseNodeVisitorCases:
|
||||
])
|
||||
self.assertEqual([str(w.message) for w in wlog], [
|
||||
'visit_Num is deprecated; add visit_Constant',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'visit_Num is deprecated; add visit_Constant',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'visit_Num is deprecated; add visit_Constant',
|
||||
'Attribute n is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'visit_Str is deprecated; add visit_Constant',
|
||||
'Attribute s is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'visit_Bytes is deprecated; add visit_Constant',
|
||||
'Attribute s is deprecated and will be removed in Python 3.14; use value instead',
|
||||
'visit_NameConstant is deprecated; add visit_Constant',
|
||||
'visit_NameConstant is deprecated; add visit_Constant',
|
||||
'visit_Ellipsis is deprecated; add visit_Constant',
|
||||
|
@ -0,0 +1,4 @@
|
||||
Deprecation warnings are now emitted for :class:`!ast.Num`,
|
||||
:class:`!ast.Bytes`, :class:`!ast.Str`, :class:`!ast.NameConstant` and
|
||||
:class:`!ast.Ellipsis`. These have been documented as deprecated since Python
|
||||
3.8, and will be removed in Python 3.14.
|
Loading…
Reference in New Issue
Block a user