Skip to content

Commit 09cbced

Browse files
committed
pythongh-118720: Add PyFrame_SetLineNumber() function
Add tests on PyFrame_GetLineNumber() and PyFrame_SetLineNumber().
1 parent c497f83 commit 09cbced

File tree

13 files changed

+99
-4
lines changed

13 files changed

+99
-4
lines changed

Diff for: Doc/c-api/frame.rst

+12-2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,18 @@ See also :ref:`Reflection <reflection>`.
140140
Return the line number that *frame* is currently executing.
141141
142142
143+
.. c:function:: int PyFrame_SetLineNumber(PyFrameObject *frame, int lineno)
144+
145+
Set a frame line number.
146+
147+
*lineno* must be greater than or equal to ``1``.
148+
149+
Do not check if *lineno* is valid for the frame code object.
150+
Do not update the instruction pointer.
151+
152+
.. versionadded:: next
153+
154+
143155
Frame Locals Proxies
144156
^^^^^^^^^^^^^^^^^^^^
145157
@@ -191,5 +203,3 @@ Unless using :pep:`523`, you will not need this.
191203
Return the currently executing line number, or -1 if there is no line number.
192204
193205
.. versionadded:: 3.12
194-
195-

Diff for: Doc/whatsnew/3.14.rst

+3
Original file line numberDiff line numberDiff line change
@@ -1590,6 +1590,9 @@ New features
15901590
take a C integer and produce a Python :class:`bool` object. (Contributed by
15911591
Pablo Galindo in :issue:`45325`.)
15921592

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

15941597
Limited C API changes
15951598
---------------------

Diff for: Include/cpython/pyframe.h

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ PyAPI_FUNC(PyObject *) PyFrame_GetGenerator(PyFrameObject *frame);
1818
PyAPI_FUNC(int) PyFrame_GetLasti(PyFrameObject *frame);
1919
PyAPI_FUNC(PyObject*) PyFrame_GetVar(PyFrameObject *frame, PyObject *name);
2020
PyAPI_FUNC(PyObject*) PyFrame_GetVarString(PyFrameObject *frame, const char *name);
21+
PyAPI_FUNC(int) PyFrame_SetLineNumber(PyFrameObject *frame, int lineno);
2122

2223
/* The following functions are for use by debuggers and other tools
2324
* implementing custom frame evaluators with PEP 523. */

Diff for: Lib/test/test_capi/test_frame.py

+17
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55

66
_testcapi = import_helper.import_module('_testcapi')
7+
_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
78

89

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

55+
def test_lineno(self):
56+
# Test PyFrame_GetLineNumber() and PyFrame_SetLineNumber()
57+
frame_getlinenumber = _testlimitedcapi.frame_getlinenumber
58+
frame_setlinenumber = _testcapi.frame_setlinenumber
59+
60+
frame = sys._getframe()
61+
frame_setlinenumber(frame, 123)
62+
self.assertEqual(frame_getlinenumber(frame), 123)
63+
frame_setlinenumber(frame, 222)
64+
self.assertEqual(frame_getlinenumber(frame), 222)
65+
66+
for invalid in (-10, -1, 0):
67+
with self.subTest(invalid):
68+
with self.assertRaises(ValueError):
69+
frame_setlinenumber(frame, invalid)
70+
5471

5572
if __name__ == "__main__":
5673
unittest.main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :c:func:`PyFrame_SetLineNumber` function to set a frame line number.
2+
Patch by Victor Stinner.

Diff for: Modules/Setup.stdlib.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@
163163
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
164164
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
165165
@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
166-
@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
166+
@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
167167
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
168168
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
169169

Diff for: Modules/_testcapi/frame.c

+21-1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,26 @@ frame_getvarstring(PyObject *self, PyObject *args)
114114
}
115115

116116

117+
static PyObject *
118+
frame_setlinenumber(PyObject *self, PyObject *args)
119+
{
120+
PyObject *frame;
121+
int lineno;
122+
if (!PyArg_ParseTuple(args, "Oi", &frame, &lineno)) {
123+
return NULL;
124+
}
125+
if (!PyFrame_Check(frame)) {
126+
PyErr_SetString(PyExc_TypeError, "argument must be a frame");
127+
return NULL;
128+
}
129+
130+
if (PyFrame_SetLineNumber((PyFrameObject*)frame, lineno) < 0) {
131+
return NULL;
132+
}
133+
Py_RETURN_NONE;
134+
}
135+
136+
117137
static PyMethodDef test_methods[] = {
118138
{"frame_getlocals", frame_getlocals, METH_O, NULL},
119139
{"frame_getglobals", frame_getglobals, METH_O, NULL},
@@ -123,6 +143,7 @@ static PyMethodDef test_methods[] = {
123143
{"frame_new", frame_new, METH_VARARGS, NULL},
124144
{"frame_getvar", frame_getvar, METH_VARARGS, NULL},
125145
{"frame_getvarstring", frame_getvarstring, METH_VARARGS, NULL},
146+
{"frame_setlinenumber", frame_setlinenumber, METH_VARARGS, NULL},
126147
{NULL},
127148
};
128149

@@ -131,4 +152,3 @@ _PyTestCapi_Init_Frame(PyObject *m)
131152
{
132153
return PyModule_AddFunctions(m, test_methods);
133154
}
134-

Diff for: Modules/_testlimitedcapi.c

+3
Original file line numberDiff line numberDiff line change
@@ -92,5 +92,8 @@ PyInit__testlimitedcapi(void)
9292
if (_PyTestLimitedCAPI_Init_File(mod) < 0) {
9393
return NULL;
9494
}
95+
if (_PyTestLimitedCAPI_Init_Frame(mod) < 0) {
96+
return NULL;
97+
}
9598
return mod;
9699
}

Diff for: Modules/_testlimitedcapi/frame.c

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include "parts.h"
2+
#include "util.h"
3+
4+
5+
static PyObject *
6+
frame_getlinenumber(PyObject *self, PyObject *frame)
7+
{
8+
int lineno = PyFrame_GetLineNumber((PyFrameObject*)frame);
9+
return PyLong_FromLong(lineno);
10+
}
11+
12+
13+
static PyMethodDef test_methods[] = {
14+
{"frame_getlinenumber", frame_getlinenumber, METH_O, NULL},
15+
{NULL},
16+
};
17+
18+
int
19+
_PyTestLimitedCAPI_Init_Frame(PyObject *m)
20+
{
21+
return PyModule_AddFunctions(m, test_methods);
22+
}
23+

Diff for: Modules/_testlimitedcapi/parts.h

+1
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,6 @@ int _PyTestLimitedCAPI_Init_Unicode(PyObject *module);
4343
int _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *module);
4444
int _PyTestLimitedCAPI_Init_Version(PyObject *module);
4545
int _PyTestLimitedCAPI_Init_File(PyObject *module);
46+
int _PyTestLimitedCAPI_Init_Frame(PyObject *module);
4647

4748
#endif // Py_TESTLIMITEDCAPI_PARTS_H

Diff for: Objects/frameobject.c

+13
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,19 @@ PyFrame_GetLineNumber(PyFrameObject *f)
958958
return PyUnstable_InterpreterFrame_GetLine(f->f_frame);
959959
}
960960

961+
int
962+
PyFrame_SetLineNumber(PyFrameObject *frame, int lineno)
963+
{
964+
if (lineno < 1) {
965+
PyErr_SetString(PyExc_ValueError,
966+
"lineno must be greater than or equal to 1");
967+
return -1;
968+
}
969+
970+
frame->f_lineno = lineno;
971+
return 0;
972+
}
973+
961974
static PyObject *
962975
frame_getlineno(PyObject *op, void *Py_UNUSED(closure))
963976
{

Diff for: PCbuild/_testlimitedcapi.vcxproj

+1
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
<ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
116116
<ClCompile Include="..\Modules\_testlimitedcapi\version.c" />
117117
<ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
118+
<ClCompile Include="..\Modules\_testlimitedcapi\frame.c" />
118119
</ItemGroup>
119120
<ItemGroup>
120121
<ResourceCompile Include="..\PC\python_nt.rc" />

Diff for: PCbuild/_testlimitedcapi.vcxproj.filters

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
<ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
3232
<ClCompile Include="..\Modules\_testlimitedcapi\version.c" />
3333
<ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
34+
<ClCompile Include="..\Modules\_testlimitedcapi\frame.c" />
3435
<ClCompile Include="..\Modules\_testlimitedcapi.c" />
3536
</ItemGroup>
3637
<ItemGroup>

0 commit comments

Comments
 (0)