-
-
Notifications
You must be signed in to change notification settings - Fork 31.9k
gh-107603: Argument Clinic can emit includes #108486
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1075,6 +1075,11 @@ def output_templates( | |
del parameters[0] | ||
converters = [p.converter for p in parameters] | ||
|
||
# Copy includes from parameters to Clinic | ||
for converter in converters: | ||
if converter.include: | ||
clinic.add_include(*converter.include) | ||
|
||
has_option_groups = parameters and (parameters[0].group or parameters[-1].group) | ||
default_return_converter = f.return_converter.type == 'PyObject *' | ||
new_or_init = f.kind.new_or_init | ||
|
@@ -2126,8 +2131,8 @@ def print_block( | |
self, | ||
block: Block, | ||
*, | ||
clinic: Clinic, | ||
core_includes: bool = False, | ||
clinic: Clinic | None = None, | ||
) -> None: | ||
input = block.input | ||
output = block.output | ||
|
@@ -2156,18 +2161,22 @@ def print_block( | |
write("\n") | ||
|
||
output = '' | ||
if clinic: | ||
limited_capi = clinic.limited_capi | ||
else: | ||
limited_capi = DEFAULT_LIMITED_CAPI | ||
if core_includes and not limited_capi: | ||
output += textwrap.dedent(""" | ||
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) | ||
# include "pycore_gc.h" // PyGC_Head | ||
# include "pycore_runtime.h" // _Py_ID() | ||
#endif | ||
|
||
""") | ||
if core_includes: | ||
if not clinic.limited_capi: | ||
output += textwrap.dedent(""" | ||
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE) | ||
# include "pycore_gc.h" // PyGC_Head | ||
# include "pycore_runtime.h" // _Py_ID() | ||
#endif | ||
|
||
""") | ||
|
||
if clinic is not None: | ||
# Emit optional includes | ||
for include, reason in sorted(clinic.includes.items()): | ||
line = f'#include "{include}"' | ||
line = line.ljust(35) + f'// {reason}\n' | ||
output += line | ||
|
||
input = ''.join(block.input) | ||
output += ''.join(block.output) | ||
|
@@ -2311,7 +2320,7 @@ def __init__(self, clinic: Clinic) -> None: ... | |
def parse(self, block: Block) -> None: ... | ||
|
||
|
||
clinic = None | ||
clinic: Clinic | None = None | ||
class Clinic: | ||
|
||
presets_text = """ | ||
|
@@ -2379,6 +2388,9 @@ def __init__( | |
self.modules: ModuleDict = {} | ||
self.classes: ClassDict = {} | ||
self.functions: list[Function] = [] | ||
# dict: include name => reason | ||
# Example: 'pycore_long.h' => '_PyLong_UnsignedShort_Converter()' | ||
self.includes: dict[str, str] = {} | ||
|
||
self.line_prefix = self.line_suffix = '' | ||
|
||
|
@@ -2437,6 +2449,12 @@ def __init__( | |
global clinic | ||
clinic = self | ||
|
||
def add_include(self, name: str, reason: str) -> None: | ||
if name in self.includes: | ||
# Mention a single reason is enough, no need to list all of them | ||
Comment on lines
+2453
to
+2454
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For generated code we could list all the known reasons? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see the point. It would generate giant lines for little value. For #include, I like to show at least one imported symbol to explain why the include was needed. In manually written code, I use it to remove unused imports time to time. IMO here it's interesting to repeat this habit to explain why an include is needed. |
||
return | ||
self.includes[name] = reason | ||
|
||
def add_destination( | ||
self, | ||
name: str, | ||
|
@@ -3066,6 +3084,10 @@ class CConverter(metaclass=CConverterAutoRegister): | |
# Only set by self_converter. | ||
signature_name: str | None = None | ||
|
||
# Optional (name, reason) include which generate a line like: | ||
# "#include "name" // reason" | ||
include: tuple[str, str] | None = None | ||
|
||
# keep in sync with self_converter.__init__! | ||
def __init__(self, | ||
# Positional args: | ||
|
@@ -3370,6 +3392,11 @@ def parser_name(self) -> str: | |
else: | ||
return self.name | ||
|
||
def add_include(self, name: str, reason: str) -> None: | ||
if self.include is not None: | ||
raise ValueError("a converter only supports a single include") | ||
self.include = (name, reason) | ||
|
||
type_checks = { | ||
'&PyLong_Type': ('PyLong_Check', 'int'), | ||
'&PyTuple_Type': ('PyTuple_Check', 'tuple'), | ||
|
@@ -5989,9 +6016,6 @@ def do_post_block_processing_cleanup(self, lineno: int) -> None: | |
} | ||
|
||
|
||
clinic = None | ||
|
||
|
||
def create_cli() -> argparse.ArgumentParser: | ||
cmdline = argparse.ArgumentParser( | ||
prog="clinic.py", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add a comment to explain the indent amount?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
35 is my arbitrary convention that I'm using in Python. I prefer to keep ljust(35) since it reminds me the 35th column :-)