Skip to content

Commit c1fc445

Browse files
gh-155373: Inline the parsing code of optional groups in Argument Clinic (GH-155374)
Instead of one PyArg_ParseTuple() call per number of arguments, generate the inlined code of the converters, as for all other parsing methods. Every argument is parsed once. Such functions now use the fastcall convention, except for constructors and other functions which need a tuple.
1 parent e7468d1 commit c1fc445

11 files changed

Lines changed: 1288 additions & 1053 deletions

File tree

Lib/test/clinic.test.c

Lines changed: 54 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -5786,21 +5786,15 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs)
57865786
!_PyArg_NoKeywords("Test", kwargs)) {
57875787
goto exit;
57885788
}
5789-
switch (PyTuple_GET_SIZE(args)) {
5790-
case 1:
5791-
if (!PyArg_ParseTuple(args, "O:__init__", &a)) {
5792-
goto exit;
5793-
}
5794-
break;
5795-
case 2:
5796-
if (!PyArg_ParseTuple(args, "OO:__init__", &a, &b)) {
5797-
goto exit;
5798-
}
5799-
group_right_1 = 1;
5800-
break;
5801-
default:
5802-
PyErr_SetString(PyExc_TypeError, "Test.__init__ requires 1 to 2 arguments");
5803-
goto exit;
5789+
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
5790+
if (nargs < 1 || nargs > 2) {
5791+
PyErr_SetString(PyExc_TypeError, "Test.__init__ requires 1 to 2 arguments");
5792+
goto exit;
5793+
}
5794+
a = PyTuple_GET_ITEM(args, 0);
5795+
if (nargs >= 2) {
5796+
b = PyTuple_GET_ITEM(args, 1);
5797+
group_right_1 = 1;
58045798
}
58055799
return_value = Test___init___impl((TestObj *)self, a, group_right_1, b);
58065800

@@ -5811,7 +5805,7 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs)
58115805
static int
58125806
Test___init___impl(TestObj *self, PyObject *a, int group_right_1,
58135807
PyObject *b)
5814-
/*[clinic end generated code: output=2bbb8ea60e8f57a6 input=10f5d0f1e8e466ef]*/
5808+
/*[clinic end generated code: output=72fdd2de63c05b9e input=10f5d0f1e8e466ef]*/
58155809

58165810

58175811
/*[clinic input]
@@ -5828,30 +5822,25 @@ PyDoc_STRVAR(only_optional_group__doc__,
58285822
"The only parameter is in an optional group.");
58295823

58305824
#define ONLY_OPTIONAL_GROUP_METHODDEF \
5831-
{"only_optional_group", (PyCFunction)only_optional_group, METH_VARARGS, only_optional_group__doc__},
5825+
{"only_optional_group", _PyCFunction_CAST(only_optional_group), METH_FASTCALL, only_optional_group__doc__},
58325826

58335827
static PyObject *
58345828
only_optional_group_impl(PyObject *module, int group_right_1, PyObject *a);
58355829

58365830
static PyObject *
5837-
only_optional_group(PyObject *module, PyObject *args)
5831+
only_optional_group(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
58385832
{
58395833
PyObject *return_value = NULL;
58405834
int group_right_1 = 0;
58415835
PyObject *a = NULL;
58425836

5843-
switch (PyTuple_GET_SIZE(args)) {
5844-
case 0:
5845-
break;
5846-
case 1:
5847-
if (!PyArg_ParseTuple(args, "O:only_optional_group", &a)) {
5848-
goto exit;
5849-
}
5850-
group_right_1 = 1;
5851-
break;
5852-
default:
5853-
PyErr_SetString(PyExc_TypeError, "only_optional_group requires 0 to 1 arguments");
5854-
goto exit;
5837+
if (nargs > 1) {
5838+
PyErr_SetString(PyExc_TypeError, "only_optional_group requires 0 to 1 arguments");
5839+
goto exit;
5840+
}
5841+
if (nargs >= 1) {
5842+
a = args[0];
5843+
group_right_1 = 1;
58555844
}
58565845
return_value = only_optional_group_impl(module, group_right_1, a);
58575846

@@ -5861,7 +5850,7 @@ only_optional_group(PyObject *module, PyObject *args)
58615850

58625851
static PyObject *
58635852
only_optional_group_impl(PyObject *module, int group_right_1, PyObject *a)
5864-
/*[clinic end generated code: output=e7546b9441793d7d input=426c64055af7bcab]*/
5853+
/*[clinic end generated code: output=4c7959fcc06bd216 input=426c64055af7bcab]*/
58655854

58665855

58675856
/*[clinic input]
@@ -5880,39 +5869,37 @@ PyDoc_STRVAR(group_and_optional_parameter__doc__,
58805869
"The optional parameter can be omitted with or without the group.");
58815870

58825871
#define GROUP_AND_OPTIONAL_PARAMETER_METHODDEF \
5883-
{"group_and_optional_parameter", (PyCFunction)group_and_optional_parameter, METH_VARARGS, group_and_optional_parameter__doc__},
5872+
{"group_and_optional_parameter", _PyCFunction_CAST(group_and_optional_parameter), METH_FASTCALL, group_and_optional_parameter__doc__},
58845873

58855874
static PyObject *
58865875
group_and_optional_parameter_impl(PyObject *module, int group_left_1,
58875876
PyObject *a, PyObject *b, PyObject *c);
58885877

58895878
static PyObject *
5890-
group_and_optional_parameter(PyObject *module, PyObject *args)
5879+
group_and_optional_parameter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
58915880
{
58925881
PyObject *return_value = NULL;
58935882
int group_left_1 = 0;
58945883
PyObject *a = NULL;
58955884
PyObject *b = NULL;
58965885
PyObject *c = Py_None;
58975886

5898-
switch (PyTuple_GET_SIZE(args)) {
5899-
case 0:
5900-
case 1:
5901-
if (!PyArg_ParseTuple(args, "|O:group_and_optional_parameter", &c)) {
5902-
goto exit;
5903-
}
5904-
break;
5905-
case 2:
5906-
case 3:
5907-
if (!PyArg_ParseTuple(args, "OO|O:group_and_optional_parameter", &a, &b, &c)) {
5908-
goto exit;
5909-
}
5910-
group_left_1 = 1;
5911-
break;
5912-
default:
5913-
PyErr_SetString(PyExc_TypeError, "group_and_optional_parameter requires 0 to 3 arguments");
5914-
goto exit;
5887+
Py_ssize_t offset = 0;
5888+
if (nargs > 3) {
5889+
PyErr_SetString(PyExc_TypeError, "group_and_optional_parameter requires 0 to 3 arguments");
5890+
goto exit;
59155891
}
5892+
if (nargs >= 2) {
5893+
a = args[0];
5894+
b = args[1];
5895+
offset += 2;
5896+
group_left_1 = 1;
5897+
}
5898+
if (nargs <= offset) {
5899+
goto skip_optional;
5900+
}
5901+
c = args[offset];
5902+
skip_optional:
59165903
return_value = group_and_optional_parameter_impl(module, group_left_1, a, b, c);
59175904

59185905
exit:
@@ -5922,7 +5909,7 @@ group_and_optional_parameter(PyObject *module, PyObject *args)
59225909
static PyObject *
59235910
group_and_optional_parameter_impl(PyObject *module, int group_left_1,
59245911
PyObject *a, PyObject *b, PyObject *c)
5925-
/*[clinic end generated code: output=3faea69eafd5bbbe input=7f0fbb6124f5a972]*/
5912+
/*[clinic end generated code: output=651f2361ffc5e256 input=7f0fbb6124f5a972]*/
59265913

59275914

59285915
/*[clinic input]
@@ -5944,15 +5931,15 @@ PyDoc_STRVAR(two_groups_on_the_same_level__doc__,
59445931
"Groups on the same level are independent of each other.");
59455932

59465933
#define TWO_GROUPS_ON_THE_SAME_LEVEL_METHODDEF \
5947-
{"two_groups_on_the_same_level", (PyCFunction)two_groups_on_the_same_level, METH_VARARGS, two_groups_on_the_same_level__doc__},
5934+
{"two_groups_on_the_same_level", _PyCFunction_CAST(two_groups_on_the_same_level), METH_FASTCALL, two_groups_on_the_same_level__doc__},
59485935

59495936
static PyObject *
59505937
two_groups_on_the_same_level_impl(PyObject *module, int group_left_1,
59515938
PyObject *a, PyObject *b, int group_left_2,
59525939
PyObject *c, PyObject *d);
59535940

59545941
static PyObject *
5955-
two_groups_on_the_same_level(PyObject *module, PyObject *args)
5942+
two_groups_on_the_same_level(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
59565943
{
59575944
PyObject *return_value = NULL;
59585945
int group_left_1 = 0;
@@ -5962,35 +5949,20 @@ two_groups_on_the_same_level(PyObject *module, PyObject *args)
59625949
PyObject *c = NULL;
59635950
PyObject *d;
59645951

5965-
switch (PyTuple_GET_SIZE(args)) {
5966-
case 1:
5967-
if (!PyArg_ParseTuple(args, "O:two_groups_on_the_same_level", &d)) {
5968-
goto exit;
5969-
}
5970-
break;
5971-
case 2:
5972-
if (!PyArg_ParseTuple(args, "OO:two_groups_on_the_same_level", &c, &d)) {
5973-
goto exit;
5974-
}
5975-
group_left_2 = 1;
5976-
break;
5977-
case 3:
5978-
if (!PyArg_ParseTuple(args, "OOO:two_groups_on_the_same_level", &a, &b, &d)) {
5979-
goto exit;
5980-
}
5981-
group_left_1 = 1;
5982-
break;
5983-
case 4:
5984-
if (!PyArg_ParseTuple(args, "OOOO:two_groups_on_the_same_level", &a, &b, &c, &d)) {
5985-
goto exit;
5986-
}
5987-
group_left_1 = 1;
5988-
group_left_2 = 1;
5989-
break;
5990-
default:
5991-
PyErr_SetString(PyExc_TypeError, "two_groups_on_the_same_level requires 1 to 4 arguments");
5992-
goto exit;
5952+
if (nargs < 1 || nargs > 4) {
5953+
PyErr_SetString(PyExc_TypeError, "two_groups_on_the_same_level requires 1 to 4 arguments");
5954+
goto exit;
5955+
}
5956+
if (nargs >= 3) {
5957+
a = args[0];
5958+
b = args[1];
5959+
group_left_1 = 1;
5960+
}
5961+
if (nargs == 2 || nargs == 4) {
5962+
c = args[nargs - 2];
5963+
group_left_2 = 1;
59935964
}
5965+
d = args[nargs - 1];
59945966
return_value = two_groups_on_the_same_level_impl(module, group_left_1, a, b, group_left_2, c, d);
59955967

59965968
exit:
@@ -6001,7 +5973,7 @@ static PyObject *
60015973
two_groups_on_the_same_level_impl(PyObject *module, int group_left_1,
60025974
PyObject *a, PyObject *b, int group_left_2,
60035975
PyObject *c, PyObject *d)
6004-
/*[clinic end generated code: output=508a61ee582da21e input=1b45d9b675b32d1a]*/
5976+
/*[clinic end generated code: output=737c3f543296e916 input=1b45d9b675b32d1a]*/
60055977

60065978

60075979
/*[clinic input]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Argument Clinic now generates the inlined code of the converters for functions
2+
with optional groups, instead of a ``PyArg_ParseTuple()`` call for every number
3+
of arguments.
4+
Every argument is now parsed once, most such functions now use the fastcall
5+
convention, and the errors for a wrong type of an argument are the same as in
6+
other functions.

Modules/_cursesmodule.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5303,7 +5303,8 @@ static PyMethodDef PyCursesWindow_methods[] = {
53035303
{"standout", PyCursesWindow_wstandout, METH_NOARGS,
53045304
"standout($self, /)\n--\n\n"
53055305
"Turn on the A_STANDOUT attribute."},
5306-
{"subpad", _curses_window_subwin, METH_VARARGS, _curses_window_subwin__doc__},
5306+
{"subpad", _PyCFunction_CAST(_curses_window_subwin), METH_FASTCALL,
5307+
_curses_window_subwin__doc__},
53075308
_CURSES_WINDOW_SUBWIN_METHODDEF
53085309
{"syncdown", PyCursesWindow_wsyncdown, METH_NOARGS,
53095310
"syncdown($self, /)\n--\n\n"

0 commit comments

Comments
 (0)