Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions Include/internal/pycore_modsupport.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ PyAPI_FUNC(int) _PyArg_ParseStackAndKeywords(
...);

// Export for 'math' shared extension
PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords(
PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywordsEx(
PyObject *const *args,
Py_ssize_t nargs,
PyObject *kwargs,
Expand All @@ -85,20 +85,19 @@ PyAPI_FUNC(PyObject * const *) _PyArg_UnpackKeywords(
int minpos,
int maxpos,
int minkw,
int varpos,
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) : \
_PyArg_UnpackKeywords((args), (nargs), (kwargs), (kwnames), (parser), \
(minpos), (maxpos), (minkw), (buf)))

// Export for '_testclinic' shared extension
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);
(minpos) <= (nargs) && (nargs) <= (maxpos) && (args) != NULL) ? \
(args) : \
_PyArg_UnpackKeywordsEx((args), (nargs), (kwargs), (kwnames), (parser), \
(minpos), (maxpos), (minkw), 0, (buf)))
#define _PyArg_UnpackKeywordsWithVararg(args, nargs, kwargs, kwnames, parser, minpos, maxpos, minkw, buf) \
(((minkw) == 0 && (kwargs) == NULL && (kwnames) == NULL && \
(minpos) <= (nargs) && (args) != NULL) ? (args) : \
_PyArg_UnpackKeywordsEx((args), (nargs), (kwargs), (kwnames), (parser), \
(minpos), (maxpos), (minkw), 1, (buf)))

#ifdef __cplusplus
}
Expand Down
2 changes: 1 addition & 1 deletion Include/internal/pycore_tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *);

#define _PyTuple_ITEMS(op) _Py_RVALUE(_PyTuple_CAST(op)->ob_item)

extern PyObject *_PyTuple_FromArray(PyObject *const *, Py_ssize_t);
PyAPI_FUNC(PyObject *)_PyTuple_FromArray(PyObject *const *, Py_ssize_t);
PyAPI_FUNC(PyObject *)_PyTuple_FromStackRefSteal(const union _PyStackRef *, Py_ssize_t);
PyAPI_FUNC(PyObject *)_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t);

Expand Down
93 changes: 56 additions & 37 deletions Lib/test/clinic.test.c
Original file line number Diff line number Diff line change
Expand Up @@ -4161,23 +4161,22 @@ test_vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t narg
goto exit;
}
a = args[0];
__clinic_args = PyTuple_New(nargs - 1);
if (!__clinic_args) {
__clinic_args = _PyTuple_FromArray(args + 1, nargs - 1);
if (__clinic_args == NULL) {
goto exit;
}
for (Py_ssize_t i = 0; i < nargs - 1; ++i) {
PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[1 + i]));
}
return_value = test_vararg_and_posonly_impl(module, a, __clinic_args);

exit:
/* Cleanup for args */
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=79b75dc07decc8d6 input=9cfa748bbff09877]*/
/*[clinic end generated code: output=0c11c475e240869e input=9cfa748bbff09877]*/

/*[clinic input]
test_vararg
Expand Down Expand Up @@ -4228,26 +4227,34 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[2];
PyObject *argsbuf[1];
PyObject * const *fastargs;
PyObject *a;
PyObject *__clinic_args = NULL;

args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf);
if (!args) {
fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
if (!fastargs) {
goto exit;
}
a = fastargs[0];
__clinic_args = nargs > 1
? _PyTuple_FromArray(args + 1, nargs - 1)
: PyTuple_New(0);
if (__clinic_args == NULL) {
goto exit;
}
a = args[0];
__clinic_args = args[1];
return_value = test_vararg_impl(module, a, __clinic_args);

exit:
/* Cleanup for args */
Py_XDECREF(__clinic_args);

return return_value;
}

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

/*[clinic input]
test_vararg_with_default
Expand Down Expand Up @@ -4300,37 +4307,45 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[3];
PyObject *argsbuf[2];
PyObject * const *fastargs;
Py_ssize_t noptargs = Py_MIN(nargs, 1) + (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) {
fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
if (!fastargs) {
goto exit;
}
a = args[0];
__clinic_args = args[1];
a = fastargs[0];
if (!noptargs) {
goto skip_optional_kwonly;
}
b = PyObject_IsTrue(args[2]);
b = PyObject_IsTrue(fastargs[1]);
if (b < 0) {
goto exit;
}
skip_optional_kwonly:
__clinic_args = nargs > 1
? _PyTuple_FromArray(args + 1, nargs - 1)
: PyTuple_New(0);
if (__clinic_args == NULL) {
goto exit;
}
return_value = test_vararg_with_default_impl(module, a, __clinic_args, b);

exit:
/* Cleanup for args */
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=f09d4b917063ca41 input=6e110b54acd9b22d]*/
/*[clinic end generated code: output=46781f9920ecedcf input=6e110b54acd9b22d]*/

/*[clinic input]
test_vararg_with_only_defaults
Expand Down Expand Up @@ -4383,42 +4398,48 @@ test_vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[3];
PyObject *argsbuf[2];
PyObject * const *fastargs;
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) {
fastargs = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, argsbuf);
if (!fastargs) {
goto exit;
}
__clinic_args = args[0];
if (!noptargs) {
goto skip_optional_kwonly;
}
if (args[1]) {
b = PyObject_IsTrue(args[1]);
if (fastargs[0]) {
b = PyObject_IsTrue(fastargs[0]);
if (b < 0) {
goto exit;
}
if (!--noptargs) {
goto skip_optional_kwonly;
}
}
c = args[2];
c = fastargs[1];
skip_optional_kwonly:
__clinic_args = _PyTuple_FromArray(args, nargs);
if (__clinic_args == NULL) {
goto exit;
}
return_value = test_vararg_with_only_defaults_impl(module, __clinic_args, b, c);

exit:
/* Cleanup for args */
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=cc6590b8805d5433 input=fa56a709a035666e]*/
/*[clinic end generated code: output=d03daf5067039c03 input=fa56a709a035666e]*/

/*[clinic input]
test_paramname_module
Expand Down Expand Up @@ -4945,20 +4966,19 @@ Test___init__(PyObject *self, PyObject *args, PyObject *kwargs)
!_PyArg_NoKeywords("Test", kwargs)) {
goto exit;
}
if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) {
goto exit;
}
__clinic_args = PyTuple_GetSlice(0, -1);
__clinic_args = Py_NewRef(args);
return_value = Test___init___impl((TestObj *)self, __clinic_args);

exit:
/* Cleanup for args */
Py_XDECREF(__clinic_args);

return return_value;
}

static int
Test___init___impl(TestObj *self, PyObject *args)
/*[clinic end generated code: output=0ed1009fe0dcf98d input=2a8bd0033c9ac772]*/
/*[clinic end generated code: output=f172425cec373cd6 input=2a8bd0033c9ac772]*/


/*[clinic input]
Expand Down Expand Up @@ -4989,20 +5009,19 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs)
!_PyArg_NoKeywords("Test", kwargs)) {
goto exit;
}
if (!_PyArg_CheckPositional("Test", PyTuple_GET_SIZE(args), 0, PY_SSIZE_T_MAX)) {
goto exit;
}
__clinic_args = PyTuple_GetSlice(0, -1);
__clinic_args = Py_NewRef(args);
return_value = Test_impl(type, __clinic_args);

exit:
/* Cleanup for args */
Py_XDECREF(__clinic_args);

return return_value;
}

static PyObject *
Test_impl(PyTypeObject *type, PyObject *args)
/*[clinic end generated code: output=8b219f6633e2a2e9 input=70ad829df3dd9b84]*/
/*[clinic end generated code: output=ee1e8892a67abd4a input=70ad829df3dd9b84]*/


/*[clinic input]
Expand Down
37 changes: 26 additions & 11 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -3393,6 +3393,15 @@ def test_posonly_varpos(self):
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))

def test_posonly_req_opt_varpos(self):
# fn(a, b=False, /, *args)
fn = ac_tester.posonly_req_opt_varpos
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, a=1)
self.assertEqual(fn(1), (1, False, ()))
self.assertEqual(fn(1, 2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))

def test_posonly_poskw_varpos(self):
# fn(a, /, b, *args)
fn = ac_tester.posonly_poskw_varpos
Expand All @@ -3401,23 +3410,26 @@ def test_posonly_poskw_varpos(self):
self.assertEqual(fn(1, b=2), (1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, 2, (3, 4)))
self.assertRaises(TypeError, fn, b=4)
self.assertRaises(TypeError, fn, 1, 2, 3, b=4)
errmsg = re.escape("given by name ('b') and position (2)")
self.assertRaisesRegex(TypeError, errmsg, fn, 1, 2, 3, b=4)

def test_poskw_varpos(self):
# fn(a, *args)
fn = ac_tester.poskw_varpos
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, 1, b=2)
self.assertEqual(fn(a=1), (1, ()))
self.assertRaises(TypeError, fn, 1, a=2)
errmsg = re.escape("given by name ('a') and position (1)")
self.assertRaisesRegex(TypeError, errmsg, fn, 1, a=2)
self.assertEqual(fn(1), (1, ()))
self.assertEqual(fn(1, 2, 3, 4), (1, (2, 3, 4)))

def test_poskw_varpos_kwonly_opt(self):
# fn(a, *args, b=False)
fn = ac_tester.poskw_varpos_kwonly_opt
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, 1, a=2)
errmsg = re.escape("given by name ('a') and position (1)")
self.assertRaisesRegex(TypeError, errmsg, fn, 1, a=2)
self.assertEqual(fn(1, b=2), (1, (), True))
self.assertEqual(fn(1, 2, 3, 4), (1, (2, 3, 4), False))
self.assertEqual(fn(1, 2, 3, 4, b=5), (1, (2, 3, 4), True))
Expand All @@ -3428,7 +3440,8 @@ def test_poskw_varpos_kwonly_opt2(self):
# fn(a, *args, b=False, c=False)
fn = ac_tester.poskw_varpos_kwonly_opt2
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, 1, a=2)
errmsg = re.escape("given by name ('a') and position (1)")
self.assertRaisesRegex(TypeError, errmsg, fn, 1, a=2)
self.assertEqual(fn(1, b=2), (1, (), 2, False))
self.assertEqual(fn(1, b=2, c=3), (1, (), 2, 3))
self.assertEqual(fn(1, 2, 3), (1, (2, 3), False, False))
Expand Down Expand Up @@ -3490,9 +3503,10 @@ def test_null_or_tuple_for_varargs(self):
self.assertEqual(fn(covariant=True, name='a'), ('a', (), True))

self.assertRaises(TypeError, fn, covariant=True)
self.assertRaises(TypeError, fn, 1, name='a')
self.assertRaises(TypeError, fn, 1, 2, 3, name='a', covariant=True)
self.assertRaises(TypeError, fn, 1, 2, 3, covariant=True, name='a')
errmsg = re.escape("given by name ('name') and position (1)")
self.assertRaisesRegex(TypeError, errmsg, fn, 1, name='a')
self.assertRaisesRegex(TypeError, errmsg, fn, 1, 2, 3, name='a', covariant=True)
self.assertRaisesRegex(TypeError, errmsg, fn, 1, 2, 3, covariant=True, name='a')

def test_cloned_func_exception_message(self):
incorrect_arg = -1 # f1() and f2() accept a single str
Expand Down Expand Up @@ -3568,14 +3582,15 @@ def test_defclass_posonly_varpos(self):
cls = ac_tester.TestClass
obj = cls()
fn = obj.defclass_posonly_varpos
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, 1)
errmsg = 'takes at least 2 positional arguments'
self.assertRaisesRegex(TypeError, errmsg, fn)
self.assertRaisesRegex(TypeError, errmsg, fn, 1)
self.assertEqual(fn(1, 2), (cls, 1, 2, ()))
self.assertEqual(fn(1, 2, 3, 4), (cls, 1, 2, (3, 4)))
fn = cls.defclass_posonly_varpos
self.assertRaises(TypeError, fn)
self.assertRaises(TypeError, fn, obj)
self.assertRaises(TypeError, fn, obj, 1)
self.assertRaisesRegex(TypeError, errmsg, fn, obj)
self.assertRaisesRegex(TypeError, errmsg, fn, obj, 1)
self.assertEqual(fn(obj, 1, 2), (cls, 1, 2, ()))
self.assertEqual(fn(obj, 1, 2, 3, 4), (cls, 1, 2, (3, 4)))

Expand Down
Loading