From c482e9a4e6f9c0b88ea248c4a0e0aa97223b5233 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sun, 21 May 2023 22:23:14 +0200 Subject: [PATCH] gh-104050: Annotate Argument Clinic return converters (#104706) Co-authored-by: Alex Waygood --- Tools/clinic/clinic.py | 105 +++++++++++++++++++++++++++++------------ 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 863bd66bef754d..a1e8947529a0d4 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1966,15 +1966,6 @@ def dump(self): extensions['py'] = PythonLanguage -# maps strings to callables. -# these callables must be of the form: -# def foo(*, ...) -# The callable may have any number of keyword-only parameters. -# The callable must return a CConverter object. -# The callable should not call builtins.print. -return_converters = {} - - def file_changed(filename: str, new_contents: str) -> bool: """Return true if file contents changed (meaning we must update it)""" try: @@ -3005,6 +2996,16 @@ def parser_name(self): # note however that they will never be called with keyword-only parameters. legacy_converters: ConverterDict = {} +# maps strings to callables. +# these callables must be of the form: +# def foo(*, ...) +# The callable may have any number of keyword-only parameters. +# The callable must return a CReturnConverter object. +# The callable should not call builtins.print. +ReturnConverterType = Callable[..., "CReturnConverter"] +ReturnConverterDict = dict[str, ReturnConverterType] +return_converters: ReturnConverterDict = {} + TypeSet = set[bltns.type[Any]] @@ -3966,8 +3967,10 @@ def set_template_dict(self, template_dict): template_dict['base_type_ptr'] = type_ptr - -def add_c_return_converter(f, name=None): +def add_c_return_converter( + f: ReturnConverterType, + name: str | None = None +) -> ReturnConverterType: if not name: name = f.__name__ if not name.endswith('_return_converter'): @@ -3978,9 +3981,15 @@ def add_c_return_converter(f, name=None): class CReturnConverterAutoRegister(type): - def __init__(cls, name, bases, classdict): + def __init__( + cls: ReturnConverterType, + name: str, + bases: tuple[type, ...], + classdict: dict[str, Any] + ) -> None: add_c_return_converter(cls) + class CReturnConverter(metaclass=CReturnConverterAutoRegister): # The C type to use for this variable. @@ -3992,7 +4001,12 @@ class CReturnConverter(metaclass=CReturnConverterAutoRegister): # Or the magic value "unspecified" if there is no default. default: object = None - def __init__(self, *, py_default=None, **kwargs): + def __init__( + self, + *, + py_default: str | None = None, + **kwargs + ) -> None: self.py_default = py_default try: self.return_converter_init(**kwargs) @@ -4000,11 +4014,10 @@ def __init__(self, *, py_default=None, **kwargs): s = ', '.join(name + '=' + repr(value) for name, value in kwargs.items()) sys.exit(self.__class__.__name__ + '(' + s + ')\n' + str(e)) - def return_converter_init(self): - pass + def return_converter_init(self) -> None: ... - def declare(self, data): - line = [] + def declare(self, data: CRenderData) -> None: + line: list[str] = [] add = line.append add(self.type) if not self.type.endswith('*'): @@ -4013,50 +4026,70 @@ def declare(self, data): data.declarations.append(''.join(line)) data.return_value = data.converter_retval - def err_occurred_if(self, expr, data): + def err_occurred_if( + self, + expr: str, + data: CRenderData + ) -> None: line = f'if (({expr}) && PyErr_Occurred()) {{\n goto exit;\n}}\n' data.return_conversion.append(line) - def err_occurred_if_null_pointer(self, variable, data): + def err_occurred_if_null_pointer( + self, + variable: str, + data: CRenderData + ) -> None: line = f'if ({variable} == NULL) {{\n goto exit;\n}}\n' data.return_conversion.append(line) - def render(self, function, data): - """ - function is a clinic.Function instance. - data is a CRenderData instance. - """ - pass + def render( + self, + function: Function, + data: CRenderData + ) -> None: ... + add_c_return_converter(CReturnConverter, 'object') + class bool_return_converter(CReturnConverter): type = 'int' - def render(self, function, data): + def render( + self, + function: Function, + data: CRenderData + ) -> None: self.declare(data) self.err_occurred_if(f"{data.converter_retval} == -1", data) data.return_conversion.append( f'return_value = PyBool_FromLong((long){data.converter_retval});\n' ) + class long_return_converter(CReturnConverter): type = 'long' conversion_fn = 'PyLong_FromLong' cast = '' unsigned_cast = '' - def render(self, function, data): + def render( + self, + function: Function, + data: CRenderData + ) -> None: self.declare(data) self.err_occurred_if(f"{data.converter_retval} == {self.unsigned_cast}-1", data) data.return_conversion.append( f'return_value = {self.conversion_fn}({self.cast}{data.converter_retval});\n' ) + class int_return_converter(long_return_converter): type = 'int' cast = '(long)' + class init_return_converter(long_return_converter): """ Special return converter for __init__ functions. @@ -4064,23 +4097,30 @@ class init_return_converter(long_return_converter): type = 'int' cast = '(long)' - def render(self, function, data): - pass + def render( + self, + function: Function, + data: CRenderData + ) -> None: ... + class unsigned_long_return_converter(long_return_converter): type = 'unsigned long' conversion_fn = 'PyLong_FromUnsignedLong' unsigned_cast = '(unsigned long)' + class unsigned_int_return_converter(unsigned_long_return_converter): type = 'unsigned int' cast = '(unsigned long)' unsigned_cast = '(unsigned int)' + class Py_ssize_t_return_converter(long_return_converter): type = 'Py_ssize_t' conversion_fn = 'PyLong_FromSsize_t' + class size_t_return_converter(long_return_converter): type = 'size_t' conversion_fn = 'PyLong_FromSize_t' @@ -4091,13 +4131,18 @@ class double_return_converter(CReturnConverter): type = 'double' cast = '' - def render(self, function, data): + def render( + self, + function: Function, + data: CRenderData + ) -> None: self.declare(data) self.err_occurred_if(f"{data.converter_retval} == -1.0", data) data.return_conversion.append( f'return_value = PyFloat_FromDouble({self.cast}{data.converter_retval});\n' ) + class float_return_converter(double_return_converter): type = 'float' cast = '(double)'