From 9d7b2a47092e58c663e8f66a96b8df7ff2dc9727 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 2 Apr 2024 12:46:20 +0200 Subject: [PATCH 1/4] gh-113317: Argument Clinic: Add libclinic.return_converters Move the following converter classes to libclinic.return_converters: * CReturnConverter * CReturnConverterAutoRegister * Py_ssize_t_return_converter * bool_return_converter * double_return_converter * float_return_converter * int_return_converter * long_return_converter * size_t_return_converter * unsigned_int_return_converter * unsigned_long_return_converter Move also the add_c_return_converter() function there. --- Tools/clinic/clinic.py | 183 +------------------ Tools/clinic/libclinic/return_converters.py | 185 ++++++++++++++++++++ 2 files changed, 188 insertions(+), 180 deletions(-) create mode 100644 Tools/clinic/libclinic/return_converters.py diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index a4e004d5b124d1..97b1f46a13411b 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -60,6 +60,9 @@ from libclinic.converters import ( self_converter, defining_class_converter, object_converter, buffer, robuffer, rwbuffer, correct_name_for_self) +from libclinic.return_converters import ( + CReturnConverter, return_converters, + int_return_converter, ReturnConverterType) # TODO: @@ -2088,186 +2091,6 @@ def parse(self, block: Block) -> None: """.strip().split()) -ReturnConverterType = Callable[..., "CReturnConverter"] - - -# 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. -ReturnConverterDict = dict[str, ReturnConverterType] -return_converters: ReturnConverterDict = {} - - -def add_c_return_converter( - f: ReturnConverterType, - name: str | None = None -) -> ReturnConverterType: - if not name: - name = f.__name__ - if not name.endswith('_return_converter'): - return f - name = name.removesuffix('_return_converter') - return_converters[name] = f - return f - - -class CReturnConverterAutoRegister(type): - def __init__( - cls: ReturnConverterType, - name: str, - bases: tuple[type[object], ...], - classdict: dict[str, Any] - ) -> None: - add_c_return_converter(cls) - - -class CReturnConverter(metaclass=CReturnConverterAutoRegister): - - # The C type to use for this variable. - # 'type' should be a Python string specifying the type, e.g. "int". - # If this is a pointer type, the type string should end with ' *'. - type = 'PyObject *' - - # The Python default value for this parameter, as a Python value. - # Or the magic value "unspecified" if there is no default. - default: object = None - - def __init__( - self, - *, - py_default: str | None = None, - **kwargs: Any - ) -> None: - self.py_default = py_default - try: - self.return_converter_init(**kwargs) - except TypeError as e: - 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) -> None: ... - - def declare(self, data: CRenderData) -> None: - line: list[str] = [] - add = line.append - add(self.type) - if not self.type.endswith('*'): - add(' ') - add(data.converter_retval + ';') - data.declarations.append(''.join(line)) - data.return_value = data.converter_retval - - 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: str, - data: CRenderData - ) -> None: - line = f'if ({variable} == NULL) {{\n goto exit;\n}}\n' - data.return_conversion.append(line) - - 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: 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: 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 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' - unsigned_cast = '(size_t)' - - -class double_return_converter(CReturnConverter): - type = 'double' - cast = '' - - 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)' - - def eval_ast_expr( node: ast.expr, *, diff --git a/Tools/clinic/libclinic/return_converters.py b/Tools/clinic/libclinic/return_converters.py new file mode 100644 index 00000000000000..eaf67b7827075f --- /dev/null +++ b/Tools/clinic/libclinic/return_converters.py @@ -0,0 +1,185 @@ +import sys +from collections.abc import Callable +from libclinic.crenderdata import CRenderData +from libclinic.function import Function +from typing import Any + + +ReturnConverterType = Callable[..., "CReturnConverter"] + + +# 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. +ReturnConverterDict = dict[str, ReturnConverterType] +return_converters: ReturnConverterDict = {} + + +def add_c_return_converter( + f: ReturnConverterType, + name: str | None = None +) -> ReturnConverterType: + if not name: + name = f.__name__ + if not name.endswith('_return_converter'): + return f + name = name.removesuffix('_return_converter') + return_converters[name] = f + return f + + +class CReturnConverterAutoRegister(type): + def __init__( + cls: ReturnConverterType, + name: str, + bases: tuple[type[object], ...], + classdict: dict[str, Any] + ) -> None: + add_c_return_converter(cls) + + +class CReturnConverter(metaclass=CReturnConverterAutoRegister): + + # The C type to use for this variable. + # 'type' should be a Python string specifying the type, e.g. "int". + # If this is a pointer type, the type string should end with ' *'. + type = 'PyObject *' + + # The Python default value for this parameter, as a Python value. + # Or the magic value "unspecified" if there is no default. + default: object = None + + def __init__( + self, + *, + py_default: str | None = None, + **kwargs: Any + ) -> None: + self.py_default = py_default + try: + self.return_converter_init(**kwargs) + except TypeError as e: + 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) -> None: ... + + def declare(self, data: CRenderData) -> None: + line: list[str] = [] + add = line.append + add(self.type) + if not self.type.endswith('*'): + add(' ') + add(data.converter_retval + ';') + data.declarations.append(''.join(line)) + data.return_value = data.converter_retval + + 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: str, + data: CRenderData + ) -> None: + line = f'if ({variable} == NULL) {{\n goto exit;\n}}\n' + data.return_conversion.append(line) + + 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: 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: 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 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' + unsigned_cast = '(size_t)' + + +class double_return_converter(CReturnConverter): + type = 'double' + cast = '' + + 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)' From 6567f08a6e4fcb071a43834ba82a07d352741c8c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 2 Apr 2024 12:53:25 +0200 Subject: [PATCH 2/4] Fix mypy --- Tools/clinic/libclinic/function.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Tools/clinic/libclinic/function.py b/Tools/clinic/libclinic/function.py index b0dd08446e802d..1bfaad00cd0f08 100644 --- a/Tools/clinic/libclinic/function.py +++ b/Tools/clinic/libclinic/function.py @@ -6,9 +6,10 @@ import inspect from typing import Final, Any, TYPE_CHECKING if TYPE_CHECKING: - from clinic import Clinic, CReturnConverter + from clinic import Clinic from libclinic.converter import CConverter from libclinic.converters import self_converter + from libclinic.return_converters import CReturnConverter from libclinic import VersionTuple, unspecified From 1798094114833c9ce95840bbc21c6f27781eaace Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 2 Apr 2024 12:55:19 +0200 Subject: [PATCH 3/4] change style formatting --- Tools/clinic/libclinic/return_converters.py | 52 ++++++++++----------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Tools/clinic/libclinic/return_converters.py b/Tools/clinic/libclinic/return_converters.py index eaf67b7827075f..07b9286738aef3 100644 --- a/Tools/clinic/libclinic/return_converters.py +++ b/Tools/clinic/libclinic/return_converters.py @@ -33,10 +33,10 @@ def add_c_return_converter( class CReturnConverterAutoRegister(type): def __init__( - cls: ReturnConverterType, - name: str, - bases: tuple[type[object], ...], - classdict: dict[str, Any] + cls: ReturnConverterType, + name: str, + bases: tuple[type[object], ...], + classdict: dict[str, Any] ) -> None: add_c_return_converter(cls) @@ -53,10 +53,10 @@ class CReturnConverter(metaclass=CReturnConverterAutoRegister): default: object = None def __init__( - self, - *, - py_default: str | None = None, - **kwargs: Any + self, + *, + py_default: str | None = None, + **kwargs: Any ) -> None: self.py_default = py_default try: @@ -78,25 +78,25 @@ def declare(self, data: CRenderData) -> None: data.return_value = data.converter_retval def err_occurred_if( - self, - expr: str, - data: CRenderData + 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: str, - data: CRenderData + 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: Function, - data: CRenderData + self, + function: Function, + data: CRenderData ) -> None: ... @@ -107,9 +107,9 @@ class bool_return_converter(CReturnConverter): type = 'int' def render( - self, - function: Function, - data: CRenderData + self, + function: Function, + data: CRenderData ) -> None: self.declare(data) self.err_occurred_if(f"{data.converter_retval} == -1", data) @@ -125,9 +125,9 @@ class long_return_converter(CReturnConverter): unsigned_cast = '' def render( - self, - function: Function, - data: CRenderData + self, + function: Function, + data: CRenderData ) -> None: self.declare(data) self.err_occurred_if(f"{data.converter_retval} == {self.unsigned_cast}-1", data) @@ -169,9 +169,9 @@ class double_return_converter(CReturnConverter): cast = '' def render( - self, - function: Function, - data: CRenderData + self, + function: Function, + data: CRenderData ) -> None: self.declare(data) self.err_occurred_if(f"{data.converter_retval} == -1.0", data) From 794e3c524948822b012babd85c43e7631b0217c0 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Tue, 2 Apr 2024 12:56:16 +0200 Subject: [PATCH 4/4] Fixup typing style --- Tools/clinic/libclinic/return_converters.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/Tools/clinic/libclinic/return_converters.py b/Tools/clinic/libclinic/return_converters.py index 07b9286738aef3..7bdd257cfa3443 100644 --- a/Tools/clinic/libclinic/return_converters.py +++ b/Tools/clinic/libclinic/return_converters.py @@ -106,11 +106,7 @@ def render( class bool_return_converter(CReturnConverter): type = 'int' - def render( - self, - function: Function, - data: CRenderData - ) -> None: + 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( @@ -124,11 +120,7 @@ class long_return_converter(CReturnConverter): cast = '' unsigned_cast = '' - def render( - self, - function: Function, - data: CRenderData - ) -> None: + 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( @@ -168,11 +160,7 @@ class double_return_converter(CReturnConverter): type = 'double' cast = '' - def render( - self, - function: Function, - data: CRenderData - ) -> None: + 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(