-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
Add pythoncapi_compat.h header #2932
Conversation
This PR is part of my draft PEP 620 which tries to hide implementation details from the C API to prepare CPython for optimizations which changing structures of the Python C API: https://www.python.org/dev/peps/pep-0620/ |
I just added PyPy support to pythoncapi_compat.h for pybind11 ;-) |
Hi @vstinner, that looks great, thanks! Just some small things:
|
I think the idea is that this is a vendored copy of cpython_capi. To indicate this, what about putting it in a "vendored" subdirectory? That's what pip and a lot of other projects do; it will make it more obvious we need to copy a new version in for fixes instead of maintaining it ourselves (except in emergencies). If not, it should be at least in detail.
This is mostly just to make this play nice with IDEs, it doesn't have an affect on the normal generation, so I think that's why this didn't get flagged. (IIRC, could be wrong).
But would it be safe to then mix them? I'd think probably not, they will be defining mostly the same things? |
+1
2cents: our setup is fine as is. Easy to fix if overlooked.
I'm actually pretty worried about this one. In my role as pybind11 maintainer for Google's internal and external needs, I have to think large scale. Versions will get mixed for sure, at a large scale. Thinking ahead could safe a lot of grief later. Question: how can we make sure in pybind11 we get our own version, even if another version was included previously, but not clash? Is that even possible without using namepaces? — @vstinner has this question been discussed previously? Another thought: baking versioning into |
include/pybind11/pythoncapi_compat.h
Outdated
extern "C" { | ||
#endif | ||
|
||
#include <Python.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another thought/concern: here we have manipulations that need to happen before Python.h is included:
pybind11/include/pybind11/detail/common.h
Line 105 in 1259db6
/* Don't let Python.h #define (v)snprintf as macro because they are implemented |
First idea: replace the existing #include <Python.h>
in common.h
with including this header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are actually several reasons to control Python.h's include order, including this one, but not all of them are even pybind11 specific. By the way, note on the header - you shouldn't need extern C when including Python.h, it explicitly supports inclusion from C++.
include/pybind11/pythoncapi_compat.h
Outdated
@@ -0,0 +1,346 @@ | |||
// Header file providing new functions of the Python C API to old Python |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason for this not to be a submodule? Would make it much easier to update it for newer Python versions and such?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We had a few submodules for a while and subdued in a library intended to be a submodule doesn’t go well. Mostly made gitlab users mad. ;) Everyone was happy to see them go.
It’s harder to fork and customize in emergencies, too; changing the url can lead to issues.
I love submodules, but probably not for pybind11 itself.
A single file is not bad, git subtree isn’t bad either.
Don't access directly PyFrameObject and PyThreadState structure members but use getter functions instead: * Replace frame->f_code with _PyFrame_GetCodeBorrow(frame) * Replace tstate->frame with _PyThreadState_GetFrameBorrow(tstate) * Replace frame->f_back with _PyFrame_GetBackBorrow(frame) Copy pythoncapi_compat.h from the https://github.com/pythoncapi/pythoncapi_compat project to have these getter functions. It supports Python 2.7-3.10 and PyPy 2.7-3.7. detail/common.h now includes pythoncapi_compat.h rather than Python.h and frameobject.h. Use detail/common.h where Python.h or pythoncapi_compat.h is needed.
I modified my PR to use detail/common.h where Python.h or pythoncapi_compat.h is needed. common.h wraps Python.h, frameobject.h and pythoncapi_compat.h headers. I also squashed commits to modify the commit message. I updated pythoncapi_compat.h to get the fix for PyPy 3.7. |
IMO it's just more convenient to simply copy the file. Unless you need a new function added to pythoncapi_compat.h, the only reason to update it would to get a fix to support a new Python version (ex: my latest update for PyPy 3.7, or another fix for Python 2.7 on Windows). Usually, you don't have to update this header file.
pythoncapi_compat.h is not designed to be included twice, that's why the whole header is surrounded by this guard:
For example, the only guard deciding if PyFrame_GetCode() should be defined or not is the Python version. You must not define the same function twice.
If pybind11 is used in a project which uses a newer pythoncapi_compat.h, but pybind11 includes its older pythoncapi_compat.h, the project can fail to build if it needs a new function. There are different options to fix this issue:
Honestly, this issue is annoying, and I'm not sure which option is the most convenient in practice. A simpler approach is to avoid copying pythoncapi_compat.h into applications using pybind11, since pybind11 already uses it, no? |
Another alternative would be to redesign pythoncapi_compat.h to put guards on every function to allow including two versions of the header file. For example, including an old versions will define 10 functions, and then including the new version defines a 11th function. If you want, I can try to redesign pythoncapi_compat.h for that. But is it really required? As I wrote, the most simple option is to avoid copying pythoncapi_compat.h in applications using pybind11, since pybind11 already includes pythoncapi_compat.h. |
I think that's a great idea, and it's essential. Thoughts that led me to the above:
|
What's inside a given include guard did change recently, because I had to change this include guard. Example of change:
_PyFrame_GetBackBorrow() is no longer defined on PyPy, since the PyFrameObject structure has no f_back member on PyPy. But I'm not sure that it is your concern. Previously, the header file didn't work on PyPy. |
Yes, that's what my concern is about. For example
I guess there'll (almost) always be a way out if people coordinate enough, but my point is mainly that spreading unversioned copies invites diamond dependency conflicts, sooner or later, and that there will be a lot of lost time dealing with them. |
What's the status of this? Just curious. Also, is there a way to detect usage of deprecated structs in CPython? Just thinking about #3368, where the changed internals in 3.11 alerted us to the fact we should have been using new public API since Python 3.9. But it was usage of struct, not a function, so we didn't know we were using something that now had a public method for access. |
I didn't update yet pythoncapi_compat to support inclusion of different versions of the same file without having conflicts. Help would be welcomed to implement this use case. |
Don't access directly PyFrameObject and PyThreadState structure
members but use getter functions instead:
Copy pythoncapi_compat.h from the
https://github.com/pythoncapi/pythoncapi_compat project to have these
getter functions. It supports Python 2.7-3.10 and PyPy 2.7-3.7.
detail/common.h now includes pythoncapi_compat.h rather than Python.h
and frameobject.h. Use detail/common.h where Python.h or
pythoncapi_compat.h is needed.
Description
Suggested changelog entry: