Skip to content

Commit

Permalink
gh-122943: Move code generation for var-positional parameter to conve…
Browse files Browse the repository at this point in the history
…rters (GH-126575)
  • Loading branch information
serhiy-storchaka authored Nov 8, 2024
1 parent 403410f commit ee0746a
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 73 deletions.
87 changes: 80 additions & 7 deletions Tools/clinic/libclinic/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -1232,22 +1232,95 @@ def set_template_dict(self, template_dict: TemplateDict) -> None:

# Converters for var-positional parameter.

class varpos_tuple_converter(CConverter):
class VarPosCConverter(CConverter):
format_unit = ''

def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
raise AssertionError('should never be called')

def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int,
fastcall: bool, limited_capi: bool) -> str:
raise NotImplementedError


class varpos_tuple_converter(VarPosCConverter):
type = 'PyObject *'
format_unit = ''
c_default = 'NULL'

def cleanup(self) -> str:
return f"""Py_XDECREF({self.parser_name});\n"""

def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
raise AssertionError('should never be called')
def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int,
fastcall: bool, limited_capi: bool) -> str:
paramname = self.parser_name
if fastcall:
if limited_capi:
if min(pos_only, min_pos) < max_pos:
size = f'Py_MAX(nargs - {max_pos}, 0)'
else:
size = f'nargs - {max_pos}' if max_pos else 'nargs'
return f"""
{paramname} = PyTuple_New({size});
if (!{paramname}) {{{{
goto exit;
}}}}
for (Py_ssize_t i = {max_pos}; i < nargs; ++i) {{{{
PyTuple_SET_ITEM({paramname}, i - {max_pos}, Py_NewRef(args[i]));
}}}}
"""
else:
self.add_include('pycore_tuple.h', '_PyTuple_FromArray()')
start = f'args + {max_pos}' if max_pos else 'args'
size = f'nargs - {max_pos}' if max_pos else 'nargs'
if min(pos_only, min_pos) < max_pos:
return f"""
{paramname} = nargs > {max_pos}
? _PyTuple_FromArray({start}, {size})
: PyTuple_New(0);
if ({paramname} == NULL) {{{{
goto exit;
}}}}
"""
else:
return f"""
{paramname} = _PyTuple_FromArray({start}, {size});
if ({paramname} == NULL) {{{{
goto exit;
}}}}
"""
else:
if max_pos:
return f"""
{paramname} = PyTuple_GetSlice(args, {max_pos}, PY_SSIZE_T_MAX);
if (!{paramname}) {{{{
goto exit;
}}}}
"""
else:
return f"{paramname} = Py_NewRef(args);\n"

class varpos_array_converter(CConverter):

class varpos_array_converter(VarPosCConverter):
type = 'PyObject * const *'
format_unit = ''
length = True
c_ignored_default = ''

def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
raise AssertionError('should never be called')
def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int,
fastcall: bool, limited_capi: bool) -> str:
paramname = self.parser_name
if not fastcall:
self.add_include('pycore_tuple.h', '_PyTuple_ITEMS()')
start = 'args' if fastcall else '_PyTuple_ITEMS(args)'
size = 'nargs' if fastcall else 'PyTuple_GET_SIZE(args)'
if max_pos:
if min(pos_only, min_pos) < max_pos:
start = f'{size} > {max_pos} ? {start} + {max_pos} : {start}'
size = f'Py_MAX(0, {size} - {max_pos})'
else:
start = f'{start} + {max_pos}'
size = f'{size} - {max_pos}'
return f"""
{paramname} = {start};
{self.length_name} = {size};
"""
77 changes: 11 additions & 66 deletions Tools/clinic/libclinic/parse_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -452,71 +452,13 @@ def parse_option_groups(self) -> None:

def _parse_vararg(self) -> str:
assert self.varpos is not None
paramname = self.varpos.converter.parser_name
if self.varpos.converter.length:
if not self.fastcall:
self.codegen.add_include('pycore_tuple.h',
'_PyTuple_ITEMS()')
start = 'args' if self.fastcall else '_PyTuple_ITEMS(args)'
size = 'nargs' if self.fastcall else 'PyTuple_GET_SIZE(args)'
if self.max_pos:
if min(self.pos_only, self.min_pos) < self.max_pos:
start = f'{size} > {self.max_pos} ? {start} + {self.max_pos} : {start}'
size = f'Py_MAX(0, {size} - {self.max_pos})'
else:
start = f'{start} + {self.max_pos}'
size = f'{size} - {self.max_pos}'
return f"""
{paramname} = {start};
{self.varpos.converter.length_name} = {size};
"""

if self.fastcall:
if self.limited_capi:
if min(self.pos_only, self.min_pos) < self.max_pos:
size = f'Py_MAX(nargs - {self.max_pos}, 0)'
else:
size = f'nargs - {self.max_pos}' if self.max_pos else 'nargs'
return f"""
{paramname} = PyTuple_New({size});
if (!{paramname}) {{{{
goto exit;
}}}}
for (Py_ssize_t i = {self.max_pos}; i < nargs; ++i) {{{{
PyTuple_SET_ITEM({paramname}, i - {self.max_pos}, Py_NewRef(args[i]));
}}}}
"""
else:
self.codegen.add_include('pycore_tuple.h',
'_PyTuple_FromArray()')
if min(self.pos_only, self.min_pos) < self.max_pos:
return f"""
{paramname} = nargs > {self.max_pos}
? _PyTuple_FromArray(args + {self.max_pos}, nargs - {self.max_pos})
: PyTuple_New(0);
if ({paramname} == NULL) {{{{
goto exit;
}}}}
"""
else:
start = f'args + {self.max_pos}' if self.max_pos else 'args'
size = f'nargs - {self.max_pos}' if self.max_pos else 'nargs'
return f"""
{paramname} = _PyTuple_FromArray({start}, {size});
if ({paramname} == NULL) {{{{
goto exit;
}}}}
"""
else:
if self.max_pos:
return f"""
{paramname} = PyTuple_GetSlice(args, {self.max_pos}, PY_SSIZE_T_MAX);
if (!{paramname}) {{{{
goto exit;
}}}}
"""
else:
return f"{paramname} = Py_NewRef(args);\n"
c = self.varpos.converter
assert isinstance(c, libclinic.converters.VarPosCConverter)
return c.parse_vararg(pos_only=self.pos_only,
min_pos=self.min_pos,
max_pos=self.max_pos,
fastcall=self.fastcall,
limited_capi=self.limited_capi)

def parse_pos_only(self) -> None:
if self.fastcall:
Expand Down Expand Up @@ -839,7 +781,10 @@ def parse_general(self, clang: CLanguage) -> None:
def copy_includes(self) -> None:
# Copy includes from parameters to Clinic after parse_arg()
# has been called above.
for converter in self.converters:
converters = self.converters
if self.varpos:
converters = converters + [self.varpos.converter]
for converter in converters:
for include in converter.get_includes():
self.codegen.add_include(
include.filename,
Expand Down

0 comments on commit ee0746a

Please sign in to comment.