summaryrefslogtreecommitdiffstats
path: root/Tools
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2019-01-11 14:01:14 (GMT)
committerGitHub <noreply@github.com>2019-01-11 14:01:14 (GMT)
commit4fa9591025b6a098f3d6402e5413ee6740ede6c5 (patch)
treea81280fdd40c6a5b8c00613b0a8903624499afc5 /Tools
parent5485085b324a45307c1ff4ec7d85b5998d7d5e0d (diff)
downloadcpython-4fa9591025b6a098f3d6402e5413ee6740ede6c5.zip
cpython-4fa9591025b6a098f3d6402e5413ee6740ede6c5.tar.gz
cpython-4fa9591025b6a098f3d6402e5413ee6740ede6c5.tar.bz2
bpo-35582: Argument Clinic: inline parsing code for positional parameters. (GH-11313)
Diffstat (limited to 'Tools')
-rwxr-xr-xTools/clinic/clinic.py249
1 files changed, 163 insertions, 86 deletions
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index 3627725..4087d3f 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -807,8 +807,13 @@ class CLanguage(Language):
{c_basename}({self_type}{self_name}, PyObject *%s)
""" % argname)
- parsearg = converters[0].parse_arg(argname)
- assert parsearg is not None
+ parsearg = converters[0].parse_arg(argname, 0)
+ if parsearg is None:
+ parsearg = """
+ if (!PyArg_Parse(%s, "{format_units}:{name}", {parse_arguments})) {{
+ goto exit;
+ }}
+ """ % argname
parser_definition = parser_body(parser_prototype,
normalize_snippet(parsearg, indent=4))
@@ -857,26 +862,58 @@ class CLanguage(Language):
flags = "METH_FASTCALL"
parser_prototype = parser_prototype_fastcall
-
- parser_definition = parser_body(parser_prototype, normalize_snippet("""
- if (!_PyArg_ParseStack(args, nargs, "{format_units}:{name}",
- {parse_arguments})) {{
- goto exit;
- }}
- """, indent=4))
+ nargs = 'nargs'
+ argname_fmt = 'args[%d]'
else:
# positional-only, but no option groups
# we only need one call to PyArg_ParseTuple
flags = "METH_VARARGS"
parser_prototype = parser_prototype_varargs
+ nargs = 'PyTuple_GET_SIZE(args)'
+ argname_fmt = 'PyTuple_GET_ITEM(args, %d)'
+
+ parser_code = []
+ has_optional = False
+ for i, converter in enumerate(converters):
+ parsearg = converter.parse_arg(argname_fmt % i, i + 1)
+ if parsearg is None:
+ #print('Cannot convert %s %r for %s' % (converter.__class__.__name__, converter.format_unit, converter.name), file=sys.stderr)
+ parser_code = None
+ break
+ if has_optional or converter.default is not unspecified:
+ has_optional = True
+ parser_code.append(normalize_snippet("""
+ if (%s < %d) {{
+ goto skip_optional;
+ }}
+ """, indent=4) % (nargs, i + 1))
+ parser_code.append(normalize_snippet(parsearg, indent=4))
- parser_definition = parser_body(parser_prototype, normalize_snippet("""
- if (!PyArg_ParseTuple(args, "{format_units}:{name}",
- {parse_arguments})) {{
+ if parser_code is not None:
+ parser_code.insert(0, normalize_snippet("""
+ if (!_PyArg_CheckPositional("{name}", %s, {unpack_min}, {unpack_max})) {{
goto exit;
}}
- """, indent=4))
+ """ % nargs, indent=4))
+ if has_optional:
+ parser_code.append("skip_optional:")
+ else:
+ if not new_or_init:
+ parser_code = [normalize_snippet("""
+ if (!_PyArg_ParseStack(args, nargs, "{format_units}:{name}",
+ {parse_arguments})) {{
+ goto exit;
+ }}
+ """, 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 new_or_init:
flags = "METH_FASTCALL|METH_KEYWORDS"
@@ -2536,7 +2573,7 @@ class CConverter(metaclass=CConverterAutoRegister):
"""
pass
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'O&':
return """
if (!{converter}({argname}, &{paramname})) {{{{
@@ -2550,25 +2587,27 @@ class CConverter(metaclass=CConverterAutoRegister):
typecheck, typename = type_checks[self.subclass_of]
return """
if (!{typecheck}({argname})) {{{{
- _PyArg_BadArgument("{{name}}", "{typename}", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "{typename}", {argname});
goto exit;
}}}}
{paramname} = {cast}{argname};
""".format(argname=argname, paramname=self.name,
+ argnum=argnum,
typecheck=typecheck, typename=typename, cast=cast)
return """
if (!PyObject_TypeCheck({argname}, {subclass_of})) {{{{
- _PyArg_BadArgument("{{name}}", ({subclass_of})->tp_name, {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, ({subclass_of})->tp_name, {argname});
goto exit;
}}}}
{paramname} = {cast}{argname};
- """.format(argname=argname, paramname=self.name,
+ """.format(argname=argname, paramname=self.name, argnum=argnum,
subclass_of=self.subclass_of, cast=cast)
- return """
- if (!PyArg_Parse(%s, "{format_units}:{name}", {parse_arguments})) {{
- goto exit;
- }}
- """ % argname
+ if self.format_unit == 'O':
+ cast = '(%s)' % self.type if self.type != 'PyObject *' else ''
+ return """
+ {paramname} = {cast}{argname};
+ """.format(argname=argname, paramname=self.name, cast=cast)
+ return None
type_checks = {
'&PyLong_Type': ('PyLong_Check', 'int'),
@@ -2598,7 +2637,7 @@ class bool_converter(CConverter):
self.default = bool(self.default)
self.c_default = str(int(self.default))
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'i':
return """
if (PyFloat_Check({argname})) {{{{
@@ -2618,7 +2657,7 @@ class bool_converter(CConverter):
goto exit;
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class char_converter(CConverter):
type = 'char'
@@ -2635,7 +2674,7 @@ class char_converter(CConverter):
if self.c_default == '"\'"':
self.c_default = r"'\''"
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'c':
return """
if (PyBytes_Check({argname}) && PyBytes_GET_SIZE({argname}) == 1) {{{{
@@ -2645,11 +2684,11 @@ class char_converter(CConverter):
{paramname} = PyByteArray_AS_STRING({argname})[0];
}}}}
else {{{{
- _PyArg_BadArgument("{{name}}", "a byte string of length 1", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "a byte string of length 1", {argname});
goto exit;
}}}}
- """.format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ """.format(argname=argname, paramname=self.name, argnum=argnum)
+ return super().parse_arg(argname, argnum)
@add_legacy_c_converter('B', bitwise=True)
@@ -2663,7 +2702,7 @@ class unsigned_char_converter(CConverter):
if bitwise:
self.format_unit = 'B'
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'b':
return """
if (PyFloat_Check({argname})) {{{{
@@ -2708,7 +2747,7 @@ class unsigned_char_converter(CConverter):
}}}}
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class byte_converter(unsigned_char_converter): pass
@@ -2718,7 +2757,7 @@ class short_converter(CConverter):
format_unit = 'h'
c_ignored_default = "0"
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'h':
return """
if (PyFloat_Check({argname})) {{{{
@@ -2746,7 +2785,7 @@ class short_converter(CConverter):
}}}}
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class unsigned_short_converter(CConverter):
type = 'unsigned short'
@@ -2759,6 +2798,21 @@ class unsigned_short_converter(CConverter):
else:
self.converter = '_PyLong_UnsignedShort_Converter'
+ def parse_arg(self, argname, argnum):
+ if self.format_unit == 'H':
+ return """
+ if (PyFloat_Check({argname})) {{{{
+ PyErr_SetString(PyExc_TypeError,
+ "integer argument expected, got float" );
+ goto exit;
+ }}}}
+ {paramname} = (unsigned short)PyLong_AsUnsignedLongMask({argname});
+ if ({paramname} == (unsigned short)-1 && PyErr_Occurred()) {{{{
+ goto exit;
+ }}}}
+ """.format(argname=argname, paramname=self.name)
+ return super().parse_arg(argname, argnum)
+
@add_legacy_c_converter('C', accept={str})
class int_converter(CConverter):
type = 'int'
@@ -2774,7 +2828,7 @@ class int_converter(CConverter):
if type != None:
self.type = type
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'i':
return """
if (PyFloat_Check({argname})) {{{{
@@ -2790,19 +2844,19 @@ class int_converter(CConverter):
elif self.format_unit == 'C':
return """
if (!PyUnicode_Check({argname})) {{{{
- _PyArg_BadArgument("{{name}}", "a unicode character", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "a unicode character", {argname});
goto exit;
}}}}
if (PyUnicode_READY({argname})) {{{{
goto exit;
}}}}
if (PyUnicode_GET_LENGTH({argname}) != 1) {{{{
- _PyArg_BadArgument("{{name}}", "a unicode character", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "a unicode character", {argname});
goto exit;
}}}}
{paramname} = PyUnicode_READ_CHAR({argname}, 0);
- """.format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ """.format(argname=argname, paramname=self.name, argnum=argnum)
+ return super().parse_arg(argname, argnum)
class unsigned_int_converter(CConverter):
type = 'unsigned int'
@@ -2815,7 +2869,7 @@ class unsigned_int_converter(CConverter):
else:
self.converter = '_PyLong_UnsignedInt_Converter'
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'I':
return """
if (PyFloat_Check({argname})) {{{{
@@ -2828,7 +2882,7 @@ class unsigned_int_converter(CConverter):
goto exit;
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class long_converter(CConverter):
type = 'long'
@@ -2836,7 +2890,7 @@ class long_converter(CConverter):
format_unit = 'l'
c_ignored_default = "0"
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'l':
return """
if (PyFloat_Check({argname})) {{{{
@@ -2849,7 +2903,7 @@ class long_converter(CConverter):
goto exit;
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class unsigned_long_converter(CConverter):
type = 'unsigned long'
@@ -2862,16 +2916,16 @@ class unsigned_long_converter(CConverter):
else:
self.converter = '_PyLong_UnsignedLong_Converter'
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'k':
return """
if (!PyLong_Check({argname})) {{{{
- _PyArg_BadArgument("{{name}}", "int", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "int", {argname});
goto exit;
}}}}
{paramname} = PyLong_AsUnsignedLongMask({argname});
- """.format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ """.format(argname=argname, paramname=self.name, argnum=argnum)
+ return super().parse_arg(argname, argnum)
class long_long_converter(CConverter):
type = 'long long'
@@ -2879,7 +2933,7 @@ class long_long_converter(CConverter):
format_unit = 'L'
c_ignored_default = "0"
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'L':
return """
if (PyFloat_Check({argname})) {{{{
@@ -2892,7 +2946,7 @@ class long_long_converter(CConverter):
goto exit;
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class unsigned_long_long_converter(CConverter):
type = 'unsigned long long'
@@ -2905,16 +2959,16 @@ class unsigned_long_long_converter(CConverter):
else:
self.converter = '_PyLong_UnsignedLongLong_Converter'
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'K':
return """
if (!PyLong_Check({argname})) {{{{
- _PyArg_BadArgument("{{name}}", "int", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "int", {argname});
goto exit;
}}}}
{paramname} = PyLong_AsUnsignedLongLongMask({argname});
- """.format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ """.format(argname=argname, paramname=self.name, argnum=argnum)
+ return super().parse_arg(argname, argnum)
class Py_ssize_t_converter(CConverter):
type = 'Py_ssize_t'
@@ -2929,7 +2983,7 @@ class Py_ssize_t_converter(CConverter):
else:
fail("Py_ssize_t_converter: illegal 'accept' argument " + repr(accept))
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'n':
return """
if (PyFloat_Check({argname})) {{{{
@@ -2950,7 +3004,7 @@ class Py_ssize_t_converter(CConverter):
{paramname} = ival;
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class slice_index_converter(CConverter):
@@ -2969,7 +3023,7 @@ class size_t_converter(CConverter):
converter = '_PyLong_Size_t_Converter'
c_ignored_default = "0"
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'n':
return """
{paramname} = PyNumber_AsSsize_t({argname}, PyExc_OverflowError);
@@ -2977,7 +3031,7 @@ class size_t_converter(CConverter):
goto exit;
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class float_converter(CConverter):
@@ -2986,7 +3040,7 @@ class float_converter(CConverter):
format_unit = 'f'
c_ignored_default = "0.0"
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'f':
return """
{paramname} = (float) PyFloat_AsDouble({argname});
@@ -2994,7 +3048,7 @@ class float_converter(CConverter):
goto exit;
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class double_converter(CConverter):
type = 'double'
@@ -3002,7 +3056,7 @@ class double_converter(CConverter):
format_unit = 'd'
c_ignored_default = "0.0"
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'd':
return """
{paramname} = PyFloat_AsDouble({argname});
@@ -3010,7 +3064,7 @@ class double_converter(CConverter):
goto exit;
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class Py_complex_converter(CConverter):
@@ -3019,7 +3073,7 @@ class Py_complex_converter(CConverter):
format_unit = 'D'
c_ignored_default = "{0.0, 0.0}"
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'D':
return """
{paramname} = PyComplex_AsCComplex({argname});
@@ -3027,7 +3081,7 @@ class Py_complex_converter(CConverter):
goto exit;
}}}}
""".format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ return super().parse_arg(argname, argnum)
class object_converter(CConverter):
@@ -3093,11 +3147,11 @@ class str_converter(CConverter):
name = self.name
return "".join(["if (", name, ") {\n PyMem_FREE(", name, ");\n}\n"])
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 's':
return """
if (!PyUnicode_Check({argname})) {{{{
- _PyArg_BadArgument("{{name}}", "str", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "str", {argname});
goto exit;
}}}}
Py_ssize_t {paramname}_length;
@@ -3109,8 +3163,29 @@ class str_converter(CConverter):
PyErr_SetString(PyExc_ValueError, "embedded null character");
goto exit;
}}}}
- """.format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ """.format(argname=argname, paramname=self.name, argnum=argnum)
+ if self.format_unit == 'z':
+ return """
+ if ({argname} == Py_None) {{{{
+ {paramname} = NULL;
+ }}}}
+ else if (PyUnicode_Check({argname})) {{{{
+ Py_ssize_t {paramname}_length;
+ {paramname} = PyUnicode_AsUTF8AndSize({argname}, &{paramname}_length);
+ if ({paramname} == NULL) {{{{
+ goto exit;
+ }}}}
+ if (strlen({paramname}) != (size_t){paramname}_length) {{{{
+ PyErr_SetString(PyExc_ValueError, "embedded null character");
+ goto exit;
+ }}}}
+ }}}}
+ else {{{{
+ _PyArg_BadArgument("{{name}}", {argnum}, "str or None", {argname});
+ goto exit;
+ }}}}
+ """.format(argname=argname, paramname=self.name, argnum=argnum)
+ return super().parse_arg(argname, argnum)
#
# This is the fourth or fifth rewrite of registering all the
@@ -3165,51 +3240,53 @@ class PyBytesObject_converter(CConverter):
format_unit = 'S'
# accept = {bytes}
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'S':
return """
if (!PyBytes_Check({argname})) {{{{
- _PyArg_BadArgument("{{name}}", "bytes", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "bytes", {argname});
goto exit;
}}}}
{paramname} = ({type}){argname};
- """.format(argname=argname, paramname=self.name, type=self.type)
- return super().parse_arg(argname)
+ """.format(argname=argname, paramname=self.name, argnum=argnum,
+ type=self.type)
+ return super().parse_arg(argname, argnum)
class PyByteArrayObject_converter(CConverter):
type = 'PyByteArrayObject *'
format_unit = 'Y'
# accept = {bytearray}
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'Y':
return """
if (!PyByteArray_Check({argname})) {{{{
- _PyArg_BadArgument("{{name}}", "bytearray", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "bytearray", {argname});
goto exit;
}}}}
{paramname} = ({type}){argname};
- """.format(argname=argname, paramname=self.name, type=self.type)
- return super().parse_arg(argname)
+ """.format(argname=argname, paramname=self.name, argnum=argnum,
+ type=self.type)
+ return super().parse_arg(argname, argnum)
class unicode_converter(CConverter):
type = 'PyObject *'
default_type = (str, Null, NoneType)
format_unit = 'U'
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'U':
return """
if (!PyUnicode_Check({argname})) {{{{
- _PyArg_BadArgument("{{name}}", "str", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "str", {argname});
goto exit;
}}}}
if (PyUnicode_READY({argname}) == -1) {{{{
goto exit;
}}}}
{paramname} = {argname};
- """.format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ """.format(argname=argname, paramname=self.name, argnum=argnum)
+ return super().parse_arg(argname, argnum)
@add_legacy_c_converter('u#', zeroes=True)
@add_legacy_c_converter('Z', accept={str, NoneType})
@@ -3258,17 +3335,17 @@ class Py_buffer_converter(CConverter):
name = self.name
return "".join(["if (", name, ".obj) {\n PyBuffer_Release(&", name, ");\n}\n"])
- def parse_arg(self, argname):
+ def parse_arg(self, argname, argnum):
if self.format_unit == 'y*':
return """
if (PyObject_GetBuffer({argname}, &{paramname}, PyBUF_SIMPLE) != 0) {{{{
goto exit;
}}}}
if (!PyBuffer_IsContiguous(&{paramname}, 'C')) {{{{
- _PyArg_BadArgument("{{name}}", "contiguous buffer", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "contiguous buffer", {argname});
goto exit;
}}}}
- """.format(argname=argname, paramname=self.name)
+ """.format(argname=argname, paramname=self.name, argnum=argnum)
elif self.format_unit == 's*':
return """
if (PyUnicode_Check({argname})) {{{{
@@ -3284,24 +3361,24 @@ class Py_buffer_converter(CConverter):
goto exit;
}}}}
if (!PyBuffer_IsContiguous(&{paramname}, 'C')) {{{{
- _PyArg_BadArgument("{{name}}", "contiguous buffer", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "contiguous buffer", {argname});
goto exit;
}}}}
}}}}
- """.format(argname=argname, paramname=self.name)
+ """.format(argname=argname, paramname=self.name, argnum=argnum)
elif self.format_unit == 'w*':
return """
if (PyObject_GetBuffer({argname}, &{paramname}, PyBUF_WRITABLE) < 0) {{{{
PyErr_Clear();
- _PyArg_BadArgument("{{name}}", "read-write bytes-like object", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "read-write bytes-like object", {argname});
goto exit;
}}}}
if (!PyBuffer_IsContiguous(&{paramname}, 'C')) {{{{
- _PyArg_BadArgument("{{name}}", "contiguous buffer", {argname});
+ _PyArg_BadArgument("{{name}}", {argnum}, "contiguous buffer", {argname});
goto exit;
}}}}
- """.format(argname=argname, paramname=self.name)
- return super().parse_arg(argname)
+ """.format(argname=argname, paramname=self.name, argnum=argnum)
+ return super().parse_arg(argname, argnum)
def correct_name_for_self(f):