summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2023-09-03 14:28:14 (GMT)
committerGitHub <noreply@github.com>2023-09-03 14:28:14 (GMT)
commit1796c191b43ed0787d83c07be7de8118fb10e8b0 (patch)
treea49fc1fa54d8d6a375516f6e6d1cd6648caf5a78
parent55846099b155833320bc6d64b03d902028bad439 (diff)
downloadcpython-1796c191b43ed0787d83c07be7de8118fb10e8b0.zip
cpython-1796c191b43ed0787d83c07be7de8118fb10e8b0.tar.gz
cpython-1796c191b43ed0787d83c07be7de8118fb10e8b0.tar.bz2
gh-108494: Argument Clinic: inline parsing code for positional-only parameters in the limited C API (GH-108622)
-rw-r--r--Modules/_io/clinic/bufferedio.c.h6
-rw-r--r--Modules/_io/clinic/bytesio.c.h3
-rw-r--r--Modules/_io/clinic/fileio.c.h3
-rw-r--r--Modules/_io/clinic/winconsoleio.c.h3
-rw-r--r--Modules/_multiprocessing/multiprocessing.c9
-rw-r--r--Modules/_posixsubprocess.c9
-rw-r--r--Modules/_struct.c12
-rw-r--r--Modules/clinic/_testclinic_limited.c.h22
-rw-r--r--Modules/overlapped.c16
-rw-r--r--Modules/resource.c9
-rw-r--r--PC/msvcrtmodule.c9
-rw-r--r--PC/winreg.c18
-rwxr-xr-xTools/clinic/clinic.py743
13 files changed, 555 insertions, 307 deletions
diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h
index b2c52cf..7577bde 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 d736477..d42ab48 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 29f8cf6..deb99fa 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 0683eec..ecc71e5 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 16b5cb5..2e6d8eb 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 ef76d26..2898eed 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 4ae21cc..be4c23a 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 93e7d7f..eff72ce 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 c682e6a..e23db22 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 9e302a3..d65509e 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 afc810a..d7c2176 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 767127d..77b8021 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 b744ca7..eca4747 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_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)
+ parser_code = None
+ fastcall = False
- 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(