You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[C API] Removed private _PyArg_Parser API has no replacement in Python 3.13 #112136
The _PyArg_Parser API is used by Argument Clinic to generate efficient code parsing function arguments.
Since Python 3.12, when targeting Python internals, Argument Clinic initializes _PyArg_Parser.kwtuple with singletons objects using the _Py_SINGLETON(name) API. This API cannot be used outside Python.
These APIs are effectively required for using METH_FASTCALL | METH_KEYWORDS which is a documented part of the API.
Notably METH_FASTCALL is part of the Stable ABI and requires the not-mentioned-here-but-related _PyArg_ParseStack().
"Private, but documented, and partially considered part of the stable ABI" is not the same thing as "Private". Removing these functions leaves no API to interact with these documented call mechanisms. Unless you intend to remove METH_FASTCALL and its derivatives from the documentation and the stable ABI, something must replace these private functions.
Anecdotally, we make extensive use of _PyArg_ParseStack() and _PyArg_ParseStackAndKeywords() internally at NYU in high-traffic CPython methods. I don't think a code search of PyPI is a responsible way to gauge the existence of C API consumers.
What I'm trying to do here is to list projects using these projects, see how they use the API, and which public API is needed.
Anecdotally, we make extensive use of _PyArg_ParseStack() and _PyArg_ParseStackAndKeywords() internally at NYU in high-traffic CPython methods.
Do you have some examples? Which calling convention do you use? METH_FASTCALL, METH_FASTCALL | METH_KEYWORDS, and/or something else?
"Private, but documented, and partially considered part of the stable ABI" is not the same thing as "Private". Removing these functions leaves no API to interact with these documented call mechanisms.
As the author of METH_FASTCALL, I would like to add a clean public, documented and tested API to make these calling convention fully usable :-)
METH_FASTCALL
METH_FASTCALL | METH_KEYWORDS
METH_METHOD | METH_FASTCALL | METH_KEYWORDS
In terms of functions, so far the following functions were mentioned:
_PyArg_ParseStack()
_PyArg_ParseStackAndKeywords() -- use _PyArg_Parser
_PyArg_ParseTupleAndKeywordsFast() -- use _PyArg_Parser
_PyArg_UnpackKeywords() -- use _PyArg_Parser
_PyArg_UnpackKeywordsWithVararg() -- use _PyArg_Parser
Is the number of arguments always a signedPy_ssize_t type? Or do we have to care about PyVectorcall_NARGS() which uses an unsignedsize_t type? If I understood correctly, unsigned size_t is only used to call a function. But inside a function implementation, the calling convention is to pass a number of arguments as a signed Py_ssize_t.
for METH_FASTCALL and METH_FASTCALL | METH_KEYWORDS respectively. I imagine if we converted the latter usage to vectorcall it would be identical with the exception of replacing nargs with PyVectorcall_NARGS(nargsf), so vectorcall vs METH_FASTCALL isn't really the problem here unless I'm completely missing some public API for parsing vectorcall arguments.
A quick Ctrl-F shows no one using kwtuple yet, but I think kwtuple is a basically good feature (and we use pre-initialized global string objects in some other places), so I would be sad to see that capacity go away as well.
I would like to add a clean public, documented and tested API to make these calling convention fully usable
Ya 100%. I understand I'm just some random and you're the core dev here, just wanted to raise the objection that breaking this functionality without having that replacement ready doesn't seem like a win to me. The APIs are already internal, there's no stability contract, but if they exist anyway in an unbroken state, what's the harm in leaving them accessible until a replacement is available?
As a practical matter if these disappeared we would be replicating the declarations in our own headers for the Python versions where no solutions existed, and that seems like a pointless maintenance burden to push out into the world.
Is the number of arguments always a signed Py_ssize_t type? Or do we have to care about PyVectorcall_NARGS() which uses an unsigned size_t type?
PyVectorcall_NARGS() accepts a size_t but returns an ssize_t, from the point of view of the parser nargs is always signed.
@vstinner in case of multidict, here's the PR that started using this private API aio-libs/multidict#681. For now, I reverted it under Python 3.13: aio-libs/multidict#909.
The original PR implies there's a 2.2x difference in performance, so it'd be useful to get an equivalent replacement.
I wrote PR gh-121262 to restore the private _PyArg_Parser structure and the private _PyArg_ParseTupleAndKeywordsFast() function. Is it enough for Blender?
@vstinner on a naive grep it looks like it should be enough, but blender at least also uses _PySet_NextEntry, so I can't get a complete compile done to verify unless that's put back in or it's ported over to the generic iterator stuff (which I'm not a good enough C coder to do myself).
I restored the removed private _PyArg_Parser structure and the private _PyArg_ParseTupleAndKeywordsFast() function in the change f8373db. I close the issue.
I think this has been prematurely closed as no solution has been provided for the other _PyArg_Parser API functions, which are not 1-to-1 clones of _PyArg_ParseTupleAndKeywordsFast()
I'm more raising the issue because there should be a blessed replacement. _PyArg_ParseTupleAndKeywordsFast() is three steps away from _PyArg_ParseStackAndKeywords(), but the latter is the more natural fit for METH_FASTCALL functions. It would be weird for the former to be the only canonized replacement going forward.
I propose to close the issue and leave the situation as it is. You should opt-in for the internal C API if you want to use these APIs sadly. For now, there is no good replacement in the public C API.
I would be very interested to see a public API to replace these internal C APIs. But so far, nobody managed to propose a better replacement. Time to time, I'm still trying to design a replacement, but I didn't find anything satisfying myself.
Maybe a less optimized API should be designed to leak less implementation details.
Activity
vstinner commentedon Nov 16, 2023
A code search on
_PyArg_Parserin PyPI top 8,000 projects (2023-11-15) found 5 projects:nickelpro commentedon Nov 16, 2023
These APIs are effectively required for using
METH_FASTCALL | METH_KEYWORDSwhich is a documented part of the API.Notably
METH_FASTCALLis part of the Stable ABI and requires the not-mentioned-here-but-related_PyArg_ParseStack()."Private, but documented, and partially considered part of the stable ABI" is not the same thing as "Private". Removing these functions leaves no API to interact with these documented call mechanisms. Unless you intend to remove
METH_FASTCALLand its derivatives from the documentation and the stable ABI, something must replace these private functions.Anecdotally, we make extensive use of
_PyArg_ParseStack()and_PyArg_ParseStackAndKeywords()internally at NYU in high-traffic CPython methods. I don't think a code search of PyPI is a responsible way to gauge the existence of C API consumers.vstinner commentedon Nov 16, 2023
What I'm trying to do here is to list projects using these projects, see how they use the API, and which public API is needed.
Do you have some examples? Which calling convention do you use?
METH_FASTCALL,METH_FASTCALL | METH_KEYWORDS, and/or something else?This situation is unfortunate. METH_FASTCALL was created as a private API but apparently, it was adopted outside Python before it was standardized by PEP 590 – Vectorcall: a fast calling protocol for CPython.
As the author of
METH_FASTCALL, I would like to add a clean public, documented and tested API to make these calling convention fully usable :-)METH_FASTCALLMETH_FASTCALL | METH_KEYWORDSMETH_METHOD | METH_FASTCALL | METH_KEYWORDSIn terms of functions, so far the following functions were mentioned:
_PyArg_ParseStack()_PyArg_ParseStackAndKeywords()-- use_PyArg_Parser_PyArg_ParseTupleAndKeywordsFast()-- use_PyArg_Parser_PyArg_UnpackKeywords()-- use_PyArg_Parser_PyArg_UnpackKeywordsWithVararg()-- use_PyArg_ParserIs the number of arguments always a signed
Py_ssize_ttype? Or do we have to care aboutPyVectorcall_NARGS()which uses an unsignedsize_ttype? If I understood correctly, unsignedsize_tis only used to call a function. But inside a function implementation, the calling convention is to pass a number of arguments as a signedPy_ssize_t.nickelpro commentedon Nov 16, 2023
I don't think we do anything fancy, basically the same straightforward code you would see pop out of argument clinic:
or
for
METH_FASTCALLandMETH_FASTCALL | METH_KEYWORDSrespectively. I imagine if we converted the latter usage to vectorcall it would be identical with the exception of replacingnargswithPyVectorcall_NARGS(nargsf), so vectorcall vsMETH_FASTCALLisn't really the problem here unless I'm completely missing some public API for parsing vectorcall arguments.A quick Ctrl-F shows no one using
kwtupleyet, but I thinkkwtupleis a basically good feature (and we use pre-initialized global string objects in some other places), so I would be sad to see that capacity go away as well.Ya 100%. I understand I'm just some random and you're the core dev here, just wanted to raise the objection that breaking this functionality without having that replacement ready doesn't seem like a win to me. The APIs are already internal, there's no stability contract, but if they exist anyway in an unbroken state, what's the harm in leaving them accessible until a replacement is available?
As a practical matter if these disappeared we would be replicating the declarations in our own headers for the Python versions where no solutions existed, and that seems like a pointless maintenance burden to push out into the world.
PyVectorcall_NARGS()accepts asize_tbut returns anssize_t, from the point of view of the parsernargsis always signed.webknjaz commentedon Jan 16, 2024
@vstinner in case of
multidict, here's the PR that started using this private API aio-libs/multidict#681. For now, I reverted it under Python 3.13: aio-libs/multidict#909.The original PR implies there's a 2.2x difference in performance, so it'd be useful to get an equivalent replacement.
AdamWill commentedon Jun 27, 2024
blender also uses
_PyArg_Parserextensively: https://projects.blender.org/blender/blender/search?q=_PyArg_Parser . I am not enough of a C coder to know how easy it would be to port all those uses to the public argument parsing API.pythongh-112136: Restore removed _PyArg_Parser
pythongh-112136: Restore removed _PyArg_Parser
vstinner commentedon Jul 2, 2024
@AdamWill:
I wrote PR gh-121262 to restore the private _PyArg_Parser structure and the private _PyArg_ParseTupleAndKeywordsFast() function. Is it enough for Blender?
pythongh-112136: Restore removed _PyArg_Parser
AdamWill commentedon Jul 3, 2024
@vstinner on a naive grep it looks like it should be enough, but blender at least also uses
_PySet_NextEntry, so I can't get a complete compile done to verify unless that's put back in or it's ported over to the generic iterator stuff (which I'm not a good enough C coder to do myself).vstinner commentedon Jul 3, 2024
Good. I was only asking for "PyArg" APIs.
gh-112136: Restore removed _PyArg_Parser (#121262)
vstinner commentedon Jul 3, 2024
I restored the removed private _PyArg_Parser structure and the private _PyArg_ParseTupleAndKeywordsFast() function in the change f8373db. I close the issue.
3 remaining items
nickelpro commentedon Jul 3, 2024
I think this has been prematurely closed as no solution has been provided for the other
_PyArg_ParserAPI functions, which are not 1-to-1 clones of_PyArg_ParseTupleAndKeywordsFast()vstinner commentedon Jul 3, 2024
Ok, I reopen the issue.
pythongh-112136: Restore removed _PyArg_Parser (python#121262)
pythongh-112136: Restore removed _PyArg_Parser (python#121262)
vstinner commentedon Sep 9, 2024
Oh, I lost track of this issue and we are now past Python 3.13 rc2 release! The next release should be 3.13 final.
Can't you use the internal C API for that? Something like:
nickelpro commentedon Sep 10, 2024
Yep, that's what we're doing.
I'm more raising the issue because there should be a blessed replacement.
_PyArg_ParseTupleAndKeywordsFast()is three steps away from_PyArg_ParseStackAndKeywords(), but the latter is the more natural fit forMETH_FASTCALLfunctions. It would be weird for the former to be the only canonized replacement going forward.vstinner commentedon Sep 10, 2024
The
_PyArg_Parserstructure has multiple issues:_PyOnceFlagatomic type: I don't think that it's available in the limited C API.nextshould not be accessible in the public structure (must be private instead).I failed to design a better replacement. So far, no one else proposed a better replacement.
vstinner commentedon Sep 19, 2024
I propose to close the issue and leave the situation as it is. You should opt-in for the internal C API if you want to use these APIs sadly. For now, there is no good replacement in the public C API.
nickelpro commentedon Sep 19, 2024
If the official upstream answer is "do not replace" then I agree there's nothing left to do here
vstinner commentedon Sep 19, 2024
I would be very interested to see a public API to replace these internal C APIs. But so far, nobody managed to propose a better replacement. Time to time, I'm still trying to design a replacement, but I didn't find anything satisfying myself.
Maybe a less optimized API should be designed to leak less implementation details.
For now, I close the issue.
pythongh-112136: Remove unused #include "pycore_lock.h"
gh-112136: Remove unused #include "pycore_lock.h" (#127093)
pythongh-112136: Remove unused #include "pycore_lock.h" (python#127093)