diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2019-01-11 14:01:14 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-11 14:01:14 (GMT) |
commit | 4fa9591025b6a098f3d6402e5413ee6740ede6c5 (patch) | |
tree | a81280fdd40c6a5b8c00613b0a8903624499afc5 /Tools | |
parent | 5485085b324a45307c1ff4ec7d85b5998d7d5e0d (diff) | |
download | cpython-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-x | Tools/clinic/clinic.py | 249 |
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): |