From 770750634656195ca48d7efd8e6c99ee0581f9a3 Mon Sep 17 00:00:00 2001 From: Himesh Rupchandani Date: Tue, 1 Sep 2026 00:25:16 +0530 Subject: [PATCH 1/3] gh-156725: document O(N) behavior of PyCode_Addr2Line and the private line-table API The C-API docs for PyCode_Addr2Line pointed to the PEP 626 section that describes PyLineTable_InitAddressRange / PyLineTable_NextAddressRange / PyLineTable_PreviousAddressRange, but those functions have since been renamed with a leading underscore and moved to the private header Include/internal/pycore_code.h. Point to the current private names, note that they are not a stable public API, and document that PyCode_Addr2Line is O(N). --- Doc/c-api/code.rst | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 57b77f92a7d2e6a..c244505217625ec 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -94,11 +94,27 @@ bound into a function. .. c:function:: int PyCode_Addr2Line(PyCodeObject *co, int byte_offset) - Return the line number of the instruction that occurs on or before ``byte_offset`` and ends after it. + Return the line number of the instruction that occurs on or before + ``byte_offset`` and ends after it. This is ``O(N)`` in the number of + instructions in the code object, so it is not suitable for iterating over + all the line numbers in a code object. + If you just need the line number of a frame, use :c:func:`PyFrame_GetLineNumber` instead. - For efficiently iterating over the line numbers in a code object, use :pep:`the API described in PEP 626 - <0626#out-of-process-debuggers-and-profilers>`. + To iterate efficiently over the line numbers in a code object, use the + private, unstable line-table iteration APIs declared in the private header + ``Include/internal/pycore_code.h``: + + * ``_PyCode_InitAddressRange`` initializes a ``PyCodeAddressRange`` for a code object. + * ``_PyLineTable_NextAddressRange`` advances the range to the next + line-number entry. + * ``_PyLineTable_PreviousAddressRange`` retreats the range to the + previous entry. + + .. warning:: + These functions are **internal and not a public C-API**. They are + declared in a private header, may change or be removed without notice, + and are not exported as stable symbols. .. c:function:: int PyCode_Addr2Location(PyObject *co, int byte_offset, int *start_line, int *start_column, int *end_line, int *end_column) From 7739eef2c643e959ed8077e3d9b69d58711fb037 Mon Sep 17 00:00:00 2001 From: Himesh Rupchandani Date: Wed, 21 Oct 2026 12:26:37 +0530 Subject: [PATCH 2/3] Update code.rst --- Doc/c-api/code.rst | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index c244505217625ec..63f1bbd7f896d9f 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -96,25 +96,13 @@ bound into a function. Return the line number of the instruction that occurs on or before ``byte_offset`` and ends after it. This is ``O(N)`` in the number of - instructions in the code object, so it is not suitable for iterating over + instructions in the code object, so it is not performant for iterating over all the line numbers in a code object. If you just need the line number of a frame, use :c:func:`PyFrame_GetLineNumber` instead. - To iterate efficiently over the line numbers in a code object, use the - private, unstable line-table iteration APIs declared in the private header - ``Include/internal/pycore_code.h``: - - * ``_PyCode_InitAddressRange`` initializes a ``PyCodeAddressRange`` for a code object. - * ``_PyLineTable_NextAddressRange`` advances the range to the next - line-number entry. - * ``_PyLineTable_PreviousAddressRange`` retreats the range to the - previous entry. - - .. warning:: - These functions are **internal and not a public C-API**. They are - declared in a private header, may change or be removed without notice, - and are not exported as stable symbols. + For efficiently iterating over the line numbers in a code object, use :pep:`the API described in PEP 626 + <0626#out-of-process-debuggers-and-profilers>`. .. c:function:: int PyCode_Addr2Location(PyObject *co, int byte_offset, int *start_line, int *start_column, int *end_line, int *end_column) From 22c7ae9934e69011d4a407ecbce6bb03996d3122 Mon Sep 17 00:00:00 2001 From: Himesh Rupchandani Date: Fri, 23 Oct 2026 15:04:09 +0000 Subject: [PATCH 3/3] Update code.rst --- Doc/c-api/code.rst | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Doc/c-api/code.rst b/Doc/c-api/code.rst index 63f1bbd7f896d9f..e3170ad34be96a6 100644 --- a/Doc/c-api/code.rst +++ b/Doc/c-api/code.rst @@ -95,15 +95,12 @@ bound into a function. .. c:function:: int PyCode_Addr2Line(PyCodeObject *co, int byte_offset) Return the line number of the instruction that occurs on or before - ``byte_offset`` and ends after it. This is ``O(N)`` in the number of + ``byte_offset`` and ends after it. This may be ``O(N)`` in the number of instructions in the code object, so it is not performant for iterating over all the line numbers in a code object. If you just need the line number of a frame, use :c:func:`PyFrame_GetLineNumber` instead. - For efficiently iterating over the line numbers in a code object, use :pep:`the API described in PEP 626 - <0626#out-of-process-debuggers-and-profilers>`. - .. c:function:: int PyCode_Addr2Location(PyObject *co, int byte_offset, int *start_line, int *start_column, int *end_line, int *end_column) Sets the passed ``int`` pointers to the source code line and column numbers