Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bpo-32892: Use ast.Constant instead of specific constant AST types. #9445

Merged
merged 8 commits into from
Sep 27, 2018
Merged
8 changes: 4 additions & 4 deletions Doc/library/ast.rst
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,16 @@ Node classes

node = ast.UnaryOp()
node.op = ast.USub()
node.operand = ast.Num()
node.operand.n = 5
node.operand = ast.Constant()
node.operand.value = 5
node.operand.lineno = 0
node.operand.col_offset = 0
node.lineno = 0
node.col_offset = 0

or the more compact ::

node = ast.UnaryOp(ast.USub(), ast.Num(5, lineno=0, col_offset=0),
node = ast.UnaryOp(ast.USub(), ast.Constant(5, lineno=0, col_offset=0),
lineno=0, col_offset=0)


Expand Down Expand Up @@ -239,7 +239,7 @@ and classes for traversing abstract syntax trees:
def visit_Name(self, node):
return copy_location(Subscript(
value=Name(id='data', ctx=Load()),
slice=Index(value=Str(s=node.id)),
slice=Index(value=Constant(value=node.id)),
ctx=node.ctx
), node)

Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.8.rst
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,11 @@ Deprecated

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

* :mod:`ast` classes ``Num``, ``Str``, ``Bytes``, ``NameConstant`` and
``Ellipsis`` are considered deprecated and will be removed in future Python
versions. :class:`~ast.Constant` should be used instead.
(Contributed by Serhiy Storchaka in :issue:`32892`.)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we mention the deprecation in ast.rst as well? Maybe near:
https://docs.python.org/dev/library/ast.html#abstract-grammar

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I didn't know good place for this, you have suggested me one.



Removed
=======
Expand Down
36 changes: 4 additions & 32 deletions Include/Python-ast.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

84 changes: 75 additions & 9 deletions Lib/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,8 @@ def literal_eval(node_or_string):
node_or_string = node_or_string.body
def _convert_num(node):
if isinstance(node, Constant):
if isinstance(node.value, (int, float, complex)):
if type(node.value) in (int, float, complex):
return node.value
elif isinstance(node, Num):
return node.n
raise ValueError('malformed node or string: ' + repr(node))
def _convert_signed_num(node):
if isinstance(node, UnaryOp) and isinstance(node.op, (UAdd, USub)):
Expand All @@ -64,10 +62,6 @@ def _convert_signed_num(node):
def _convert(node):
if isinstance(node, Constant):
return node.value
elif isinstance(node, (Str, Bytes)):
return node.s
elif isinstance(node, Num):
return node.n
elif isinstance(node, Tuple):
return tuple(map(_convert, node.elts))
elif isinstance(node, List):
Expand All @@ -77,8 +71,6 @@ def _convert(node):
elif isinstance(node, Dict):
return dict(zip(map(_convert, node.keys),
map(_convert, node.values)))
elif isinstance(node, NameConstant):
return node.value
elif isinstance(node, BinOp) and isinstance(node.op, (Add, Sub)):
left = _convert_signed_num(node.left)
right = _convert_num(node.right)
Expand Down Expand Up @@ -329,3 +321,77 @@ def generic_visit(self, node):
else:
setattr(node, field, new_node)
return node


def _getter(self):
return self.value

def _setter(self, value):
self.value = value

Constant.n = property(_getter, _setter)
Constant.s = property(_getter, _setter)

_unspecified = object()


class Num(Constant):
_fields = ('n',)

def __new__(cls, s=_unspecified, **kwargs):
if s is _unspecified:
return Constant(**kwargs)
return Constant(value=s, **kwargs)

@staticmethod
def __instancecheck__(instance):
return (isinstance(instance, Constant) and
type(instance.value) in (int, float, complex))


class Str(Constant):
_fields = ('s',)

def __new__(cls, s=_unspecified, **kwargs):
if s is _unspecified:
return Constant(**kwargs)
return Constant(value=s, **kwargs)

@staticmethod
def __instancecheck__(instance):
return isinstance(instance, Constant) and type(instance.value) is str


class Bytes(Constant):
_fields = ('s',)

def __new__(cls, s=_unspecified, **kwargs):
if s is _unspecified:
return Constant(**kwargs)
return Constant(value=s, **kwargs)

@staticmethod
def __instancecheck__(instance):
return isinstance(instance, Constant) and type(instance.value) is bytes


class NameConstant(Constant):
def __new__(cls, *args, **kwargs):
return Constant(*args, **kwargs)

@staticmethod
def __instancecheck__(instance):
return (isinstance(instance, Constant) and
(instance.value is False or instance.value is True or
instance.value is None))


class Ellipsis(Constant):
_fields = ()

def __new__(cls, **kwargs):
return Constant(..., **kwargs)

@staticmethod
def __instancecheck__(instance):
return isinstance(instance, Constant) and instance.value is ...
10 changes: 2 additions & 8 deletions Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -2005,14 +2005,8 @@ def wrap_value(s):
except NameError:
raise RuntimeError()

if isinstance(value, str):
return ast.Str(value)
if isinstance(value, (int, float)):
return ast.Num(value)
if isinstance(value, bytes):
return ast.Bytes(value)
if value in (True, False, None):
return ast.NameConstant(value)
if isinstance(value, (str, int, float, bytes, bool, type(None))):
return ast.Constant(value)
raise RuntimeError()

class RewriteSymbolics(ast.NodeTransformer):
Expand Down
Loading