Skip to content

Commit 13d52c2

Browse files
bpo-34822: Simplify AST for subscription. (GH-9605)
* Remove the slice type. * Make Slice a kind of the expr type instead of the slice type. * Replace ExtSlice(slices) with Tuple(slices, Load()). * Replace Index(value) with a value itself. All non-terminal nodes in AST for expressions are now of the expr type.
1 parent e5e5632 commit 13d52c2

File tree

15 files changed

+293
-702
lines changed

15 files changed

+293
-702
lines changed

Doc/library/ast.rst

Lines changed: 31 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -120,13 +120,26 @@ Node classes
120120

121121
Class :class:`ast.Constant` is now used for all constants.
122122

123+
.. versionchanged:: 3.9
124+
125+
Simple indices are represented by their value, extended slices are
126+
represented as tuples.
127+
123128
.. deprecated:: 3.8
124129

125130
Old classes :class:`ast.Num`, :class:`ast.Str`, :class:`ast.Bytes`,
126131
:class:`ast.NameConstant` and :class:`ast.Ellipsis` are still available,
127-
but they will be removed in future Python releases. In the meanwhile,
132+
but they will be removed in future Python releases. In the meantime,
128133
instantiating them will return an instance of a different class.
129134

135+
.. deprecated:: 3.9
136+
137+
Old classes :class:`ast.Index` and :class:`ast.ExtSlice` are still
138+
available, but they will be removed in future Python releases.
139+
In the meantime, instantiating them will return an instance of
140+
a different class.
141+
142+
130143
Literals
131144
^^^^^^^^
132145

@@ -552,30 +565,33 @@ Subscripting
552565

553566
.. class:: Subscript(value, slice, ctx)
554567

555-
A subscript, such as ``l[1]``. ``value`` is the object, often a
556-
:class:`Name`. ``slice`` is one of :class:`Index`, :class:`Slice` or
557-
:class:`ExtSlice`. ``ctx`` is :class:`Load`, :class:`Store` or :class:`Del`
568+
A subscript, such as ``l[1]``. ``value`` is the subscripted object
569+
(usually sequence or mapping). ``slice`` is an index, slice or key.
570+
It can be a :class:`Tuple` and contain a :class:`Slice`.
571+
``ctx`` is :class:`Load`, :class:`Store` or :class:`Del`
558572
according to the action performed with the subscript.
559573

560-
561-
.. class:: Index(value)
562-
563-
Simple subscripting with a single value
564-
565574
.. doctest::
566575

567-
>>> print(ast.dump(ast.parse('l[1]', mode='eval'), indent=4))
576+
>>> print(ast.dump(ast.parse('l[1:2, 3]', mode='eval'), indent=4))
568577
Expression(
569578
body=Subscript(
570579
value=Name(id='l', ctx=Load()),
571-
slice=Index(
572-
value=Constant(value=1)),
580+
slice=Tuple(
581+
elts=[
582+
Slice(
583+
lower=Constant(value=1),
584+
upper=Constant(value=2)),
585+
Constant(value=3)],
586+
ctx=Load()),
573587
ctx=Load()))
574588

575589

576590
.. class:: Slice(lower, upper, step)
577591

578-
Regular slicing (on the form x:y).
592+
Regular slicing (on the form ``lower:upper`` or ``lower:upper:step``).
593+
Can occur only inside the *slice* field of :class:`Subscript`, either
594+
directly or as an element of :class:`Tuple`.
579595

580596
.. doctest::
581597

@@ -589,27 +605,6 @@ Subscripting
589605
ctx=Load()))
590606

591607

592-
.. class:: ExtSlice(dims)
593-
594-
Advanced slicing. ``dims`` holds a list of :class:`Slice` and
595-
:class:`Index` nodes
596-
597-
.. doctest::
598-
599-
>>> print(ast.dump(ast.parse('l[1:2, 3]', mode='eval'), indent=4))
600-
Expression(
601-
body=Subscript(
602-
value=Name(id='l', ctx=Load()),
603-
slice=ExtSlice(
604-
dims=[
605-
Slice(
606-
lower=Constant(value=1),
607-
upper=Constant(value=2)),
608-
Index(
609-
value=Constant(value=3))]),
610-
ctx=Load()))
611-
612-
613608
Comprehensions
614609
~~~~~~~~~~~~~~
615610

@@ -823,8 +818,7 @@ Statements
823818
AnnAssign(
824819
target=Subscript(
825820
value=Name(id='a', ctx=Load()),
826-
slice=Index(
827-
value=Constant(value=1)),
821+
slice=Constant(value=1),
828822
ctx=Store()),
829823
annotation=Name(id='int', ctx=Load()),
830824
simple=0)],
@@ -1708,7 +1702,7 @@ and classes for traversing abstract syntax trees:
17081702
def visit_Name(self, node):
17091703
return Subscript(
17101704
value=Name(id='data', ctx=Load()),
1711-
slice=Index(value=Constant(value=node.id)),
1705+
slice=Constant(value=node.id),
17121706
ctx=node.ctx
17131707
), node)
17141708

Doc/tools/susp-ignored.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ howto/pyporting,,::,Programming Language :: Python :: 3
108108
howto/regex,,::,
109109
howto/regex,,:foo,(?:foo)
110110
howto/urllib2,,:password,"""joe:password@example.com"""
111+
library/ast,,:upper,lower:upper
112+
library/ast,,:step,lower:upper:step
111113
library/audioop,,:ipos,"# factor = audioop.findfactor(in_test[ipos*2:ipos*2+len(out_test)],"
112114
library/bisect,32,:hi,all(val >= x for val in a[i:hi])
113115
library/bisect,42,:hi,all(val > x for val in a[i:hi])

Doc/whatsnew/3.9.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,12 @@ Deprecated
535535

536536
(Contributed by Victor Stinner in :issue:`39353`.)
537537

538+
* :mod:`ast` classes ``Index`` and ``ExtSlice`` are considered deprecated
539+
and will be removed in future Python versions. ``value`` itself should be
540+
used instead of ``Index(value)``. ``Tuple(slices, Load())`` should be
541+
used instead of ``ExtSlice(slices)``.
542+
(Contributed by Serhiy Storchaka in :issue:`32892`.)
543+
538544
* The :c:func:`PyEval_InitThreads` and :c:func:`PyEval_ThreadsInitialized`
539545
functions are now deprecated and will be removed in Python 3.11. Calling
540546
:c:func:`PyEval_InitThreads` now does nothing. The :term:`GIL` is initialized
@@ -667,10 +673,17 @@ Changes in the Python API
667673
since the *buffering* parameter has been removed.
668674
(Contributed by Victor Stinner in :issue:`39357`.)
669675

676+
* Simplified AST for subscription. Simple indices will be represented by
677+
their value, extended slices will be represented as tuples.
678+
``Index(value)`` will return a ``value`` itself, ``ExtSlice(slices)``
679+
will return ``Tuple(slices, Load())``.
680+
(Contributed by Serhiy Storchaka in :issue:`34822`.)
681+
670682
* The :mod:`importlib` module now ignores the :envvar:`PYTHONCASEOK`
671683
environment variable when the :option:`-E` or :option:`-I` command line
672684
options are being used.
673685

686+
674687
CPython bytecode changes
675688
------------------------
676689

Include/Python-ast.h

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

Lib/ast.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ class RewriteName(NodeTransformer):
445445
def visit_Name(self, node):
446446
return copy_location(Subscript(
447447
value=Name(id='data', ctx=Load()),
448-
slice=Index(value=Str(s=node.id)),
448+
slice=Constant(value=node.id),
449449
ctx=node.ctx
450450
), node)
451451
@@ -552,6 +552,7 @@ def __new__(cls, *args, **kwargs):
552552
_const_types_not = {
553553
Num: (bool,),
554554
}
555+
555556
_const_node_type_names = {
556557
bool: 'NameConstant', # should be before int
557558
type(None): 'NameConstant',
@@ -563,6 +564,23 @@ def __new__(cls, *args, **kwargs):
563564
type(...): 'Ellipsis',
564565
}
565566

567+
class Index(AST):
568+
def __new__(cls, value, **kwargs):
569+
return value
570+
571+
class ExtSlice(AST):
572+
def __new__(cls, dims=(), **kwargs):
573+
return Tuple(list(dims), Load(), **kwargs)
574+
575+
def _dims_getter(self):
576+
return self.elts
577+
578+
def _dims_setter(self, value):
579+
self.elts = value
580+
581+
Tuple.dims = property(_dims_getter, _dims_setter)
582+
583+
566584
# Large float and imaginary literals get turned into infinities in the AST.
567585
# We unparse those infinities to INFSTR.
568586
_INFSTR = "1e" + repr(sys.float_info.max_10_exp + 1)
@@ -1268,10 +1286,8 @@ def visit_Subscript(self, node):
12681286
self.set_precedence(_Precedence.ATOM, node.value)
12691287
self.traverse(node.value)
12701288
with self.delimit("[", "]"):
1271-
if (isinstance(node.slice, Index)
1272-
and isinstance(node.slice.value, Tuple)
1273-
and node.slice.value.elts):
1274-
self.items_view(self.traverse, node.slice.value.elts)
1289+
if isinstance(node.slice, Tuple) and node.slice.elts:
1290+
self.items_view(self.traverse, node.slice.elts)
12751291
else:
12761292
self.traverse(node.slice)
12771293

@@ -1283,10 +1299,6 @@ def visit_Starred(self, node):
12831299
def visit_Ellipsis(self, node):
12841300
self.write("...")
12851301

1286-
def visit_Index(self, node):
1287-
self.set_precedence(_Precedence.TUPLE, node.value)
1288-
self.traverse(node.value)
1289-
12901302
def visit_Slice(self, node):
12911303
if node.lower:
12921304
self.traverse(node.lower)
@@ -1297,9 +1309,6 @@ def visit_Slice(self, node):
12971309
self.write(":")
12981310
self.traverse(node.step)
12991311

1300-
def visit_ExtSlice(self, node):
1301-
self.items_view(self.traverse, node.dims)
1302-
13031312
def visit_arg(self, node):
13041313
self.write(node.arg)
13051314
if node.annotation:

Lib/test/test_ast.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,10 @@ def test_base_classes(self):
343343
def test_field_attr_existence(self):
344344
for name, item in ast.__dict__.items():
345345
if self._is_ast_node(name, item):
346+
if name == 'Index':
347+
# Index(value) just returns value now.
348+
# The argument is required.
349+
continue
346350
x = item()
347351
if isinstance(x, ast.AST):
348352
self.assertEqual(type(x._fields), tuple)
@@ -1308,21 +1312,21 @@ def test_attribute(self):
13081312
self.expr(attr, "must have Load context")
13091313

13101314
def test_subscript(self):
1311-
sub = ast.Subscript(ast.Name("x", ast.Store()), ast.Index(ast.Num(3)),
1315+
sub = ast.Subscript(ast.Name("x", ast.Store()), ast.Num(3),
13121316
ast.Load())
13131317
self.expr(sub, "must have Load context")
13141318
x = ast.Name("x", ast.Load())
1315-
sub = ast.Subscript(x, ast.Index(ast.Name("y", ast.Store())),
1319+
sub = ast.Subscript(x, ast.Name("y", ast.Store()),
13161320
ast.Load())
13171321
self.expr(sub, "must have Load context")
13181322
s = ast.Name("x", ast.Store())
13191323
for args in (s, None, None), (None, s, None), (None, None, s):
13201324
sl = ast.Slice(*args)
13211325
self.expr(ast.Subscript(x, sl, ast.Load()),
13221326
"must have Load context")
1323-
sl = ast.ExtSlice([])
1324-
self.expr(ast.Subscript(x, sl, ast.Load()), "empty dims on ExtSlice")
1325-
sl = ast.ExtSlice([ast.Index(s)])
1327+
sl = ast.Tuple([], ast.Load())
1328+
self.expr(ast.Subscript(x, sl, ast.Load()))
1329+
sl = ast.Tuple([s], ast.Load())
13261330
self.expr(ast.Subscript(x, sl, ast.Load()), "must have Load context")
13271331

13281332
def test_starred(self):
@@ -1664,11 +1668,11 @@ def test_slices(self):
16641668
''').strip()
16651669
i1, i2, im = map(self._parse_value, (s1, s2, sm))
16661670
self._check_content(s1, i1.value, 'f()[1, 2]')
1667-
self._check_content(s1, i1.value.slice.value, '1, 2')
1671+
self._check_content(s1, i1.value.slice, '1, 2')
16681672
self._check_content(s2, i2.slice.lower, 'a.b')
16691673
self._check_content(s2, i2.slice.upper, 'c.d')
1670-
self._check_content(sm, im.slice.dims[0].upper, 'f ()')
1671-
self._check_content(sm, im.slice.dims[1].lower, 'g ()')
1674+
self._check_content(sm, im.slice.elts[0].upper, 'f ()')
1675+
self._check_content(sm, im.slice.elts[1].lower, 'g ()')
16721676
self._check_end_pos(im, 3, 3)
16731677

16741678
def test_binop(self):
@@ -1989,13 +1993,13 @@ def main():
19891993
('Expression', ('Constant', (1, 0, 1, 2), 10, None)),
19901994
('Expression', ('Constant', (1, 0, 1, 8), 'string', None)),
19911995
('Expression', ('Attribute', (1, 0, 1, 3), ('Name', (1, 0, 1, 1), 'a', ('Load',)), 'b', ('Load',))),
1992-
('Expression', ('Subscript', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Slice', ('Name', (1, 2, 1, 3), 'b', ('Load',)), ('Name', (1, 4, 1, 5), 'c', ('Load',)), None), ('Load',))),
1996+
('Expression', ('Subscript', (1, 0, 1, 6), ('Name', (1, 0, 1, 1), 'a', ('Load',)), ('Slice', (1, 2, 1, 5), ('Name', (1, 2, 1, 3), 'b', ('Load',)), ('Name', (1, 4, 1, 5), 'c', ('Load',)), None), ('Load',))),
19931997
('Expression', ('Name', (1, 0, 1, 1), 'v', ('Load',))),
19941998
('Expression', ('List', (1, 0, 1, 7), [('Constant', (1, 1, 1, 2), 1, None), ('Constant', (1, 3, 1, 4), 2, None), ('Constant', (1, 5, 1, 6), 3, None)], ('Load',))),
19951999
('Expression', ('List', (1, 0, 1, 2), [], ('Load',))),
19962000
('Expression', ('Tuple', (1, 0, 1, 5), [('Constant', (1, 0, 1, 1), 1, None), ('Constant', (1, 2, 1, 3), 2, None), ('Constant', (1, 4, 1, 5), 3, None)], ('Load',))),
19972001
('Expression', ('Tuple', (1, 0, 1, 7), [('Constant', (1, 1, 1, 2), 1, None), ('Constant', (1, 3, 1, 4), 2, None), ('Constant', (1, 5, 1, 6), 3, None)], ('Load',))),
19982002
('Expression', ('Tuple', (1, 0, 1, 2), [], ('Load',))),
1999-
('Expression', ('Call', (1, 0, 1, 17), ('Attribute', (1, 0, 1, 7), ('Attribute', (1, 0, 1, 5), ('Attribute', (1, 0, 1, 3), ('Name', (1, 0, 1, 1), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',)), 'd', ('Load',)), [('Subscript', (1, 8, 1, 16), ('Attribute', (1, 8, 1, 11), ('Name', (1, 8, 1, 9), 'a', ('Load',)), 'b', ('Load',)), ('Slice', ('Constant', (1, 12, 1, 13), 1, None), ('Constant', (1, 14, 1, 15), 2, None), None), ('Load',))], [])),
2003+
('Expression', ('Call', (1, 0, 1, 17), ('Attribute', (1, 0, 1, 7), ('Attribute', (1, 0, 1, 5), ('Attribute', (1, 0, 1, 3), ('Name', (1, 0, 1, 1), 'a', ('Load',)), 'b', ('Load',)), 'c', ('Load',)), 'd', ('Load',)), [('Subscript', (1, 8, 1, 16), ('Attribute', (1, 8, 1, 11), ('Name', (1, 8, 1, 9), 'a', ('Load',)), 'b', ('Load',)), ('Slice', (1, 12, 1, 15), ('Constant', (1, 12, 1, 13), 1, None), ('Constant', (1, 14, 1, 15), 2, None), None), ('Load',))], [])),
20002004
]
20012005
main()

0 commit comments

Comments
 (0)