Skip to content

Commit e3b6f28

Browse files
authored
gh-113317: Argument Clinic: Add libclinic.return_converters (#117451)
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.
1 parent c32dc47 commit e3b6f28

File tree

3 files changed

+178
-181
lines changed

3 files changed

+178
-181
lines changed

Tools/clinic/clinic.py

Lines changed: 3 additions & 180 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@
6060
from libclinic.converters import (
6161
self_converter, defining_class_converter, object_converter, buffer,
6262
robuffer, rwbuffer, correct_name_for_self)
63+
from libclinic.return_converters import (
64+
CReturnConverter, return_converters,
65+
int_return_converter, ReturnConverterType)
6366

6467

6568
# TODO:
@@ -2088,186 +2091,6 @@ def parse(self, block: Block) -> None:
20882091
""".strip().split())
20892092

20902093

2091-
ReturnConverterType = Callable[..., "CReturnConverter"]
2092-
2093-
2094-
# maps strings to callables.
2095-
# these callables must be of the form:
2096-
# def foo(*, ...)
2097-
# The callable may have any number of keyword-only parameters.
2098-
# The callable must return a CReturnConverter object.
2099-
# The callable should not call builtins.print.
2100-
ReturnConverterDict = dict[str, ReturnConverterType]
2101-
return_converters: ReturnConverterDict = {}
2102-
2103-
2104-
def add_c_return_converter(
2105-
f: ReturnConverterType,
2106-
name: str | None = None
2107-
) -> ReturnConverterType:
2108-
if not name:
2109-
name = f.__name__
2110-
if not name.endswith('_return_converter'):
2111-
return f
2112-
name = name.removesuffix('_return_converter')
2113-
return_converters[name] = f
2114-
return f
2115-
2116-
2117-
class CReturnConverterAutoRegister(type):
2118-
def __init__(
2119-
cls: ReturnConverterType,
2120-
name: str,
2121-
bases: tuple[type[object], ...],
2122-
classdict: dict[str, Any]
2123-
) -> None:
2124-
add_c_return_converter(cls)
2125-
2126-
2127-
class CReturnConverter(metaclass=CReturnConverterAutoRegister):
2128-
2129-
# The C type to use for this variable.
2130-
# 'type' should be a Python string specifying the type, e.g. "int".
2131-
# If this is a pointer type, the type string should end with ' *'.
2132-
type = 'PyObject *'
2133-
2134-
# The Python default value for this parameter, as a Python value.
2135-
# Or the magic value "unspecified" if there is no default.
2136-
default: object = None
2137-
2138-
def __init__(
2139-
self,
2140-
*,
2141-
py_default: str | None = None,
2142-
**kwargs: Any
2143-
) -> None:
2144-
self.py_default = py_default
2145-
try:
2146-
self.return_converter_init(**kwargs)
2147-
except TypeError as e:
2148-
s = ', '.join(name + '=' + repr(value) for name, value in kwargs.items())
2149-
sys.exit(self.__class__.__name__ + '(' + s + ')\n' + str(e))
2150-
2151-
def return_converter_init(self) -> None: ...
2152-
2153-
def declare(self, data: CRenderData) -> None:
2154-
line: list[str] = []
2155-
add = line.append
2156-
add(self.type)
2157-
if not self.type.endswith('*'):
2158-
add(' ')
2159-
add(data.converter_retval + ';')
2160-
data.declarations.append(''.join(line))
2161-
data.return_value = data.converter_retval
2162-
2163-
def err_occurred_if(
2164-
self,
2165-
expr: str,
2166-
data: CRenderData
2167-
) -> None:
2168-
line = f'if (({expr}) && PyErr_Occurred()) {{\n goto exit;\n}}\n'
2169-
data.return_conversion.append(line)
2170-
2171-
def err_occurred_if_null_pointer(
2172-
self,
2173-
variable: str,
2174-
data: CRenderData
2175-
) -> None:
2176-
line = f'if ({variable} == NULL) {{\n goto exit;\n}}\n'
2177-
data.return_conversion.append(line)
2178-
2179-
def render(
2180-
self,
2181-
function: Function,
2182-
data: CRenderData
2183-
) -> None: ...
2184-
2185-
2186-
add_c_return_converter(CReturnConverter, 'object')
2187-
2188-
2189-
class bool_return_converter(CReturnConverter):
2190-
type = 'int'
2191-
2192-
def render(
2193-
self,
2194-
function: Function,
2195-
data: CRenderData
2196-
) -> None:
2197-
self.declare(data)
2198-
self.err_occurred_if(f"{data.converter_retval} == -1", data)
2199-
data.return_conversion.append(
2200-
f'return_value = PyBool_FromLong((long){data.converter_retval});\n'
2201-
)
2202-
2203-
2204-
class long_return_converter(CReturnConverter):
2205-
type = 'long'
2206-
conversion_fn = 'PyLong_FromLong'
2207-
cast = ''
2208-
unsigned_cast = ''
2209-
2210-
def render(
2211-
self,
2212-
function: Function,
2213-
data: CRenderData
2214-
) -> None:
2215-
self.declare(data)
2216-
self.err_occurred_if(f"{data.converter_retval} == {self.unsigned_cast}-1", data)
2217-
data.return_conversion.append(
2218-
f'return_value = {self.conversion_fn}({self.cast}{data.converter_retval});\n'
2219-
)
2220-
2221-
2222-
class int_return_converter(long_return_converter):
2223-
type = 'int'
2224-
cast = '(long)'
2225-
2226-
2227-
class unsigned_long_return_converter(long_return_converter):
2228-
type = 'unsigned long'
2229-
conversion_fn = 'PyLong_FromUnsignedLong'
2230-
unsigned_cast = '(unsigned long)'
2231-
2232-
2233-
class unsigned_int_return_converter(unsigned_long_return_converter):
2234-
type = 'unsigned int'
2235-
cast = '(unsigned long)'
2236-
unsigned_cast = '(unsigned int)'
2237-
2238-
2239-
class Py_ssize_t_return_converter(long_return_converter):
2240-
type = 'Py_ssize_t'
2241-
conversion_fn = 'PyLong_FromSsize_t'
2242-
2243-
2244-
class size_t_return_converter(long_return_converter):
2245-
type = 'size_t'
2246-
conversion_fn = 'PyLong_FromSize_t'
2247-
unsigned_cast = '(size_t)'
2248-
2249-
2250-
class double_return_converter(CReturnConverter):
2251-
type = 'double'
2252-
cast = ''
2253-
2254-
def render(
2255-
self,
2256-
function: Function,
2257-
data: CRenderData
2258-
) -> None:
2259-
self.declare(data)
2260-
self.err_occurred_if(f"{data.converter_retval} == -1.0", data)
2261-
data.return_conversion.append(
2262-
f'return_value = PyFloat_FromDouble({self.cast}{data.converter_retval});\n'
2263-
)
2264-
2265-
2266-
class float_return_converter(double_return_converter):
2267-
type = 'float'
2268-
cast = '(double)'
2269-
2270-
22712094
def eval_ast_expr(
22722095
node: ast.expr,
22732096
*,

Tools/clinic/libclinic/function.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
import inspect
77
from typing import Final, Any, TYPE_CHECKING
88
if TYPE_CHECKING:
9-
from clinic import Clinic, CReturnConverter
9+
from clinic import Clinic
1010
from libclinic.converter import CConverter
1111
from libclinic.converters import self_converter
12+
from libclinic.return_converters import CReturnConverter
1213

1314
from libclinic import VersionTuple, unspecified
1415

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import sys
2+
from collections.abc import Callable
3+
from libclinic.crenderdata import CRenderData
4+
from libclinic.function import Function
5+
from typing import Any
6+
7+
8+
ReturnConverterType = Callable[..., "CReturnConverter"]
9+
10+
11+
# maps strings to callables.
12+
# these callables must be of the form:
13+
# def foo(*, ...)
14+
# The callable may have any number of keyword-only parameters.
15+
# The callable must return a CReturnConverter object.
16+
# The callable should not call builtins.print.
17+
ReturnConverterDict = dict[str, ReturnConverterType]
18+
return_converters: ReturnConverterDict = {}
19+
20+
21+
def add_c_return_converter(
22+
f: ReturnConverterType,
23+
name: str | None = None
24+
) -> ReturnConverterType:
25+
if not name:
26+
name = f.__name__
27+
if not name.endswith('_return_converter'):
28+
return f
29+
name = name.removesuffix('_return_converter')
30+
return_converters[name] = f
31+
return f
32+
33+
34+
class CReturnConverterAutoRegister(type):
35+
def __init__(
36+
cls: ReturnConverterType,
37+
name: str,
38+
bases: tuple[type[object], ...],
39+
classdict: dict[str, Any]
40+
) -> None:
41+
add_c_return_converter(cls)
42+
43+
44+
class CReturnConverter(metaclass=CReturnConverterAutoRegister):
45+
46+
# The C type to use for this variable.
47+
# 'type' should be a Python string specifying the type, e.g. "int".
48+
# If this is a pointer type, the type string should end with ' *'.
49+
type = 'PyObject *'
50+
51+
# The Python default value for this parameter, as a Python value.
52+
# Or the magic value "unspecified" if there is no default.
53+
default: object = None
54+
55+
def __init__(
56+
self,
57+
*,
58+
py_default: str | None = None,
59+
**kwargs: Any
60+
) -> None:
61+
self.py_default = py_default
62+
try:
63+
self.return_converter_init(**kwargs)
64+
except TypeError as e:
65+
s = ', '.join(name + '=' + repr(value) for name, value in kwargs.items())
66+
sys.exit(self.__class__.__name__ + '(' + s + ')\n' + str(e))
67+
68+
def return_converter_init(self) -> None: ...
69+
70+
def declare(self, data: CRenderData) -> None:
71+
line: list[str] = []
72+
add = line.append
73+
add(self.type)
74+
if not self.type.endswith('*'):
75+
add(' ')
76+
add(data.converter_retval + ';')
77+
data.declarations.append(''.join(line))
78+
data.return_value = data.converter_retval
79+
80+
def err_occurred_if(
81+
self,
82+
expr: str,
83+
data: CRenderData
84+
) -> None:
85+
line = f'if (({expr}) && PyErr_Occurred()) {{\n goto exit;\n}}\n'
86+
data.return_conversion.append(line)
87+
88+
def err_occurred_if_null_pointer(
89+
self,
90+
variable: str,
91+
data: CRenderData
92+
) -> None:
93+
line = f'if ({variable} == NULL) {{\n goto exit;\n}}\n'
94+
data.return_conversion.append(line)
95+
96+
def render(
97+
self,
98+
function: Function,
99+
data: CRenderData
100+
) -> None: ...
101+
102+
103+
add_c_return_converter(CReturnConverter, 'object')
104+
105+
106+
class bool_return_converter(CReturnConverter):
107+
type = 'int'
108+
109+
def render(self, function: Function, data: CRenderData) -> None:
110+
self.declare(data)
111+
self.err_occurred_if(f"{data.converter_retval} == -1", data)
112+
data.return_conversion.append(
113+
f'return_value = PyBool_FromLong((long){data.converter_retval});\n'
114+
)
115+
116+
117+
class long_return_converter(CReturnConverter):
118+
type = 'long'
119+
conversion_fn = 'PyLong_FromLong'
120+
cast = ''
121+
unsigned_cast = ''
122+
123+
def render(self, function: Function, data: CRenderData) -> None:
124+
self.declare(data)
125+
self.err_occurred_if(f"{data.converter_retval} == {self.unsigned_cast}-1", data)
126+
data.return_conversion.append(
127+
f'return_value = {self.conversion_fn}({self.cast}{data.converter_retval});\n'
128+
)
129+
130+
131+
class int_return_converter(long_return_converter):
132+
type = 'int'
133+
cast = '(long)'
134+
135+
136+
class unsigned_long_return_converter(long_return_converter):
137+
type = 'unsigned long'
138+
conversion_fn = 'PyLong_FromUnsignedLong'
139+
unsigned_cast = '(unsigned long)'
140+
141+
142+
class unsigned_int_return_converter(unsigned_long_return_converter):
143+
type = 'unsigned int'
144+
cast = '(unsigned long)'
145+
unsigned_cast = '(unsigned int)'
146+
147+
148+
class Py_ssize_t_return_converter(long_return_converter):
149+
type = 'Py_ssize_t'
150+
conversion_fn = 'PyLong_FromSsize_t'
151+
152+
153+
class size_t_return_converter(long_return_converter):
154+
type = 'size_t'
155+
conversion_fn = 'PyLong_FromSize_t'
156+
unsigned_cast = '(size_t)'
157+
158+
159+
class double_return_converter(CReturnConverter):
160+
type = 'double'
161+
cast = ''
162+
163+
def render(self, function: Function, data: CRenderData) -> None:
164+
self.declare(data)
165+
self.err_occurred_if(f"{data.converter_retval} == -1.0", data)
166+
data.return_conversion.append(
167+
f'return_value = PyFloat_FromDouble({self.cast}{data.converter_retval});\n'
168+
)
169+
170+
171+
class float_return_converter(double_return_converter):
172+
type = 'float'
173+
cast = '(double)'

0 commit comments

Comments
 (0)