diff options
author | Serhiy Storchaka <storchaka@gmail.com> | 2018-12-25 11:23:47 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-25 11:23:47 (GMT) |
commit | 32d96a2b5bc3136d45a66adbdb45fac351b520ce (patch) | |
tree | acf51c9945f764ab103597c9cba376f154aa600d /Tools | |
parent | 65ce60aef150776f884715b4315a10a0d6ae769e (diff) | |
download | cpython-32d96a2b5bc3136d45a66adbdb45fac351b520ce.zip cpython-32d96a2b5bc3136d45a66adbdb45fac351b520ce.tar.gz cpython-32d96a2b5bc3136d45a66adbdb45fac351b520ce.tar.bz2 |
bpo-23867: Argument Clinic: inline parsing code for a single positional parameter. (GH-9689)
Diffstat (limited to 'Tools')
-rwxr-xr-x | Tools/clinic/clinic.py | 449 |
1 files changed, 433 insertions, 16 deletions
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index b0acbcf..3627725 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -807,11 +807,10 @@ class CLanguage(Language): {c_basename}({self_type}{self_name}, PyObject *%s) """ % argname) - parser_definition = parser_body(parser_prototype, normalize_snippet(""" - if (!PyArg_Parse(%s, "{format_units}:{name}", {parse_arguments})) {{ - goto exit; - }} - """ % argname, indent=4)) + parsearg = converters[0].parse_arg(argname) + assert parsearg is not None + parser_definition = parser_body(parser_prototype, + normalize_snippet(parsearg, indent=4)) elif has_option_groups: # positional parameters with option groups @@ -2346,7 +2345,7 @@ class CConverter(metaclass=CConverterAutoRegister): # keep in sync with self_converter.__init__! def __init__(self, name, py_name, function, default=unspecified, *, c_default=None, py_default=None, annotation=unspecified, **kwargs): - self.name = name + self.name = ensure_legal_c_identifier(name) self.py_name = py_name if default is not unspecified: @@ -2383,8 +2382,7 @@ class CConverter(metaclass=CConverterAutoRegister): def _render_self(self, parameter, data): self.parameter = parameter - original_name = self.name - name = ensure_legal_c_identifier(original_name) + name = self.name # impl_arguments s = ("&" if self.impl_by_reference else "") + name @@ -2399,8 +2397,7 @@ class CConverter(metaclass=CConverterAutoRegister): def _render_non_self(self, parameter, data): self.parameter = parameter - original_name = self.name - name = ensure_legal_c_identifier(original_name) + name = self.name # declarations d = self.declaration() @@ -2449,7 +2446,7 @@ class CConverter(metaclass=CConverterAutoRegister): """Computes the name of the associated "length" variable.""" if not self.length: return None - return ensure_legal_c_identifier(self.name) + "_length" + return self.name + "_length" # Why is this one broken out separately? # For "positional-only" function parsing, @@ -2465,8 +2462,7 @@ class CConverter(metaclass=CConverterAutoRegister): elif self.subclass_of: list.append(self.subclass_of) - legal_name = ensure_legal_c_identifier(self.name) - s = ("&" if self.parse_by_reference else "") + legal_name + s = ("&" if self.parse_by_reference else "") + self.name list.append(s) if self.length: @@ -2487,7 +2483,7 @@ class CConverter(metaclass=CConverterAutoRegister): prototype.append(" ") if by_reference: prototype.append('*') - prototype.append(ensure_legal_c_identifier(self.name)) + prototype.append(self.name) return "".join(prototype) def declaration(self): @@ -2540,6 +2536,52 @@ class CConverter(metaclass=CConverterAutoRegister): """ pass + def parse_arg(self, argname): + if self.format_unit == 'O&': + return """ + if (!{converter}({argname}, &{paramname})) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name, + 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 """ + if (!{typecheck}({argname})) {{{{ + _PyArg_BadArgument("{{name}}", "{typename}", {argname}); + goto exit; + }}}} + {paramname} = {cast}{argname}; + """.format(argname=argname, paramname=self.name, + typecheck=typecheck, typename=typename, cast=cast) + return """ + if (!PyObject_TypeCheck({argname}, {subclass_of})) {{{{ + _PyArg_BadArgument("{{name}}", ({subclass_of})->tp_name, {argname}); + goto exit; + }}}} + {paramname} = {cast}{argname}; + """.format(argname=argname, paramname=self.name, + subclass_of=self.subclass_of, cast=cast) + return """ + if (!PyArg_Parse(%s, "{format_units}:{name}", {parse_arguments})) {{ + goto exit; + }} + """ % argname + +type_checks = { + '&PyLong_Type': ('PyLong_Check', 'int'), + '&PyTuple_Type': ('PyTuple_Check', 'tuple'), + '&PyList_Type': ('PyList_Check', 'list'), + '&PySet_Type': ('PySet_Check', 'set'), + '&PyFrozenSet_Type': ('PyFrozenSet_Check', 'frozenset'), + '&PyDict_Type': ('PyDict_Check', 'dict'), + '&PyUnicode_Type': ('PyUnicode_Check', 'str'), + '&PyBytes_Type': ('PyBytes_Check', 'bytes'), + '&PyByteArray_Type': ('PyByteArray_Check', 'bytearray'), +} + class bool_converter(CConverter): type = 'int' @@ -2556,6 +2598,28 @@ class bool_converter(CConverter): self.default = bool(self.default) self.c_default = str(int(self.default)) + def parse_arg(self, argname): + if self.format_unit == 'i': + return """ + if (PyFloat_Check({argname})) {{{{ + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + }}}} + {paramname} = _PyLong_AsInt({argname}); + if ({paramname} == -1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + elif self.format_unit == 'p': + return """ + {paramname} = PyObject_IsTrue({argname}); + if ({paramname} < 0) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class char_converter(CConverter): type = 'char' default_type = (bytes, bytearray) @@ -2571,6 +2635,22 @@ class char_converter(CConverter): if self.c_default == '"\'"': self.c_default = r"'\''" + def parse_arg(self, argname): + if self.format_unit == 'c': + return """ + if (PyBytes_Check({argname}) && PyBytes_GET_SIZE({argname}) == 1) {{{{ + {paramname} = PyBytes_AS_STRING({argname})[0]; + }}}} + else if (PyByteArray_Check({argname}) && PyByteArray_GET_SIZE({argname}) == 1) {{{{ + {paramname} = PyByteArray_AS_STRING({argname})[0]; + }}}} + else {{{{ + _PyArg_BadArgument("{{name}}", "a byte string of length 1", {argname}); + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + @add_legacy_c_converter('B', bitwise=True) class unsigned_char_converter(CConverter): @@ -2583,6 +2663,53 @@ class unsigned_char_converter(CConverter): if bitwise: self.format_unit = 'B' + def parse_arg(self, argname): + if self.format_unit == 'b': + return """ + if (PyFloat_Check({argname})) {{{{ + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + }}}} + {{{{ + long ival = PyLong_AsLong({argname}); + if (ival == -1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + else if (ival < 0) {{{{ + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is less than minimum"); + goto exit; + }}}} + else if (ival > UCHAR_MAX) {{{{ + PyErr_SetString(PyExc_OverflowError, + "unsigned byte integer is greater than maximum"); + goto exit; + }}}} + else {{{{ + {paramname} = (unsigned char) ival; + }}}} + }}}} + """.format(argname=argname, paramname=self.name) + elif self.format_unit == 'B': + return """ + if (PyFloat_Check({argname})) {{{{ + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + }}}} + {{{{ + long ival = PyLong_AsUnsignedLongMask({argname}); + if (ival == -1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + else {{{{ + {paramname} = (unsigned char) ival; + }}}} + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class byte_converter(unsigned_char_converter): pass class short_converter(CConverter): @@ -2591,6 +2718,36 @@ class short_converter(CConverter): format_unit = 'h' c_ignored_default = "0" + def parse_arg(self, argname): + if self.format_unit == 'h': + return """ + if (PyFloat_Check({argname})) {{{{ + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + }}}} + {{{{ + long ival = PyLong_AsLong({argname}); + if (ival == -1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + else if (ival < SHRT_MIN) {{{{ + PyErr_SetString(PyExc_OverflowError, + "signed short integer is less than minimum"); + goto exit; + }}}} + else if (ival > SHRT_MAX) {{{{ + PyErr_SetString(PyExc_OverflowError, + "signed short integer is greater than maximum"); + goto exit; + }}}} + else {{{{ + {paramname} = (short) ival; + }}}} + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class unsigned_short_converter(CConverter): type = 'unsigned short' default_type = int @@ -2617,6 +2774,36 @@ class int_converter(CConverter): if type != None: self.type = type + def parse_arg(self, argname): + if self.format_unit == 'i': + return """ + if (PyFloat_Check({argname})) {{{{ + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + }}}} + {paramname} = _PyLong_AsInt({argname}); + if ({paramname} == -1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + elif self.format_unit == 'C': + return """ + if (!PyUnicode_Check({argname})) {{{{ + _PyArg_BadArgument("{{name}}", "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}); + goto exit; + }}}} + {paramname} = PyUnicode_READ_CHAR({argname}, 0); + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class unsigned_int_converter(CConverter): type = 'unsigned int' default_type = int @@ -2628,12 +2815,42 @@ class unsigned_int_converter(CConverter): else: self.converter = '_PyLong_UnsignedInt_Converter' + def parse_arg(self, argname): + if self.format_unit == 'I': + return """ + if (PyFloat_Check({argname})) {{{{ + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + }}}} + {paramname} = (unsigned int)PyLong_AsUnsignedLongMask({argname}); + if ({paramname} == (unsigned int)-1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class long_converter(CConverter): type = 'long' default_type = int format_unit = 'l' c_ignored_default = "0" + def parse_arg(self, argname): + if self.format_unit == 'l': + return """ + if (PyFloat_Check({argname})) {{{{ + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + }}}} + {paramname} = PyLong_AsLong({argname}); + if ({paramname} == -1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class unsigned_long_converter(CConverter): type = 'unsigned long' default_type = int @@ -2645,12 +2862,38 @@ class unsigned_long_converter(CConverter): else: self.converter = '_PyLong_UnsignedLong_Converter' + def parse_arg(self, argname): + if self.format_unit == 'k': + return """ + if (!PyLong_Check({argname})) {{{{ + _PyArg_BadArgument("{{name}}", "int", {argname}); + goto exit; + }}}} + {paramname} = PyLong_AsUnsignedLongMask({argname}); + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class long_long_converter(CConverter): type = 'long long' default_type = int format_unit = 'L' c_ignored_default = "0" + def parse_arg(self, argname): + if self.format_unit == 'L': + return """ + if (PyFloat_Check({argname})) {{{{ + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + }}}} + {paramname} = PyLong_AsLongLong({argname}); + if ({paramname} == (PY_LONG_LONG)-1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class unsigned_long_long_converter(CConverter): type = 'unsigned long long' default_type = int @@ -2662,6 +2905,17 @@ class unsigned_long_long_converter(CConverter): else: self.converter = '_PyLong_UnsignedLongLong_Converter' + def parse_arg(self, argname): + if self.format_unit == 'K': + return """ + if (!PyLong_Check({argname})) {{{{ + _PyArg_BadArgument("{{name}}", "int", {argname}); + goto exit; + }}}} + {paramname} = PyLong_AsUnsignedLongLongMask({argname}); + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class Py_ssize_t_converter(CConverter): type = 'Py_ssize_t' c_ignored_default = "0" @@ -2675,6 +2929,29 @@ class Py_ssize_t_converter(CConverter): else: fail("Py_ssize_t_converter: illegal 'accept' argument " + repr(accept)) + def parse_arg(self, argname): + if self.format_unit == 'n': + return """ + if (PyFloat_Check({argname})) {{{{ + PyErr_SetString(PyExc_TypeError, + "integer argument expected, got float" ); + goto exit; + }}}} + {{{{ + Py_ssize_t ival = -1; + PyObject *iobj = PyNumber_Index({argname}); + if (iobj != NULL) {{{{ + ival = PyLong_AsSsize_t(iobj); + Py_DECREF(iobj); + }}}} + if (ival == -1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + {paramname} = ival; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class slice_index_converter(CConverter): type = 'Py_ssize_t' @@ -2692,6 +2969,16 @@ class size_t_converter(CConverter): converter = '_PyLong_Size_t_Converter' c_ignored_default = "0" + def parse_arg(self, argname): + if self.format_unit == 'n': + return """ + {paramname} = PyNumber_AsSsize_t({argname}, PyExc_OverflowError); + if ({paramname} == -1 && PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class float_converter(CConverter): type = 'float' @@ -2699,12 +2986,32 @@ class float_converter(CConverter): format_unit = 'f' c_ignored_default = "0.0" + def parse_arg(self, argname): + if self.format_unit == 'f': + return """ + {paramname} = (float) PyFloat_AsDouble({argname}); + if (PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class double_converter(CConverter): type = 'double' default_type = float format_unit = 'd' c_ignored_default = "0.0" + def parse_arg(self, argname): + if self.format_unit == 'd': + return """ + {paramname} = PyFloat_AsDouble({argname}); + if (PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class Py_complex_converter(CConverter): type = 'Py_complex' @@ -2712,6 +3019,16 @@ class Py_complex_converter(CConverter): format_unit = 'D' c_ignored_default = "{0.0, 0.0}" + def parse_arg(self, argname): + if self.format_unit == 'D': + return """ + {paramname} = PyComplex_AsCComplex({argname}); + if (PyErr_Occurred()) {{{{ + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + class object_converter(CConverter): type = 'PyObject *' @@ -2773,9 +3090,28 @@ class str_converter(CConverter): def cleanup(self): if self.encoding: - name = ensure_legal_c_identifier(self.name) + name = self.name return "".join(["if (", name, ") {\n PyMem_FREE(", name, ");\n}\n"]) + def parse_arg(self, argname): + if self.format_unit == 's': + return """ + if (!PyUnicode_Check({argname})) {{{{ + _PyArg_BadArgument("{{name}}", "str", {argname}); + goto exit; + }}}} + 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; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + # # This is the fourth or fifth rewrite of registering all the # string converter format units. Previous approaches hid @@ -2829,16 +3165,52 @@ class PyBytesObject_converter(CConverter): format_unit = 'S' # accept = {bytes} + def parse_arg(self, argname): + if self.format_unit == 'S': + return """ + if (!PyBytes_Check({argname})) {{{{ + _PyArg_BadArgument("{{name}}", "bytes", {argname}); + goto exit; + }}}} + {paramname} = ({type}){argname}; + """.format(argname=argname, paramname=self.name, type=self.type) + return super().parse_arg(argname) + class PyByteArrayObject_converter(CConverter): type = 'PyByteArrayObject *' format_unit = 'Y' # accept = {bytearray} + def parse_arg(self, argname): + if self.format_unit == 'Y': + return """ + if (!PyByteArray_Check({argname})) {{{{ + _PyArg_BadArgument("{{name}}", "bytearray", {argname}); + goto exit; + }}}} + {paramname} = ({type}){argname}; + """.format(argname=argname, paramname=self.name, type=self.type) + return super().parse_arg(argname) + class unicode_converter(CConverter): type = 'PyObject *' default_type = (str, Null, NoneType) format_unit = 'U' + def parse_arg(self, argname): + if self.format_unit == 'U': + return """ + if (!PyUnicode_Check({argname})) {{{{ + _PyArg_BadArgument("{{name}}", "str", {argname}); + goto exit; + }}}} + if (PyUnicode_READY({argname}) == -1) {{{{ + goto exit; + }}}} + {paramname} = {argname}; + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + @add_legacy_c_converter('u#', zeroes=True) @add_legacy_c_converter('Z', accept={str, NoneType}) @add_legacy_c_converter('Z#', accept={str, NoneType}, zeroes=True) @@ -2883,9 +3255,54 @@ class Py_buffer_converter(CConverter): self.format_unit = format_unit def cleanup(self): - name = ensure_legal_c_identifier(self.name) + name = self.name return "".join(["if (", name, ".obj) {\n PyBuffer_Release(&", name, ");\n}\n"]) + def parse_arg(self, argname): + 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}); + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + elif self.format_unit == 's*': + return """ + if (PyUnicode_Check({argname})) {{{{ + Py_ssize_t len; + const char *ptr = PyUnicode_AsUTF8AndSize({argname}, &len); + if (ptr == NULL) {{{{ + goto exit; + }}}} + PyBuffer_FillInfo(&{paramname}, {argname}, (void *)ptr, len, 1, 0); + }}}} + else {{{{ /* any bytes-like object */ + if (PyObject_GetBuffer({argname}, &{paramname}, PyBUF_SIMPLE) != 0) {{{{ + goto exit; + }}}} + if (!PyBuffer_IsContiguous(&{paramname}, 'C')) {{{{ + _PyArg_BadArgument("{{name}}", "contiguous buffer", {argname}); + goto exit; + }}}} + }}}} + """.format(argname=argname, paramname=self.name) + 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}); + goto exit; + }}}} + if (!PyBuffer_IsContiguous(&{paramname}, 'C')) {{{{ + _PyArg_BadArgument("{{name}}", "contiguous buffer", {argname}); + goto exit; + }}}} + """.format(argname=argname, paramname=self.name) + return super().parse_arg(argname) + def correct_name_for_self(f): if f.kind in (CALLABLE, METHOD_INIT): |