mirror of https://github.com/python/cpython
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
|
@ -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 @@ class TestSpecifics(unittest.TestCase):
|
||||||
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,8 +941,9 @@ class TestSpecifics(unittest.TestCase):
|
||||||
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):
|
||||||
|
code = compile(src, "<test>", mode, optimize=opt)
|
||||||
output = io.StringIO()
|
output = io.StringIO()
|
||||||
with contextlib.redirect_stdout(output):
|
with contextlib.redirect_stdout(output):
|
||||||
dis.dis(code)
|
dis.dis(code)
|
||||||
|
|
|
@ -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…
Reference in New Issue