Skip to content

Commit

Permalink
bpo-20201: varargs support for argument clinic and refactor print wit…
Browse files Browse the repository at this point in the history
…h AC
  • Loading branch information
isidentical committed Jul 13, 2021
1 parent 3b5b99d commit 4186327
Show file tree
Hide file tree
Showing 8 changed files with 664 additions and 100 deletions.
11 changes: 10 additions & 1 deletion Include/modsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ PyAPI_FUNC(PyObject *) Py_BuildValue(const char *, ...);
PyAPI_FUNC(PyObject *) _Py_BuildValue_SizeT(const char *, ...);


#define ANY_VARARGS(n) (n == PY_SSIZE_T_MAX)
#ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PyArg_UnpackStack(
PyObject *const *args,
Expand All @@ -73,7 +74,7 @@ PyAPI_FUNC(void) _PyArg_BadArgument(const char *, const char *, const char *, Py
PyAPI_FUNC(int) _PyArg_CheckPositional(const char *, Py_ssize_t,
Py_ssize_t, Py_ssize_t);
#define _PyArg_CheckPositional(funcname, nargs, min, max) \
(((min) <= (nargs) && (nargs) <= (max)) \
((!ANY_VARARGS(max) && (min) <= (nargs) && (nargs) <= (max)) \
|| _PyArg_CheckPositional((funcname), (nargs), (min), (max)))

#endif
Expand Down Expand Up @@ -127,6 +128,14 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords(
struct _PyArg_Parser *parser,
int minpos, int maxpos, int minkw,
PyObject **buf);

PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsWithVararg(
PyObject *const *args, Py_ssize_t nargs,
PyObject *kwargs, PyObject *kwnames,
struct _PyArg_Parser *parser,
int minpos, int maxpos, int minkw,
int vararg, PyObject **buf);

#define _PyArg_UnpackKeywords(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \
(((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \
(minpos) <= (nargs) && (nargs) <= (maxpos) && args != NULL) ? (args) : \
Expand Down
220 changes: 220 additions & 0 deletions Lib/test/clinic.test
Original file line number Diff line number Diff line change
Expand Up @@ -3304,3 +3304,223 @@ test_preprocessor_guarded_else(PyObject *module, PyObject *Py_UNUSED(ignored))
#define TEST_PREPROCESSOR_GUARDED_ELSE_METHODDEF
#endif /* !defined(TEST_PREPROCESSOR_GUARDED_ELSE_METHODDEF) */
/*[clinic end generated code: output=3804bb18d454038c input=3fc80c9989d2f2e1]*/

/*[clinic input]
test_vararg_and_posonly


a: object
*args: object
/

[clinic start generated code]*/

PyDoc_STRVAR(test_vararg_and_posonly__doc__,
"test_vararg_and_posonly($module, a, /, *args)\n"
"--\n"
"\n");

#define TEST_VARARG_AND_POSONLY_METHODDEF \
{"test_vararg_and_posonly", (PyCFunction)(void(*)(void))test_vararg_and_posonly, METH_FASTCALL, test_vararg_and_posonly__doc__},

static PyObject *
test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args);

static PyObject *
test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
PyObject *a;
PyObject *__clinic_args = NULL;

if (!_PyArg_CheckPositional("test_vararg_and_posonly", nargs, 1, PY_SSIZE_T_MAX)) {
goto exit;
}
a = args[0];
__clinic_args = PyTuple_New(nargs - 1);
for (Py_ssize_t i = 0; i < nargs - 1; ++i) {
PyTuple_SET_ITEM(__clinic_args, i, args[1 + i]);
}
return_value = test_vararg_and_posonly_impl(module, a, __clinic_args);

exit:
Py_XDECREF(__clinic_args);
return return_value;
}

static PyObject *
test_vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args)
/*[clinic end generated code: output=ada613d2d87c9341 input=08dc2bf7afbf1613]*/

/*[clinic input]
test_vararg


a: object
*args: object

[clinic start generated code]*/

PyDoc_STRVAR(test_vararg__doc__,
"test_vararg($module, /, a, *args)\n"
"--\n"
"\n");

#define TEST_VARARG_METHODDEF \
{"test_vararg", (PyCFunction)(void(*)(void))test_vararg, METH_FASTCALL|METH_KEYWORDS, test_vararg__doc__},

static PyObject *
test_vararg_impl(PyObject *module, PyObject *a, PyObject *args);

static PyObject *
test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
static const char * const _keywords[] = {"a", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg", 0};
PyObject *argsbuf[2];
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *a;
PyObject *__clinic_args = NULL;

args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf);
if (!args) {
goto exit;
}
a = args[0];
__clinic_args = args[1];
return_value = test_vararg_impl(module, a, __clinic_args);

exit:
Py_XDECREF(__clinic_args);
return return_value;
}

static PyObject *
test_vararg_impl(PyObject *module, PyObject *a, PyObject *args)
/*[clinic end generated code: output=f721025731c3bfe8 input=81d33815ad1bae6e]*/

/*[clinic input]
test_vararg_with_default


a: object
*args: object
b: bool = False

[clinic start generated code]*/

PyDoc_STRVAR(test_vararg_with_default__doc__,
"test_vararg_with_default($module, /, a, *args, b=False)\n"
"--\n"
"\n");

#define TEST_VARARG_WITH_DEFAULT_METHODDEF \
{"test_vararg_with_default", (PyCFunction)(void(*)(void))test_vararg_with_default, METH_FASTCALL|METH_KEYWORDS, test_vararg_with_default__doc__},

static PyObject *
test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args,
int b);

static PyObject *
test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
static const char * const _keywords[] = {"a", "b", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_default", 0};
PyObject *argsbuf[3];
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *a;
PyObject *__clinic_args = NULL;
int b = 0;

args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf);
if (!args) {
goto exit;
}
a = args[0];
__clinic_args = args[1];
if (!noptargs) {
goto skip_optional_kwonly;
}
b = PyObject_IsTrue(args[2]);
if (b < 0) {
goto exit;
}
skip_optional_kwonly:
return_value = test_vararg_with_default_impl(module, a, __clinic_args, b);

exit:
Py_XDECREF(__clinic_args);
return return_value;
}

static PyObject *
test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args,
int b)
/*[clinic end generated code: output=63b34d3241c52fda input=6e110b54acd9b22d]*/

/*[clinic input]
test_vararg_with_only_defaults


*args: object
b: bool = False
c: object = ' '

[clinic start generated code]*/

PyDoc_STRVAR(test_vararg_with_only_defaults__doc__,
"test_vararg_with_only_defaults($module, /, *args, b=False, c=\' \')\n"
"--\n"
"\n");

#define TEST_VARARG_WITH_ONLY_DEFAULTS_METHODDEF \
{"test_vararg_with_only_defaults", (PyCFunction)(void(*)(void))test_vararg_with_only_defaults, METH_FASTCALL|METH_KEYWORDS, test_vararg_with_only_defaults__doc__},

static PyObject *
test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b,
PyObject *c);

static PyObject *
test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
static const char * const _keywords[] = {"b", "c", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_only_defaults", 0};
PyObject *argsbuf[3];
Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
PyObject *__clinic_args = NULL;
int b = 0;
PyObject *c = " ";

args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf);
if (!args) {
goto exit;
}
__clinic_args = args[0];
if (!noptargs) {
goto skip_optional_kwonly;
}
if (args[1]) {
b = PyObject_IsTrue(args[1]);
if (b < 0) {
goto exit;
}
if (!--noptargs) {
goto skip_optional_kwonly;
}
}
c = args[2];
skip_optional_kwonly:
return_value = test_vararg_with_only_defaults_impl(module, __clinic_args, b, c);

exit:
Py_XDECREF(__clinic_args);
return return_value;
}

static PyObject *
test_vararg_with_only_defaults_impl(PyObject *module, PyObject *args, int b,
PyObject *c)
/*[clinic end generated code: output=dc29ce6ebc2ec10c input=fa56a709a035666e]*/
2 changes: 1 addition & 1 deletion Lib/test/test_call.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ def test_varargs16_kw(self):
min, 0, default=1, key=2, foo=3)

def test_varargs17_kw(self):
msg = r"^print\(\) takes at most 4 keyword arguments \(5 given\)$"
msg = r"'foo' is an invalid keyword argument for print\(\)$"
self.assertRaisesRegex(TypeError, msg,
print, 0, sep=1, end=2, file=3, flush=4, foo=5)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added support for variadic positional parameters in Argument Clinic.
Loading

0 comments on commit 4186327

Please sign in to comment.