10000 gh-113317: Add Codegen class to Argument Clinic (#117626) · python/cpython@a2ae847 · GitHub
[go: up one dir, main page]

Skip to content

Commit a2ae847

Browse files
authored
gh-113317: Add Codegen class to Argument Clinic (#117626)
* Move ifndef_symbols, includes and add_include() from Clinic to Codegen. Add a 'codegen' (Codegen) attribute to Clinic. * Remove libclinic.crenderdata module: move code to libclinic.codegen. * BlockPrinter.print_block(): remove unused 'limited_capi' argument. Remove also 'core_includes' parameter. * Add get_includes() methods. * Make Codegen.ifndef_symbols private. * Make Codegen.includes private. * Make CConverter.includes private.
1 parent d496387 commit a2ae847

File tree

8 files changed

+183
-180
lines changed

8 files changed

+183
-180
lines changed

Lib/test/test_clinic.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -877,9 +877,8 @@ def _test(self, input, output):
877877

878878
blocks = list(BlockParser(input, language))
879879
writer = BlockPrinter(language)
880-
c = _make_clinic()
881880
for block in blocks:
882-
writer.print_block(block, limited_capi=c.limited_capi, header_includes=c.includes)
881+
writer.print_block(block)
883882
output = writer.f.getvalue()
884883
assert output == input, "output != input!\n\noutput " + repr(output) + "\n\n input " + repr(input)
885884

Tools/clinic/libclinic/app.py

Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@
99
from libclinic import fail, warn
1010
from libclinic.function import Class
1111
from libclinic.block_parser import Block, BlockParser
12-
from libclinic.crenderdata import Include
13-
from libclinic.codegen import BlockPrinter, Destination
12+
from libclinic.codegen import BlockPrinter, Destination, Codegen
1413
from libclinic.parser import Parser, PythonParser
1514
from libclinic.dsl_parser import DSLParser
1615
if TYPE_CHECKING:
@@ -102,8 +101,7 @@ def __init__(
102101
self.modules: ModuleDict = {}
103102
self.classes: ClassDict = {}
104103
self.functions: list[Function] = []
105-
# dict: include name => Include instance
106-
self.includes: dict[str, Include] = {}
104+
self.codegen = Codegen(self.limited_capi)
107105

108106
self.line_prefix = self.line_suffix = ''
109107

@@ -132,7 +130,6 @@ def __init__(
132130
DestBufferList = list[DestBufferType]
133131

134132
self.destination_buffers_stack: DestBufferList = []
135-
self.ifndef_symbols: set[str] = set()
136133

137134
self.presets: dict[str, dict[Any, Any]] = {}
138135
preset = None
@@ -159,24 +156,6 @@ def __init__(
159156
assert name in self.destination_buffers
160157
preset[name] = buffer
161158

162-
def add_include(self, name: str, reason: str,
163-
*, condition: str | None = None) -> None:
164-
try:
165-
existing = self.includes[name]
166-
except KeyError:
167-
pass
168-
else:
169-
if existing.condition and not condition:
170-
# If the previous include has a condition and the new one is
171-
# unconditional, override the include.
172-
pass
173-
else:
174-
# Already included, do nothing. Only mention a single reason,
175-
# no need to list all of them.
176-
return
177-
178-
self.includes[name] = Include(name, reason, condition)
179-
180159
def add_destination(
181160
self,
182161
name: str,
@@ -212,9 +191,7 @@ def parse(self, input: str) -> str:
212191
self.parsers[dsl_name] = parsers[dsl_name](self)
213192
parser = self.parsers[dsl_name]
214193
parser.parse(block)
215-
printer.print_block(block,
216-
limited_capi=self.limited_capi,
217-
header_includes=self.includes)
194+
printer.print_block(block)
218195

219196
# these are destinations not buffers
220197
for name, destination in self.destinations.items():
@@ -229,9 +206,7 @@ def parse(self, input: str) -> str:
229206
block.input = "dump " + name + "\n"
230207
warn("Destination buffer " + repr(name) + " not empty at end of file, emptying.")
231208
printer.write("\n")
232-
printer.print_block(block,
233-
limited_capi=self.limited_capi,
234-
header_includes=self.includes)
209+
printer.print_block(block)
235210
continue
236211

237212
if destination.type == 'file':
@@ -255,11 +230,10 @@ def parse(self, input: str) -> str:
255230
pass
256231

257232
block.input = 'preserve\n'
233+
includes = self.codegen.get_includes()
234+
258235
printer_2 = BlockPrinter(self.language)
259-
printer_2.print_block(block,
260-
core_includes=True,
261-
limited_capi=self.limited_capi,
262-
header_includes=self.includes)
236+
printer_2.print_block(block, header_includes=includes)
263237
libclinic.write_file(destination.filename,
264238
printer_2.f.getvalue())
265239
continue

Tools/clinic/libclinic/clanguage.py

Lines changed: 48 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
unspecified, fail, warn, Sentinels, VersionTuple)
1212
from libclinic.function import (
1313
GETTER, SETTER, METHOD_INIT, METHOD_NEW)
14-
from libclinic.crenderdata import CRenderData, TemplateDict
14+
from libclinic.codegen import CRenderData, TemplateDict, Codegen
1515
from libclinic.language import Language
1616
from libclinic.function import (
1717
Module, Class, Function, Parameter,
@@ -26,15 +26,15 @@ def declare_parser(
2626
f: Function,
2727
*,
2828
hasformat: bool = False,
29-
clinic: Clinic,
30-
limited_capi: bool,
29+
codegen: Codegen,
3130
) -> str:
3231
"""
3332
Generates the code template for a static local PyArg_Parser variable,
3433
with an initializer. For core code (incl. builtin modules) the
3534
kwtuple field is also statically initialized. Otherwise
3635
it is initialized at runtime.
3736
"""
37+
limited_capi = codegen.limited_capi
3838
if hasformat:
3939
fname = ''
4040
format_ = '.format = "{format_units}:{name}",'
@@ -80,8 +80,8 @@ def declare_parser(
8080
""" % num_keywords
8181

8282
condition = '#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)'
83-
clinic.add_include('pycore_gc.h', 'PyGC_Head', condition=condition)
84-
clinic.add_include('pycore_runtime.h', '_Py_ID()', condition=condition)
83+
codegen.add_include('pycore_gc.h', 'PyGC_Head', condition=condition)
84+
codegen.add_include('pycore_runtime.h', '_Py_ID()', condition=condition)
8585

8686
declarations += """
8787
static const char * const _keywords[] = {{{keywords_c} NULL}};
@@ -317,14 +317,14 @@ def deprecate_keyword_use(
317317
self,
318318
func: Function,
319319
params: dict[int, Parameter],
320-
argname_fmt: str | None,
320+
argname_fmt: str | None = None,
321321
*,
322322
fastcall: bool,
323-
limited_capi: bool,
324-
clinic: Clinic,
323+
codegen: Codegen,
325324
) -> str:
326325
assert len(params) > 0
327326
last_param = next(reversed(params.values()))
327+
limited_capi = codegen.limited_capi
328328

329329
# Format the deprecation message.
330330
containscheck = ""
@@ -336,11 +336,11 @@ def deprecate_keyword_use(
336336
elif fastcall:
337337
conditions.append(f"nargs < {i+1} && PySequence_Contains(kwnames, &_Py_ID({p.name}))")
338338
containscheck = "PySequence_Contains"
339-
clinic.add_include('pycore_runtime.h', '_Py_ID()')
339+
codegen.add_include('pycore_runtime.h', '_Py_ID()')
340340
else:
341341
conditions.append(f"nargs < {i+1} && PyDict_Contains(kwargs, &_Py_ID({p.name}))")
342342
containscheck = "PyDict_Contains"
343-
clinic.add_include('pycore_runtime.h', '_Py_ID()')
343+
codegen.add_include('pycore_runtime.h', '_Py_ID()')
344344
else:
345345
conditions = [f"nargs < {i+1}"]
346346
condition = ") || (".join(conditions)
@@ -399,7 +399,7 @@ def deprecate_keyword_use(
399399
def output_templates(
400400
self,
401401
f: Function,
402-
clinic: Clinic
402+
codegen: Codegen,
403403
) -> dict[str, str]:
404404
parameters = list(f.parameters.values())
405405
assert parameters
@@ -412,7 +412,7 @@ def output_templates(
412412
converters = [p.converter for p in parameters]
413413

414414
if f.critical_section:
415-
clinic.add_include('pycore_critical_section.h', 'Py_BEGIN_CRITICAL_SECTION()')
415+
codegen.add_include('pycore_critical_section.h', 'Py_BEGIN_CRITICAL_SECTION()')
416416
has_option_groups = parameters and (parameters[0].group or parameters[-1].group)
417417
simple_return = (f.return_converter.type == 'PyObject *'
418418
and not f.critical_section)
@@ -517,7 +517,7 @@ def parser_body(
517517
parser_declarations=declarations)
518518

519519
fastcall = not new_or_init
520-
limited_capi = clinic.limited_capi
520+
limited_capi = codegen.limited_capi
521521
if limited_capi and (pseudo_args or
522522
(any(p.is_optional() for p in parameters) and
523523
any(p.is_keyword_only() and not p.is_optional() for p in parameters)) or
@@ -673,8 +673,8 @@ def parser_body(
673673
""",
674674
indent=4))
675675
else:
676-
clinic.add_include('pycore_modsupport.h',
677-
'_PyArg_CheckPositional()')
676+
codegen.add_include('pycore_modsupport.h',
677+
'_PyArg_CheckPositional()')
678678
parser_code = [libclinic.normalize_snippet(f"""
679679
if (!_PyArg_CheckPositional("{{name}}", {nargs}, {min_pos}, {max_args})) {{{{
680680
goto exit;
@@ -735,8 +735,8 @@ def parser_body(
735735
if limited_capi:
736736
fastcall = False
737737
if fastcall:
738-
clinic.add_include('pycore_modsupport.h',
739-
'_PyArg_ParseStack()')
738+
codegen.add_include('pycore_modsupport.h',
739+
'_PyArg_ParseStack()')
740740
parser_code = [libclinic.normalize_snippet("""
741741
if (!_PyArg_ParseStack(args, nargs, "{format_units}:{name}",
742742
{parse_arguments})) {{
@@ -773,17 +773,17 @@ def parser_body(
773773
fastcall = False
774774
else:
775775
if vararg == self.NO_VARARG:
776-
clinic.add_include('pycore_modsupport.h',
777-
'_PyArg_UnpackKeywords()')
776+
codegen.add_include('pycore_modsupport.h',
777+
'_PyArg_UnpackKeywords()')
778778
args_declaration = "_PyArg_UnpackKeywords", "%s, %s, %s" % (
779779
min_pos,
780780
max_pos,
781781
min_kw_only
782782
)
783783
nargs = "nargs"
784784
else:
785-
clinic.add_include('pycore_modsupport.h',
786-
'_PyArg_UnpackKeywordsWithVararg()')
785+
codegen.add_include('pycore_modsupport.h',
786+
'_PyArg_UnpackKeywordsWithVararg()')
787787
args_declaration = "_PyArg_UnpackKeywordsWithVararg", "%s, %s, %s, %s" % (
788788
min_pos,
789789
max_pos,
@@ -796,8 +796,7 @@ def parser_body(
796796
flags = "METH_FASTCALL|METH_KEYWORDS"
797797
parser_prototype = self.PARSER_PROTOTYPE_FASTCALL_KEYWORDS
798798
argname_fmt = 'args[%d]'
799-
declarations = declare_parser(f, clinic=clinic,
800-
limited_capi=clinic.limited_capi)
799+
declarations = declare_parser(f, codegen=codegen)
801800
declarations += "\nPyObject *argsbuf[%s];" % len(converters)
802801
if has_optional_kw:
803802
declarations += "\nPy_ssize_t noptargs = %s + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - %d;" % (nargs, min_pos + min_kw_only)
@@ -812,8 +811,7 @@ def parser_body(
812811
flags = "METH_VARARGS|METH_KEYWORDS"
813812
parser_prototype = self.PARSER_PROTOTYPE_KEYWORD
814813
argname_fmt = 'fastargs[%d]'
815-
declarations = declare_parser(f, clinic=clinic,
816-
limited_capi=clinic.limited_capi)
814+
declarations = declare_parser(f, codegen=codegen) 325D
817815
declarations += "\nPyObject *argsbuf[%s];" % len(converters)
818816
declarations += "\nPyObject * const *fastargs;"
819817
declarations += "\nPy_ssize_t nargs = PyTuple_GET_SIZE(args);"
@@ -832,10 +830,10 @@ def parser_body(
832830

833831
if parser_code is not None:
834832
if deprecated_keywords:
835-
code = self.deprecate_keyword_use(f, deprecated_keywords, argname_fmt,
836-
clinic=clinic,
837-
fastcall=fastcall,
838-
limited_capi=limited_capi)
833+
code = self.deprecate_keyword_use(f, deprecated_keywords,
834+
argname_fmt,
835+
codegen=codegen,
836+
fastcall=fastcall)
839837
parser_code.append(code)
840838

841839
add_label: str | None = None
@@ -903,9 +901,8 @@ def parser_body(
903901
for parameter in parameters:
904902
parameter.converter.use_converter()
905903

906-
declarations = declare_parser(f, clinic=clinic,
907-
hasformat=True,
908-
limited_capi=limited_capi)
904+
declarations = declare_parser(f, codegen=codegen,
905+
hasformat=True)
909906
if limited_capi:
910907
# positional-or-keyword arguments
911908
assert not fastcall
@@ -921,17 +918,17 @@ def parser_body(
921918
declarations += "\nPy_ssize_t nargs = PyTuple_Size(args);"
922919

923920
elif fastcall:
924-
clinic.add_include('pycore_modsupport.h',
925-
'_PyArg_ParseStackAndKeywords()')
921+
codegen.add_include('pycore_modsupport.h',
922+
'_PyArg_ParseStackAndKeywords()')
926923
parser_code = [libclinic.normalize_snippet("""
927924
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser{parse_arguments_comma}
928925
{parse_arguments})) {{
929926
goto exit;
930927
}}
931928
""", indent=4)]
932929
else:
933-
clinic.add_include('pycore_modsupport.h',
934-
'_PyArg_ParseTupleAndKeywordsFast()')
930+
codegen.add_include('pycore_modsupport.h',
931+
'_PyArg_ParseTupleAndKeywordsFast()')
935932
parser_code = [libclinic.normalize_snippet("""
936933
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
937934
{parse_arguments})) {{
@@ -941,10 +938,9 @@ def parser_body(
941938
if deprecated_positionals or deprecated_keywords:
942939
declarations += "\nPy_ssize_t nargs = PyTuple_GET_SIZE(args);"
943940
if deprecated_keywords:
944-
code = self.deprecate_keyword_use(f, deprecated_keywords, None,
945-
clinic=clinic,
946-
fastcall=fastcall,
947-
limited_capi=limited_capi)
941+
code = self.deprecate_keyword_use(f, deprecated_keywords,
942+
codegen=codegen,
943+
fastcall=fastcall)
948944
parser_code.append(code)
949945

950946
if deprecated_positionals:
@@ -960,9 +956,9 @@ def parser_body(
960956
# Copy includes from parameters to Clinic after parse_arg() has been
961957
# called above.
962958
for converter in converters:
963-
for include in converter.includes:
964-
clinic.add_include(include.filename, include.reason,
965-
condition=include.condition)
959+
for include in converter.get_includes():
960+
codegen.add_include(include.filename, include.reason,
961+
condition=include.condition)
966962

967963
if new_or_init:
968964
methoddef_define = ''
@@ -984,16 +980,16 @@ def parser_body(
984980

985981
if not parses_keywords:
986982
declarations = '{base_type_ptr}'
987-
clinic.add_include('pycore_modsupport.h',
988-
'_PyArg_NoKeywords()')
983+
codegen.add_include('pycore_modsupport.h',
984+
'_PyArg_NoKeywords()')
989985
fields.insert(0, libclinic.normalize_snippet("""
990986
if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) {{
991987
goto exit;
992988
}}
993989
""", indent=4))
994990
if not parses_positional:
995-
clinic.add_include('pycore_modsupport.h',
996-
'_PyArg_NoPositional()')
991+
codegen.add_include('pycore_modsupport.h',
992+
'_PyArg_NoPositional()')
997993
fields.insert(0, libclinic.normalize_snippet("""
998994
if ({self_type_check}!_PyArg_NoPositional("{name}", args)) {{
999995
goto exit;
@@ -1030,8 +1026,7 @@ def parser_body(
10301026
cpp_if = "#if " + conditional
10311027
cpp_endif = "#endif /* " + conditional + " */"
10321028

1033-
if methoddef_define and f.full_name not in clinic.ifndef_symbols:
1034-
clinic.ifndef_symbols.add(f.full_name)
1029+
if methoddef_define and codegen.add_ifndef_symbol(f.full_name):
10351030
methoddef_ifndef = self.METHODDEF_PROTOTYPE_IFNDEF
10361031

10371032
# add ';' to the end of parser_prototype and impl_prototype
@@ -1190,16 +1185,17 @@ def render_function(
11901185
clinic: Clinic,
11911186
f: Function | None
11921187
) -> str:
1193-
if f is None or clinic is None:
1188+
if f is None:
11941189
return ""
11951190

1191+
codegen = clinic.codegen
11961192
data = CRenderData()
11971193

11981194
assert f.parameters, "We should always have a 'self' at this point!"
11991195
parameters = f.render_parameters
12001196
converters = [p.converter for p in parameters]
12011197

1202-
templates = self.output_templates(f, clinic)
1198+
templates = self.output_templates(f, codegen)
12031199

12041200
f_self = parameters[0]
12051201
selfless = parameters[1:]
@@ -1323,7 +1319,7 @@ def render_function(
13231319

13241320
if has_option_groups:
13251321
self.render_option_group_parsing(f, template_dict,
1326-
limited_capi=clinic.limited_capi)
1322+
limited_capi=codegen.limited_capi)
13271323

13281324
# buffers, not destination
13291325
for name, destination in clinic.destination_buffers.items():

0 commit comments

Comments
 (0)
0