Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-104683: Make Argument Clinic template strings class level members #107556

Merged
merged 1 commit into from
Aug 2, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 69 additions & 71 deletions Tools/clinic/clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -760,6 +760,59 @@ class CLanguage(Language):
stop_line = "[{dsl_name} start generated code]*/"
checksum_line = "/*[{dsl_name} end generated code: {arguments}]*/"

PARSER_PROTOTYPE_KEYWORD: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
""")
PARSER_PROTOTYPE_KEYWORD___INIT__: Final[str] = normalize_snippet("""
static int
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
""")
PARSER_PROTOTYPE_VARARGS: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *args)
""")
PARSER_PROTOTYPE_FASTCALL: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs)
""")
PARSER_PROTOTYPE_FASTCALL_KEYWORDS: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
""")
PARSER_PROTOTYPE_DEF_CLASS: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyTypeObject *{defining_class_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
""")
PARSER_PROTOTYPE_NOARGS: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored))
""")
METH_O_PROTOTYPE: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({impl_parameters})
""")
DOCSTRING_PROTOTYPE_VAR: Final[str] = normalize_snippet("""
PyDoc_VAR({c_basename}__doc__);
""")
DOCSTRING_PROTOTYPE_STRVAR: Final[str] = normalize_snippet("""
PyDoc_STRVAR({c_basename}__doc__,
{docstring});
""")
IMPL_DEFINITION_PROTOTYPE: Final[str] = normalize_snippet("""
static {impl_return_type}
{c_basename}_impl({impl_parameters})
""")
METHODDEF_PROTOTYPE_DEFINE: Final[str] = normalize_snippet(r"""
#define {methoddef_name} \
{{"{name}", {methoddef_cast}{c_basename}{methoddef_cast_end}, {methoddef_flags}, {c_basename}__doc__}},
""")
METHODDEF_PROTOTYPE_IFNDEF: Final[str] = normalize_snippet("""
#ifndef {methoddef_name}
#define {methoddef_name}
#endif /* !defined({methoddef_name}) */
""")

def __init__(self, filename: str) -> None:
super().__init__(filename)
self.cpp = cpp.Monitor(filename)
Expand Down Expand Up @@ -865,52 +918,15 @@ def output_templates(
# methoddef_ifndef

return_value_declaration = "PyObject *return_value = NULL;"

methoddef_define = normalize_snippet("""
#define {methoddef_name} \\
{{"{name}", {methoddef_cast}{c_basename}{methoddef_cast_end}, {methoddef_flags}, {c_basename}__doc__}},
""")
methoddef_define = self.METHODDEF_PROTOTYPE_DEFINE
if new_or_init and not f.docstring:
docstring_prototype = docstring_definition = ''
else:
docstring_prototype = normalize_snippet("""
PyDoc_VAR({c_basename}__doc__);
""")
docstring_definition = normalize_snippet("""
PyDoc_STRVAR({c_basename}__doc__,
{docstring});
""")
impl_definition = normalize_snippet("""
static {impl_return_type}
{c_basename}_impl({impl_parameters})
""")
docstring_prototype = self.DOCSTRING_PROTOTYPE_VAR
docstring_definition = self.DOCSTRING_PROTOTYPE_STRVAR
impl_definition = self.IMPL_DEFINITION_PROTOTYPE
impl_prototype = parser_prototype = parser_definition = None

parser_prototype_keyword = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
""")

parser_prototype_varargs = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *args)
""")

parser_prototype_fastcall = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs)
""")

parser_prototype_fastcall_keywords = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
""")

parser_prototype_def_class = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyTypeObject *{defining_class_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
""")

# parser_body_fields remembers the fields passed in to the
# previous call to parser_body. this is used for an awful hack.
parser_body_fields: tuple[str, ...] = ()
Expand Down Expand Up @@ -953,19 +969,13 @@ def parser_body(
if not requires_defining_class:
# no parameters, METH_NOARGS
flags = "METH_NOARGS"

parser_prototype = normalize_snippet("""
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored))
""")
parser_prototype = self.PARSER_PROTOTYPE_NOARGS
parser_code = []

else:
assert not new_or_init

flags = "METH_METHOD|METH_FASTCALL|METH_KEYWORDS"

parser_prototype = parser_prototype_def_class
parser_prototype = self.PARSER_PROTOTYPE_DEF_CLASS
return_error = ('return NULL;' if default_return_converter
else 'goto exit;')
parser_code = [normalize_snippet("""
Expand All @@ -990,10 +1000,7 @@ def parser_body(

if (isinstance(converters[0], object_converter) and
converters[0].format_unit == 'O'):
meth_o_prototype = normalize_snippet("""
static PyObject *
{c_basename}({impl_parameters})
""")
meth_o_prototype = self.METH_O_PROTOTYPE

if default_return_converter:
# maps perfectly to METH_O, doesn't need a return converter.
Expand Down Expand Up @@ -1033,8 +1040,7 @@ def parser_body(
# in a big switch statement)

flags = "METH_VARARGS"
parser_prototype = parser_prototype_varargs

parser_prototype = self.PARSER_PROTOTYPE_VARARGS
parser_definition = parser_body(parser_prototype, ' {option_group_parsing}')

elif not requires_defining_class and pos_only == len(parameters) - pseudo_args:
Expand All @@ -1043,15 +1049,15 @@ def parser_body(
# we only need one call to _PyArg_ParseStack

flags = "METH_FASTCALL"
parser_prototype = parser_prototype_fastcall
parser_prototype = self.PARSER_PROTOTYPE_FASTCALL
nargs = 'nargs'
argname_fmt = 'args[%d]'
else:
# positional-only, but no option groups
# we only need one call to PyArg_ParseTuple

flags = "METH_VARARGS"
parser_prototype = parser_prototype_varargs
parser_prototype = self.PARSER_PROTOTYPE_VARARGS
nargs = 'PyTuple_GET_SIZE(args)'
argname_fmt = 'PyTuple_GET_ITEM(args, %d)'

Expand Down Expand Up @@ -1149,7 +1155,7 @@ def parser_body(
nargs = f"Py_MIN(nargs, {max_pos})" if max_pos else "0"
if not new_or_init:
flags = "METH_FASTCALL|METH_KEYWORDS"
parser_prototype = parser_prototype_fastcall_keywords
parser_prototype = self.PARSER_PROTOTYPE_FASTCALL_KEYWORDS
argname_fmt = 'args[%d]'
declarations = declare_parser(f)
declarations += "\nPyObject *argsbuf[%s];" % len(converters)
Expand All @@ -1164,7 +1170,7 @@ def parser_body(
else:
# positional-or-keyword arguments
flags = "METH_VARARGS|METH_KEYWORDS"
parser_prototype = parser_prototype_keyword
parser_prototype = self.PARSER_PROTOTYPE_KEYWORD
argname_fmt = 'fastargs[%d]'
declarations = declare_parser(f)
declarations += "\nPyObject *argsbuf[%s];" % len(converters)
Expand All @@ -1181,7 +1187,7 @@ def parser_body(

if requires_defining_class:
flags = 'METH_METHOD|' + flags
parser_prototype = parser_prototype_def_class
parser_prototype = self.PARSER_PROTOTYPE_DEF_CLASS

add_label: str | None = None
for i, p in enumerate(parameters):
Expand Down Expand Up @@ -1269,13 +1275,10 @@ def parser_body(
methoddef_define = ''

if f.kind is METHOD_NEW:
parser_prototype = parser_prototype_keyword
parser_prototype = self.PARSER_PROTOTYPE_KEYWORD
else:
return_value_declaration = "int return_value = -1;"
parser_prototype = normalize_snippet("""
static int
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
""")
parser_prototype = self.PARSER_PROTOTYPE_KEYWORD___INIT__

fields = list(parser_body_fields)
parses_positional = 'METH_NOARGS' not in flags
Expand Down Expand Up @@ -1328,12 +1331,7 @@ def parser_body(

if methoddef_define and f.full_name not in clinic.ifndef_symbols:
clinic.ifndef_symbols.add(f.full_name)
methoddef_ifndef = normalize_snippet("""
#ifndef {methoddef_name}
#define {methoddef_name}
#endif /* !defined({methoddef_name}) */
""")

methoddef_ifndef = self.METHODDEF_PROTOTYPE_IFNDEF

# add ';' to the end of parser_prototype and impl_prototype
# (they mustn't be None, but they could be an empty string.)
Expand Down
Loading