Skip to content

Commit 9a7c32b

Browse files
eendebakptclaude
andcommitted
gh-136681: Reduce the size of _PyArg_Parser
Narrow pos, min, max and is_kwtuple_owned, which are small counts, and remove custom_msg: it is only needed to report a conversion error, and can then be looked up in the format string. Keep format, keywords and fname first, so that existing positional initializers still work. _PyArg_Parser shrinks from 72 to 48 bytes on 64-bit platforms. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 5169a7a commit 9a7c32b

2 files changed

Lines changed: 24 additions & 27 deletions

File tree

‎Include/cpython/modsupport.h‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,13 @@ typedef struct _PyArg_Parser {
2525
const char *format;
2626
const char * const *keywords;
2727
const char *fname;
28-
const char *custom_msg;
29-
_PyOnceFlag once; /* atomic one-time initialization flag */
30-
int is_kwtuple_owned; /* does this parser own the kwtuple object? */
31-
int pos; /* number of positional-only arguments */
32-
int min; /* minimal number of arguments */
33-
int max; /* maximal number of positional arguments */
3428
PyObject *kwtuple; /* tuple of keyword parameter names */
3529
struct _PyArg_Parser *next;
30+
_PyOnceFlag once; /* atomic one-time initialization flag */
31+
uint8_t is_kwtuple_owned; /* does this parser own the kwtuple object? */
32+
uint16_t pos; /* number of positional-only arguments */
33+
uint16_t min; /* minimal number of arguments */
34+
uint16_t max; /* maximal number of positional arguments */
3635
} _PyArg_Parser;
3736

3837
PyAPI_FUNC(int) _PyArg_ParseTupleAndKeywordsFast(PyObject *, PyObject *,

‎Python/getargs.c‎

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2045,21 +2045,11 @@ scan_keywords(const char * const *keywords, int *ptotal, int *pposonly)
20452045

20462046
static int
20472047
parse_format(const char *format, int total, int npos,
2048-
const char **pfname, const char **pcustommsg,
2049-
int *pmin, int *pmax)
2048+
const char **pfname, int *pmin, int *pmax)
20502049
{
2051-
/* grab the function name or custom error msg first (mutually exclusive) */
2052-
const char *custommsg;
20532050
const char *fname = strchr(format, ':');
20542051
if (fname) {
20552052
fname++;
2056-
custommsg = NULL;
2057-
}
2058-
else {
2059-
custommsg = strchr(format,';');
2060-
if (custommsg) {
2061-
custommsg++;
2062-
}
20632053
}
20642054

20652055
int min = INT_MAX;
@@ -2118,7 +2108,6 @@ parse_format(const char *format, int total, int npos,
21182108
}
21192109

21202110
*pfname = fname;
2121-
*pcustommsg = custommsg;
21222111
*pmin = min;
21232112
*pmax = max;
21242113
return 0;
@@ -2153,22 +2142,25 @@ _parser_init(void *arg)
21532142
const char * const *keywords = parser->keywords;
21542143
assert(keywords != NULL);
21552144
assert((parser->format == NULL || parser->fname == NULL) &&
2156-
parser->custom_msg == NULL &&
21572145
parser->min == 0 &&
21582146
parser->max == 0);
21592147

21602148
int len, pos;
21612149
if (scan_keywords(keywords, &len, &pos) < 0) {
21622150
return -1;
21632151
}
2152+
if (len > UINT16_MAX) {
2153+
PyErr_SetString(PyExc_SystemError, "Too many keyword parameters");
2154+
return -1;
2155+
}
21642156
assert(parser->pos == 0 || parser->pos == pos); // may be set statically
21652157

2166-
const char *fname, *custommsg = NULL;
2158+
const char *fname;
21672159
int min = 0, max = 0;
21682160
if (parser->format) {
21692161
assert(parser->fname == NULL);
21702162
if (parse_format(parser->format, len, pos,
2171-
&fname, &custommsg, &min, &max) < 0) {
2163+
&fname, &min, &max) < 0) {
21722164
return -1;
21732165
}
21742166
}
@@ -2206,11 +2198,10 @@ _parser_init(void *arg)
22062198
owned = 0;
22072199
}
22082200

2209-
parser->pos = pos;
2201+
parser->pos = (uint16_t)pos;
22102202
parser->fname = fname;
2211-
parser->custom_msg = custommsg;
2212-
parser->min = min;
2213-
parser->max = max;
2203+
parser->min = (uint16_t)min;
2204+
parser->max = (uint16_t)max;
22142205
parser->is_kwtuple_owned = owned;
22152206
// Set last: see _PyArg_UnpackKeywords()
22162207
_Py_atomic_store_ptr_release(&parser->kwtuple, kwtuple);
@@ -2243,7 +2234,6 @@ parser_clear(struct _PyArg_Parser *parser)
22432234
else {
22442235
assert(parser->fname != NULL);
22452236
}
2246-
parser->custom_msg = NULL;
22472237
parser->min = 0;
22482238
parser->max = 0;
22492239
parser->is_kwtuple_owned = 0;
@@ -2412,7 +2402,15 @@ vgetargskeywordsfast_impl(PyObject *const *args, Py_ssize_t nargs,
24122402
levels, msgbuf, sizeof(msgbuf), &freelist);
24132403
Py_DECREF(current_arg);
24142404
if (msg) {
2415-
seterror(i+1, msg, levels, parser->fname, parser->custom_msg);
2405+
const char *custom_msg = NULL;
2406+
if (parser->fname == NULL) {
2407+
// The format has ";message" instead of ":name"
2408+
custom_msg = strchr(parser->format, ';');
2409+
if (custom_msg != NULL) {
2410+
custom_msg++;
2411+
}
2412+
}
2413+
seterror(i+1, msg, levels, parser->fname, custom_msg);
24162414
return cleanreturn(0, &freelist);
24172415
}
24182416
continue;

0 commit comments

Comments
 (0)