@@ -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 )
0 commit comments