msg413795 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-23 15:04 |
I propose to move the PyFrameObject structure to the internal C API.
--
Between Python 3.10 and Python 3.11, the work on optimizing ceval.c modified deeply the PyFrameObject structure. Examples:
* The f_code member has been removed by in bpo-44032 by the commit b11a951f16f0603d98de24fee5c023df83ea552c.
* The f_frame member has been added in bpo-44590 by the commit ae0a2b756255629140efcbe57fc2e714f0267aa3.
Most members have been moved to a new PyFrameObject.f_frame member which has the type "struct _interpreter_frame*". Problem: this type is only part of the *internal* C API.
Moreover, accessing the few remaining members which "didn't change" became dangerous. For example, f_back can be NULL even if the frame has a previous frame: the PyFrame_GetBack() function *must* now be called. See bpo-46356 "[C API] Enforce usage of PyFrame_GetBack()".
Reading directly f_lineno was already dangerous since Python 2.3: the value is only valid if the value is greater than 0. It's way safer to use the clean PyFrame_GetLineNumber() API instead.
PyFrame_GetBack() was added to Python 3.9. You can use the pythoncapi_compat project to get this function on Python 3.8 and older:
=> https://pythoncapi-compat.readthedocs.io/
PyFrame_GetLineNumber() was added to the limited API in Python 3.10.
=> Documentation: https://docs.python.org/dev/c-api/reflection.html#c.PyFrame_GetBack
--
There *are* projects accessing directly PyFrameObject like the gevent project which sets the f_code member (moved to f_frame.f_code in Python 3.11). It's broken on Python 3.11:
https://bugs.python.org/issue40421#msg413719
Debuggers and profilers also want to read PyFrameObject directly. IMO for these *specific* use cases, using the *internal* C API is a legit use case and it's fine.
Moving PyFrameObject to the internal C API would clarify the situation. Currently, What's New in Python 3.11 documents the change this with warning:
"While the documentation notes that the fields of PyFrameObject are subject to change at any time, they have been stable for a long time and were used in several popular extensions. "
--
I'm mostly worried about Cython which still get and set many PyFrameObject members directly (ex: f_lasti, f_lineno, f_localsplus, f_trace), since there are no public functions for that.
=> https://bugs.python.org/issue40421#msg367550
Right now, I would suggest Cython to use the internal C API, and *later* consider adding new getter and setter functions. I don't think that we can solve all problems at once: it takes take to design clean API and use them in Cython.
Python 3.11 already broke Cython since most PyFrameObject members moved into the new "internal" PyFrameObject.f_frame API which requires using the internal C API to get "struct _interpreter_frame".
=> https://github.com/cython/cython/issues/4500
--
Using a frame using the *public* C API was and remains supported. Short example:
--
PyThreadState *tstate = PyThreadState_Get();
PyFrameObject* frame = PyThreadState_GetFrame(tstate);
int lineno = PyFrame_GetLineNumber(frame);
---
The PyFrameObject structure is opaque and members are not accessed directly: it's fine.
|
msg413796 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-23 15:05 |
By the way, Include/cpython/ceval.h uses the "struct _interpreter_frame*" type whereas this type is part of the internal C API:
PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _interpreter_frame *f, int exc);
Maybe we should move this defintion to the internal C API pycore_ceval.h.
|
msg413801 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-23 15:17 |
See also bpo-40421 "[C API] Add getter functions for PyFrameObject and maybe move PyFrameObject to the internal C API". I added getter functions in recent Python versions:
* PyFrame_GetBack(): Python 3.9
* PyFrame_GetCode(): Python 3.9
* PyFrame_GetLineNumber() added to the limited C API version 3.10
|
msg413802 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-23 15:19 |
See also bpo-44800 "Code readability: rename InterpreterFrame to _Py_framedata".
|
msg413803 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-23 15:21 |
See also bpo-45247: [C API] Add explicit support for Cython to the C API.
|
msg413805 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-23 15:23 |
I marked my PR as a draft since this change is an incompatible change. Even if PyFrameObject structure is excluded from the limited C API and not documented, it's used by a few projects. I plan to check how this change impacts these projects before merging the change.
For example, test this change on:
* Cython: https://github.com/cython/cython/issues/4500
* gevent: https://github.com/gevent/gevent/issues/1867
* coverage uses f_lasti: https://bugs.python.org/issue40421#msg403814
|
msg413807 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-23 15:29 |
See also the bpo-39947: "[C API] Make the PyThreadState structure opaque (move it to the internal C API)". Recently, I also added helper functions:
* PyThreadState_GetFrame(): Python 3.9 and limited C API version 3.10
* PyThreadState_GetID(): Python 3.9 and limited C API version 3.10
* PyThreadState_GetInterpreter(): Python 3.9 and limited C API version 3.10
* PyThreadState_EnterTracing(), PyThreadState_LeaveTracing(): Python 3.11
See also pending GH-29121 of bpo-39947: "Add PyThreadState_SetTrace() function".
|
msg413828 - (view) |
Author: Petr Viktorin (petr.viktorin) * |
Date: 2022-02-23 17:20 |
So, this will break Cython and gevent, but (unlike the optimization work that broke f_code/f_frame) it won't provide any value to users?
I don't see how that's a good idea.
|
msg413829 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-23 17:27 |
Petr Viktorin:
> So, this will break Cython and gevent,
This change doesn't break Cython and gevent: they are already broken.
> but (unlike the optimization work that broke f_code/f_frame) it won't provide any value to users?
The problem is that the C API changed silently: existing code which gets directly PyFrameObject.f_back still compiles successfully, but it will no longer work in some cases.
See bpo-46356 "[C API] Enforce usage of PyFrame_GetBack()" for more details.
The intent of moving the structure to the internal C API is to clarify its status: we provide no backward compatibility warranty, you are on our own if you use it.
It's also a way to promote the usage of the new clean public C API: it is now reliable, whereas accessing directly PyFrameObject members break at each Python version.
The internal C API cannot be used easily on purpose: you have to opt-in for this API by defining the Py_BUILD_CORE_MODULE macro and you need to use different #include. It's a way to enforce the usage of the clean public C API.
|
msg413853 - (view) |
Author: Stefan Behnel (scoder) * |
Date: 2022-02-23 19:45 |
I haven't looked fully into this yet, but I *think* that Cython can get rid of most of the direct usages of PyFrameObject by switching to the new InterpreterFrame struct instead. It looks like the important fields have now been moved over to that.
That won't improve the situation regarding the usage of CPython internals, but it's probably worth keeping in mind before we start adding new API functions that work on frame objects.
|
msg413854 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-23 19:48 |
Stefan Behnel:
> I haven't looked fully into this yet, but I *think* that Cython can get rid of most of the direct usages of PyFrameObject by switching to the new InterpreterFrame struct instead. It looks like the important fields have now been moved over to that.
InterpreterFrame is part of the internal C API. As I wrote, IMO it's fine for now that Cython uses the internal C API.
> That won't improve the situation regarding the usage of CPython internals, but it's probably worth keeping in mind before we start adding new API functions that work on frame objects.
Right.
My hope is also that making the structure internal should help to identify which members should be exposed with getter functions, or even setter functions (Mark would prefer to no add setter functions).
|
msg413917 - (view) |
Author: Petr Viktorin (petr.viktorin) * |
Date: 2022-02-24 15:00 |
OK, looking at it more carefully, it makes sense to do the change.
|
msg413921 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-24 15:43 |
> PyAPI_FUNC(PyObject *) _PyEval_EvalFrameDefault(PyThreadState *tstate, struct _interpreter_frame *f, int exc);
I created bpo-46850 "[C API] Move _PyEval_EvalFrameDefault() to the internal C API" for this issue.
|
msg413981 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-25 11:53 |
New changeset 18b5dd68c6b616257ae243c0b6bb965ffc885a23 by Victor Stinner in branch 'main':
bpo-46836: Move PyFrameObject to pycore_frame.h (GH-31530)
https://github.com/python/cpython/commit/18b5dd68c6b616257ae243c0b6bb965ffc885a23
|
msg413990 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-25 12:18 |
I announced the change on the capi-sig mailing list:
https://mail.python.org/archives/list/capi-sig@python.org/thread/RCT4SB5LY5UPRRRALEOHWEQHIXFNTHYF/
|
msg414009 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-02-25 15:22 |
New changeset 87af12bff33b3e7546fa26158b7d8680ecb6ecec by Victor Stinner in branch 'main':
bpo-46836: Rename InterpreterFrame to _PyInterpreterFrame (GH-31583)
https://github.com/python/cpython/commit/87af12bff33b3e7546fa26158b7d8680ecb6ecec
|
msg414279 - (view) |
Author: Brandt Bucher (brandtbucher) * |
Date: 2022-03-01 17:02 |
Victor, can we please revert these changes? They broke Greenlet, a required dependency for three of our performance benchmarks:
https://github.com/python-greenlet/greenlet/issues/288#issuecomment-1055632607
I've already spent considerable effort contributing workarounds for other 3.11 breakages in Greenlet and coaxing them to put out a compatible release:
https://github.com/python-greenlet/greenlet/pull/280
These changes, however, just seem like needless breakage that are now also impacting our performance work.
|
msg414280 - (view) |
Author: Brandt Bucher (brandtbucher) * |
Date: 2022-03-01 17:03 |
I'm also very uncomfortable with the lack of review on these PRs. The most recent one (https://github.com/python/cpython/pull/31583) was open for less than 30 minutes before merging, from 6:57 to 7:22 am in my local time zone.
|
msg414283 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-03-01 17:25 |
I plan to update Cython, greenlet and gevent for this change.
|
msg414292 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-03-01 20:55 |
Draft PR for greenlet: https://github.com/python-greenlet/greenlet/pull/294
I made these changes close to the Python 3.11 alpha 6 release to be able to test "#if PY_VERSION_HEX < 0x30B00A6" to have code compatible with Python 3.11 alpha 5 and older.
|
msg414297 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-03-01 21:38 |
Draft PR for Cython: https://github.com/cython/cython/pull/4671
|
msg414310 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-03-01 22:25 |
Draft PR for gevent: https://github.com/gevent/gevent/pull/1872
|
msg414315 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-03-01 22:52 |
> Draft PR for Cython: https://github.com/cython/cython/pull/4671
Notes on how Cython access PyFrameObject fields: https://bugs.python.org/issue40421#msg414314
|
msg415868 - (view) |
Author: STINNER Victor (vstinner) * |
Date: 2022-03-23 12:19 |
New changeset b0f886d1bca499db1118a60b707923fa8e157073 by Victor Stinner in branch 'main':
bpo-46836: Add Doc/c-api/frame.rst (GH-32051)
https://github.com/python/cpython/commit/b0f886d1bca499db1118a60b707923fa8e157073
|
|
Date |
User |
Action |
Args |
2022-04-11 14:59:56 | admin | set | github: 90992 |
2022-03-23 12:19:19 | vstinner | set | messages:
+ msg415868 |
2022-03-22 15:25:50 | vstinner | set | pull_requests:
+ pull_request30140 |
2022-03-01 22:52:00 | vstinner | set | messages:
+ msg414315 |
2022-03-01 22:25:14 | vstinner | set | messages:
+ msg414310 |
2022-03-01 21:38:53 | vstinner | set | messages:
+ msg414297 |
2022-03-01 20:55:16 | vstinner | set | messages:
+ msg414292 |
2022-03-01 20:33:37 | gvanrossum | set | nosy:
+ gvanrossum
|
2022-03-01 17:25:31 | vstinner | set | messages:
+ msg414283 |
2022-03-01 17:03:48 | brandtbucher | set | messages:
+ msg414280 |
2022-03-01 17:02:23 | brandtbucher | set | messages:
+ msg414279 |
2022-02-25 18:36:57 | brandtbucher | set | nosy:
+ brandtbucher
|
2022-02-25 15:22:05 | vstinner | set | messages:
+ msg414009 |
2022-02-25 14:57:06 | vstinner | set | pull_requests:
+ pull_request29706 |
2022-02-25 12:18:29 | vstinner | set | messages:
+ msg413990 |
2022-02-25 11:53:29 | vstinner | set | messages:
+ msg413981 |
2022-02-24 15:44:07 | vstinner | set | pull_requests:
- pull_request29656 |
2022-02-24 15:43:54 | vstinner | set | messages:
+ msg413921 |
2022-02-24 15:00:01 | petr.viktorin | set | messages:
+ msg413917 |
2022-02-23 19:48:12 | vstinner | set | messages:
+ msg413854 |
2022-02-23 19:45:04 | scoder | set | messages:
+ msg413853 |
2022-02-23 17:27:01 | vstinner | set | messages:
+ msg413829 |
2022-02-23 17:20:03 | petr.viktorin | set | messages:
+ msg413828 |
2022-02-23 16:04:09 | vstinner | set | pull_requests:
+ pull_request29656 |
2022-02-23 15:29:18 | vstinner | set | messages:
+ msg413807 |
2022-02-23 15:24:06 | vstinner | set | nosy:
+ scoder, petr.viktorin, Mark.Shannon, corona10, erlendaasland
|
2022-02-23 15:23:21 | vstinner | set | messages:
+ msg413805 |
2022-02-23 15:21:21 | vstinner | set | messages:
+ msg413803 |
2022-02-23 15:19:54 | vstinner | set | messages:
+ msg413802 |
2022-02-23 15:17:31 | vstinner | set | messages:
+ msg413801 |
2022-02-23 15:13:39 | vstinner | set | keywords:
+ patch stage: patch review pull_requests:
+ pull_request29654 |
2022-02-23 15:05:22 | vstinner | set | messages:
+ msg413796 |
2022-02-23 15:04:12 | vstinner | create | |