pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/b74f3bed51378896f2c7c720e505e87373e68c79

8faa60c69660fa.css" /> gh-137308: Replace a single docstring with `pass` in `-OO` mode (#137… · python/cpython@b74f3be · GitHub
Skip to content

Commit b74f3be

Browse files
authored
gh-137308: Replace a single docstring with pass in -OO mode (#137318)
This is required so we would never have empty node bodies. Refs #130087
1 parent fe0e921 commit b74f3be

3 files changed

Lines changed: 154 additions & 1 deletion

File tree

Lib/test/test_ast/test_ast.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,131 @@ def test_negative_locations_for_compile(self):
220220
# This also must not crash:
221221
ast.parse(tree, optimize=2)
222222

223+
def test_docstring_optimization_single_node(self):
224+
# https://github.com/python/cpython/issues/137308
225+
class_example1 = textwrap.dedent('''
226+
class A:
227+
"""Docstring"""
228+
''')
229+
class_example2 = textwrap.dedent('''
230+
class A:
231+
"""
232+
Docstring"""
233+
''')
234+
def_example1 = textwrap.dedent('''
235+
def some():
236+
"""Docstring"""
237+
''')
238+
def_example2 = textwrap.dedent('''
239+
def some():
240+
"""Docstring
241+
"""
242+
''')
243+
async_def_example1 = textwrap.dedent('''
244+
async def some():
245+
"""Docstring"""
246+
''')
247+
async_def_example2 = textwrap.dedent('''
248+
async def some():
249+
"""
250+
Docstring
251+
"""
252+
''')
253+
for code in [
254+
class_example1,
255+
class_example2,
256+
def_example1,
257+
def_example2,
258+
async_def_example1,
259+
async_def_example2,
260+
]:
261+
for opt_level in [0, 1, 2]:
262+
with self.subTest(code=code, opt_level=opt_level):
263+
mod = ast.parse(code, optimize=opt_level)
264+
self.assertEqual(len(mod.body[0].body), 1)
265+
if opt_level == 2:
266+
pass_stmt = mod.body[0].body[0]
267+
self.assertIsInstance(pass_stmt, ast.Pass)
268+
self.assertEqual(
269+
vars(pass_stmt),
270+
{
271+
'lineno': 3,
272+
'col_offset': 4,
273+
'end_lineno': 3,
274+
'end_col_offset': 8,
275+
},
276+
)
277+
else:
278+
self.assertIsInstance(mod.body[0].body[0], ast.Expr)
279+
self.assertIsInstance(
280+
mod.body[0].body[0].value,
281+
ast.Constant,
282+
)
283+
284+
compile(code, "a", "exec")
285+
compile(code, "a", "exec", optimize=opt_level)
286+
compile(mod, "a", "exec")
287+
compile(mod, "a", "exec", optimize=opt_level)
288+
289+
def test_docstring_optimization_multiple_nodes(self):
290+
# https://github.com/python/cpython/issues/137308
291+
class_example = textwrap.dedent(
292+
"""
293+
class A:
294+
'''
295+
Docstring
296+
'''
297+
x = 1
298+
"""
299+
)
300+
301+
def_example = textwrap.dedent(
302+
"""
303+
def some():
304+
'''
305+
Docstring
306+
307+
'''
308+
x = 1
309+
"""
310+
)
311+
312+
async_def_example = textwrap.dedent(
313+
"""
314+
async def some():
315+
316+
'''Docstring
317+
318+
'''
319+
x = 1
320+
"""
321+
)
322+
323+
for code in [
324+
class_example,
325+
def_example,
326+
async_def_example,
327+
]:
328+
for opt_level in [0, 1, 2]:
329+
with self.subTest(code=code, opt_level=opt_level):
330+
mod = ast.parse(code, optimize=opt_level)
331+
if opt_level == 2:
332+
self.assertNotIsInstance(
333+
mod.body[0].body[0],
334+
(ast.Pass, ast.Expr),
335+
)
336+
else:
337+
self.assertIsInstance(mod.body[0].body[0], ast.Expr)
338+
self.assertIsInstance(
339+
mod.body[0].body[0].value,
340+
ast.Constant,
341+
)
342+
343+
compile(code, "a", "exec")
344+
compile(code, "a", "exec", optimize=opt_level)
345+
compile(mod, "a", "exec")
346+
compile(mod, "a", "exec", optimize=opt_level)
347+
223348
def test_slice(self):
224349
slc = ast.parse("x[::]").body[0].value.slice
225350
self.assertIsNone(slc.upper)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
A standalone docstring in a node body is optimized as a :keyword:`pass`
2+
statement to ensure that the node's body is never empty. There was a
3+
:exc:`ValueError` in :func:`compile` otherwise.

Python/ast_preprocess.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,13 +435,38 @@ stmt_seq_remove_item(asdl_stmt_seq *stmts, Py_ssize_t idx)
435435
return 1;
436436
}
437437

438+
static int
439+
remove_docstring(asdl_stmt_seq *stmts, Py_ssize_t idx, PyArena *ctx_)
440+
{
441+
assert(_PyAST_GetDocString(stmts) != NULL);
442+
// In case there's just the docstring in the body, replace it with `pass`
443+
// keyword, so body won't be empty.
444+
if (asdl_seq_LEN(stmts) == 1) {
445+
stmt_ty docstring = (stmt_ty)asdl_seq_GET(stmts, 0);
446+
stmt_ty pass = _PyAST_Pass(
447+
docstring->lineno, docstring->col_offset,
448+
// we know that `pass` always takes 4 chars and a single line,
449+
// while docstring can span on multiple lines
450+
docstring->lineno, docstring->col_offset + 4,
451+
ctx_
452+
);
453+
if (pass == NULL) {
454+
return 0;
455+
}
456+
asdl_seq_SET(stmts, 0, pass);
457+
return 1;
458+
}
459+
// In case there are more than 1 body items, just remove the docstring.
460+
return stmt_seq_remove_item(stmts, idx);
461+
}
462+
438463
static int
439464
astfold_body(asdl_stmt_seq *stmts, PyArena *ctx_, _PyASTPreprocessState *state)
440465
{
441466
int docstring = _PyAST_GetDocString(stmts) != NULL;
442467
if (docstring && (state->optimize >= 2)) {
443468
/* remove the docstring */
444-
if (!stmt_seq_remove_item(stmts, 0)) {
469+
if (!remove_docstring(stmts, 0, ctx_)) {
445470
return 0;
446471
}
447472
docstring = 0;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy