Skip to content

Commit 3f22811

Browse files
bpo-32892: Use ast.Constant instead of specific constant AST types. (GH-9445)
1 parent a94ee12 commit 3f22811

File tree

20 files changed

+337
-678
lines changed

20 files changed

+337
-678
lines changed

Doc/library/ast.rst

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,25 @@ Node classes
7878

7979
node = ast.UnaryOp()
8080
node.op = ast.USub()
81-
node.operand = ast.Num()
82-
node.operand.n = 5
81+
node.operand = ast.Constant()
82+
node.operand.value = 5
8383
node.operand.lineno = 0
8484
node.operand.col_offset = 0
8585
node.lineno = 0
8686
node.col_offset = 0
8787

8888
or the more compact ::
8989

90-
node = ast.UnaryOp(ast.USub(), ast.Num(5, lineno=0, col_offset=0),
90+
node = ast.UnaryOp(ast.USub(), ast.Constant(5, lineno=0, col_offset=0),
9191
lineno=0, col_offset=0)
9292

93+
.. deprecated:: 3.8
94+
95+
Class :class:`ast.Constant` is now used for all constants. Old classes
96+
:class:`ast.Num`, :class:`ast.Str`, :class:`ast.Bytes`,
97+
:class:`ast.NameConstant` and :class:`ast.Ellipsis` are still available,
98+
but they will be removed in future Python releases.
99+
93100

94101
.. _abstract-grammar:
95102

@@ -239,7 +246,7 @@ and classes for traversing abstract syntax trees:
239246
def visit_Name(self, node):
240247
return copy_location(Subscript(
241248
value=Name(id='data', ctx=Load()),
242-
slice=Index(value=Str(s=node.id)),
249+
slice=Index(value=Constant(value=node.id)),
243250
ctx=node.ctx
244251
), node)
245252

Doc/whatsnew/3.8.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,11 @@ Deprecated
262262

263263
(Contributed by Berker Peksag in :issue:`9372`.)
264264

265+
* :mod:`ast` classes ``Num``, ``Str``, ``Bytes``, ``NameConstant`` and
266+
``Ellipsis`` are considered deprecated and will be removed in future Python
267+
versions. :class:`~ast.Constant` should be used instead.
268+
(Contributed by Serhiy Storchaka in :issue:`32892`.)
269+
265270

266271
Removed
267272
=======

Include/Python-ast.h

Lines changed: 4 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/ast.py

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,10 +48,8 @@ def literal_eval(node_or_string):
4848
node_or_string = node_or_string.body
4949
def _convert_num(node):
5050
if isinstance(node, Constant):
51-
if isinstance(node.value, (int, float, complex)):
51+
if type(node.value) in (int, float, complex):
5252
return node.value
53-
elif isinstance(node, Num):
54-
return node.n
5553
raise ValueError('malformed node or string: ' + repr(node))
5654
def _convert_signed_num(node):
5755
if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
@@ -64,10 +62,6 @@ def _convert_signed_num(node):
6462
def _convert(node):
6563
if isinstance(node, Constant):
6664
return node.value
67-
elif isinstance(node, (Str, Bytes)):
68-
return node.s
69-
elif isinstance(node, Num):
70-
return node.n
7165
elif isinstance(node, Tuple):
7266
return tuple(map(_convert, node.elts))
7367
elif isinstance(node, List):
@@ -77,8 +71,6 @@ def _convert(node):
7771
elif isinstance(node, Dict):
7872
return dict(zip(map(_convert, node.keys),
7973
map(_convert, node.values)))
80-
elif isinstance(node, NameConstant):
81-
return node.value
8274
elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
8375
left = _convert_signed_num(node.left)
8476
right = _convert_num(node.right)
@@ -329,3 +321,66 @@ def generic_visit(self, node):
329321
else:
330322
setattr(node, field, new_node)
331323
return node
324+
325+
326+
# The following code is for backward compatibility.
327+
# It will be removed in future.
328+
329+
def _getter(self):
330+
return self.value
331+
332+
def _setter(self, value):
333+
self.value = value
334+
335+
Constant.n = property(_getter, _setter)
336+
Constant.s = property(_getter, _setter)
337+
338+
class _ABC(type):
339+
340+
def __instancecheck__(cls, inst):
341+
if not isinstance(inst, Constant):
342+
return False
343+
if cls in _const_types:
344+
try:
345+
value = inst.value
346+
except AttributeError:
347+
return False
348+
else:
349+
return type(value) in _const_types[cls]
350+
return type.__instancecheck__(cls, inst)
351+
352+
def _new(cls, *args, **kwargs):
353+
if cls in _const_types:
354+
return Constant(*args, **kwargs)
355+
return Constant.__new__(cls, *args, **kwargs)
356+
357+
class Num(Constant, metaclass=_ABC):
358+
_fields = ('n',)
359+
__new__ = _new
360+
361+
class Str(Constant, metaclass=_ABC):
362+
_fields = ('s',)
363+
__new__ = _new
364+
365+
class Bytes(Constant, metaclass=_ABC):
366+
_fields = ('s',)
367+
__new__ = _new
368+
369+
class NameConstant(Constant, metaclass=_ABC):
370+
__new__ = _new
371+
372+
class Ellipsis(Constant, metaclass=_ABC):
373+
_fields = ()
374+
375+
def __new__(cls, *args, **kwargs):
376+
if cls is Ellipsis:
377+
return Constant(..., *args, **kwargs)
378+
return Constant.__new__(cls, *args, **kwargs)
379+
380+
_const_types = {
381+
Num: (int, float, complex),
382+
Str: (str,),
383+
Bytes: (bytes,),
384+
NameConstant: (type(None), bool),
385+
Ellipsis: (type(...),),
386+
}

Lib/inspect.py

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,14 +2005,8 @@ def wrap_value(s):
20052005
except NameError:
20062006
raise RuntimeError()
20072007

2008-
if isinstance(value, str):
2009-
return ast.Str(value)
2010-
if isinstance(value, (int, float)):
2011-
return ast.Num(value)
2012-
if isinstance(value, bytes):
2013-
return ast.Bytes(value)
2014-
if value in (True, False, None):
2015-
return ast.NameConstant(value)
2008+
if isinstance(value, (str, int, float, bytes, bool, type(None))):
2009+
return ast.Constant(value)
20162010
raise RuntimeError()
20172011

20182012
class RewriteSymbolics(ast.NodeTransformer):

0 commit comments

Comments
 (0)