Skip to content

Commit bcdd307

Browse files
gh-104683: Make Argument Clinic template strings class level members (#107556)
The motivation for this change is to clean up the output_templates() method a little bit, as it accounts for ~10% of the lines of code in clinic.py; removing some clutter helps readability.
1 parent af8141c commit bcdd307

File tree

1 file changed

+69
-71
lines changed

1 file changed

+69
-71
lines changed

Tools/clinic/clinic.py

+69-71
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,59 @@ class CLanguage(Language):
760760
stop_line = "[{dsl_name} start generated code]*/"
761761
checksum_line = "/*[{dsl_name} end generated code: {arguments}]*/"
762762

763+
PARSER_PROTOTYPE_KEYWORD: Final[str] = normalize_snippet("""
764+
static PyObject *
765+
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
766+
""")
767+
PARSER_PROTOTYPE_KEYWORD___INIT__: Final[str] = normalize_snippet("""
768+
static int
769+
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
770+
""")
771+
PARSER_PROTOTYPE_VARARGS: Final[str] = normalize_snippet("""
772+
static PyObject *
773+
{c_basename}({self_type}{self_name}, PyObject *args)
774+
""")
775+
PARSER_PROTOTYPE_FASTCALL: Final[str] = normalize_snippet("""
776+
static PyObject *
777+
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs)
778+
""")
779+
PARSER_PROTOTYPE_FASTCALL_KEYWORDS: Final[str] = normalize_snippet("""
780+
static PyObject *
781+
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
782+
""")
783+
PARSER_PROTOTYPE_DEF_CLASS: Final[str] = normalize_snippet("""
784+
static PyObject *
785+
{c_basename}({self_type}{self_name}, PyTypeObject *{defining_class_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
786+
""")
787+
PARSER_PROTOTYPE_NOARGS: Final[str] = normalize_snippet("""
788+
static PyObject *
789+
{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored))
790+
""")
791+
METH_O_PROTOTYPE: Final[str] = normalize_snippet("""
792+
static PyObject *
793+
{c_basename}({impl_parameters})
794+
""")
795+
DOCSTRING_PROTOTYPE_VAR: Final[str] = normalize_snippet("""
796+
PyDoc_VAR({c_basename}__doc__);
797+
""")
798+
DOCSTRING_PROTOTYPE_STRVAR: Final[str] = normalize_snippet("""
799+
PyDoc_STRVAR({c_basename}__doc__,
800+
{docstring});
801+
""")
802+
IMPL_DEFINITION_PROTOTYPE: Final[str] = normalize_snippet("""
803+
static {impl_return_type}
804+
{c_basename}_impl({impl_parameters})
805+
""")
806+
METHODDEF_PROTOTYPE_DEFINE: Final[str] = normalize_snippet(r"""
807+
#define {methoddef_name} \
808+
{{"{name}", {methoddef_cast}{c_basename}{methoddef_cast_end}, {methoddef_flags}, {c_basename}__doc__}},
809+
""")
810+
METHODDEF_PROTOTYPE_IFNDEF: Final[str] = normalize_snippet("""
811+
#ifndef {methoddef_name}
812+
#define {methoddef_name}
813+
#endif /* !defined({methoddef_name}) */
814+
""")
815+
763816
def __init__(self, filename: str) -> None:
764817
super().__init__(filename)
765818
self.cpp = cpp.Monitor(filename)
@@ -862,52 +915,15 @@ def output_templates(
862915
# methoddef_ifndef
863916

864917
return_value_declaration = "PyObject *return_value = NULL;"
865-
866-
methoddef_define = normalize_snippet("""
867-
#define {methoddef_name} \\
868-
{{"{name}", {methoddef_cast}{c_basename}{methoddef_cast_end}, {methoddef_flags}, {c_basename}__doc__}},
869-
""")
918+
methoddef_define = self.METHODDEF_PROTOTYPE_DEFINE
870919
if new_or_init and not f.docstring:
871920
docstring_prototype = docstring_definition = ''
872921
else:
873-
docstring_prototype = normalize_snippet("""
874-
PyDoc_VAR({c_basename}__doc__);
875-
""")
876-
docstring_definition = normalize_snippet("""
877-
PyDoc_STRVAR({c_basename}__doc__,
878-
{docstring});
879-
""")
880-
impl_definition = normalize_snippet("""
881-
static {impl_return_type}
882-
{c_basename}_impl({impl_parameters})
883-
""")
922+
docstring_prototype = self.DOCSTRING_PROTOTYPE_VAR
923+
docstring_definition = self.DOCSTRING_PROTOTYPE_STRVAR
924+
impl_definition = self.IMPL_DEFINITION_PROTOTYPE
884925
impl_prototype = parser_prototype = parser_definition = None
885926

886-
parser_prototype_keyword = normalize_snippet("""
887-
static PyObject *
888-
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
889-
""")
890-
891-
parser_prototype_varargs = normalize_snippet("""
892-
static PyObject *
893-
{c_basename}({self_type}{self_name}, PyObject *args)
894-
""")
895-
896-
parser_prototype_fastcall = normalize_snippet("""
897-
static PyObject *
898-
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs)
899-
""")
900-
901-
parser_prototype_fastcall_keywords = normalize_snippet("""
902-
static PyObject *
903-
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
904-
""")
905-
906-
parser_prototype_def_class = normalize_snippet("""
907-
static PyObject *
908-
{c_basename}({self_type}{self_name}, PyTypeObject *{defining_class_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
909-
""")
910-
911927
# parser_body_fields remembers the fields passed in to the
912928
# previous call to parser_body. this is used for an awful hack.
913929
parser_body_fields: tuple[str, ...] = ()
@@ -950,19 +966,13 @@ def parser_body(
950966
if not requires_defining_class:
951967
# no parameters, METH_NOARGS
952968
flags = "METH_NOARGS"
953-
954-
parser_prototype = normalize_snippet("""
955-
static PyObject *
956-
{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored))
957-
""")
969+
parser_prototype = self.PARSER_PROTOTYPE_NOARGS
958970
parser_code = []
959-
960971
else:
961972
assert not new_or_init
962973

963974
flags = "METH_METHOD|METH_FASTCALL|METH_KEYWORDS"
964-
965-
parser_prototype = parser_prototype_def_class
975+
parser_prototype = self.PARSER_PROTOTYPE_DEF_CLASS
966976
return_error = ('return NULL;' if default_return_converter
967977
else 'goto exit;')
968978
parser_code = [normalize_snippet("""
@@ -987,10 +997,7 @@ def parser_body(
987997

988998
if (isinstance(converters[0], object_converter) and
989999
converters[0].format_unit == 'O'):
990-
meth_o_prototype = normalize_snippet("""
991-
static PyObject *
992-
{c_basename}({impl_parameters})
993-
""")
1000+
meth_o_prototype = self.METH_O_PROTOTYPE
9941001

9951002
if default_return_converter:
9961003
# maps perfectly to METH_O, doesn't need a return converter.
@@ -1030,8 +1037,7 @@ def parser_body(
10301037
# in a big switch statement)
10311038

10321039
flags = "METH_VARARGS"
1033-
parser_prototype = parser_prototype_varargs
1034-
1040+
parser_prototype = self.PARSER_PROTOTYPE_VARARGS
10351041
parser_definition = parser_body(parser_prototype, ' {option_group_parsing}')
10361042

10371043
elif not requires_defining_class and pos_only == len(parameters) - pseudo_args:
@@ -1040,15 +1046,15 @@ def parser_body(
10401046
# we only need one call to _PyArg_ParseStack
10411047

10421048
flags = "METH_FASTCALL"
1043-
parser_prototype = parser_prototype_fastcall
1049+
parser_prototype = self.PARSER_PROTOTYPE_FASTCALL
10441050
nargs = 'nargs'
10451051
argname_fmt = 'args[%d]'
10461052
else:
10471053
# positional-only, but no option groups
10481054
# we only need one call to PyArg_ParseTuple
10491055

10501056
flags = "METH_VARARGS"
1051-
parser_prototype = parser_prototype_varargs
1057+
parser_prototype = self.PARSER_PROTOTYPE_VARARGS
10521058
nargs = 'PyTuple_GET_SIZE(args)'
10531059
argname_fmt = 'PyTuple_GET_ITEM(args, %d)'
10541060

@@ -1145,7 +1151,7 @@ def parser_body(
11451151
nargs = f"Py_MIN(nargs, {max_pos})" if max_pos else "0"
11461152
if not new_or_init:
11471153
flags = "METH_FASTCALL|METH_KEYWORDS"
1148-
parser_prototype = parser_prototype_fastcall_keywords
1154+
parser_prototype = self.PARSER_PROTOTYPE_FASTCALL_KEYWORDS
11491155
argname_fmt = 'args[%d]'
11501156
declarations = declare_parser(f)
11511157
declarations += "\nPyObject *argsbuf[%s];" % len(converters)
@@ -1160,7 +1166,7 @@ def parser_body(
11601166
else:
11611167
# positional-or-keyword arguments
11621168
flags = "METH_VARARGS|METH_KEYWORDS"
1163-
parser_prototype = parser_prototype_keyword
1169+
parser_prototype = self.PARSER_PROTOTYPE_KEYWORD
11641170
argname_fmt = 'fastargs[%d]'
11651171
declarations = declare_parser(f)
11661172
declarations += "\nPyObject *argsbuf[%s];" % len(converters)
@@ -1177,7 +1183,7 @@ def parser_body(
11771183

11781184
if requires_defining_class:
11791185
flags = 'METH_METHOD|' + flags
1180-
parser_prototype = parser_prototype_def_class
1186+
parser_prototype = self.PARSER_PROTOTYPE_DEF_CLASS
11811187

11821188
add_label: str | None = None
11831189
for i, p in enumerate(parameters):
@@ -1264,13 +1270,10 @@ def parser_body(
12641270
methoddef_define = ''
12651271

12661272
if f.kind is METHOD_NEW:
1267-
parser_prototype = parser_prototype_keyword
1273+
parser_prototype = self.PARSER_PROTOTYPE_KEYWORD
12681274
else:
12691275
return_value_declaration = "int return_value = -1;"
1270-
parser_prototype = normalize_snippet("""
1271-
static int
1272-
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
1273-
""")
1276+
parser_prototype = self.PARSER_PROTOTYPE_KEYWORD___INIT__
12741277

12751278
fields = list(parser_body_fields)
12761279
parses_positional = 'METH_NOARGS' not in flags
@@ -1323,12 +1326,7 @@ def parser_body(
13231326

13241327
if methoddef_define and f.full_name not in clinic.ifndef_symbols:
13251328
clinic.ifndef_symbols.add(f.full_name)
1326-
methoddef_ifndef = normalize_snippet("""
1327-
#ifndef {methoddef_name}
1328-
#define {methoddef_name}
1329-
#endif /* !defined({methoddef_name}) */
1330-
""")
1331-
1329+
methoddef_ifndef = self.METHODDEF_PROTOTYPE_IFNDEF
13321330

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

0 commit comments

Comments
 (0)