mirror of
https://github.com/python/cpython.git
synced 2026-01-06 07:22:09 +00:00
gh-135763: AC: Implement `allow_negative for Py_ssize_t` (#138150)
Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com>
This commit is contained in:
parent
7c92497e5c
commit
47bc10e6b3
7 changed files with 200 additions and 24 deletions
|
|
@ -51,6 +51,10 @@ extern int _PyObject_RealIsSubclass(PyObject *derived, PyObject *cls);
|
|||
// Export for '_bisect' shared extension.
|
||||
PyAPI_FUNC(int) _Py_convert_optional_to_ssize_t(PyObject *, void *);
|
||||
|
||||
// Convert Python int to Py_ssize_t. Do nothing if the argument is None.
|
||||
// Raises ValueError if argument is negative.
|
||||
PyAPI_FUNC(int) _Py_convert_optional_to_non_negative_ssize_t(PyObject *, void *);
|
||||
|
||||
// Same as PyNumber_Index() but can return an instance of a subclass of int.
|
||||
// Export for 'math' shared extension.
|
||||
PyAPI_FUNC(PyObject*) _PyNumber_Index(PyObject *o);
|
||||
|
|
|
|||
|
|
@ -1612,12 +1612,17 @@ test_Py_ssize_t_converter
|
|||
a: Py_ssize_t = 12
|
||||
b: Py_ssize_t(accept={int}) = 34
|
||||
c: Py_ssize_t(accept={int, NoneType}) = 56
|
||||
d: Py_ssize_t(accept={int}, allow_negative=False) = 78
|
||||
e: Py_ssize_t(accept={int, NoneType}, allow_negative=False) = 90
|
||||
f: Py_ssize_t(accept={int}, allow_negative=True) = -12
|
||||
g: Py_ssize_t(accept={int, NoneType}, allow_negative=True) = -34
|
||||
/
|
||||
|
||||
[clinic start generated code]*/
|
||||
|
||||
PyDoc_STRVAR(test_Py_ssize_t_converter__doc__,
|
||||
"test_Py_ssize_t_converter($module, a=12, b=34, c=56, /)\n"
|
||||
"test_Py_ssize_t_converter($module, a=12, b=34, c=56, d=78, e=90, f=-12,\n"
|
||||
" g=-34, /)\n"
|
||||
"--\n"
|
||||
"\n");
|
||||
|
||||
|
|
@ -1626,7 +1631,8 @@ PyDoc_STRVAR(test_Py_ssize_t_converter__doc__,
|
|||
|
||||
static PyObject *
|
||||
test_Py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b,
|
||||
Py_ssize_t c);
|
||||
Py_ssize_t c, Py_ssize_t d, Py_ssize_t e,
|
||||
Py_ssize_t f, Py_ssize_t g);
|
||||
|
||||
static PyObject *
|
||||
test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
|
|
@ -1635,8 +1641,12 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na
|
|||
Py_ssize_t a = 12;
|
||||
Py_ssize_t b = 34;
|
||||
Py_ssize_t c = 56;
|
||||
Py_ssize_t d = 78;
|
||||
Py_ssize_t e = 90;
|
||||
Py_ssize_t f = -12;
|
||||
Py_ssize_t g = -34;
|
||||
|
||||
if (!_PyArg_CheckPositional("test_Py_ssize_t_converter", nargs, 0, 3)) {
|
||||
if (!_PyArg_CheckPositional("test_Py_ssize_t_converter", nargs, 0, 7)) {
|
||||
goto exit;
|
||||
}
|
||||
if (nargs < 1) {
|
||||
|
|
@ -1675,8 +1685,55 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na
|
|||
if (!_Py_convert_optional_to_ssize_t(args[2], &c)) {
|
||||
goto exit;
|
||||
}
|
||||
if (nargs < 4) {
|
||||
goto skip_optional;
|
||||
}
|
||||
{
|
||||
Py_ssize_t ival = -1;
|
||||
PyObject *iobj = _PyNumber_Index(args[3]);
|
||||
if (iobj != NULL) {
|
||||
ival = PyLong_AsSsize_t(iobj);
|
||||
Py_DECREF(iobj);
|
||||
}
|
||||
if (ival == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
d = ival;
|
||||
if (d < 0) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"d cannot be negative");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (nargs < 5) {
|
||||
goto skip_optional;
|
||||
}
|
||||
if (!_Py_convert_optional_to_non_negative_ssize_t(args[4], &e)) {
|
||||
goto exit;
|
||||
}
|
||||
if (nargs < 6) {
|
||||
goto skip_optional;
|
||||
}
|
||||
{
|
||||
Py_ssize_t ival = -1;
|
||||
PyObject *iobj = _PyNumber_Index(args[5]);
|
||||
if (iobj != NULL) {
|
||||
ival = PyLong_AsSsize_t(iobj);
|
||||
Py_DECREF(iobj);
|
||||
}
|
||||
if (ival == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
f = ival;
|
||||
}
|
||||
if (nargs < 7) {
|
||||
goto skip_optional;
|
||||
}
|
||||
if (!_Py_convert_optional_to_ssize_t(args[6], &g)) {
|
||||
goto exit;
|
||||
}
|
||||
skip_optional:
|
||||
return_value = test_Py_ssize_t_converter_impl(module, a, b, c);
|
||||
return_value = test_Py_ssize_t_converter_impl(module, a, b, c, d, e, f, g);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
|
@ -1684,8 +1741,9 @@ test_Py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t na
|
|||
|
||||
static PyObject *
|
||||
test_Py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b,
|
||||
Py_ssize_t c)
|
||||
/*[clinic end generated code: output=48214bc3d01f4dd7 input=3855f184bb3f299d]*/
|
||||
Py_ssize_t c, Py_ssize_t d, Py_ssize_t e,
|
||||
Py_ssize_t f, Py_ssize_t g)
|
||||
/*[clinic end generated code: output=4ae0a56a1447fba9 input=a25bac8ecf2890aa]*/
|
||||
|
||||
|
||||
/*[clinic input]
|
||||
|
|
|
|||
|
|
@ -2610,6 +2610,19 @@ def test_disallow_defining_class_at_module_level(self):
|
|||
"""
|
||||
self.expect_failure(block, err, lineno=2)
|
||||
|
||||
def test_allow_negative_accepted_by_py_ssize_t_converter_only(self):
|
||||
errmsg = re.escape("converter_init() got an unexpected keyword argument 'allow_negative'")
|
||||
unsupported_converters = [converter_name for converter_name in converters.keys()
|
||||
if converter_name != "Py_ssize_t"]
|
||||
for converter in unsupported_converters:
|
||||
with self.subTest(converter=converter):
|
||||
block = f"""
|
||||
module m
|
||||
m.func
|
||||
a: {converter}(allow_negative=True)
|
||||
"""
|
||||
with self.assertRaisesRegex((AssertionError, TypeError), errmsg):
|
||||
self.parse_function(block)
|
||||
|
||||
class ClinicExternalTest(TestCase):
|
||||
maxDiff = None
|
||||
|
|
@ -3194,8 +3207,12 @@ def test_py_ssize_t_converter(self):
|
|||
ac_tester.py_ssize_t_converter(PY_SSIZE_T_MAX + 1)
|
||||
with self.assertRaises(TypeError):
|
||||
ac_tester.py_ssize_t_converter([])
|
||||
self.assertEqual(ac_tester.py_ssize_t_converter(), (12, 34, 56))
|
||||
self.assertEqual(ac_tester.py_ssize_t_converter(1, 2, None), (1, 2, 56))
|
||||
with self.assertRaises(ValueError):
|
||||
ac_tester.py_ssize_t_converter(12, 34, 56, -1)
|
||||
with self.assertRaises(ValueError):
|
||||
ac_tester.py_ssize_t_converter(12, 34, 56, 78, -1)
|
||||
self.assertEqual(ac_tester.py_ssize_t_converter(), (12, 34, 56, 78, 90, -12, -34))
|
||||
self.assertEqual(ac_tester.py_ssize_t_converter(1, 2, None, 3, None, 4, None), (1, 2, 56, 3, 90, 4, -34))
|
||||
|
||||
def test_slice_index_converter(self):
|
||||
from _testcapi import PY_SSIZE_T_MIN, PY_SSIZE_T_MAX
|
||||
|
|
|
|||
|
|
@ -443,16 +443,21 @@ py_ssize_t_converter
|
|||
a: Py_ssize_t = 12
|
||||
b: Py_ssize_t(accept={int}) = 34
|
||||
c: Py_ssize_t(accept={int, NoneType}) = 56
|
||||
d: Py_ssize_t(accept={int}, allow_negative=False) = 78
|
||||
e: Py_ssize_t(accept={int, NoneType}, allow_negative=False) = 90
|
||||
f: Py_ssize_t(accept={int}, allow_negative=False) = -12
|
||||
g: Py_ssize_t(accept={int, NoneType}, py_default="-34", allow_negative=False) = -34
|
||||
/
|
||||
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b,
|
||||
Py_ssize_t c)
|
||||
/*[clinic end generated code: output=ce252143e0ed0372 input=76d0f342e9317a1f]*/
|
||||
Py_ssize_t c, Py_ssize_t d, Py_ssize_t e,
|
||||
Py_ssize_t f, Py_ssize_t g)
|
||||
/*[clinic end generated code: output=ecf8e1a4a9abc95e input=7b7fa954780c1cb0]*/
|
||||
{
|
||||
RETURN_PACKED_ARGS(3, PyLong_FromSsize_t, Py_ssize_t, a, b, c);
|
||||
RETURN_PACKED_ARGS(7, PyLong_FromSsize_t, Py_ssize_t, a, b, c, d, e, f, g);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
68
Modules/clinic/_testclinic.c.h
generated
68
Modules/clinic/_testclinic.c.h
generated
|
|
@ -1196,7 +1196,8 @@ exit:
|
|||
}
|
||||
|
||||
PyDoc_STRVAR(py_ssize_t_converter__doc__,
|
||||
"py_ssize_t_converter($module, a=12, b=34, c=56, /)\n"
|
||||
"py_ssize_t_converter($module, a=12, b=34, c=56, d=78, e=90, f=-12,\n"
|
||||
" g=-34, /)\n"
|
||||
"--\n"
|
||||
"\n");
|
||||
|
||||
|
|
@ -1205,7 +1206,8 @@ PyDoc_STRVAR(py_ssize_t_converter__doc__,
|
|||
|
||||
static PyObject *
|
||||
py_ssize_t_converter_impl(PyObject *module, Py_ssize_t a, Py_ssize_t b,
|
||||
Py_ssize_t c);
|
||||
Py_ssize_t c, Py_ssize_t d, Py_ssize_t e,
|
||||
Py_ssize_t f, Py_ssize_t g);
|
||||
|
||||
static PyObject *
|
||||
py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
|
|
@ -1214,8 +1216,12 @@ py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
|||
Py_ssize_t a = 12;
|
||||
Py_ssize_t b = 34;
|
||||
Py_ssize_t c = 56;
|
||||
Py_ssize_t d = 78;
|
||||
Py_ssize_t e = 90;
|
||||
Py_ssize_t f = -12;
|
||||
Py_ssize_t g = -34;
|
||||
|
||||
if (!_PyArg_CheckPositional("py_ssize_t_converter", nargs, 0, 3)) {
|
||||
if (!_PyArg_CheckPositional("py_ssize_t_converter", nargs, 0, 7)) {
|
||||
goto exit;
|
||||
}
|
||||
if (nargs < 1) {
|
||||
|
|
@ -1254,8 +1260,60 @@ py_ssize_t_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
|||
if (!_Py_convert_optional_to_ssize_t(args[2], &c)) {
|
||||
goto exit;
|
||||
}
|
||||
if (nargs < 4) {
|
||||
goto skip_optional;
|
||||
}
|
||||
{
|
||||
Py_ssize_t ival = -1;
|
||||
PyObject *iobj = _PyNumber_Index(args[3]);
|
||||
if (iobj != NULL) {
|
||||
ival = PyLong_AsSsize_t(iobj);
|
||||
Py_DECREF(iobj);
|
||||
}
|
||||
if (ival == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
d = ival;
|
||||
if (d < 0) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"d cannot be negative");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (nargs < 5) {
|
||||
goto skip_optional;
|
||||
}
|
||||
if (!_Py_convert_optional_to_non_negative_ssize_t(args[4], &e)) {
|
||||
goto exit;
|
||||
}
|
||||
if (nargs < 6) {
|
||||
goto skip_optional;
|
||||
}
|
||||
{
|
||||
Py_ssize_t ival = -1;
|
||||
PyObject *iobj = _PyNumber_Index(args[5]);
|
||||
if (iobj != NULL) {
|
||||
ival = PyLong_AsSsize_t(iobj);
|
||||
Py_DECREF(iobj);
|
||||
}
|
||||
if (ival == -1 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
f = ival;
|
||||
if (f < 0) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"f cannot be negative");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (nargs < 7) {
|
||||
goto skip_optional;
|
||||
}
|
||||
if (!_Py_convert_optional_to_non_negative_ssize_t(args[6], &g)) {
|
||||
goto exit;
|
||||
}
|
||||
skip_optional:
|
||||
return_value = py_ssize_t_converter_impl(module, a, b, c);
|
||||
return_value = py_ssize_t_converter_impl(module, a, b, c, d, e, f, g);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
|
|
@ -4542,4 +4600,4 @@ _testclinic_TestClass_posonly_poskw_varpos_array_no_fastcall(PyObject *type, PyO
|
|||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=6b04671afdafbecf input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=0764e6f8c9d94057 input=a9049054013a1b77]*/
|
||||
|
|
|
|||
|
|
@ -33,6 +33,19 @@ _Py_convert_optional_to_ssize_t(PyObject *obj, void *result)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
_Py_convert_optional_to_non_negative_ssize_t(PyObject *obj, void *result)
|
||||
{
|
||||
if (!_Py_convert_optional_to_ssize_t(obj, result)) {
|
||||
return 0;
|
||||
}
|
||||
if (obj != Py_None && *((Py_ssize_t *)result) < 0) {
|
||||
PyErr_SetString(PyExc_ValueError, "argument cannot be negative");
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Helper for mkvalue() to scan the length of a format */
|
||||
|
||||
|
|
|
|||
|
|
@ -420,21 +420,39 @@ class Py_ssize_t_converter(CConverter):
|
|||
type = 'Py_ssize_t'
|
||||
c_ignored_default = "0"
|
||||
|
||||
def converter_init(self, *, accept: TypeSet = {int}) -> None:
|
||||
def converter_init(self, *, accept: TypeSet = {int},
|
||||
allow_negative: bool = True) -> None:
|
||||
self.allow_negative = allow_negative
|
||||
if accept == {int}:
|
||||
self.format_unit = 'n'
|
||||
self.default_type = int
|
||||
elif accept == {int, NoneType}:
|
||||
self.converter = '_Py_convert_optional_to_ssize_t'
|
||||
if self.allow_negative:
|
||||
self.converter = '_Py_convert_optional_to_ssize_t'
|
||||
else:
|
||||
self.converter = '_Py_convert_optional_to_non_negative_ssize_t'
|
||||
else:
|
||||
fail(f"Py_ssize_t_converter: illegal 'accept' argument {accept!r}")
|
||||
|
||||
def use_converter(self) -> None:
|
||||
if self.converter == '_Py_convert_optional_to_ssize_t':
|
||||
self.add_include('pycore_abstract.h',
|
||||
'_Py_convert_optional_to_ssize_t()')
|
||||
if self.converter in {
|
||||
'_Py_convert_optional_to_ssize_t',
|
||||
'_Py_convert_optional_to_non_negative_ssize_t',
|
||||
}:
|
||||
self.add_include('pycore_abstract.h', f'{self.converter}()')
|
||||
|
||||
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
|
||||
if self.allow_negative:
|
||||
non_negative_check = ''
|
||||
else:
|
||||
non_negative_check = self.format_code("""
|
||||
if ({paramname} < 0) {{{{
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"{paramname} cannot be negative");
|
||||
goto exit;
|
||||
}}}}""",
|
||||
argname=argname,
|
||||
)
|
||||
if self.format_unit == 'n':
|
||||
if limited_capi:
|
||||
PyNumber_Index = 'PyNumber_Index'
|
||||
|
|
@ -452,11 +470,13 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
|
|||
if (ival == -1 && PyErr_Occurred()) {{{{
|
||||
goto exit;
|
||||
}}}}
|
||||
{paramname} = ival;
|
||||
{paramname} = ival;{non_negative_check}
|
||||
}}}}
|
||||
""",
|
||||
argname=argname,
|
||||
PyNumber_Index=PyNumber_Index)
|
||||
PyNumber_Index=PyNumber_Index,
|
||||
non_negative_check=non_negative_check,
|
||||
)
|
||||
if not limited_capi:
|
||||
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
|
||||
return self.format_code("""
|
||||
|
|
@ -465,7 +485,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
|
|||
{paramname} = PyNumber_AsSsize_t({argname}, PyExc_OverflowError);
|
||||
if ({paramname} == -1 && PyErr_Occurred()) {{{{
|
||||
goto exit;
|
||||
}}}}
|
||||
}}}}{non_negative_check}
|
||||
}}}}
|
||||
else {{{{
|
||||
{bad_argument}
|
||||
|
|
@ -475,6 +495,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
|
|||
""",
|
||||
argname=argname,
|
||||
bad_argument=self.bad_argument(displayname, 'integer or None', limited_capi=limited_capi),
|
||||
non_negative_check=non_negative_check,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue