gh-108494: AC supports pos-only args in limited C API (#108498)

AC now checks for "#define Py_LIMITED_API" pattern to use the limited
C API.
This commit is contained in:
Victor Stinner 2023-08-26 00:39:24 +02:00 committed by GitHub
parent 73d33c1a30
commit 86bc9e35c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 82 additions and 5 deletions

View file

@ -3542,6 +3542,17 @@ class LimitedCAPIFunctionalTest(unittest.TestCase):
with self.assertRaises(TypeError):
_testclinic_limited.my_int_func("xyz")
def test_my_int_sum(self):
with self.assertRaises(TypeError):
_testclinic_limited.my_int_sum()
with self.assertRaises(TypeError):
_testclinic_limited.my_int_sum(1)
self.assertEqual(_testclinic_limited.my_int_sum(1, 2), 3)
with self.assertRaises(TypeError):
_testclinic_limited.my_int_sum(1.0, 2)
with self.assertRaises(TypeError):
_testclinic_limited.my_int_sum(1, "str")
class PermutationTests(unittest.TestCase):

View file

@ -45,9 +45,27 @@ my_int_func_impl(PyObject *module, int arg)
}
/*[clinic input]
my_int_sum -> int
x: int
y: int
/
[clinic start generated code]*/
static int
my_int_sum_impl(PyObject *module, int x, int y)
/*[clinic end generated code: output=3e52db9ab5f37e2f input=0edb6796813bf2d3]*/
{
return x + y;
}
static PyMethodDef tester_methods[] = {
TEST_EMPTY_FUNCTION_METHODDEF
MY_INT_FUNC_METHODDEF
MY_INT_SUM_METHODDEF
{NULL, NULL}
};

View file

@ -50,4 +50,36 @@ my_int_func(PyObject *module, PyObject *arg_)
exit:
return return_value;
}
/*[clinic end generated code: output=07e2e8ed6923cd16 input=a9049054013a1b77]*/
PyDoc_STRVAR(my_int_sum__doc__,
"my_int_sum($module, x, y, /)\n"
"--\n"
"\n");
#define MY_INT_SUM_METHODDEF \
{"my_int_sum", (PyCFunction)my_int_sum, METH_VARARGS, my_int_sum__doc__},
static int
my_int_sum_impl(PyObject *module, int x, int y);
static PyObject *
my_int_sum(PyObject *module, PyObject *args)
{
PyObject *return_value = NULL;
int x;
int y;
int _return_value;
if (!PyArg_ParseTuple(args, "ii:my_int_sum",
&x, &y))
goto exit;
_return_value = my_int_sum_impl(module, x, y);
if ((_return_value == -1) && PyErr_Occurred()) {
goto exit;
}
return_value = PyLong_FromLong((long)_return_value);
exit:
return return_value;
}
/*[clinic end generated code: output=f9f7209255bb969e input=a9049054013a1b77]*/

View file

@ -78,6 +78,7 @@ CLINIC_PREFIXED_ARGS = {
"noptargs",
"return_value",
}
LIMITED_CAPI_REGEX = re.compile(r'#define +Py_LIMITED_API')
class Sentinels(enum.Enum):
@ -1249,6 +1250,22 @@ class CLanguage(Language):
parser_prototype = self.PARSER_PROTOTYPE_VARARGS
parser_definition = parser_body(parser_prototype, ' {option_group_parsing}')
elif not requires_defining_class and pos_only == len(parameters) - pseudo_args and clinic.limited_capi:
# positional-only for the limited C API
flags = "METH_VARARGS"
parser_prototype = self.PARSER_PROTOTYPE_VARARGS
parser_code = [normalize_snippet("""
if (!PyArg_ParseTuple(args, "{format_units}:{name}",
{parse_arguments}))
goto exit;
""", indent=4)]
argname_fmt = 'args[%d]'
declarations = ""
parser_definition = parser_body(parser_prototype, *parser_code,
declarations=declarations)
elif not requires_defining_class and pos_only == len(parameters) - pseudo_args:
if not new_or_init:
# positional-only, but no option groups
@ -2581,10 +2598,6 @@ def parse_file(
) -> None:
verify = not ns.force
limited_capi = ns.limited_capi
# XXX Temporary solution
if os.path.basename(filename) == '_testclinic_limited.c':
print(f"{filename} uses limited C API")
limited_capi = True
if not output:
output = filename
@ -2605,6 +2618,9 @@ def parse_file(
if not find_start_re.search(raw):
return
if LIMITED_CAPI_REGEX.search(raw):
limited_capi = True
assert isinstance(language, CLanguage)
clinic = Clinic(language,
verify=verify,