mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	gh-124022: Fix bug where class docstring is removed in interactive mode (#124023)
Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
This commit is contained in:
		
							parent
							
								
									cfe6074d1f
								
							
						
					
					
						commit
						a9594a34c6
					
				
					 5 changed files with 37 additions and 12 deletions
				
			
		|  | @ -172,7 +172,8 @@ int _PyCompile_AddDeferredAnnotaion(struct _PyCompiler *c, stmt_ty s); | ||||||
| int _PyCodegen_AddReturnAtEnd(struct _PyCompiler *c, int addNone); | int _PyCodegen_AddReturnAtEnd(struct _PyCompiler *c, int addNone); | ||||||
| int _PyCodegen_EnterAnonymousScope(struct _PyCompiler* c, mod_ty mod); | int _PyCodegen_EnterAnonymousScope(struct _PyCompiler* c, mod_ty mod); | ||||||
| int _PyCodegen_Expression(struct _PyCompiler *c, expr_ty e); | int _PyCodegen_Expression(struct _PyCompiler *c, expr_ty e); | ||||||
| int _PyCodegen_Body(struct _PyCompiler *c, _Py_SourceLocation loc, asdl_stmt_seq *stmts); | int _PyCodegen_Body(struct _PyCompiler *c, _Py_SourceLocation loc, asdl_stmt_seq *stmts, | ||||||
|  |                     bool is_interactive); | ||||||
| 
 | 
 | ||||||
| /* Utility for a number of growing arrays used in the compiler */ | /* Utility for a number of growing arrays used in the compiler */ | ||||||
| int _PyCompile_EnsureArrayLargeEnough( | int _PyCompile_EnsureArrayLargeEnough( | ||||||
|  |  | ||||||
|  | @ -902,6 +902,28 @@ def with_const_expression(): | ||||||
|                 self.assertIsNone(ns['with_fstring'].__doc__) |                 self.assertIsNone(ns['with_fstring'].__doc__) | ||||||
|                 self.assertIsNone(ns['with_const_expression'].__doc__) |                 self.assertIsNone(ns['with_const_expression'].__doc__) | ||||||
| 
 | 
 | ||||||
|  |     @support.cpython_only | ||||||
|  |     def test_docstring_interactive_mode(self): | ||||||
|  |         srcs = [ | ||||||
|  |             """def with_docstring(): | ||||||
|  |                 "docstring" | ||||||
|  |             """, | ||||||
|  |             """class with_docstring: | ||||||
|  |                 "docstring" | ||||||
|  |             """, | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |         for opt in [0, 1, 2]: | ||||||
|  |             for src in srcs: | ||||||
|  |                 with self.subTest(opt=opt, src=src): | ||||||
|  |                     code = compile(textwrap.dedent(src), "<test>", "single", optimize=opt) | ||||||
|  |                     ns = {} | ||||||
|  |                     exec(code, ns) | ||||||
|  |                     if opt < 2: | ||||||
|  |                         self.assertEqual(ns['with_docstring'].__doc__, "docstring") | ||||||
|  |                     else: | ||||||
|  |                         self.assertIsNone(ns['with_docstring'].__doc__) | ||||||
|  | 
 | ||||||
|     @support.cpython_only |     @support.cpython_only | ||||||
|     def test_docstring_omitted(self): |     def test_docstring_omitted(self): | ||||||
|         # See gh-115347 |         # See gh-115347 | ||||||
|  | @ -919,12 +941,13 @@ class C: | ||||||
|                 return h |                 return h | ||||||
|         """) |         """) | ||||||
|         for opt in [-1, 0, 1, 2]: |         for opt in [-1, 0, 1, 2]: | ||||||
|             with self.subTest(opt=opt): |             for mode in ["exec", "single"]: | ||||||
|                 code = compile(src, "<test>", "exec", optimize=opt) |                 with self.subTest(opt=opt, mode=mode): | ||||||
|                 output = io.StringIO() |                     code = compile(src, "<test>", mode, optimize=opt) | ||||||
|                 with contextlib.redirect_stdout(output): |                     output = io.StringIO() | ||||||
|                     dis.dis(code) |                     with contextlib.redirect_stdout(output): | ||||||
|                 self.assertNotIn('NOP' , output.getvalue()) |                         dis.dis(code) | ||||||
|  |                     self.assertNotIn('NOP', output.getvalue()) | ||||||
| 
 | 
 | ||||||
|     def test_dont_merge_constants(self): |     def test_dont_merge_constants(self): | ||||||
|         # Issue #25843: compile() must not merge constants which are equal |         # Issue #25843: compile() must not merge constants which are equal | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | Fix bug where docstring is removed from classes in interactive mode. | ||||||
|  | @ -746,7 +746,7 @@ _PyCodegen_Expression(compiler *c, expr_ty e) | ||||||
|    and for annotations. */ |    and for annotations. */ | ||||||
| 
 | 
 | ||||||
| int | int | ||||||
| _PyCodegen_Body(compiler *c, location loc, asdl_stmt_seq *stmts) | _PyCodegen_Body(compiler *c, location loc, asdl_stmt_seq *stmts, bool is_interactive) | ||||||
| { | { | ||||||
|     /* If from __future__ import annotations is active,
 |     /* If from __future__ import annotations is active,
 | ||||||
|      * every annotated class and module should have __annotations__. |      * every annotated class and module should have __annotations__. | ||||||
|  | @ -758,7 +758,7 @@ _PyCodegen_Body(compiler *c, location loc, asdl_stmt_seq *stmts) | ||||||
|         return SUCCESS; |         return SUCCESS; | ||||||
|     } |     } | ||||||
|     Py_ssize_t first_instr = 0; |     Py_ssize_t first_instr = 0; | ||||||
|     if (!IS_INTERACTIVE(c)) { |     if (!is_interactive) { /* A string literal on REPL prompt is not a docstring */ | ||||||
|         PyObject *docstring = _PyAST_GetDocString(stmts); |         PyObject *docstring = _PyAST_GetDocString(stmts); | ||||||
|         if (docstring) { |         if (docstring) { | ||||||
|             first_instr = 1; |             first_instr = 1; | ||||||
|  | @ -1432,7 +1432,7 @@ codegen_class_body(compiler *c, stmt_ty s, int firstlineno) | ||||||
|         ADDOP_N_IN_SCOPE(c, loc, STORE_DEREF, &_Py_ID(__classdict__), cellvars); |         ADDOP_N_IN_SCOPE(c, loc, STORE_DEREF, &_Py_ID(__classdict__), cellvars); | ||||||
|     } |     } | ||||||
|     /* compile the body proper */ |     /* compile the body proper */ | ||||||
|     RETURN_IF_ERROR_IN_SCOPE(c, _PyCodegen_Body(c, loc, s->v.ClassDef.body)); |     RETURN_IF_ERROR_IN_SCOPE(c, _PyCodegen_Body(c, loc, s->v.ClassDef.body, false)); | ||||||
|     PyObject *static_attributes = _PyCompile_StaticAttributesAsTuple(c); |     PyObject *static_attributes = _PyCompile_StaticAttributesAsTuple(c); | ||||||
|     if (static_attributes == NULL) { |     if (static_attributes == NULL) { | ||||||
|         _PyCompile_ExitScope(c); |         _PyCompile_ExitScope(c); | ||||||
|  |  | ||||||
|  | @ -790,13 +790,13 @@ compiler_codegen(compiler *c, mod_ty mod) | ||||||
|     switch (mod->kind) { |     switch (mod->kind) { | ||||||
|     case Module_kind: { |     case Module_kind: { | ||||||
|         asdl_stmt_seq *stmts = mod->v.Module.body; |         asdl_stmt_seq *stmts = mod->v.Module.body; | ||||||
|         RETURN_IF_ERROR(_PyCodegen_Body(c, start_location(stmts), stmts)); |         RETURN_IF_ERROR(_PyCodegen_Body(c, start_location(stmts), stmts, false)); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case Interactive_kind: { |     case Interactive_kind: { | ||||||
|         c->c_interactive = 1; |         c->c_interactive = 1; | ||||||
|         asdl_stmt_seq *stmts = mod->v.Interactive.body; |         asdl_stmt_seq *stmts = mod->v.Interactive.body; | ||||||
|         RETURN_IF_ERROR(_PyCodegen_Body(c, start_location(stmts), stmts)); |         RETURN_IF_ERROR(_PyCodegen_Body(c, start_location(stmts), stmts, true)); | ||||||
|         break; |         break; | ||||||
|     } |     } | ||||||
|     case Expression_kind: { |     case Expression_kind: { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Irit Katriel
						Irit Katriel