summaryrefslogtreecommitdiffstats
path: root/Tools/clinic
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 /Tools/clinic
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)
Diffstat (limited to 'Tools/clinic')
-rwxr-xr-xTools/clinic/clinic.py743
1 files changed, 489 insertions, 254 deletions
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(