Skip to content

gh-118720: Add PyFrame_SetLineNumber() function #131246

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions Doc/c-api/frame.rst
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,18 @@ See also :ref:`Reflection <reflection>`.
Return the line number that *frame* is currently executing.


.. c:function:: int PyFrame_SetLineNumber(PyFrameObject *frame, int lineno)

Set a frame line number.

*lineno* must be greater than or equal to ``1``.

Do not check if *lineno* is valid for the frame code object.
Do not update the instruction pointer.

.. versionadded:: next


Frame Locals Proxies
^^^^^^^^^^^^^^^^^^^^

Expand Down Expand Up @@ -191,5 +203,3 @@ Unless using :pep:`523`, you will not need this.
Return the currently executing line number, or -1 if there is no line number.

.. versionadded:: 3.12


3 changes: 3 additions & 0 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1590,6 +1590,9 @@ New features
take a C integer and produce a Python :class:`bool` object. (Contributed by
Pablo Galindo in :issue:`45325`.)

* Add :c:func:`PyFrame_SetLineNumber` function to set a frame line number.
(Contributed by Victor Stinner in :gh:`118720`.)


Limited C API changes
---------------------
Expand Down
1 change: 1 addition & 0 deletions Include/cpython/pyframe.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ PyAPI_FUNC(PyObject *) PyFrame_GetGenerator(PyFrameObject *frame);
PyAPI_FUNC(int) PyFrame_GetLasti(PyFrameObject *frame);
PyAPI_FUNC(PyObject*) PyFrame_GetVar(PyFrameObject *frame, PyObject *name);
PyAPI_FUNC(PyObject*) PyFrame_GetVarString(PyFrameObject *frame, const char *name);
PyAPI_FUNC(int) PyFrame_SetLineNumber(PyFrameObject *frame, int lineno);

/* The following functions are for use by debuggers and other tools
* implementing custom frame evaluators with PEP 523. */
Expand Down
17 changes: 17 additions & 0 deletions Lib/test/test_capi/test_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@


_testcapi = import_helper.import_module('_testcapi')
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')


class FrameTest(unittest.TestCase):
Expand Down Expand Up @@ -51,6 +52,22 @@ def dummy():
# The following line should not cause a segmentation fault.
self.assertIsNone(frame.f_back)

def test_lineno(self):
# Test PyFrame_GetLineNumber() and PyFrame_SetLineNumber()
frame_getlinenumber = _testlimitedcapi.frame_getlinenumber
frame_setlinenumber = _testcapi.frame_setlinenumber

frame = sys._getframe()
frame_setlinenumber(frame, 123)
self.assertEqual(frame_getlinenumber(frame), 123)
frame_setlinenumber(frame, 222)
self.assertEqual(frame_getlinenumber(frame), 222)

for invalid in (-10, -1, 0):
with self.subTest(invalid):
with self.assertRaises(ValueError):
frame_setlinenumber(frame, invalid)


if __name__ == "__main__":
unittest.main()
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :c:func:`PyFrame_SetLineNumber` function to set a frame line number.
Patch by Victor Stinner.
2 changes: 1 addition & 1 deletion Modules/Setup.stdlib.in
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c _testcapi/type.c _testcapi/function.c
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c _testlimitedcapi/frame.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c

Expand Down
22 changes: 21 additions & 1 deletion Modules/_testcapi/frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,26 @@ frame_getvarstring(PyObject *self, PyObject *args)
}


static PyObject *
frame_setlinenumber(PyObject *self, PyObject *args)
{
PyObject *frame;
int lineno;
if (!PyArg_ParseTuple(args, "Oi", &frame, &lineno)) {
return NULL;
}
if (!PyFrame_Check(frame)) {
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
return NULL;
}

if (PyFrame_SetLineNumber((PyFrameObject*)frame, lineno) < 0) {
return NULL;
}
Py_RETURN_NONE;
}


static PyMethodDef test_methods[] = {
{"frame_getlocals", frame_getlocals, METH_O, NULL},
{"frame_getglobals", frame_getglobals, METH_O, NULL},
Expand All @@ -123,6 +143,7 @@ static PyMethodDef test_methods[] = {
{"frame_new", frame_new, METH_VARARGS, NULL},
{"frame_getvar", frame_getvar, METH_VARARGS, NULL},
{"frame_getvarstring", frame_getvarstring, METH_VARARGS, NULL},
{"frame_setlinenumber", frame_setlinenumber, METH_VARARGS, NULL},
{NULL},
};

Expand All @@ -131,4 +152,3 @@ _PyTestCapi_Init_Frame(PyObject *m)
{
return PyModule_AddFunctions(m, test_methods);
}

3 changes: 3 additions & 0 deletions Modules/_testlimitedcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,8 @@ PyInit__testlimitedcapi(void)
if (_PyTestLimitedCAPI_Init_File(mod) < 0) {
return NULL;
}
if (_PyTestLimitedCAPI_Init_Frame(mod) < 0) {
return NULL;
}
return mod;
}
22 changes: 22 additions & 0 deletions Modules/_testlimitedcapi/frame.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "parts.h"
#include "util.h"


static PyObject *
frame_getlinenumber(PyObject *self, PyObject *frame)
{
int lineno = PyFrame_GetLineNumber((PyFrameObject*)frame);
return PyLong_FromLong(lineno);
}


static PyMethodDef test_methods[] = {
{"frame_getlinenumber", frame_getlinenumber, METH_O, NULL},
{NULL},
};

int
_PyTestLimitedCAPI_Init_Frame(PyObject *m)
{
return PyModule_AddFunctions(m, test_methods);
}
1 change: 1 addition & 0 deletions Modules/_testlimitedcapi/parts.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ int _PyTestLimitedCAPI_Init_Unicode(PyObject *module);
int _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *module);
int _PyTestLimitedCAPI_Init_Version(PyObject *module);
int _PyTestLimitedCAPI_Init_File(PyObject *module);
int _PyTestLimitedCAPI_Init_Frame(PyObject *module);

#endif // Py_TESTLIMITEDCAPI_PARTS_H
15 changes: 15 additions & 0 deletions Objects/frameobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -971,6 +971,21 @@ frame_getlineno(PyObject *op, void *Py_UNUSED(closure))
}
}


int
PyFrame_SetLineNumber(PyFrameObject *frame, int lineno)
{
if (lineno < 1) {
PyErr_SetString(PyExc_ValueError,
"lineno must be greater than or equal to 1");
return -1;
}

frame->f_lineno = lineno;
return 0;
}


static PyObject *
frame_getlasti(PyObject *op, void *Py_UNUSED(closure))
{
Expand Down
1 change: 1 addition & 0 deletions PCbuild/_testlimitedcapi.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
<ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\version.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\frame.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc" />
Expand Down
1 change: 1 addition & 0 deletions PCbuild/_testlimitedcapi.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\version.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\frame.c" />
<ClCompile Include="..\Modules\_testlimitedcapi.c" />
</ItemGroup>
<ItemGroup>
Expand Down
Loading