https://github.com/python/cpython/commit/a9594a34c62487961be86c0925daaa43bb467bb9
commit: a9594a34c62487961be86c0925daaa43bb467bb9
branch: main
author: Irit Katriel <[email protected]>
committer: iritkatriel <[email protected]>
date: 2024-09-13T14:06:06Z
summary:

gh-124022: Fix bug where class docstring is removed in interactive mode 
(#124023)


Co-authored-by: Jelle Zijlstra <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2024-09-12-21-53-26.gh-issue-124022.fQzUiW.rst
M Include/internal/pycore_compile.h
M Lib/test/test_compile.py
M Python/codegen.c
M Python/compile.c

diff --git a/Include/internal/pycore_compile.h 
b/Include/internal/pycore_compile.h
index 51db6fc608caf7..5359f2d650e2a2 100644
--- a/Include/internal/pycore_compile.h
+++ b/Include/internal/pycore_compile.h
@@ -172,7 +172,8 @@ int _PyCompile_AddDeferredAnnotaion(struct _PyCompiler *c, 
stmt_ty s);
 int _PyCodegen_AddReturnAtEnd(struct _PyCompiler *c, int addNone);
 int _PyCodegen_EnterAnonymousScope(struct _PyCompiler* c, mod_ty mod);
 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 */
 int _PyCompile_EnsureArrayLargeEnough(
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index f22761f0a3af9f..736eff35c1d5f2 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -902,6 +902,28 @@ def with_const_expression():
                 self.assertIsNone(ns['with_fstring'].__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
     def test_docstring_omitted(self):
         # See gh-115347
@@ -919,12 +941,13 @@ class C:
                 return h
         """)
         for opt in [-1, 0, 1, 2]:
-            with self.subTest(opt=opt):
-                code = compile(src, "<test>", "exec", optimize=opt)
-                output = io.StringIO()
-                with contextlib.redirect_stdout(output):
-                    dis.dis(code)
-                self.assertNotIn('NOP' , output.getvalue())
+            for mode in ["exec", "single"]:
+                with self.subTest(opt=opt, mode=mode):
+                    code = compile(src, "<test>", mode, optimize=opt)
+                    output = io.StringIO()
+                    with contextlib.redirect_stdout(output):
+                        dis.dis(code)
+                    self.assertNotIn('NOP', output.getvalue())
 
     def test_dont_merge_constants(self):
         # Issue #25843: compile() must not merge constants which are equal
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2024-09-12-21-53-26.gh-issue-124022.fQzUiW.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-12-21-53-26.gh-issue-124022.fQzUiW.rst
new file mode 100644
index 00000000000000..90a77a5346d22b
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2024-09-12-21-53-26.gh-issue-124022.fQzUiW.rst
@@ -0,0 +1 @@
+Fix bug where docstring is removed from classes in interactive mode.
diff --git a/Python/codegen.c b/Python/codegen.c
index 2ca5db1fc6ad34..5565d3011c465a 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -746,7 +746,7 @@ _PyCodegen_Expression(compiler *c, expr_ty e)
    and for annotations. */
 
 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,
      * every annotated class and module should have __annotations__.
@@ -758,7 +758,7 @@ _PyCodegen_Body(compiler *c, location loc, asdl_stmt_seq 
*stmts)
         return SUCCESS;
     }
     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);
         if (docstring) {
             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);
     }
     /* 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);
     if (static_attributes == NULL) {
         _PyCompile_ExitScope(c);
diff --git a/Python/compile.c b/Python/compile.c
index d54c320babc2b7..e1d2c30944d132 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -790,13 +790,13 @@ compiler_codegen(compiler *c, mod_ty mod)
     switch (mod->kind) {
     case Module_kind: {
         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;
     }
     case Interactive_kind: {
         c->c_interactive = 1;
         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;
     }
     case Expression_kind: {

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to