Skip to content

Commit 73d33c1

Browse files
authored
gh-107603: Argument Clinic can emit includes (#108486)
* Add Clinic.add_include() method * Add CConverter.include and CConverter.add_include() * Printer.print_block() gets a second parameter: clinic. * Remove duplicated declaration of "clinic" global variable.
1 parent 1dd9510 commit 73d33c1

File tree

1 file changed

+41
-17
lines changed

1 file changed

+41
-17
lines changed

Tools/clinic/clinic.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1075,6 +1075,11 @@ def output_templates(
10751075
del parameters[0]
10761076
converters = [p.converter for p in parameters]
10771077

1078+
# Copy includes from parameters to Clinic
1079+
for converter in converters:
1080+
if converter.include:
1081+
clinic.add_include(*converter.include)
1082+
10781083
has_option_groups = parameters and (parameters[0].group or parameters[-1].group)
10791084
default_return_converter = f.return_converter.type == 'PyObject *'
10801085
new_or_init = f.kind.new_or_init
@@ -2126,8 +2131,8 @@ def print_block(
21262131
self,
21272132
block: Block,
21282133
*,
2134+
clinic: Clinic,
21292135
core_includes: bool = False,
2130-
clinic: Clinic | None = None,
21312136
) -> None:
21322137
input = block.input
21332138
output = block.output
@@ -2156,18 +2161,22 @@ def print_block(
21562161
write("\n")
21572162

21582163
output = ''
2159-
if clinic:
2160-
limited_capi = clinic.limited_capi
2161-
else:
2162-
limited_capi = DEFAULT_LIMITED_CAPI
2163-
if core_includes and not limited_capi:
2164-
output += textwrap.dedent("""
2165-
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
2166-
# include "pycore_gc.h" // PyGC_Head
2167-
# include "pycore_runtime.h" // _Py_ID()
2168-
#endif
2169-
2170-
""")
2164+
if core_includes:
2165+
if not clinic.limited_capi:
2166+
output += textwrap.dedent("""
2167+
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
2168+
# include "pycore_gc.h" // PyGC_Head
2169+
# include "pycore_runtime.h" // _Py_ID()
2170+
#endif
2171+
2172+
""")
2173+
2174+
if clinic is not None:
2175+
# Emit optional includes
2176+
for include, reason in sorted(clinic.includes.items()):
2177+
line = f'#include "{include}"'
2178+
line = line.ljust(35) + f'// {reason}\n'
2179+
output += line
21712180

21722181
input = ''.join(block.input)
21732182
output += ''.join(block.output)
@@ -2311,7 +2320,7 @@ def __init__(self, clinic: Clinic) -> None: ...
23112320
def parse(self, block: Block) -> None: ...
23122321

23132322

2314-
clinic = None
2323+
clinic: Clinic | None = None
23152324
class Clinic:
23162325

23172326
presets_text = """
@@ -2379,6 +2388,9 @@ def __init__(
23792388
self.modules: ModuleDict = {}
23802389
self.classes: ClassDict = {}
23812390
self.functions: list[Function] = []
2391+
# dict: include name => reason
2392+
# Example: 'pycore_long.h' => '_PyLong_UnsignedShort_Converter()'
2393+
self.includes: dict[str, str] = {}
23822394

23832395
self.line_prefix = self.line_suffix = ''
23842396

@@ -2437,6 +2449,12 @@ def __init__(
24372449
global clinic
24382450
clinic = self
24392451

2452+
def add_include(self, name: str, reason: str) -> None:
2453+
if name in self.includes:
2454+
# Mention a single reason is enough, no need to list all of them
2455+
return
2456+
self.includes[name] = reason
2457+
24402458
def add_destination(
24412459
self,
24422460
name: str,
@@ -3066,6 +3084,10 @@ class CConverter(metaclass=CConverterAutoRegister):
30663084
# Only set by self_converter.
30673085
signature_name: str | None = None
30683086

3087+
# Optional (name, reason) include which generate a line like:
3088+
# "#include "name" // reason"
3089+
include: tuple[str, str] | None = None
3090+
30693091
# keep in sync with self_converter.__init__!
30703092
def __init__(self,
30713093
# Positional args:
@@ -3370,6 +3392,11 @@ def parser_name(self) -> str:
33703392
else:
33713393
return self.name
33723394

3395+
def add_include(self, name: str, reason: str) -> None:
3396+
if self.include is not None:
3397+
raise ValueError("a converter only supports a single include")
3398+
self.include = (name, reason)
3399+
33733400
type_checks = {
33743401
'&PyLong_Type': ('PyLong_Check', 'int'),
33753402
'&PyTuple_Type': ('PyTuple_Check', 'tuple'),
@@ -5989,9 +6016,6 @@ def do_post_block_processing_cleanup(self, lineno: int) -> None:
59896016
}
59906017

59916018

5992-
clinic = None
5993-
5994-
59956019
def create_cli() -> argparse.ArgumentParser:
59966020
cmdline = argparse.ArgumentParser(
59976021
prog="clinic.py",

0 commit comments

Comments
 (0)