diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h index b2c52cf2a95..7577bdec5c3 100644 --- a/Modules/_io/clinic/bufferedio.c.h +++ b/Modules/_io/clinic/bufferedio.c.h @@ -26,7 +26,6 @@ _io__BufferedIOBase_readinto(PyObject *self, PyObject *arg) Py_buffer buffer = {NULL, NULL}; if (PyObject_GetBuffer(arg, &buffer, PyBUF_WRITABLE) < 0) { - PyErr_Clear(); _PyArg_BadArgument("readinto", "argument", "read-write bytes-like object", arg); goto exit; } @@ -63,7 +62,6 @@ _io__BufferedIOBase_readinto1(PyObject *self, PyObject *arg) Py_buffer buffer = {NULL, NULL}; if (PyObject_GetBuffer(arg, &buffer, PyBUF_WRITABLE) < 0) { - PyErr_Clear(); _PyArg_BadArgument("readinto1", "argument", "read-write bytes-like object", arg); goto exit; } @@ -590,7 +588,6 @@ _io__Buffered_readinto(buffered *self, PyObject *arg) Py_buffer buffer = {NULL, NULL}; if (PyObject_GetBuffer(arg, &buffer, PyBUF_WRITABLE) < 0) { - PyErr_Clear(); _PyArg_BadArgument("readinto", "argument", "read-write bytes-like object", arg); goto exit; } @@ -627,7 +624,6 @@ _io__Buffered_readinto1(buffered *self, PyObject *arg) Py_buffer buffer = {NULL, NULL}; if (PyObject_GetBuffer(arg, &buffer, PyBUF_WRITABLE) < 0) { - PyErr_Clear(); _PyArg_BadArgument("readinto1", "argument", "read-write bytes-like object", arg); goto exit; } @@ -1098,4 +1094,4 @@ skip_optional_pos: exit: return return_value; } -/*[clinic end generated code: output=9e09091995ae02b0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=f940cea085f0bf91 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/bytesio.c.h b/Modules/_io/clinic/bytesio.c.h index d7364779200..d42ab48cef2 100644 --- a/Modules/_io/clinic/bytesio.c.h +++ b/Modules/_io/clinic/bytesio.c.h @@ -328,7 +328,6 @@ _io_BytesIO_readinto(bytesio *self, PyObject *arg) Py_buffer buffer = {NULL, NULL}; if (PyObject_GetBuffer(arg, &buffer, PyBUF_WRITABLE) < 0) { - PyErr_Clear(); _PyArg_BadArgument("readinto", "argument", "read-write bytes-like object", arg); goto exit; } @@ -538,4 +537,4 @@ skip_optional_pos: exit: return return_value; } -/*[clinic end generated code: output=8ab65edc03edbfe0 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=b753fdf1ba36c461 input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/fileio.c.h b/Modules/_io/clinic/fileio.c.h index 29f8cf6aa9a..deb99fa9d99 100644 --- a/Modules/_io/clinic/fileio.c.h +++ b/Modules/_io/clinic/fileio.c.h @@ -245,7 +245,6 @@ _io_FileIO_readinto(fileio *self, PyTypeObject *cls, PyObject *const *args, Py_s goto exit; } if (PyObject_GetBuffer(args[0], &buffer, PyBUF_WRITABLE) < 0) { - PyErr_Clear(); _PyArg_BadArgument("readinto", "argument 1", "read-write bytes-like object", args[0]); goto exit; } @@ -536,4 +535,4 @@ _io_FileIO_isatty(fileio *self, PyObject *Py_UNUSED(ignored)) #ifndef _IO_FILEIO_TRUNCATE_METHODDEF #define _IO_FILEIO_TRUNCATE_METHODDEF #endif /* !defined(_IO_FILEIO_TRUNCATE_METHODDEF) */ -/*[clinic end generated code: output=238dd48819076434 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=2ce6ce923ccef86e input=a9049054013a1b77]*/ diff --git a/Modules/_io/clinic/winconsoleio.c.h b/Modules/_io/clinic/winconsoleio.c.h index 0683eecdfeb..ecc71e552c2 100644 --- a/Modules/_io/clinic/winconsoleio.c.h +++ b/Modules/_io/clinic/winconsoleio.c.h @@ -243,7 +243,6 @@ _io__WindowsConsoleIO_readinto(winconsoleio *self, PyTypeObject *cls, PyObject * goto exit; } if (PyObject_GetBuffer(args[0], &buffer, PyBUF_WRITABLE) < 0) { - PyErr_Clear(); _PyArg_BadArgument("readinto", "argument 1", "read-write bytes-like object", args[0]); goto exit; } @@ -465,4 +464,4 @@ _io__WindowsConsoleIO_isatty(winconsoleio *self, PyObject *Py_UNUSED(ignored)) #ifndef _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF #define _IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF #endif /* !defined(_IO__WINDOWSCONSOLEIO_ISATTY_METHODDEF) */ -/*[clinic end generated code: output=7be51d48ddb7c8c8 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=37febc4c96732b3b input=a9049054013a1b77]*/ diff --git a/Modules/_multiprocessing/multiprocessing.c b/Modules/_multiprocessing/multiprocessing.c index 16b5cb5dd9e..2e6d8eb68c0 100644 --- a/Modules/_multiprocessing/multiprocessing.c +++ b/Modules/_multiprocessing/multiprocessing.c @@ -14,16 +14,17 @@ class HANDLE_converter(CConverter): type = "HANDLE" format_unit = '"F_HANDLE"' - def parse_arg(self, argname, displayname): - return """ + def parse_arg(self, argname, displayname, *, limited_capi): + return self.format_code(""" {paramname} = PyLong_AsVoidPtr({argname}); if (!{paramname} && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) + """, + argname=argname) [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=3e537d244034affb]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=3cf0318efc6a8772]*/ /*[clinic input] module _multiprocessing diff --git a/Modules/_posixsubprocess.c b/Modules/_posixsubprocess.c index ef76d26282e..2898eedc3e3 100644 --- a/Modules/_posixsubprocess.c +++ b/Modules/_posixsubprocess.c @@ -87,15 +87,16 @@ class pid_t_converter(CConverter): type = 'pid_t' format_unit = '" _Py_PARSE_PID "' - def parse_arg(self, argname, displayname): - return """ + def parse_arg(self, argname, displayname, *, limited_capi): + return self.format_code(""" {paramname} = PyLong_AsPid({argname}); if ({paramname} == -1 && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) + """, + argname=argname) [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=5af1c116d56cbb5a]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=c94349aa1aad151d]*/ #include "clinic/_posixsubprocess.c.h" diff --git a/Modules/_struct.c b/Modules/_struct.c index 4ae21cce74f..be4c23af384 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -110,18 +110,20 @@ class cache_struct_converter(CConverter): c_default = "NULL" broken_limited_capi = True - def parse_arg(self, argname, displayname): - return """ + def parse_arg(self, argname, displayname, *, limited_capi): + assert not limited_capi + return self.format_code(""" if (!{converter}(module, {argname}, &{paramname})) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.name, - converter=self.converter) + """, + argname=argname, + converter=self.converter) def cleanup(self): return "Py_XDECREF(%s);\n" % self.name [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=14e83804f599ed8f]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=c33b27d6b06006c6]*/ static int cache_struct_converter(PyObject *, PyObject *, PyStructObject **); diff --git a/Modules/clinic/_testclinic_limited.c.h b/Modules/clinic/_testclinic_limited.c.h index 93e7d7f9876..eff72cee249 100644 --- a/Modules/clinic/_testclinic_limited.c.h +++ b/Modules/clinic/_testclinic_limited.c.h @@ -37,7 +37,8 @@ my_int_func(PyObject *module, PyObject *arg_) int arg; int _return_value; - if (!PyArg_Parse(arg_, "i:my_int_func", &arg)) { + arg = PyLong_AsInt(arg_); + if (arg == -1 && PyErr_Occurred()) { goto exit; } _return_value = my_int_func_impl(module, arg); @@ -56,22 +57,31 @@ PyDoc_STRVAR(my_int_sum__doc__, "\n"); #define MY_INT_SUM_METHODDEF \ - {"my_int_sum", (PyCFunction)my_int_sum, METH_VARARGS, my_int_sum__doc__}, + {"my_int_sum", (PyCFunction)(void(*)(void))my_int_sum, METH_FASTCALL, 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) +my_int_sum(PyObject *module, PyObject *const *args, Py_ssize_t nargs) { PyObject *return_value = NULL; int x; int y; int _return_value; - if (!PyArg_ParseTuple(args, "ii:my_int_sum", - &x, &y)) + if (nargs != 2) { + PyErr_Format(PyExc_TypeError, "my_int_sum expected 2 arguments, got %zd", nargs); goto exit; + } + x = PyLong_AsInt(args[0]); + if (x == -1 && PyErr_Occurred()) { + goto exit; + } + y = PyLong_AsInt(args[1]); + if (y == -1 && PyErr_Occurred()) { + goto exit; + } _return_value = my_int_sum_impl(module, x, y); if ((_return_value == -1) && PyErr_Occurred()) { goto exit; @@ -81,4 +91,4 @@ my_int_sum(PyObject *module, PyObject *args) exit: return return_value; } -/*[clinic end generated code: output=dcd5203d0d29df3a input=a9049054013a1b77]*/ +/*[clinic end generated code: output=5cf64baf978d2288 input=a9049054013a1b77]*/ diff --git a/Modules/overlapped.c b/Modules/overlapped.c index c682e6adccc..e23db22dadb 100644 --- a/Modules/overlapped.c +++ b/Modules/overlapped.c @@ -38,13 +38,14 @@ class pointer_converter(CConverter): format_unit = '"F_POINTER"' - def parse_arg(self, argname, displayname): - return """ + def parse_arg(self, argname, displayname, *, limited_capi): + return self.format_code(""" {paramname} = PyLong_AsVoidPtr({argname}); if (!{paramname} && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) + """, + argname=argname) class OVERLAPPED_converter(pointer_converter): type = 'OVERLAPPED *' @@ -55,13 +56,14 @@ class HANDLE_converter(pointer_converter): class ULONG_PTR_converter(pointer_converter): type = 'ULONG_PTR' - def parse_arg(self, argname, displayname): - return """ + def parse_arg(self, argname, displayname, *, limited_capi): + return self.format_code(""" {paramname} = (uintptr_t)PyLong_AsVoidPtr({argname}); if (!{paramname} && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) + """, + argname=argname) class DWORD_converter(unsigned_long_converter): type = 'DWORD' @@ -69,7 +71,7 @@ class DWORD_converter(unsigned_long_converter): class BOOL_converter(int_converter): type = 'BOOL' [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=8a07ea3018f4cec8]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=436f4440630a304c]*/ /*[clinic input] module _overlapped diff --git a/Modules/resource.c b/Modules/resource.c index 9e302a3a1ed..d65509ec343 100644 --- a/Modules/resource.c +++ b/Modules/resource.c @@ -23,15 +23,16 @@ class pid_t_converter(CConverter): type = 'pid_t' format_unit = '" _Py_PARSE_PID "' - def parse_arg(self, argname, displayname): - return """ + def parse_arg(self, argname, displayname, *, limited_capi): + return self.format_code(""" {paramname} = PyLong_AsPid({argname}); if ({paramname} == -1 && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) + """, + argname=argname) [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=5af1c116d56cbb5a]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=c94349aa1aad151d]*/ #include "clinic/resource.c.h" diff --git a/PC/msvcrtmodule.c b/PC/msvcrtmodule.c index afc810adcf7..d7c21766d70 100644 --- a/PC/msvcrtmodule.c +++ b/PC/msvcrtmodule.c @@ -38,13 +38,14 @@ class HANDLE_converter(CConverter): type = 'void *' format_unit = '"_Py_PARSE_UINTPTR"' - def parse_arg(self, argname, displayname): - return """ + def parse_arg(self, argname, displayname, *, limited_capi): + return self.format_code(""" {paramname} = PyLong_AsVoidPtr({argname}); if (!{paramname} && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) + """, + argname=argname) class HANDLE_return_converter(CReturnConverter): type = 'void *' @@ -74,7 +75,7 @@ class wchar_t_return_converter(CReturnConverter): data.return_conversion.append( 'return_value = PyUnicode_FromOrdinal(_return_value);\n') [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=1e8e9fa3538ec08f]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=ff031be44ab3250d]*/ /*[clinic input] module msvcrt diff --git a/PC/winreg.c b/PC/winreg.c index 767127d34ca..77b80217ac0 100644 --- a/PC/winreg.c +++ b/PC/winreg.c @@ -222,13 +222,15 @@ class HKEY_converter(CConverter): converter = 'clinic_HKEY_converter' broken_limited_capi = True - def parse_arg(self, argname, displayname): - return """ - if (!{converter}(_PyModule_GetState(module), {argname}, &{paramname})) {{{{ - goto exit; - }}}} - """.format(argname=argname, paramname=self.parser_name, - converter=self.converter) + def parse_arg(self, argname, displayname, *, limited_capi): + assert not limited_capi + return self.format_code(""" + if (!{converter}(_PyModule_GetState(module), {argname}, &{paramname})) {{{{ + goto exit; + }}}} + """, + argname=argname, + converter=self.converter) class HKEY_return_converter(CReturnConverter): type = 'HKEY' @@ -250,7 +252,7 @@ class self_return_converter(CReturnConverter): data.return_conversion.append( 'return_value = (PyObject *)_return_value;\n') [python start generated code]*/ -/*[python end generated code: output=da39a3ee5e6b4b0d input=f8cb7034338aeaba]*/ +/*[python end generated code: output=da39a3ee5e6b4b0d input=4979f33998ffb6f8]*/ #include "clinic/winreg.c.h" diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index b744ca7e5c8..eca4747a3e4 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -990,6 +990,7 @@ class CLanguage(Language): params: dict[int, Parameter], argname_fmt: str | None, *, + fastcall: bool, limited_capi: bool, clinic: Clinic, ) -> str: @@ -1003,26 +1004,30 @@ class CLanguage(Language): if p.is_optional(): if argname_fmt: conditions.append(f"nargs < {i+1} && {argname_fmt % i}") - elif func.kind.new_or_init or limited_capi: - conditions.append(f"nargs < {i+1} && PyDict_Contains(kwargs, &_Py_ID({p.name}))") - containscheck = "PyDict_Contains" - clinic.add_include('pycore_runtime.h', '_Py_ID()') - else: + elif fastcall: conditions.append(f"nargs < {i+1} && PySequence_Contains(kwnames, &_Py_ID({p.name}))") containscheck = "PySequence_Contains" clinic.add_include('pycore_runtime.h', '_Py_ID()') + else: + conditions.append(f"nargs < {i+1} && PyDict_Contains(kwargs, &_Py_ID({p.name}))") + containscheck = "PyDict_Contains" + clinic.add_include('pycore_runtime.h', '_Py_ID()') else: conditions = [f"nargs < {i+1}"] condition = ") || (".join(conditions) if len(conditions) > 1: condition = f"(({condition}))" if last_param.is_optional(): - if limited_capi: - condition = f"kwargs && PyDict_Size(kwargs) && {condition}" - elif func.kind.new_or_init: - condition = f"kwargs && PyDict_GET_SIZE(kwargs) && {condition}" + if fastcall: + if limited_capi: + condition = f"kwnames && PyTuple_Size(kwnames) && {condition}" + else: + condition = f"kwnames && PyTuple_GET_SIZE(kwnames) && {condition}" else: - condition = f"kwnames && PyTuple_GET_SIZE(kwnames) && {condition}" + if limited_capi: + condition = f"kwargs && PyDict_Size(kwargs) && {condition}" + else: + condition = f"kwargs && PyDict_GET_SIZE(kwargs) && {condition}" names = [repr(p.name) for p in params.values()] pstr = pprint_words(names) pl = 's' if len(params) != 1 else '' @@ -1193,6 +1198,7 @@ class CLanguage(Language): add(field) return linear_format(output(), parser_declarations=declarations) + fastcall = not new_or_init limited_capi = clinic.limited_capi if limited_capi and (requires_defining_class or pseudo_args or (any(p.is_optional() for p in parameters) and @@ -1210,7 +1216,7 @@ class CLanguage(Language): parser_prototype = self.PARSER_PROTOTYPE_NOARGS parser_code = [] else: - assert not new_or_init + assert fastcall flags = "METH_METHOD|METH_FASTCALL|METH_KEYWORDS" parser_prototype = self.PARSER_PROTOTYPE_DEF_CLASS @@ -1261,11 +1267,8 @@ class CLanguage(Language): {c_basename}({self_type}{self_name}, PyObject *%s) """ % argname) - if limited_capi: - parsearg = None - else: - displayname = parameters[0].get_displayname(0) - parsearg = converters[0].parse_arg(argname, displayname) + displayname = parameters[0].get_displayname(0) + parsearg = converters[0].parse_arg(argname, displayname, limited_capi=limited_capi) if parsearg is None: parsearg = """ if (!PyArg_Parse(%s, "{format_units}:{name}", {parse_arguments})) {{ @@ -1284,27 +1287,8 @@ 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) and - not pseudo_args and limited_capi): - # positional-only for the limited C API - flags = "METH_VARARGS" - parser_prototype = self.PARSER_PROTOTYPE_VARARGS - if all(isinstance(c, object_converter) and c.format_unit == 'O' for c in converters): - parser_code = [normalize_snippet(""" - if (!PyArg_UnpackTuple(args, "{name}", %d, %d, - {parse_arguments})) - goto exit; - """ % (min_pos, max_pos), indent=4)] - else: - parser_code = [normalize_snippet(""" - if (!PyArg_ParseTuple(args, "{format_units}:{name}", - {parse_arguments})) - goto exit; - """, indent=4)] - parser_definition = parser_body(parser_prototype, *parser_code) - elif not requires_defining_class and pos_only == len(parameters) - pseudo_args: - if not new_or_init: + if fastcall: # positional-only, but no option groups # we only need one call to _PyArg_ParseStack @@ -1318,24 +1302,59 @@ class CLanguage(Language): flags = "METH_VARARGS" parser_prototype = self.PARSER_PROTOTYPE_VARARGS - nargs = 'PyTuple_GET_SIZE(args)' - argname_fmt = 'PyTuple_GET_ITEM(args, %d)' + if limited_capi: + nargs = 'PyTuple_Size(args)' + argname_fmt = 'PyTuple_GetItem(args, %d)' + else: + nargs = 'PyTuple_GET_SIZE(args)' + argname_fmt = 'PyTuple_GET_ITEM(args, %d)' left_args = f"{nargs} - {max_pos}" max_args = NO_VARARG if (vararg != NO_VARARG) else max_pos - parser_code = [normalize_snippet(""" - if (!_PyArg_CheckPositional("{name}", %s, %d, %s)) {{ - goto exit; - }} - """ % (nargs, min_pos, max_args), indent=4)] + if limited_capi: + parser_code = [] + if nargs != 'nargs': + parser_code.append(normalize_snippet(f'Py_ssize_t nargs = {nargs};', indent=4)) + nargs = 'nargs' + if min_pos == max_args: + pl = '' if min_pos == 1 else 's' + parser_code.append(normalize_snippet(f""" + if ({nargs} != {min_pos}) {{{{ + PyErr_Format(PyExc_TypeError, "{{name}} expected {min_pos} argument{pl}, got %zd", {nargs}); + goto exit; + }}}} + """, + indent=4)) + else: + if min_pos: + pl = '' if min_pos == 1 else 's' + parser_code.append(normalize_snippet(f""" + if ({nargs} < {min_pos}) {{{{ + PyErr_Format(PyExc_TypeError, "{{name}} expected at least {min_pos} argument{pl}, got %zd", {nargs}); + goto exit; + }}}} + """, + indent=4)) + if max_args != NO_VARARG: + pl = '' if max_args == 1 else 's' + parser_code.append(normalize_snippet(f""" + if ({nargs} > {max_args}) {{{{ + PyErr_Format(PyExc_TypeError, "{{name}} expected at most {max_args} argument{pl}, got %zd", {nargs}); + goto exit; + }}}} + """, + indent=4)) + else: + parser_code = [normalize_snippet(f""" + if (!_PyArg_CheckPositional("{{name}}", {nargs}, {min_pos}, {max_args})) {{{{ + goto exit; + }}}} + """, indent=4)] has_optional = False for i, p in enumerate(parameters): - displayname = p.get_displayname(i+1) - argname = argname_fmt % i - if p.is_vararg(): - if not new_or_init: + if fastcall: parser_code.append(normalize_snippet(""" %s = PyTuple_New(%s); if (!%s) {{ @@ -1361,7 +1380,9 @@ class CLanguage(Language): ), indent=4)) continue - parsearg = p.converter.parse_arg(argname, displayname) + displayname = p.get_displayname(i+1) + argname = argname_fmt % i + parsearg = p.converter.parse_arg(argname, displayname, limited_capi=limited_capi) if parsearg is None: parser_code = None break @@ -1378,7 +1399,9 @@ class CLanguage(Language): if has_optional: parser_code.append("skip_optional:") else: - if not new_or_init: + if limited_capi: + fastcall = False + if fastcall: parser_code = [normalize_snippet(""" if (!_PyArg_ParseStack(args, nargs, "{format_units}:{name}", {parse_arguments})) {{ @@ -1386,6 +1409,8 @@ class CLanguage(Language): }} """, indent=4)] else: + flags = "METH_VARARGS" + parser_prototype = self.PARSER_PROTOTYPE_VARARGS parser_code = [normalize_snippet(""" if (!PyArg_ParseTuple(args, "{format_units}:{name}", {parse_arguments})) {{ @@ -1421,25 +1446,10 @@ class CLanguage(Language): nargs = f"Py_MIN(nargs, {max_pos})" if max_pos else "0" if limited_capi: - # positional-or-keyword arguments - flags = "METH_VARARGS|METH_KEYWORDS" + parser_code = None + fastcall = False - parser_prototype = self.PARSER_PROTOTYPE_KEYWORD - parser_code = [normalize_snippet(""" - if (!PyArg_ParseTupleAndKeywords(args, kwargs, "{format_units}:{name}", _keywords, - {parse_arguments})) - goto exit; - """, indent=4)] - declarations = "static char *_keywords[] = {{{keywords_c} NULL}};" - if deprecated_positionals or deprecated_keywords: - declarations += "\nPy_ssize_t nargs = PyTuple_Size(args);" - if deprecated_keywords: - code = self.deprecate_keyword_use(f, deprecated_keywords, None, - limited_capi=limited_capi, - clinic=clinic) - parser_code.append(code) - - elif not new_or_init: + elif fastcall: flags = "METH_FASTCALL|METH_KEYWORDS" parser_prototype = self.PARSER_PROTOTYPE_FASTCALL_KEYWORDS argname_fmt = 'args[%d]' @@ -1477,11 +1487,12 @@ class CLanguage(Language): flags = 'METH_METHOD|' + flags parser_prototype = self.PARSER_PROTOTYPE_DEF_CLASS - if not limited_capi: + if parser_code is not None: if deprecated_keywords: code = self.deprecate_keyword_use(f, deprecated_keywords, argname_fmt, - limited_capi=limited_capi, - clinic=clinic) + clinic=clinic, + fastcall=fastcall, + limited_capi=limited_capi) parser_code.append(code) add_label: str | None = None @@ -1490,7 +1501,7 @@ class CLanguage(Language): raise ValueError("defining_class should be the first " "parameter (after self)") displayname = p.get_displayname(i+1) - parsearg = p.converter.parse_arg(argname_fmt % i, displayname) + parsearg = p.converter.parse_arg(argname_fmt % i, displayname, limited_capi=limited_capi) if parsearg is None: parser_code = None break @@ -1542,40 +1553,56 @@ class CLanguage(Language): }} """ % add_label, indent=4)) - if parser_code is not None: - if add_label: - parser_code.append("%s:" % add_label) + if parser_code is not None: + if add_label: + parser_code.append("%s:" % add_label) + else: + declarations = declare_parser(f, clinic=clinic, + hasformat=True, + limited_capi=limited_capi) + if limited_capi: + # positional-or-keyword arguments + assert not fastcall + flags = "METH_VARARGS|METH_KEYWORDS" + parser_prototype = self.PARSER_PROTOTYPE_KEYWORD + parser_code = [normalize_snippet(""" + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "{format_units}:{name}", _keywords, + {parse_arguments})) + goto exit; + """, indent=4)] + declarations = "static char *_keywords[] = {{{keywords_c} NULL}};" + if deprecated_positionals or deprecated_keywords: + declarations += "\nPy_ssize_t nargs = PyTuple_Size(args);" + + elif fastcall: + parser_code = [normalize_snippet(""" + if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser{parse_arguments_comma} + {parse_arguments})) {{ + goto exit; + }} + """, indent=4)] else: - declarations = declare_parser(f, clinic=clinic, - hasformat=True, - limited_capi=clinic.limited_capi) - if not new_or_init: - parser_code = [normalize_snippet(""" - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser{parse_arguments_comma} - {parse_arguments})) {{ - goto exit; - }} - """, indent=4)] - else: - parser_code = [normalize_snippet(""" - if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser, - {parse_arguments})) {{ - goto exit; - }} - """, indent=4)] - if deprecated_positionals or deprecated_keywords: - declarations += "\nPy_ssize_t nargs = PyTuple_GET_SIZE(args);" - if deprecated_keywords: - code = self.deprecate_keyword_use(f, deprecated_keywords, None, - limited_capi=limited_capi, - clinic=clinic) - parser_code.append(code) + parser_code = [normalize_snippet(""" + if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser, + {parse_arguments})) {{ + goto exit; + }} + """, indent=4)] + if deprecated_positionals or deprecated_keywords: + declarations += "\nPy_ssize_t nargs = PyTuple_GET_SIZE(args);" + if deprecated_keywords: + code = self.deprecate_keyword_use(f, deprecated_keywords, None, + clinic=clinic, + fastcall=fastcall, + limited_capi=limited_capi) + parser_code.append(code) if deprecated_positionals: code = self.deprecate_positional_use(f, deprecated_positionals) # Insert the deprecation code before parameter parsing. parser_code.insert(0, code) + assert parser_prototype is not None parser_definition = parser_body(parser_prototype, *parser_code, declarations=declarations) @@ -1619,6 +1646,9 @@ class CLanguage(Language): if flags in ('METH_NOARGS', 'METH_O', 'METH_VARARGS'): methoddef_cast = "(PyCFunction)" methoddef_cast_end = "" + elif limited_capi: + methoddef_cast = "(PyCFunction)(void(*)(void))" + methoddef_cast_end = "" else: methoddef_cast = "_PyCFunction_CAST(" methoddef_cast_end = ")" @@ -3030,11 +3060,11 @@ class Parameter: def get_displayname(self, i: int) -> str: if i == 0: - return '"argument"' + return 'argument' if not self.is_positional_only(): - return f'"argument {self.name!r}"' + return f'argument {self.name!r}' else: - return f'"argument {i}"' + return f'argument {i}' def render_docstring(self) -> str: add, out = text_accumulator() @@ -3458,41 +3488,78 @@ class CConverter(metaclass=CConverterAutoRegister): """ pass - def parse_arg(self, argname: str, displayname: str) -> str | None: + def bad_argument(self, displayname: str, expected: str, *, limited_capi: bool, expected_literal: bool = True) -> str: + assert '"' not in expected + if limited_capi: + if expected_literal: + return (f'PyErr_Format(PyExc_TypeError, ' + f'"{{{{name}}}}() {displayname} must be {expected}, not %.50s", ' + f'{{argname}} == Py_None ? "None" : Py_TYPE({{argname}})->tp_name);') + else: + return (f'PyErr_Format(PyExc_TypeError, ' + f'"{{{{name}}}}() {displayname} must be %.50s, not %.50s", ' + f'"{expected}", ' + f'{{argname}} == Py_None ? "None" : Py_TYPE({{argname}})->tp_name);') + else: + if expected_literal: + expected = f'"{expected}"' + return f'_PyArg_BadArgument("{{{{name}}}}", "{displayname}", {expected}, {{argname}});' + + def format_code(self, fmt: str, *, + argname: str, + bad_argument: str | None = None, + bad_argument2: str | None = None, + **kwargs: Any) -> str: + if '{bad_argument}' in fmt: + if not bad_argument: + raise TypeError("required 'bad_argument' argument") + fmt = fmt.replace('{bad_argument}', bad_argument) + if '{bad_argument2}' in fmt: + if not bad_argument2: + raise TypeError("required 'bad_argument2' argument") + fmt = fmt.replace('{bad_argument2}', bad_argument2) + return fmt.format(argname=argname, paramname=self.parser_name, **kwargs) + + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'O&': - return """ + return self.format_code(""" if (!{converter}({argname}, &{paramname})) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name, - converter=self.converter) + """, + argname=argname, + converter=self.converter) if self.format_unit == 'O!': cast = '(%s)' % self.type if self.type != 'PyObject *' else '' if self.subclass_of in type_checks: typecheck, typename = type_checks[self.subclass_of] - return """ + return self.format_code(""" if (!{typecheck}({argname})) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "{typename}", {argname}); + {bad_argument} goto exit; }}}} {paramname} = {cast}{argname}; - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname, typecheck=typecheck, - typename=typename, cast=cast) - return """ + """, + argname=argname, + bad_argument=self.bad_argument(displayname, typename, limited_capi=limited_capi), + typecheck=typecheck, typename=typename, cast=cast) + return self.format_code(""" if (!PyObject_TypeCheck({argname}, {subclass_of})) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, ({subclass_of})->tp_name, {argname}); + {bad_argument} goto exit; }}}} {paramname} = {cast}{argname}; - """.format(argname=argname, paramname=self.parser_name, - subclass_of=self.subclass_of, cast=cast, - displayname=displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, '({subclass_of})->tp_name', + expected_literal=False, limited_capi=limited_capi), + subclass_of=self.subclass_of, cast=cast) if self.format_unit == 'O': cast = '(%s)' % self.type if self.type != 'PyObject *' else '' - return """ + return self.format_code(""" {paramname} = {cast}{argname}; - """.format(argname=argname, paramname=self.parser_name, cast=cast) + """, + argname=argname, cast=cast) return None def set_template_dict(self, template_dict: TemplateDict) -> None: @@ -3567,22 +3634,24 @@ class bool_converter(CConverter): self.default = bool(self.default) self.c_default = str(int(self.default)) - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'i': - return """ + return self.format_code(""" {paramname} = PyLong_AsInt({argname}); if ({paramname} == -1 && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) + """, + argname=argname) elif self.format_unit == 'p': - return """ + return self.format_code(""" {paramname} = PyObject_IsTrue({argname}); if ({paramname} < 0) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class defining_class_converter(CConverter): """ @@ -3618,9 +3687,9 @@ class char_converter(CConverter): if self.c_default == '"\'"': self.c_default = r"'\''" - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'c': - return """ + return self.format_code(""" if (PyBytes_Check({argname}) && PyBytes_GET_SIZE({argname}) == 1) {{{{ {paramname} = PyBytes_AS_STRING({argname})[0]; }}}} @@ -3628,12 +3697,14 @@ class char_converter(CConverter): {paramname} = PyByteArray_AS_STRING({argname})[0]; }}}} else {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "a byte string of length 1", {argname}); + {bad_argument} goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname) - return super().parse_arg(argname, displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'a byte string of length 1', limited_capi=limited_capi), + ) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) @add_legacy_c_converter('B', bitwise=True) @@ -3647,9 +3718,9 @@ class unsigned_char_converter(CConverter): if bitwise: self.format_unit = 'B' - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'b': - return """ + return self.format_code(""" {{{{ long ival = PyLong_AsLong({argname}); if (ival == -1 && PyErr_Occurred()) {{{{ @@ -3669,9 +3740,10 @@ class unsigned_char_converter(CConverter): {paramname} = (unsigned char) ival; }}}} }}}} - """.format(argname=argname, paramname=self.parser_name) + """, + argname=argname) elif self.format_unit == 'B': - return """ + return self.format_code(""" {{{{ unsigned long ival = PyLong_AsUnsignedLongMask({argname}); if (ival == (unsigned long)-1 && PyErr_Occurred()) {{{{ @@ -3681,8 +3753,9 @@ class unsigned_char_converter(CConverter): {paramname} = (unsigned char) ival; }}}} }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class byte_converter(unsigned_char_converter): pass @@ -3692,9 +3765,9 @@ class short_converter(CConverter): format_unit = 'h' c_ignored_default = "0" - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'h': - return """ + return self.format_code(""" {{{{ long ival = PyLong_AsLong({argname}); if (ival == -1 && PyErr_Occurred()) {{{{ @@ -3714,8 +3787,9 @@ class short_converter(CConverter): {paramname} = (short) ival; }}}} }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class unsigned_short_converter(CConverter): type = 'unsigned short' @@ -3730,15 +3804,33 @@ class unsigned_short_converter(CConverter): self.add_include('pycore_long.h', '_PyLong_UnsignedShort_Converter()') - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'H': - return """ + return self.format_code(""" {paramname} = (unsigned short)PyLong_AsUnsignedLongMask({argname}); if ({paramname} == (unsigned short)-1 && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + if not limited_capi: + return super().parse_arg(argname, displayname, limited_capi=limited_capi) + # NOTE: Raises OverflowError for negative integer. + return self.format_code(""" + {{{{ + unsigned long uval = PyLong_AsUnsignedLong({argname}); + if (uval == (unsigned long)-1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + if (uval > USHRT_MAX) {{{{ + PyErr_SetString(PyExc_OverflowError, + "Python int too large for C unsigned short"); + goto exit; + }}}} + {paramname} = (unsigned short) uval; + }}}} + """, + argname=argname) @add_legacy_c_converter('C', accept={str}) class int_converter(CConverter): @@ -3757,28 +3849,31 @@ class int_converter(CConverter): if type is not None: self.type = type - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'i': - return """ + return self.format_code(""" {paramname} = PyLong_AsInt({argname}); if ({paramname} == -1 && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) + """, + argname=argname) elif self.format_unit == 'C': - return """ + return self.format_code(""" if (!PyUnicode_Check({argname})) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "a unicode character", {argname}); + {bad_argument} goto exit; }}}} if (PyUnicode_GET_LENGTH({argname}) != 1) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "a unicode character", {argname}); + {bad_argument} goto exit; }}}} {paramname} = PyUnicode_READ_CHAR({argname}, 0); - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname) - return super().parse_arg(argname, displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'a unicode character', limited_capi=limited_capi), + ) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class unsigned_int_converter(CConverter): type = 'unsigned int' @@ -3793,15 +3888,33 @@ class unsigned_int_converter(CConverter): self.add_include('pycore_long.h', '_PyLong_UnsignedInt_Converter()') - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'I': - return """ + return self.format_code(""" {paramname} = (unsigned int)PyLong_AsUnsignedLongMask({argname}); if ({paramname} == (unsigned int)-1 && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + if not limited_capi: + return super().parse_arg(argname, displayname, limited_capi=limited_capi) + # NOTE: Raises OverflowError for negative integer. + return self.format_code(""" + {{{{ + unsigned long uval = PyLong_AsUnsignedLong({argname}); + if (uval == (unsigned long)-1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + if (uval > UINT_MAX) {{{{ + PyErr_SetString(PyExc_OverflowError, + "Python int too large for C unsigned int"); + goto exit; + }}}} + {paramname} = (unsigned int) uval; + }}}} + """, + argname=argname) class long_converter(CConverter): type = 'long' @@ -3809,15 +3922,16 @@ class long_converter(CConverter): format_unit = 'l' c_ignored_default = "0" - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'l': - return """ + return self.format_code(""" {paramname} = PyLong_AsLong({argname}); if ({paramname} == -1 && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class unsigned_long_converter(CConverter): type = 'unsigned long' @@ -3832,17 +3946,28 @@ class unsigned_long_converter(CConverter): self.add_include('pycore_long.h', '_PyLong_UnsignedLong_Converter()') - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'k': - return """ + return self.format_code(""" if (!PyLong_Check({argname})) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "int", {argname}); + {bad_argument} goto exit; }}}} {paramname} = PyLong_AsUnsignedLongMask({argname}); - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname) - return super().parse_arg(argname, displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi), + ) + if not limited_capi: + return super().parse_arg(argname, displayname, limited_capi=limited_capi) + # NOTE: Raises OverflowError for negative integer. + return self.format_code(""" + {paramname} = PyLong_AsUnsignedLong({argname}); + if ({paramname} == (unsigned long)-1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """, + argname=argname) class long_long_converter(CConverter): type = 'long long' @@ -3850,15 +3975,16 @@ class long_long_converter(CConverter): format_unit = 'L' c_ignored_default = "0" - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'L': - return """ + return self.format_code(""" {paramname} = PyLong_AsLongLong({argname}); if ({paramname} == -1 && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class unsigned_long_long_converter(CConverter): type = 'unsigned long long' @@ -3873,17 +3999,28 @@ class unsigned_long_long_converter(CConverter): self.add_include('pycore_long.h', '_PyLong_UnsignedLongLong_Converter()') - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'K': - return """ + return self.format_code(""" if (!PyLong_Check({argname})) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "int", {argname}); + {bad_argument} goto exit; }}}} {paramname} = PyLong_AsUnsignedLongLongMask({argname}); - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname) - return super().parse_arg(argname, displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi), + ) + if not limited_capi: + return super().parse_arg(argname, displayname, limited_capi=limited_capi) + # NOTE: Raises OverflowError for negative integer. + return self.format_code(""" + {paramname} = PyLong_AsUnsignedLongLong({argname}); + if ({paramname} == (unsigned long long)-1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """, + argname=argname) class Py_ssize_t_converter(CConverter): type = 'Py_ssize_t' @@ -3901,12 +4038,16 @@ class Py_ssize_t_converter(CConverter): else: fail(f"Py_ssize_t_converter: illegal 'accept' argument {accept!r}") - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'n': - return """ + if limited_capi: + PyNumber_Index = 'PyNumber_Index' + else: + PyNumber_Index = '_PyNumber_Index' + return self.format_code(""" {{{{ Py_ssize_t ival = -1; - PyObject *iobj = _PyNumber_Index({argname}); + PyObject *iobj = {PyNumber_Index}({argname}); if (iobj != NULL) {{{{ ival = PyLong_AsSsize_t(iobj); Py_DECREF(iobj); @@ -3916,8 +4057,28 @@ class Py_ssize_t_converter(CConverter): }}}} {paramname} = ival; }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname, + PyNumber_Index=PyNumber_Index) + if not limited_capi: + return super().parse_arg(argname, displayname, limited_capi=limited_capi) + return self.format_code(""" + if ({argname} != Py_None) {{{{ + if (PyIndex_Check({argname})) {{{{ + {paramname} = PyNumber_AsSsize_t({argname}, PyExc_OverflowError); + if ({paramname} == -1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + }}}} + else {{{{ + {bad_argument} + goto exit; + }}}} + }}}} + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'integer or None', limited_capi=limited_capi), + ) class slice_index_converter(CConverter): @@ -3926,11 +4087,51 @@ class slice_index_converter(CConverter): def converter_init(self, *, accept: TypeSet = {int, NoneType}) -> None: if accept == {int}: self.converter = '_PyEval_SliceIndexNotNone' + self.nullable = False elif accept == {int, NoneType}: self.converter = '_PyEval_SliceIndex' + self.nullable = True else: fail(f"slice_index_converter: illegal 'accept' argument {accept!r}") + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: + if not limited_capi: + return super().parse_arg(argname, displayname, limited_capi=limited_capi) + if self.nullable: + return self.format_code(""" + if (!Py_IsNone({argname})) {{{{ + if (PyIndex_Check({argname})) {{{{ + {paramname} = PyNumber_AsSsize_t({argname}, NULL); + if ({paramname} == -1 && PyErr_Occurred()) {{{{ + return 0; + }}}} + }}}} + else {{{{ + PyErr_SetString(PyExc_TypeError, + "slice indices must be integers or " + "None or have an __index__ method"); + goto exit; + }}}} + }}}} + """, + argname=argname) + else: + return self.format_code(""" + if (PyIndex_Check({argname})) {{{{ + {paramname} = PyNumber_AsSsize_t({argname}, NULL); + if ({paramname} == -1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + }}}} + else {{{{ + PyErr_SetString(PyExc_TypeError, + "slice indices must be integers or " + "have an __index__ method"); + goto exit; + }}}} + """, + argname=argname) + class size_t_converter(CConverter): type = 'size_t' converter = '_PyLong_Size_t_Converter' @@ -3940,15 +4141,25 @@ class size_t_converter(CConverter): self.add_include('pycore_long.h', '_PyLong_Size_t_Converter()') - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'n': - return """ + return self.format_code(""" {paramname} = PyNumber_AsSsize_t({argname}, PyExc_OverflowError); if ({paramname} == -1 && PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + if not limited_capi: + return super().parse_arg(argname, displayname, limited_capi=limited_capi) + # NOTE: Raises OverflowError for negative integer. + return self.format_code(""" + {paramname} = PyLong_AsSize_t({argname}); + if ({paramname} == (size_t)-1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """, + argname=argname) class fildes_converter(CConverter): @@ -3960,12 +4171,13 @@ class fildes_converter(CConverter): '_PyLong_FileDescriptor_Converter()') def _parse_arg(self, argname: str, displayname: str) -> str | None: - return """ + return self.format_code(""" {paramname} = PyObject_AsFileDescriptor({argname}); if ({paramname} == -1) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.name) + """, + argname=argname) class float_converter(CConverter): @@ -3974,9 +4186,9 @@ class float_converter(CConverter): format_unit = 'f' c_ignored_default = "0.0" - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'f': - return """ + return self.format_code(""" if (PyFloat_CheckExact({argname})) {{{{ {paramname} = (float) (PyFloat_AS_DOUBLE({argname})); }}}} @@ -3987,8 +4199,9 @@ class float_converter(CConverter): goto exit; }}}} }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class double_converter(CConverter): type = 'double' @@ -3996,9 +4209,9 @@ class double_converter(CConverter): format_unit = 'd' c_ignored_default = "0.0" - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'd': - return """ + return self.format_code(""" if (PyFloat_CheckExact({argname})) {{{{ {paramname} = PyFloat_AS_DOUBLE({argname}); }}}} @@ -4009,8 +4222,9 @@ class double_converter(CConverter): goto exit; }}}} }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class Py_complex_converter(CConverter): @@ -4019,15 +4233,16 @@ class Py_complex_converter(CConverter): format_unit = 'D' c_ignored_default = "{0.0, 0.0}" - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'D': - return """ + return self.format_code(""" {paramname} = PyComplex_AsCComplex({argname}); if (PyErr_Occurred()) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name) - return super().parse_arg(argname, displayname) + """, + argname=argname) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class object_converter(CConverter): @@ -4112,11 +4327,11 @@ class str_converter(CConverter): else: return "" - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 's': - return """ + return self.format_code(""" if (!PyUnicode_Check({argname})) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "str", {argname}); + {bad_argument} goto exit; }}}} Py_ssize_t {length_name}; @@ -4128,10 +4343,12 @@ class str_converter(CConverter): PyErr_SetString(PyExc_ValueError, "embedded null character"); goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname, length_name=self.length_name) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'str', limited_capi=limited_capi), + length_name=self.length_name) if self.format_unit == 'z': - return """ + return self.format_code(""" if ({argname} == Py_None) {{{{ {paramname} = NULL; }}}} @@ -4147,12 +4364,14 @@ class str_converter(CConverter): }}}} }}}} else {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "str or None", {argname}); + {bad_argument} goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname, length_name=self.length_name) - return super().parse_arg(argname, displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'str or None', limited_capi=limited_capi), + length_name=self.length_name) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) # # This is the fourth or fifth rewrite of registering all the @@ -4214,51 +4433,57 @@ class PyBytesObject_converter(CConverter): format_unit = 'S' # accept = {bytes} - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'S': - return """ + return self.format_code(""" if (!PyBytes_Check({argname})) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "bytes", {argname}); + {bad_argument} goto exit; }}}} {paramname} = ({type}){argname}; - """.format(argname=argname, paramname=self.parser_name, - type=self.type, displayname=displayname) - return super().parse_arg(argname, displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'bytes', limited_capi=limited_capi), + type=self.type) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class PyByteArrayObject_converter(CConverter): type = 'PyByteArrayObject *' format_unit = 'Y' # accept = {bytearray} - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'Y': - return """ + return self.format_code(""" if (!PyByteArray_Check({argname})) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "bytearray", {argname}); + {bad_argument} goto exit; }}}} {paramname} = ({type}){argname}; - """.format(argname=argname, paramname=self.parser_name, - type=self.type, displayname=displayname) - return super().parse_arg(argname, displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'bytearray', limited_capi=limited_capi), + type=self.type) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) class unicode_converter(CConverter): type = 'PyObject *' default_type = (str, Null, NoneType) format_unit = 'U' - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'U': - return """ + return self.format_code(""" if (!PyUnicode_Check({argname})) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "str", {argname}); + {bad_argument} goto exit; }}}} {paramname} = {argname}; - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname) - return super().parse_arg(argname, displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'str', limited_capi=limited_capi), + ) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) @add_legacy_c_converter('u') @add_legacy_c_converter('u#', zeroes=True) @@ -4292,25 +4517,26 @@ class Py_UNICODE_converter(CConverter): if self.length: return "" else: - return """\ -PyMem_Free((void *){name}); -""".format(name=self.name) + return f"""PyMem_Free((void *){self.parser_name});\n""" - def parse_arg(self, argname: str, argnum: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if not self.length: if self.accept == {str}: - return """ + return self.format_code(""" if (!PyUnicode_Check({argname})) {{{{ - _PyArg_BadArgument("{{name}}", {argnum}, "str", {argname}); + {bad_argument} goto exit; }}}} {paramname} = PyUnicode_AsWideCharString({argname}, NULL); if ({paramname} == NULL) {{{{ goto exit; }}}} - """.format(argname=argname, paramname=self.name, argnum=argnum) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'str', limited_capi=limited_capi), + ) elif self.accept == {str, NoneType}: - return """ + return self.format_code(""" if ({argname} == Py_None) {{{{ {paramname} = NULL; }}}} @@ -4321,11 +4547,14 @@ PyMem_Free((void *){name}); }}}} }}}} else {{{{ - _PyArg_BadArgument("{{name}}", {argnum}, "str or None", {argname}); + {bad_argument} goto exit; }}}} - """.format(argname=argname, paramname=self.name, argnum=argnum) - return super().parse_arg(argname, argnum) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'str or None', limited_capi=limited_capi), + ) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) @add_legacy_c_converter('s*', accept={str, buffer}) @add_legacy_c_converter('z*', accept={str, buffer, NoneType}) @@ -4359,20 +4588,22 @@ class Py_buffer_converter(CConverter): name = self.name return "".join(["if (", name, ".obj) {\n PyBuffer_Release(&", name, ");\n}\n"]) - def parse_arg(self, argname: str, displayname: str) -> str | None: + def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None: if self.format_unit == 'y*': - return """ + return self.format_code(""" if (PyObject_GetBuffer({argname}, &{paramname}, PyBUF_SIMPLE) != 0) {{{{ goto exit; }}}} if (!PyBuffer_IsContiguous(&{paramname}, 'C')) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "contiguous buffer", {argname}); + {bad_argument} goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'contiguous buffer', limited_capi=limited_capi), + ) elif self.format_unit == 's*': - return """ + return self.format_code(""" if (PyUnicode_Check({argname})) {{{{ Py_ssize_t len; const char *ptr = PyUnicode_AsUTF8AndSize({argname}, &len); @@ -4386,26 +4617,30 @@ class Py_buffer_converter(CConverter): goto exit; }}}} if (!PyBuffer_IsContiguous(&{paramname}, 'C')) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "contiguous buffer", {argname}); + {bad_argument} goto exit; }}}} }}}} - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'contiguous buffer', limited_capi=limited_capi), + ) elif self.format_unit == 'w*': - return """ + return self.format_code(""" if (PyObject_GetBuffer({argname}, &{paramname}, PyBUF_WRITABLE) < 0) {{{{ - PyErr_Clear(); - _PyArg_BadArgument("{{name}}", {displayname}, "read-write bytes-like object", {argname}); + {bad_argument} goto exit; }}}} if (!PyBuffer_IsContiguous(&{paramname}, 'C')) {{{{ - _PyArg_BadArgument("{{name}}", {displayname}, "contiguous buffer", {argname}); + {bad_argument2} goto exit; }}}} - """.format(argname=argname, paramname=self.parser_name, - displayname=displayname) - return super().parse_arg(argname, displayname) + """, + argname=argname, + bad_argument=self.bad_argument(displayname, 'read-write bytes-like object', limited_capi=limited_capi), + bad_argument2=self.bad_argument(displayname, 'contiguous buffer', limited_capi=limited_capi), + ) + return super().parse_arg(argname, displayname, limited_capi=limited_capi) def correct_name_for_self(