aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Doc/library/inspect.rst8
-rw-r--r--Doc/reference/datamodel.rst6
-rw-r--r--Include/cpython/code.h5
-rw-r--r--Include/internal/pycore_symtable.h1
-rw-r--r--Lib/dis.py21
-rw-r--r--Lib/inspect.py2
-rw-r--r--Lib/test/test_code.py59
-rw-r--r--Lib/test/test_compile.py8
-rw-r--r--Lib/test/test_compiler_assemble.py2
-rw-r--r--Lib/test/test_dis.py68
-rw-r--r--Lib/test/test_inspect/test_inspect.py2
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2024-10-29-10-37-39.gh-issue-126072.XLKlxv.rst3
-rw-r--r--Objects/funcobject.c3
-rw-r--r--Python/codegen.c6
-rw-r--r--Python/compile.c2
-rw-r--r--Python/symtable.c10
16 files changed, 148 insertions, 58 deletions
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst
index 892f5ba9a76..ca5dac87aff 100644
--- a/Doc/library/inspect.rst
+++ b/Doc/library/inspect.rst
@@ -1700,6 +1700,14 @@ which is a bitmap of the following flags:
.. versionadded:: 3.6
+.. data:: CO_HAS_DOCSTRING
+
+ The flag is set when there is a docstring for the code object in
+ the source code. If set, it will be the first item in
+ :attr:`~codeobject.co_consts`.
+
+ .. versionadded:: 3.14
+
.. note::
The flags are specific to CPython, and may not be defined in other
Python implementations. Furthermore, the flags are an implementation
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index e5f2dcd5bc9..aceb95f2758 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -1536,9 +1536,9 @@ Other bits in :attr:`~codeobject.co_flags` are reserved for internal use.
.. index:: single: documentation string
-If a code object represents a function, the first item in
-:attr:`~codeobject.co_consts` is
-the documentation string of the function, or ``None`` if undefined.
+If a code object represents a function and has a docstring,
+the first item in :attr:`~codeobject.co_consts` is
+the docstring of the function.
Methods on code objects
~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/Include/cpython/code.h b/Include/cpython/code.h
index af9149b9c38..2561b2b88ba 100644
--- a/Include/cpython/code.h
+++ b/Include/cpython/code.h
@@ -174,6 +174,11 @@ struct PyCodeObject _PyCode_DEF(1);
#define CO_NO_MONITORING_EVENTS 0x2000000
+/* Whether the code object has a docstring,
+ If so, it will be the first item in co_consts
+*/
+#define CO_HAS_DOCSTRING 0x4000000
+
/* This should be defined if a future statement modifies the syntax.
For example, when a keyword is added.
*/
diff --git a/Include/internal/pycore_symtable.h b/Include/internal/pycore_symtable.h
index 7e3d45adcec..91dac767d58 100644
--- a/Include/internal/pycore_symtable.h
+++ b/Include/internal/pycore_symtable.h
@@ -123,6 +123,7 @@ typedef struct _symtable_entry {
unsigned ste_comp_iter_target : 1; /* true if visiting comprehension target */
unsigned ste_can_see_class_scope : 1; /* true if this block can see names bound in an
enclosing class scope */
+ unsigned ste_has_docstring : 1; /* true if docstring present */
int ste_comp_iter_expr; /* non-zero if visiting a comprehension range expression */
_Py_SourceLocation ste_loc; /* source location of block */
struct _symtable_entry *ste_annotation_block; /* symbol table entry for this entry's annotations */
diff --git a/Lib/dis.py b/Lib/dis.py
index c28fa6b3dc4..1718e39cceb 100644
--- a/Lib/dis.py
+++ b/Lib/dis.py
@@ -151,16 +151,17 @@ def distb(tb=None, *, file=None, show_caches=False, adaptive=False, show_offsets
# list of CO_* constants. It is also used by pretty_flags to
# turn the co_flags field into a human readable list.
COMPILER_FLAG_NAMES = {
- 1: "OPTIMIZED",
- 2: "NEWLOCALS",
- 4: "VARARGS",
- 8: "VARKEYWORDS",
- 16: "NESTED",
- 32: "GENERATOR",
- 64: "NOFREE",
- 128: "COROUTINE",
- 256: "ITERABLE_COROUTINE",
- 512: "ASYNC_GENERATOR",
+ 1: "OPTIMIZED",
+ 2: "NEWLOCALS",
+ 4: "VARARGS",
+ 8: "VARKEYWORDS",
+ 16: "NESTED",
+ 32: "GENERATOR",
+ 64: "NOFREE",
+ 128: "COROUTINE",
+ 256: "ITERABLE_COROUTINE",
+ 512: "ASYNC_GENERATOR",
+ 0x4000000: "HAS_DOCSTRING",
}
def pretty_flags(flags):
diff --git a/Lib/inspect.py b/Lib/inspect.py
index ea0d992436e..08718d82e91 100644
--- a/Lib/inspect.py
+++ b/Lib/inspect.py
@@ -56,6 +56,7 @@ __all__ = [
"CO_OPTIMIZED",
"CO_VARARGS",
"CO_VARKEYWORDS",
+ "CO_HAS_DOCSTRING",
"ClassFoundException",
"ClosureVars",
"EndOfBlock",
@@ -409,6 +410,7 @@ def iscode(object):
co_flags bitmap: 1=optimized | 2=newlocals | 4=*arg | 8=**arg
| 16=nested | 32=generator | 64=nofree | 128=coroutine
| 256=iterable_coroutine | 512=async_generator
+ | 0x4000000=has_docstring
co_freevars tuple of names of free variables
co_posonlyargcount number of positional only arguments
co_kwonlyargcount number of keyword only arguments (not including ** arg)
diff --git a/Lib/test/test_code.py b/Lib/test/test_code.py
index dcdd15a43e6..93c65a82508 100644
--- a/Lib/test/test_code.py
+++ b/Lib/test/test_code.py
@@ -17,7 +17,7 @@ cellvars: ('x',)
freevars: ()
nlocals: 2
flags: 3
-consts: ('None', '<code object g>')
+consts: ('<code object g>',)
>>> dump(f(4).__code__)
name: g
@@ -86,7 +86,7 @@ varnames: ()
cellvars: ()
freevars: ()
nlocals: 0
-flags: 3
+flags: 67108867
consts: ("'doc string'", 'None')
>>> def keywordonly_args(a,b,*,k1):
@@ -123,6 +123,61 @@ nlocals: 3
flags: 3
consts: ('None',)
+>>> def has_docstring(x: str):
+... 'This is a one-line doc string'
+... x += x
+... x += "hello world"
+... # co_flags should be 0x4000003 = 67108867
+... return x
+
+>>> dump(has_docstring.__code__)
+name: has_docstring
+argcount: 1
+posonlyargcount: 0
+kwonlyargcount: 0
+names: ()
+varnames: ('x',)
+cellvars: ()
+freevars: ()
+nlocals: 1
+flags: 67108867
+consts: ("'This is a one-line doc string'", "'hello world'")
+
+>>> async def async_func_docstring(x: str, y: str):
+... "This is a docstring from async function"
+... import asyncio
+... await asyncio.sleep(1)
+... # co_flags should be 0x4000083 = 67108995
+... return x + y
+
+>>> dump(async_func_docstring.__code__)
+name: async_func_docstring
+argcount: 2
+posonlyargcount: 0
+kwonlyargcount: 0
+names: ('asyncio', 'sleep')
+varnames: ('x', 'y', 'asyncio')
+cellvars: ()
+freevars: ()
+nlocals: 3
+flags: 67108995
+consts: ("'This is a docstring from async function'", 'None')
+
+>>> def no_docstring(x, y, z):
+... return x + "hello" + y + z + "world"
+
+>>> dump(no_docstring.__code__)
+name: no_docstring
+argcount: 3
+posonlyargcount: 0
+kwonlyargcount: 0
+names: ()
+varnames: ('x', 'y', 'z')
+cellvars: ()
+freevars: ()
+nlocals: 3
+flags: 3
+consts: ("'hello'", "'world'")
"""
import copy
diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py
index 958170f675e..85ae71c1f77 100644
--- a/Lib/test/test_compile.py
+++ b/Lib/test/test_compile.py
@@ -834,7 +834,7 @@ class TestSpecifics(unittest.TestCase):
return "unused"
self.assertEqual(f.__code__.co_consts,
- (None, "used"))
+ (True, "used"))
@support.cpython_only
def test_remove_unused_consts_extended_args(self):
@@ -852,9 +852,9 @@ class TestSpecifics(unittest.TestCase):
eval(compile(code, "file.py", "exec"), g)
exec(code, g)
f = g['f']
- expected = tuple([None, ''] + [f't{i}' for i in range(N)])
+ expected = tuple([''] + [f't{i}' for i in range(N)])
self.assertEqual(f.__code__.co_consts, expected)
- expected = "".join(expected[2:])
+ expected = "".join(expected[1:])
self.assertEqual(expected, f())
# Stripping unused constants is not a strict requirement for the
@@ -1244,7 +1244,7 @@ class TestSpecifics(unittest.TestCase):
y)
genexp_lines = [0, 4, 2, 0, 4]
- genexp_code = return_genexp.__code__.co_consts[1]
+ genexp_code = return_genexp.__code__.co_consts[0]
code_lines = self.get_code_lines(genexp_code)
self.assertEqual(genexp_lines, code_lines)
diff --git a/Lib/test/test_compiler_assemble.py b/Lib/test/test_compiler_assemble.py
index 625d3c704c3..c4962e35999 100644
--- a/Lib/test/test_compiler_assemble.py
+++ b/Lib/test/test_compiler_assemble.py
@@ -84,7 +84,7 @@ class IsolatedAssembleTests(AssemblerTestCase):
return x
return inner() % 2
- inner_code = mod_two.__code__.co_consts[1]
+ inner_code = mod_two.__code__.co_consts[0]
assert isinstance(inner_code, types.CodeType)
metadata = {
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index bd1d1735181..3c6570afa50 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -197,7 +197,7 @@ dis_bug1333982 = """\
%3d RESUME 0
%3d LOAD_COMMON_CONSTANT 0 (AssertionError)
- LOAD_CONST 1 (<code object <genexpr> at 0x..., file "%s", line %d>)
+ LOAD_CONST 0 (<code object <genexpr> at 0x..., file "%s", line %d>)
MAKE_FUNCTION
LOAD_FAST 0 (x)
GET_ITER
@@ -276,10 +276,10 @@ dis_kw_names = """\
LOAD_SMALL_INT 1
LOAD_SMALL_INT 2
LOAD_SMALL_INT 5
- LOAD_CONST 1 (('c',))
+ LOAD_CONST 0 (('c',))
CALL_KW 3
POP_TOP
- LOAD_CONST 0 (None)
+ LOAD_CONST 1 (None)
RETURN_VALUE
""" % (wrap_func_w_kwargs.__code__.co_firstlineno,
wrap_func_w_kwargs.__code__.co_firstlineno + 1)
@@ -500,18 +500,18 @@ dis_fstring = """\
%3d LOAD_FAST 0 (a)
FORMAT_SIMPLE
- LOAD_CONST 1 (' ')
+ LOAD_CONST 0 (' ')
LOAD_FAST 1 (b)
- LOAD_CONST 2 ('4')
+ LOAD_CONST 1 ('4')
FORMAT_WITH_SPEC
- LOAD_CONST 1 (' ')
+ LOAD_CONST 0 (' ')
LOAD_FAST 2 (c)
CONVERT_VALUE 2 (repr)
FORMAT_SIMPLE
- LOAD_CONST 1 (' ')
+ LOAD_CONST 0 (' ')
LOAD_FAST 3 (d)
CONVERT_VALUE 2 (repr)
- LOAD_CONST 2 ('4')
+ LOAD_CONST 1 ('4')
FORMAT_WITH_SPEC
BUILD_STRING 7
RETURN_VALUE
@@ -785,7 +785,7 @@ dis_nested_0 = """\
%4d LOAD_FAST 0 (y)
BUILD_TUPLE 1
- LOAD_CONST 1 (<code object foo at 0x..., file "%s", line %d>)
+ LOAD_CONST 0 (<code object foo at 0x..., file "%s", line %d>)
MAKE_FUNCTION
SET_FUNCTION_ATTRIBUTE 8 (closure)
STORE_FAST 1 (foo)
@@ -884,7 +884,7 @@ dis_loop_test_quickened_code = """\
%3d RESUME_CHECK 0
%3d BUILD_LIST 0
- LOAD_CONST 1 ((1, 2, 3))
+ LOAD_CONST 0 ((1, 2, 3))
LIST_EXTEND 1
LOAD_SMALL_INT 3
BINARY_OP 5 (*)
@@ -900,7 +900,7 @@ dis_loop_test_quickened_code = """\
%3d L2: END_FOR
POP_TOP
- LOAD_CONST_IMMORTAL 0 (None)
+ LOAD_CONST_IMMORTAL 1 (None)
RETURN_VALUE
""" % (loop_test.__code__.co_firstlineno,
loop_test.__code__.co_firstlineno + 1,
@@ -913,12 +913,12 @@ def extended_arg_quick():
dis_extended_arg_quick_code = """\
%3d RESUME 0
-%3d LOAD_CONST 1 (Ellipsis)
+%3d LOAD_CONST 0 (Ellipsis)
EXTENDED_ARG 1
UNPACK_EX 256
POP_TOP
STORE_FAST 0 (_)
- LOAD_CONST 0 (None)
+ LOAD_CONST 1 (None)
RETURN_VALUE
"""% (extended_arg_quick.__code__.co_firstlineno,
extended_arg_quick.__code__.co_firstlineno + 1,)
@@ -1465,7 +1465,7 @@ Positional-only arguments: 0
Kw-only arguments: 0
Number of locals: 1
Stack size: \\d+
-Flags: OPTIMIZED, NEWLOCALS
+Flags: OPTIMIZED, NEWLOCALS, HAS_DOCSTRING
Constants:
{code_info_consts}
Names:
@@ -1491,8 +1491,8 @@ Number of locals: 10
Stack size: \\d+
Flags: OPTIMIZED, NEWLOCALS, VARARGS, VARKEYWORDS, GENERATOR
Constants:
- 0: None
- 1: <code object f at (.*), file "(.*)", line (.*)>
+ 0: <code object f at (.*), file "(.*)", line (.*)>
+ 1: None
Variable names:
0: a
1: b
@@ -1510,10 +1510,12 @@ Cell variables:
2: [abedfxyz]
3: [abedfxyz]
4: [abedfxyz]
- 5: [abedfxyz]"""
+ 5: [abedfxyz]
+ 6: [abedfxyz]
+ 7: [abedfxyz]"""
# NOTE: the order of the cell variables above depends on dictionary order!
-co_tricky_nested_f = tricky.__func__.__code__.co_consts[1]
+co_tricky_nested_f = tricky.__func__.__code__.co_consts[0]
code_info_tricky_nested_f = """\
Filename: (.*)
@@ -1677,9 +1679,9 @@ def jumpy():
# End fodder for opinfo generation tests
expected_outer_line = 1
_line_offset = outer.__code__.co_firstlineno - 1
-code_object_f = outer.__code__.co_consts[1]
+code_object_f = outer.__code__.co_consts[0]
expected_f_line = code_object_f.co_firstlineno - _line_offset
-code_object_inner = code_object_f.co_consts[1]
+code_object_inner = code_object_f.co_consts[0]
expected_inner_line = code_object_inner.co_firstlineno - _line_offset
expected_jumpy_line = 1
@@ -1735,11 +1737,11 @@ expected_opinfo_outer = [
Instruction(opname='MAKE_CELL', opcode=92, arg=0, argval='a', argrepr='a', offset=0, start_offset=0, starts_line=True, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='MAKE_CELL', opcode=92, arg=1, argval='b', argrepr='b', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=4, start_offset=4, starts_line=True, line_number=1, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=79, arg=4, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval=(3, 4), argrepr='(3, 4)', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='a', argrepr='a', offset=8, start_offset=8, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_FAST', opcode=81, arg=1, argval='b', argrepr='b', offset=10, start_offset=10, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
Instruction(opname='BUILD_TUPLE', opcode=48, arg=2, argval=2, argrepr='', offset=12, start_offset=12, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=79, arg=1, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval=code_object_f, argrepr=repr(code_object_f), offset=14, start_offset=14, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
Instruction(opname='MAKE_FUNCTION', opcode=23, arg=None, argval=None, argrepr='', offset=16, start_offset=16, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=103, arg=8, argval=8, argrepr='closure', offset=18, start_offset=18, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=103, arg=1, argval=1, argrepr='defaults', offset=20, start_offset=20, starts_line=False, line_number=2, label=None, positions=None, cache_info=None),
@@ -1747,11 +1749,11 @@ expected_opinfo_outer = [
Instruction(opname='LOAD_GLOBAL', opcode=87, arg=1, argval='print', argrepr='print + NULL', offset=24, start_offset=24, starts_line=True, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
Instruction(opname='LOAD_DEREF', opcode=80, arg=0, argval='a', argrepr='a', offset=34, start_offset=34, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_DEREF', opcode=80, arg=1, argval='b', argrepr='b', offset=36, start_offset=36, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=79, arg=2, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=1, argval='', argrepr="''", offset=38, start_offset=38, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_SMALL_INT', opcode=89, arg=1, argval=1, argrepr='', offset=40, start_offset=40, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
Instruction(opname='BUILD_LIST', opcode=43, arg=0, argval=0, argrepr='', offset=42, start_offset=42, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
Instruction(opname='BUILD_MAP', opcode=44, arg=0, argval=0, argrepr='', offset=44, start_offset=44, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=2, argval='Hello world!', argrepr="'Hello world!'", offset=46, start_offset=46, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
Instruction(opname='CALL', opcode=49, arg=7, argval=7, argrepr='', offset=48, start_offset=48, starts_line=False, line_number=7, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=56, start_offset=56, starts_line=False, line_number=7, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_FAST', opcode=81, arg=2, argval='f', argrepr='f', offset=58, start_offset=58, starts_line=True, line_number=8, label=None, positions=None, cache_info=None),
@@ -1763,13 +1765,13 @@ expected_opinfo_f = [
Instruction(opname='MAKE_CELL', opcode=92, arg=0, argval='c', argrepr='c', offset=2, start_offset=2, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='MAKE_CELL', opcode=92, arg=1, argval='d', argrepr='d', offset=4, start_offset=4, starts_line=False, line_number=None, label=None, positions=None, cache_info=None),
Instruction(opname='RESUME', opcode=149, arg=0, argval=0, argrepr='', offset=6, start_offset=6, starts_line=True, line_number=2, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=79, arg=2, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=1, argval=(5, 6), argrepr='(5, 6)', offset=8, start_offset=8, starts_line=True, line_number=3, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_FAST', opcode=81, arg=3, argval='a', argrepr='a', offset=10, start_offset=10, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_FAST', opcode=81, arg=4, argval='b', argrepr='b', offset=12, start_offset=12, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_FAST', opcode=81, arg=0, argval='c', argrepr='c', offset=14, start_offset=14, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_FAST', opcode=81, arg=1, argval='d', argrepr='d', offset=16, start_offset=16, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
Instruction(opname='BUILD_TUPLE', opcode=48, arg=4, argval=4, argrepr='', offset=18, start_offset=18, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=79, arg=1, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval=code_object_inner, argrepr=repr(code_object_inner), offset=20, start_offset=20, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
Instruction(opname='MAKE_FUNCTION', opcode=23, arg=None, argval=None, argrepr='', offset=22, start_offset=22, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=103, arg=8, argval=8, argrepr='closure', offset=24, start_offset=24, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
Instruction(opname='SET_FUNCTION_ATTRIBUTE', opcode=103, arg=1, argval=1, argrepr='defaults', offset=26, start_offset=26, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
@@ -1827,7 +1829,7 @@ expected_opinfo_jumpy = [
Instruction(opname='END_FOR', opcode=9, arg=None, argval=None, argrepr='', offset=88, start_offset=88, starts_line=True, line_number=3, label=4, positions=None, cache_info=None),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=90, start_offset=90, starts_line=False, line_number=3, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=92, start_offset=92, starts_line=True, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_CONST', opcode=79, arg=1, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=102, start_offset=102, starts_line=False, line_number=10, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval='I can haz else clause?', argrepr="'I can haz else clause?'", offset=102, start_offset=102, starts_line=False, line_number=10, label=None, positions=None, cache_info=None),
Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=104, start_offset=104, starts_line=False, line_number=10, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=112, start_offset=112, starts_line=False, line_number=10, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_FAST_CHECK', opcode=83, arg=0, argval='i', argrepr='i', offset=114, start_offset=114, starts_line=True, line_number=11, label=5, positions=None, cache_info=None),
@@ -1853,7 +1855,7 @@ expected_opinfo_jumpy = [
Instruction(opname='JUMP_BACKWARD', opcode=72, arg=39, argval=114, argrepr='to L5', offset=188, start_offset=188, starts_line=False, line_number=16, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00')]),
Instruction(opname='JUMP_FORWARD', opcode=74, arg=11, argval=216, argrepr='to L9', offset=192, start_offset=192, starts_line=True, line_number=17, label=7, positions=None, cache_info=None),
Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=194, start_offset=194, starts_line=True, line_number=19, label=8, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_CONST', opcode=79, arg=2, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=204, start_offset=204, starts_line=False, line_number=19, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=1, argval='Who let lolcatz into this test suite?', argrepr="'Who let lolcatz into this test suite?'", offset=204, start_offset=204, starts_line=False, line_number=19, label=None, positions=None, cache_info=None),
Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=206, start_offset=206, starts_line=False, line_number=19, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=214, start_offset=214, starts_line=False, line_number=19, label=None, positions=None, cache_info=None),
Instruction(opname='NOP', opcode=27, arg=None, argval=None, argrepr='', offset=216, start_offset=216, starts_line=True, line_number=20, label=9, positions=None, cache_info=None),
@@ -1870,19 +1872,19 @@ expected_opinfo_jumpy = [
Instruction(opname='CALL', opcode=49, arg=0, argval=0, argrepr='', offset=240, start_offset=240, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='STORE_FAST', opcode=107, arg=1, argval='dodgy', argrepr='dodgy', offset=248, start_offset=248, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=250, start_offset=250, starts_line=True, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
- Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval='Never reach this', argrepr="'Never reach this'", offset=260, start_offset=260, starts_line=False, line_number=26, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=2, argval='Never reach this', argrepr="'Never reach this'", offset=260, start_offset=260, starts_line=False, line_number=26, label=None, positions=None, cache_info=None),
Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=262, start_offset=262, starts_line=False, line_number=26, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=270, start_offset=270, starts_line=False, line_number=26, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval=None, argrepr='None', offset=272, start_offset=272, starts_line=True, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval=None, argrepr='None', offset=274, start_offset=274, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval=None, argrepr='None', offset=276, start_offset=276, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval=None, argrepr='None', offset=272, start_offset=272, starts_line=True, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval=None, argrepr='None', offset=274, start_offset=274, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval=None, argrepr='None', offset=276, start_offset=276, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
Instruction(opname='CALL', opcode=49, arg=3, argval=3, argrepr='', offset=278, start_offset=278, starts_line=False, line_number=25, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=286, start_offset=286, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
Instruction(opname='LOAD_GLOBAL', opcode=87, arg=3, argval='print', argrepr='print + NULL', offset=288, start_offset=288, starts_line=True, line_number=28, label=10, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('index', 1, b'\x00\x00'), ('module_keys_version', 1, b'\x00\x00'), ('builtin_keys_version', 1, b'\x00\x00')]),
Instruction(opname='LOAD_CONST', opcode=79, arg=5, argval="OK, now we're done", argrepr='"OK, now we\'re done"', offset=298, start_offset=298, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
Instruction(opname='CALL', opcode=49, arg=1, argval=1, argrepr='', offset=300, start_offset=300, starts_line=False, line_number=28, label=None, positions=None, cache_info=[('counter', 1, b'\x00\x00'), ('func_version', 2, b'\x00\x00\x00\x00')]),
Instruction(opname='POP_TOP', opcode=29, arg=None, argval=None, argrepr='', offset=308, start_offset=308, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
- Instruction(opname='LOAD_CONST', opcode=79, arg=0, argval=None, argrepr='None', offset=310, start_offset=310, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
+ Instruction(opname='LOAD_CONST', opcode=79, arg=3, argval=None, argrepr='None', offset=310, start_offset=310, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
Instruction(opname='RETURN_VALUE', opcode=33, arg=None, argval=None, argrepr='', offset=312, start_offset=312, starts_line=False, line_number=28, label=None, positions=None, cache_info=None),
Instruction(opname='PUSH_EXC_INFO', opcode=30, arg=None, argval=None, argrepr='', offset=314, start_offset=314, starts_line=True, line_number=25, label=None, positions=None, cache_info=None),
Instruction(opname='WITH_EXCEPT_START', opcode=41, arg=None, argval=None, argrepr='', offset=316, start_offset=316, starts_line=False, line_number=25, label=None, positions=None, cache_info=None),
diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py
index a4857dadec2..2250b7e76da 100644
--- a/Lib/test/test_inspect/test_inspect.py
+++ b/Lib/test/test_inspect/test_inspect.py
@@ -5373,7 +5373,7 @@ class TestSignatureBind(unittest.TestCase):
# Issue #19611: getcallargs should work with comprehensions
def make_set():
return set(z * z for z in range(5))
- gencomp_code = make_set.__code__.co_consts[1]
+ gencomp_code = make_set.__code__.co_consts[0]
gencomp_func = types.FunctionType(gencomp_code, {})
iterator = iter(range(5))
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-10-29-10-37-39.gh-issue-126072.XLKlxv.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-29-10-37-39.gh-issue-126072.XLKlxv.rst
new file mode 100644
index 00000000000..4ad30e9f954
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-10-29-10-37-39.gh-issue-126072.XLKlxv.rst
@@ -0,0 +1,3 @@
+Add a new attribute in :attr:`~codeobject.co_flags` to indicate whether the
+first item in :attr:`~codeobject.co_consts` is the docstring. If a code
+object has no docstring, ``None`` will **NOT** be inserted.
diff --git a/Objects/funcobject.c b/Objects/funcobject.c
index e72a7d98c0a..1f2387f6844 100644
--- a/Objects/funcobject.c
+++ b/Objects/funcobject.c
@@ -162,7 +162,8 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
PyObject *consts = code_obj->co_consts;
assert(PyTuple_Check(consts));
PyObject *doc;
- if (PyTuple_Size(consts) >= 1) {
+ if (code_obj->co_flags & CO_HAS_DOCSTRING) {
+ assert(PyTuple_Size(consts) >= 1);
doc = PyTuple_GetItem(consts, 0);
if (!PyUnicode_Check(doc)) {
doc = Py_None;
diff --git a/Python/codegen.c b/Python/codegen.c
index 976c94234d6..d79aee4859e 100644
--- a/Python/codegen.c
+++ b/Python/codegen.c
@@ -1243,10 +1243,10 @@ codegen_function_body(compiler *c, stmt_ty s, int is_async, Py_ssize_t funcflags
_PyCompile_ExitScope(c);
return ERROR;
}
+ Py_ssize_t idx = _PyCompile_AddConst(c, docstring);
+ Py_DECREF(docstring);
+ RETURN_IF_ERROR_IN_SCOPE(c, idx < 0 ? ERROR : SUCCESS);
}
- Py_ssize_t idx = _PyCompile_AddConst(c, docstring ? docstring : Py_None);
- Py_XDECREF(docstring);
- RETURN_IF_ERROR_IN_SCOPE(c, idx < 0 ? ERROR : SUCCESS);
NEW_JUMP_TARGET_LABEL(c, start);
USE_LABEL(c, start);
diff --git a/Python/compile.c b/Python/compile.c
index d463fcde204..4dcb9a1b5ac 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -1285,6 +1285,8 @@ compute_code_flags(compiler *c)
flags |= CO_VARARGS;
if (ste->ste_varkeywords)
flags |= CO_VARKEYWORDS;
+ if (ste->ste_has_docstring)
+ flags |= CO_HAS_DOCSTRING;
}
if (ste->ste_coroutine && !ste->ste_generator) {
diff --git a/Python/symtable.c b/Python/symtable.c
index 709918b27af..32d715197c5 100644
--- a/Python/symtable.c
+++ b/Python/symtable.c
@@ -136,6 +136,8 @@ ste_new(struct symtable *st, identifier name, _Py_block_ty block,
ste->ste_needs_classdict = 0;
ste->ste_annotation_block = NULL;
+ ste->ste_has_docstring = 0;
+
ste->ste_symbols = PyDict_New();
ste->ste_varnames = PyList_New(0);
ste->ste_children = PyList_New(0);
@@ -1841,6 +1843,10 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
return 0;
}
+ if (_PyAST_GetDocString(s->v.FunctionDef.body)) {
+ new_ste->ste_has_docstring = 1;
+ }
+
if (!symtable_visit_annotations(st, s, s->v.FunctionDef.args,
s->v.FunctionDef.returns, new_ste)) {
Py_DECREF(new_ste);
@@ -2168,6 +2174,10 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
return 0;
}
+ if (_PyAST_GetDocString(s->v.AsyncFunctionDef.body)) {
+ new_ste->ste_has_docstring = 1;
+ }
+
if (!symtable_visit_annotations(st, s, s->v.AsyncFunctionDef.args,
s->v.AsyncFunctionDef.returns, new_ste)) {
Py_DECREF(new_ste);