unparser: Don't omit parenthesis when unparsing a slice

Backport of
  c102a14825

Includes support for Python 2.
This commit is contained in:
Todd Gamblin 2021-12-23 08:35:25 -08:00 committed by Greg Becker
parent 0776c3b4d6
commit 39afe946a0

View file

@ -65,6 +65,22 @@ def interleave(inter, f, seq):
f(x)
def is_simple_tuple(slice_value):
# when unparsing a non-empty tuple, the parantheses can be safely
# omitted if there aren't any elements that explicitly requires
# parantheses (such as starred expressions).
return (
isinstance(slice_value, ast.Tuple)
and slice_value.elts
and (
# Python 2 doesn't allow starred elements in tuples like Python 3
six.PY2 or not any(
isinstance(elt, ast.Starred) for elt in slice_value.elts
)
)
)
class Unparser:
"""Methods in this class recursively traverse an AST and
output source code for the abstract syntax; original formatting
@ -103,6 +119,16 @@ def __init__(self, py_ver_consistent=False):
self._py_ver_consistent = py_ver_consistent
self._precedences = {}
def items_view(self, traverser, items):
"""Traverse and separate the given *items* with a comma and append it to
the buffer. If *items* is a single item sequence, a trailing comma
will be added."""
if len(items) == 1:
traverser(items[0])
self.write(",")
else:
interleave(lambda: self.write(", "), traverser, items)
def visit(self, tree, output_file):
"""Traverse tree and write source code to output_file."""
self.f = output_file
@ -657,11 +683,7 @@ def _Constant(self, t):
value = t.value
if isinstance(value, tuple):
with self.delimit("(", ")"):
if len(value) == 1:
self._write_constant(value[0])
self.write(",")
else:
interleave(lambda: self.write(", "), self._write_constant, value)
self.items_view(self._write_constant, value)
elif value is Ellipsis: # instead of `...` for Py2 compatibility
self.write("...")
else:
@ -762,12 +784,7 @@ def write_item(item):
def _Tuple(self, t):
with self.delimit("(", ")"):
if len(t.elts) == 1:
elt = t.elts[0]
self.dispatch(elt)
self.write(",")
else:
interleave(lambda: self.write(", "), self.dispatch, t.elts)
self.items_view(self.dispatch, t.elts)
unop = {
"Invert": "~",
@ -974,7 +991,10 @@ def _Subscript(self, t):
self.set_precedence(_Precedence.ATOM, t.value)
self.dispatch(t.value)
with self.delimit("[", "]"):
self.dispatch(t.slice)
if is_simple_tuple(t.slice):
self.items_view(self.dispatch, t.slice.elts)
else:
self.dispatch(t.slice)
def _Starred(self, t):
self.write("*")
@ -985,9 +1005,14 @@ def _Starred(self, t):
def _Ellipsis(self, t):
self.write("...")
# used in Python <= 3.8 -- see _Subscript for 3.9+
def _Index(self, t):
self.set_precedence(_Precedence.TUPLE, t.value)
self.dispatch(t.value)
if is_simple_tuple(t.value):
self.set_precedence(_Precedence.ATOM, t.value)
self.items_view(self.dispatch, t.value.elts)
else:
self.set_precedence(_Precedence.TUPLE, t.value)
self.dispatch(t.value)
def _Slice(self, t):
if t.lower: