From 869a484541d8f398e716cda2f5210dec79653732 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 8 Apr 2024 10:23:07 +0200 Subject: [PATCH 1/3] gh-113317: Add Codegen class to Argument Clinic * 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. --- Lib/test/test_clinic.py | 2 +- Tools/clinic/libclinic/app.py | 37 +----- Tools/clinic/libclinic/clanguage.py | 98 ++++++++-------- Tools/clinic/libclinic/codegen.py | 119 +++++++++++++++++++- Tools/clinic/libclinic/converter.py | 2 +- Tools/clinic/libclinic/converters.py | 2 +- Tools/clinic/libclinic/crenderdata.py | 81 ------------- Tools/clinic/libclinic/return_converters.py | 2 +- 8 files changed, 170 insertions(+), 173 deletions(-) delete mode 100644 Tools/clinic/libclinic/crenderdata.py diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index a5887cdb56e3ca..8380a96520dbe2 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -824,7 +824,7 @@ def _test(self, input, output): writer = BlockPrinter(language) c = _make_clinic() for block in blocks: - writer.print_block(block, limited_capi=c.limited_capi, header_includes=c.includes) + writer.print_block(block) output = writer.f.getvalue() assert output == input, "output != input!\n\noutput " + repr(output) + "\n\n input " + repr(input) diff --git a/Tools/clinic/libclinic/app.py b/Tools/clinic/libclinic/app.py index 47a897712d053e..cd06ba51116594 100644 --- a/Tools/clinic/libclinic/app.py +++ b/Tools/clinic/libclinic/app.py @@ -9,8 +9,7 @@ from libclinic import fail, warn from libclinic.function import Class from libclinic.block_parser import Block, BlockParser -from libclinic.crenderdata import Include -from libclinic.codegen import BlockPrinter, Destination +from libclinic.codegen import BlockPrinter, Destination, Codegen from libclinic.parser import Parser, PythonParser from libclinic.dsl_parser import DSLParser if TYPE_CHECKING: @@ -102,8 +101,7 @@ def __init__( self.modules: ModuleDict = {} self.classes: ClassDict = {} self.functions: list[Function] = [] - # dict: include name => Include instance - self.includes: dict[str, Include] = {} + self.codegen = Codegen(self.limited_capi) self.line_prefix = self.line_suffix = '' @@ -132,7 +130,6 @@ def __init__( DestBufferList = list[DestBufferType] self.destination_buffers_stack: DestBufferList = [] - self.ifndef_symbols: set[str] = set() self.presets: dict[str, dict[Any, Any]] = {} preset = None @@ -159,24 +156,6 @@ def __init__( assert name in self.destination_buffers preset[name] = buffer - def add_include(self, name: str, reason: str, - *, condition: str | None = None) -> None: - try: - existing = self.includes[name] - except KeyError: - pass - else: - if existing.condition and not condition: - # If the previous include has a condition and the new one is - # unconditional, override the include. - pass - else: - # Already included, do nothing. Only mention a single reason, - # no need to list all of them. - return - - self.includes[name] = Include(name, reason, condition) - def add_destination( self, name: str, @@ -212,9 +191,7 @@ def parse(self, input: str) -> str: self.parsers[dsl_name] = parsers[dsl_name](self) parser = self.parsers[dsl_name] parser.parse(block) - printer.print_block(block, - limited_capi=self.limited_capi, - header_includes=self.includes) + printer.print_block(block) # these are destinations not buffers for name, destination in self.destinations.items(): @@ -229,9 +206,7 @@ def parse(self, input: str) -> str: block.input = "dump " + name + "\n" warn("Destination buffer " + repr(name) + " not empty at end of file, emptying.") printer.write("\n") - printer.print_block(block, - limited_capi=self.limited_capi, - header_includes=self.includes) + printer.print_block(block) continue if destination.type == 'file': @@ -257,9 +232,7 @@ def parse(self, input: str) -> str: block.input = 'preserve\n' printer_2 = BlockPrinter(self.language) printer_2.print_block(block, - core_includes=True, - limited_capi=self.limited_capi, - header_includes=self.includes) + header_includes=self.codegen.includes) libclinic.write_file(destination.filename, printer_2.f.getvalue()) continue diff --git a/Tools/clinic/libclinic/clanguage.py b/Tools/clinic/libclinic/clanguage.py index ed08d12d8bfb29..e68f84df730815 100644 --- a/Tools/clinic/libclinic/clanguage.py +++ b/Tools/clinic/libclinic/clanguage.py @@ -11,7 +11,7 @@ unspecified, fail, warn, Sentinels, VersionTuple) from libclinic.function import ( GETTER, SETTER, METHOD_INIT, METHOD_NEW) -from libclinic.crenderdata import CRenderData, TemplateDict +from libclinic.codegen import CRenderData, TemplateDict, Codegen from libclinic.language import Language from libclinic.function import ( Module, Class, Function, Parameter, @@ -26,8 +26,7 @@ def declare_parser( f: Function, *, hasformat: bool = False, - clinic: Clinic, - limited_capi: bool, + codegen: Codegen, ) -> str: """ Generates the code template for a static local PyArg_Parser variable, @@ -35,6 +34,7 @@ def declare_parser( kwtuple field is also statically initialized. Otherwise it is initialized at runtime. """ + limited_capi = codegen.limited_capi if hasformat: fname = '' format_ = '.format = "{format_units}:{name}",' @@ -80,8 +80,8 @@ def declare_parser( """ % num_keywords condition = '#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)' - clinic.add_include('pycore_gc.h', 'PyGC_Head', condition=condition) - clinic.add_include('pycore_runtime.h', '_Py_ID()', condition=condition) + codegen.add_include('pycore_gc.h', 'PyGC_Head', condition=condition) + codegen.add_include('pycore_runtime.h', '_Py_ID()', condition=condition) declarations += """ static const char * const _keywords[] = {{{keywords_c} NULL}}; @@ -317,14 +317,14 @@ def deprecate_keyword_use( self, func: Function, params: dict[int, Parameter], - argname_fmt: str | None, + argname_fmt: str | None = None, *, fastcall: bool, - limited_capi: bool, - clinic: Clinic, + codegen: Codegen, ) -> str: assert len(params) > 0 last_param = next(reversed(params.values())) + limited_capi = codegen.limited_capi # Format the deprecation message. containscheck = "" @@ -336,11 +336,11 @@ def deprecate_keyword_use( elif fastcall: conditions.append(f"nargs < {i+1} && PySequence_Contains(kwnames, &_Py_ID({p.name}))") containscheck = "PySequence_Contains" - clinic.add_include('pycore_runtime.h', '_Py_ID()') + codegen.add_include('pycore_runtime.h', '_Py_ID()') else: conditions.append(f"nargs < {i+1} && PyDict_Contains(kwargs, &_Py_ID({p.name}))") containscheck = "PyDict_Contains" - clinic.add_include('pycore_runtime.h', '_Py_ID()') + codegen.add_include('pycore_runtime.h', '_Py_ID()') else: conditions = [f"nargs < {i+1}"] condition = ") || (".join(conditions) @@ -399,7 +399,7 @@ def deprecate_keyword_use( def output_templates( self, f: Function, - clinic: Clinic + codegen: Codegen, ) -> dict[str, str]: parameters = list(f.parameters.values()) assert parameters @@ -412,7 +412,7 @@ def output_templates( converters = [p.converter for p in parameters] if f.critical_section: - clinic.add_include('pycore_critical_section.h', 'Py_BEGIN_CRITICAL_SECTION()') + codegen.add_include('pycore_critical_section.h', 'Py_BEGIN_CRITICAL_SECTION()') has_option_groups = parameters and (parameters[0].group or parameters[-1].group) simple_return = (f.return_converter.type == 'PyObject *' and not f.critical_section) @@ -517,7 +517,7 @@ def parser_body( parser_declarations=declarations) fastcall = not new_or_init - limited_capi = clinic.limited_capi + limited_capi = codegen.limited_capi if limited_capi and (pseudo_args or (any(p.is_optional() for p in parameters) and any(p.is_keyword_only() and not p.is_optional() for p in parameters)) or @@ -673,8 +673,8 @@ def parser_body( """, indent=4)) else: - clinic.add_include('pycore_modsupport.h', - '_PyArg_CheckPositional()') + codegen.add_include('pycore_modsupport.h', + '_PyArg_CheckPositional()') parser_code = [libclinic.normalize_snippet(f""" if (!_PyArg_CheckPositional("{{name}}", {nargs}, {min_pos}, {max_args})) {{{{ goto exit; @@ -735,8 +735,8 @@ def parser_body( if limited_capi: fastcall = False if fastcall: - clinic.add_include('pycore_modsupport.h', - '_PyArg_ParseStack()') + codegen.add_include('pycore_modsupport.h', + '_PyArg_ParseStack()') parser_code = [libclinic.normalize_snippet(""" if (!_PyArg_ParseStack(args, nargs, "{format_units}:{name}", {parse_arguments})) {{ @@ -773,8 +773,8 @@ def parser_body( fastcall = False else: if vararg == self.NO_VARARG: - clinic.add_include('pycore_modsupport.h', - '_PyArg_UnpackKeywords()') + codegen.add_include('pycore_modsupport.h', + '_PyArg_UnpackKeywords()') args_declaration = "_PyArg_UnpackKeywords", "%s, %s, %s" % ( min_pos, max_pos, @@ -782,8 +782,8 @@ def parser_body( ) nargs = "nargs" else: - clinic.add_include('pycore_modsupport.h', - '_PyArg_UnpackKeywordsWithVararg()') + codegen.add_include('pycore_modsupport.h', + '_PyArg_UnpackKeywordsWithVararg()') args_declaration = "_PyArg_UnpackKeywordsWithVararg", "%s, %s, %s, %s" % ( min_pos, max_pos, @@ -796,8 +796,7 @@ def parser_body( flags = "METH_FASTCALL|METH_KEYWORDS" parser_prototype = self.PARSER_PROTOTYPE_FASTCALL_KEYWORDS argname_fmt = 'args[%d]' - declarations = declare_parser(f, clinic=clinic, - limited_capi=clinic.limited_capi) + declarations = declare_parser(f, codegen=codegen) declarations += "\nPyObject *argsbuf[%s];" % len(converters) if has_optional_kw: 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( flags = "METH_VARARGS|METH_KEYWORDS" parser_prototype = self.PARSER_PROTOTYPE_KEYWORD argname_fmt = 'fastargs[%d]' - declarations = declare_parser(f, clinic=clinic, - limited_capi=clinic.limited_capi) + declarations = declare_parser(f, codegen=codegen) declarations += "\nPyObject *argsbuf[%s];" % len(converters) declarations += "\nPyObject * const *fastargs;" declarations += "\nPy_ssize_t nargs = PyTuple_GET_SIZE(args);" @@ -832,10 +830,10 @@ def parser_body( if parser_code is not None: if deprecated_keywords: - code = self.deprecate_keyword_use(f, deprecated_keywords, argname_fmt, - clinic=clinic, - fastcall=fastcall, - limited_capi=limited_capi) + code = self.deprecate_keyword_use(f, deprecated_keywords, + argname_fmt, + codegen=codegen, + fastcall=fastcall) parser_code.append(code) add_label: str | None = None @@ -903,9 +901,8 @@ def parser_body( for parameter in parameters: parameter.converter.use_converter() - declarations = declare_parser(f, clinic=clinic, - hasformat=True, - limited_capi=limited_capi) + declarations = declare_parser(f, codegen=codegen, + hasformat=True) if limited_capi: # positional-or-keyword arguments assert not fastcall @@ -921,8 +918,8 @@ def parser_body( declarations += "\nPy_ssize_t nargs = PyTuple_Size(args);" elif fastcall: - clinic.add_include('pycore_modsupport.h', - '_PyArg_ParseStackAndKeywords()') + codegen.add_include('pycore_modsupport.h', + '_PyArg_ParseStackAndKeywords()') parser_code = [libclinic.normalize_snippet(""" if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser{parse_arguments_comma} {parse_arguments})) {{ @@ -930,8 +927,8 @@ def parser_body( }} """, indent=4)] else: - clinic.add_include('pycore_modsupport.h', - '_PyArg_ParseTupleAndKeywordsFast()') + codegen.add_include('pycore_modsupport.h', + '_PyArg_ParseTupleAndKeywordsFast()') parser_code = [libclinic.normalize_snippet(""" if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser, {parse_arguments})) {{ @@ -941,10 +938,9 @@ def parser_body( if deprecated_positionals or deprecated_keywords: declarations += "\nPy_ssize_t nargs = PyTuple_GET_SIZE(args);" if deprecated_keywords: - code = self.deprecate_keyword_use(f, deprecated_keywords, None, - clinic=clinic, - fastcall=fastcall, - limited_capi=limited_capi) + code = self.deprecate_keyword_use(f, deprecated_keywords, + codegen=codegen, + fastcall=fastcall) parser_code.append(code) if deprecated_positionals: @@ -961,8 +957,8 @@ def parser_body( # called above. for converter in converters: for include in converter.includes: - clinic.add_include(include.filename, include.reason, - condition=include.condition) + codegen.add_include(include.filename, include.reason, + condition=include.condition) if new_or_init: methoddef_define = '' @@ -984,16 +980,16 @@ def parser_body( if not parses_keywords: declarations = '{base_type_ptr}' - clinic.add_include('pycore_modsupport.h', - '_PyArg_NoKeywords()') + codegen.add_include('pycore_modsupport.h', + '_PyArg_NoKeywords()') fields.insert(0, libclinic.normalize_snippet(""" if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) {{ goto exit; }} """, indent=4)) if not parses_positional: - clinic.add_include('pycore_modsupport.h', - '_PyArg_NoPositional()') + codegen.add_include('pycore_modsupport.h', + '_PyArg_NoPositional()') fields.insert(0, libclinic.normalize_snippet(""" if ({self_type_check}!_PyArg_NoPositional("{name}", args)) {{ goto exit; @@ -1030,8 +1026,7 @@ def parser_body( cpp_if = "#if " + conditional cpp_endif = "#endif /* " + conditional + " */" - if methoddef_define and f.full_name not in clinic.ifndef_symbols: - clinic.ifndef_symbols.add(f.full_name) + if methoddef_define and codegen.add_ifndef_symbol(f.full_name): methoddef_ifndef = self.METHODDEF_PROTOTYPE_IFNDEF # add ';' to the end of parser_prototype and impl_prototype @@ -1190,16 +1185,17 @@ def render_function( clinic: Clinic, f: Function | None ) -> str: - if f is None or clinic is None: + if f is None: return "" + codegen = clinic.codegen data = CRenderData() assert f.parameters, "We should always have a 'self' at this point!" parameters = f.render_parameters converters = [p.converter for p in parameters] - templates = self.output_templates(f, clinic) + templates = self.output_templates(f, codegen) f_self = parameters[0] selfless = parameters[1:] @@ -1323,7 +1319,7 @@ def render_function( if has_option_groups: self.render_option_group_parsing(f, template_dict, - limited_capi=clinic.limited_capi) + limited_capi=codegen.limited_capi) # buffers, not destination for name, destination in clinic.destination_buffers.items(): diff --git a/Tools/clinic/libclinic/codegen.py b/Tools/clinic/libclinic/codegen.py index ad08e22e2e1c2c..2e46ed83049474 100644 --- a/Tools/clinic/libclinic/codegen.py +++ b/Tools/clinic/libclinic/codegen.py @@ -6,13 +6,92 @@ import libclinic from libclinic import fail -from libclinic.crenderdata import Include from libclinic.language import Language from libclinic.block_parser import Block if TYPE_CHECKING: from libclinic.app import Clinic +TemplateDict = dict[str, str] + + +class CRenderData: + def __init__(self) -> None: + + # The C statements to declare variables. + # Should be full lines with \n eol characters. + self.declarations: list[str] = [] + + # The C statements required to initialize the variables before the parse call. + # Should be full lines with \n eol characters. + self.initializers: list[str] = [] + + # The C statements needed to dynamically modify the values + # parsed by the parse call, before calling the impl. + self.modifications: list[str] = [] + + # The entries for the "keywords" array for PyArg_ParseTuple. + # Should be individual strings representing the names. + self.keywords: list[str] = [] + + # The "format units" for PyArg_ParseTuple. + # Should be individual strings that will get + self.format_units: list[str] = [] + + # The varargs arguments for PyArg_ParseTuple. + self.parse_arguments: list[str] = [] + + # The parameter declarations for the impl function. + self.impl_parameters: list[str] = [] + + # The arguments to the impl function at the time it's called. + self.impl_arguments: list[str] = [] + + # For return converters: the name of the variable that + # should receive the value returned by the impl. + self.return_value = "return_value" + + # For return converters: the code to convert the return + # value from the parse function. This is also where + # you should check the _return_value for errors, and + # "goto exit" if there are any. + self.return_conversion: list[str] = [] + self.converter_retval = "_return_value" + + # The C statements required to do some operations + # after the end of parsing but before cleaning up. + # These operations may be, for example, memory deallocations which + # can only be done without any error happening during argument parsing. + self.post_parsing: list[str] = [] + + # The C statements required to clean up after the impl call. + self.cleanup: list[str] = [] + + # The C statements to generate critical sections (per-object locking). + self.lock: list[str] = [] + self.unlock: list[str] = [] + + +@dc.dataclass(slots=True, frozen=True) +class Include: + """ + An include like: #include "pycore_long.h" // _Py_ID() + """ + # Example: "pycore_long.h". + filename: str + + # Example: "_Py_ID()". + reason: str + + # None means unconditional include. + # Example: "#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)". + condition: str | None + + def sort_key(self) -> tuple[str, str]: + # order: '#if' comes before 'NO_CONDITION' + return (self.condition or 'NO_CONDITION', self.filename) + + @dc.dataclass(slots=True) class BlockPrinter: language: Language @@ -25,9 +104,7 @@ def print_block( self, block: Block, *, - core_includes: bool = False, - limited_capi: bool, - header_includes: dict[str, Include], + header_includes: dict[str, Include] | None = None, ) -> None: input = block.input output = block.output @@ -56,7 +133,7 @@ def print_block( write("\n") output = '' - if core_includes and header_includes: + if header_includes: # Emit optional "#include" directives for C headers output += '\n' @@ -188,3 +265,35 @@ def dump(self) -> str: DestinationDict = dict[str, Destination] + + +class Codegen: + def __init__(self, limited_capi: bool) -> None: + self.limited_capi = limited_capi + self.ifndef_symbols: set[str] = set() + # dict: include name => Include instance + self.includes: dict[str, Include] = {} + + def add_ifndef_symbol(self, name: str) -> bool: + if name in self.ifndef_symbols: + return False + self.ifndef_symbols.add(name) + return True + + def add_include(self, name: str, reason: str, + *, condition: str | None = None) -> None: + try: + existing = self.includes[name] + except KeyError: + pass + else: + if existing.condition and not condition: + # If the previous include has a condition and the new one is + # unconditional, override the include. + pass + else: + # Already included, do nothing. Only mention a single reason, + # no need to list all of them. + return + + self.includes[name] = Include(name, reason, condition) diff --git a/Tools/clinic/libclinic/converter.py b/Tools/clinic/libclinic/converter.py index ac78be3f7958da..b17b07d4ddae7b 100644 --- a/Tools/clinic/libclinic/converter.py +++ b/Tools/clinic/libclinic/converter.py @@ -7,7 +7,7 @@ import libclinic from libclinic import fail from libclinic import Sentinels, unspecified, unknown -from libclinic.crenderdata import CRenderData, Include, TemplateDict +from libclinic.codegen import CRenderData, Include, TemplateDict from libclinic.function import Function, Parameter diff --git a/Tools/clinic/libclinic/converters.py b/Tools/clinic/libclinic/converters.py index 7fc16f17450aaa..0778961f5b5875 100644 --- a/Tools/clinic/libclinic/converters.py +++ b/Tools/clinic/libclinic/converters.py @@ -9,7 +9,7 @@ Function, Parameter, CALLABLE, STATIC_METHOD, CLASS_METHOD, METHOD_INIT, METHOD_NEW, GETTER, SETTER) -from libclinic.crenderdata import CRenderData, TemplateDict +from libclinic.codegen import CRenderData, TemplateDict from libclinic.converter import ( CConverter, legacy_converters, add_legacy_c_converter) diff --git a/Tools/clinic/libclinic/crenderdata.py b/Tools/clinic/libclinic/crenderdata.py deleted file mode 100644 index 58976b8185ebae..00000000000000 --- a/Tools/clinic/libclinic/crenderdata.py +++ /dev/null @@ -1,81 +0,0 @@ -import dataclasses as dc - - -TemplateDict = dict[str, str] - - -class CRenderData: - def __init__(self) -> None: - - # The C statements to declare variables. - # Should be full lines with \n eol characters. - self.declarations: list[str] = [] - - # The C statements required to initialize the variables before the parse call. - # Should be full lines with \n eol characters. - self.initializers: list[str] = [] - - # The C statements needed to dynamically modify the values - # parsed by the parse call, before calling the impl. - self.modifications: list[str] = [] - - # The entries for the "keywords" array for PyArg_ParseTuple. - # Should be individual strings representing the names. - self.keywords: list[str] = [] - - # The "format units" for PyArg_ParseTuple. - # Should be individual strings that will get - self.format_units: list[str] = [] - - # The varargs arguments for PyArg_ParseTuple. - self.parse_arguments: list[str] = [] - - # The parameter declarations for the impl function. - self.impl_parameters: list[str] = [] - - # The arguments to the impl function at the time it's called. - self.impl_arguments: list[str] = [] - - # For return converters: the name of the variable that - # should receive the value returned by the impl. - self.return_value = "return_value" - - # For return converters: the code to convert the return - # value from the parse function. This is also where - # you should check the _return_value for errors, and - # "goto exit" if there are any. - self.return_conversion: list[str] = [] - self.converter_retval = "_return_value" - - # The C statements required to do some operations - # after the end of parsing but before cleaning up. - # These operations may be, for example, memory deallocations which - # can only be done without any error happening during argument parsing. - self.post_parsing: list[str] = [] - - # The C statements required to clean up after the impl call. - self.cleanup: list[str] = [] - - # The C statements to generate critical sections (per-object locking). - self.lock: list[str] = [] - self.unlock: list[str] = [] - - -@dc.dataclass(slots=True, frozen=True) -class Include: - """ - An include like: #include "pycore_long.h" // _Py_ID() - """ - # Example: "pycore_long.h". - filename: str - - # Example: "_Py_ID()". - reason: str - - # None means unconditional include. - # Example: "#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)". - condition: str | None - - def sort_key(self) -> tuple[str, str]: - # order: '#if' comes before 'NO_CONDITION' - return (self.condition or 'NO_CONDITION', self.filename) diff --git a/Tools/clinic/libclinic/return_converters.py b/Tools/clinic/libclinic/return_converters.py index 7bdd257cfa3443..b41e053bae5f3a 100644 --- a/Tools/clinic/libclinic/return_converters.py +++ b/Tools/clinic/libclinic/return_converters.py @@ -1,6 +1,6 @@ import sys from collections.abc import Callable -from libclinic.crenderdata import CRenderData +from libclinic.codegen import CRenderData from libclinic.function import Function from typing import Any From a7479ba2e86740f54fc95c61611c5b3072d66d5d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 8 Apr 2024 10:57:47 +0200 Subject: [PATCH 2/3] Update test_clinic to fix the Lint CI --- Lib/test/test_clinic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 8380a96520dbe2..e26a40c7b196dd 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -822,7 +822,6 @@ def _test(self, input, output): blocks = list(BlockParser(input, language)) writer = BlockPrinter(language) - c = _make_clinic() for block in blocks: writer.print_block(block) output = writer.f.getvalue() From e446461d607e71ce53eb83cf5258879ffb26c411 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 9 Apr 2024 21:08:32 +0200 Subject: [PATCH 3/3] Add get_includes() methods * Make Codegen.ifndef_symbols private. * Make Codegen.includes private. * Make CConverter.includes private. --- Tools/clinic/libclinic/app.py | 5 +++-- Tools/clinic/libclinic/clanguage.py | 2 +- Tools/clinic/libclinic/codegen.py | 21 ++++++++++++--------- Tools/clinic/libclinic/converter.py | 7 +++++-- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Tools/clinic/libclinic/app.py b/Tools/clinic/libclinic/app.py index cd06ba51116594..62e9892eb95a2b 100644 --- a/Tools/clinic/libclinic/app.py +++ b/Tools/clinic/libclinic/app.py @@ -230,9 +230,10 @@ def parse(self, input: str) -> str: pass block.input = 'preserve\n' + includes = self.codegen.get_includes() + printer_2 = BlockPrinter(self.language) - printer_2.print_block(block, - header_includes=self.codegen.includes) + printer_2.print_block(block, header_includes=includes) libclinic.write_file(destination.filename, printer_2.f.getvalue()) continue diff --git a/Tools/clinic/libclinic/clanguage.py b/Tools/clinic/libclinic/clanguage.py index e68f84df730815..5d1a273312dec5 100644 --- a/Tools/clinic/libclinic/clanguage.py +++ b/Tools/clinic/libclinic/clanguage.py @@ -956,7 +956,7 @@ def parser_body( # Copy includes from parameters to Clinic after parse_arg() has been # called above. for converter in converters: - for include in converter.includes: + for include in converter.get_includes(): codegen.add_include(include.filename, include.reason, condition=include.condition) diff --git a/Tools/clinic/libclinic/codegen.py b/Tools/clinic/libclinic/codegen.py index 2e46ed83049474..83f345124a67cb 100644 --- a/Tools/clinic/libclinic/codegen.py +++ b/Tools/clinic/libclinic/codegen.py @@ -104,7 +104,7 @@ def print_block( self, block: Block, *, - header_includes: dict[str, Include] | None = None, + header_includes: list[Include] | None = None, ) -> None: input = block.input output = block.output @@ -138,8 +138,7 @@ def print_block( output += '\n' current_condition: str | None = None - includes = sorted(header_includes.values(), key=Include.sort_key) - for include in includes: + for include in header_includes: if include.condition != current_condition: if current_condition: output += '#endif\n' @@ -270,20 +269,20 @@ def dump(self) -> str: class Codegen: def __init__(self, limited_capi: bool) -> None: self.limited_capi = limited_capi - self.ifndef_symbols: set[str] = set() + self._ifndef_symbols: set[str] = set() # dict: include name => Include instance - self.includes: dict[str, Include] = {} + self._includes: dict[str, Include] = {} def add_ifndef_symbol(self, name: str) -> bool: - if name in self.ifndef_symbols: + if name in self._ifndef_symbols: return False - self.ifndef_symbols.add(name) + self._ifndef_symbols.add(name) return True def add_include(self, name: str, reason: str, *, condition: str | None = None) -> None: try: - existing = self.includes[name] + existing = self._includes[name] except KeyError: pass else: @@ -296,4 +295,8 @@ def add_include(self, name: str, reason: str, # no need to list all of them. return - self.includes[name] = Include(name, reason, condition) + self._includes[name] = Include(name, reason, condition) + + def get_includes(self) -> list[Include]: + return sorted(self._includes.values(), + key=Include.sort_key) diff --git a/Tools/clinic/libclinic/converter.py b/Tools/clinic/libclinic/converter.py index b17b07d4ddae7b..86853bb4fba253 100644 --- a/Tools/clinic/libclinic/converter.py +++ b/Tools/clinic/libclinic/converter.py @@ -180,7 +180,7 @@ def __init__(self, self.name = libclinic.ensure_legal_c_identifier(name) self.py_name = py_name self.unused = unused - self.includes: list[Include] = [] + self._includes: list[Include] = [] if default is not unspecified: if (self.default_type @@ -513,7 +513,10 @@ def parser_name(self) -> str: def add_include(self, name: str, reason: str, *, condition: str | None = None) -> None: include = Include(name, reason, condition) - self.includes.append(include) + self._includes.append(include) + + def get_includes(self) -> list[Include]: + return self._includes ConverterType = Callable[..., CConverter] pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy