From 6dc7246c61f8fb9cdebc03f91ae12bf50d8bf1e4 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Wed, 20 Dec 2023 12:27:29 +0100 Subject: [PATCH 01/10] gh-113317: Clean up Argument Clinic global namespace Split up clinic.py by establishing libclinic as a support package for Argument Clinic. Get rid of clinic.py globals by either making them class members, or by putting them into libclinic. - Move INCLUDE_COMMENT_COLUMN to BlockPrinter - Move NO_VARARG to CLanguage - Move formatting helpers to libclinic - Move some constants to libclinic (and annotate them as Final) --- Lib/test/test_clinic.py | 29 ++--- Tools/clinic/clinic.py | 160 +++++++-------------------- Tools/clinic/libclinic/__init__.py | 38 +++++++ Tools/clinic/libclinic/formatting.py | 83 ++++++++++++++ 4 files changed, 175 insertions(+), 135 deletions(-) create mode 100644 Tools/clinic/libclinic/__init__.py create mode 100644 Tools/clinic/libclinic/formatting.py diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 199c9fec80205a..cfb84bcaa3f3b7 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -16,6 +16,7 @@ test_tools.skip_if_missing('clinic') with test_tools.imports_under_tool('clinic'): + import libclinic import clinic from clinic import DSLParser @@ -3761,19 +3762,19 @@ def test_normalize_snippet(self): actual = clinic.normalize_snippet(snippet, indent=indent) self.assertEqual(actual, expected) - def test_quoted_for_c_string(self): + def test_escaped_docstring(self): dataset = ( # input, expected - (r"abc", r"abc"), - (r"\abc", r"\\abc"), - (r"\a\bc", r"\\a\\bc"), - (r"\a\\bc", r"\\a\\\\bc"), - (r'"abc"', r'\"abc\"'), - (r"'a'", r"\'a\'"), + (r"abc", r'"abc"'), + (r"\abc", r'"\\abc"'), + (r"\a\bc", r'"\\a\\bc"'), + (r"\a\\bc", r'"\\a\\\\bc"'), + (r'"abc"', r'"\"abc\""'), + (r"'a'", r'"\'a\'"'), ) for line, expected in dataset: with self.subTest(line=line, expected=expected): - out = clinic.quoted_for_c_string(line) + out = libclinic.docstring_for_c_string(line) self.assertEqual(out, expected) def test_format_escape(self): @@ -3784,7 +3785,7 @@ def test_format_escape(self): def test_indent_all_lines(self): # Blank lines are expected to be unchanged. - self.assertEqual(clinic.indent_all_lines("", prefix="bar"), "") + self.assertEqual(libclinic.indent_all_lines("", prefix="bar"), "") lines = ( "one\n" @@ -3794,7 +3795,7 @@ def test_indent_all_lines(self): "barone\n" "bartwo" ) - out = clinic.indent_all_lines(lines, prefix="bar") + out = libclinic.indent_all_lines(lines, prefix="bar") self.assertEqual(out, expected) # If last line is empty, expect it to be unchanged. @@ -3810,12 +3811,12 @@ def test_indent_all_lines(self): "bartwo\n" "" ) - out = clinic.indent_all_lines(lines, prefix="bar") + out = libclinic.indent_all_lines(lines, prefix="bar") self.assertEqual(out, expected) def test_suffix_all_lines(self): # Blank lines are expected to be unchanged. - self.assertEqual(clinic.suffix_all_lines("", suffix="foo"), "") + self.assertEqual(libclinic.suffix_all_lines("", suffix="foo"), "") lines = ( "one\n" @@ -3825,7 +3826,7 @@ def test_suffix_all_lines(self): "onefoo\n" "twofoo" ) - out = clinic.suffix_all_lines(lines, suffix="foo") + out = libclinic.suffix_all_lines(lines, suffix="foo") self.assertEqual(out, expected) # If last line is empty, expect it to be unchanged. @@ -3841,7 +3842,7 @@ def test_suffix_all_lines(self): "twofoo\n" "" ) - out = clinic.suffix_all_lines(lines, suffix="foo") + out = libclinic.suffix_all_lines(lines, suffix="foo") self.assertEqual(out, expected) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 92092e97000ad0..c9034d65a258aa 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -50,6 +50,11 @@ overload, ) + +# Local imports. +import libclinic + + # TODO: # # soon: @@ -61,23 +66,6 @@ # and keyword-only # -NO_VARARG = "PY_SSIZE_T_MAX" -CLINIC_PREFIX = "__clinic_" -CLINIC_PREFIXED_ARGS = { - "_keywords", - "_parser", - "args", - "argsbuf", - "fastargs", - "kwargs", - "kwnames", - "nargs", - "noptargs", - "return_value", -} - -# '#include "header.h" // reason': column of '//' comment -INCLUDE_COMMENT_COLUMN = 35 # match '#define Py_LIMITED_API' LIMITED_CAPI_REGEX = re.compile(r'#define +Py_LIMITED_API') @@ -103,8 +91,6 @@ def __repr__(self) -> str: NULL = Null() -sig_end_marker = '--' - TemplateDict = dict[str, str] @@ -179,33 +165,6 @@ def fail( warn_or_fail(*args, filename=filename, line_number=line_number, fail=True) -def quoted_for_c_string(s: str) -> str: - for old, new in ( - ('\\', '\\\\'), # must be first! - ('"', '\\"'), - ("'", "\\'"), - ): - s = s.replace(old, new) - return s - -def c_repr(s: str) -> str: - return '"' + s + '"' - - -def wrapped_c_string_literal( - text: str, - *, - width: int = 72, - suffix: str = '', - initial_indent: int = 0, - subsequent_indent: int = 4 -) -> str: - wrapped = textwrap.wrap(text, width=width, replace_whitespace=False, - drop_whitespace=False, break_on_hyphens=False) - separator = '"' + suffix + '\n' + subsequent_indent * ' ' + '"' - return initial_indent * ' ' + '"' + separator.join(wrapped) + '"' - - is_legal_c_identifier = re.compile('^[A-Za-z_][A-Za-z0-9_]*$').match def is_legal_py_identifier(s: str) -> bool: @@ -251,7 +210,6 @@ def linear_format(s: str, **kwargs: str) -> str: by the indent of the source line. * A newline will be added to the end. """ - lines = [] for line in s.split('\n'): indent, curly, trailing = line.partition('{') @@ -281,34 +239,6 @@ def linear_format(s: str, **kwargs: str) -> str: return "".join(lines[:-1]) -def _add_prefix_and_suffix(s: str, prefix: str = "", suffix: str = "") -> str: - """ - Return 's', with 'prefix' prepended and 'suffix' appended to all lines. - - If the last line is empty, it remains unchanged. - If s is blank, returns s unchanged. - - (textwrap.indent only adds to non-blank lines.) - """ - *split, last = s.split("\n") - lines = [prefix + line + suffix + "\n" for line in split] - if last: - lines.append(prefix + last + suffix) - return "".join(lines) - -def indent_all_lines(s: str, prefix: str) -> str: - return _add_prefix_and_suffix(s, prefix=prefix) - -def suffix_all_lines(s: str, suffix: str) -> str: - return _add_prefix_and_suffix(s, suffix=suffix) - - -def pprint_words(items: list[str]) -> str: - if len(items) <= 2: - return " and ".join(items) - else: - return ", ".join(items[:-1]) + " and " + items[-1] - class CRenderData: def __init__(self) -> None: @@ -710,6 +640,8 @@ class CLanguage(Language): stop_line = "[{dsl_name} start generated code]*/" checksum_line = "/*[{dsl_name} end generated code: {arguments}]*/" + NO_VARARG: Final[str] = "PY_SSIZE_T_MAX" + PARSER_PROTOTYPE_KEYWORD: Final[str] = normalize_snippet(""" static PyObject * {c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs) @@ -863,7 +795,7 @@ def compiler_deprecated_warning( code = self.COMPILER_DEPRECATION_WARNING_PROTOTYPE.format( major=minversion[0], minor=minversion[1], - message=c_repr(message), + message=libclinic.c_repr(message), ) return normalize_snippet(code) @@ -894,7 +826,7 @@ def deprecate_positional_use( params.values(), key=attrgetter("deprecated_positional") ): names = [repr(p.name) for p in group] - pstr = pprint_words(names) + pstr = libclinic.pprint_words(names) if len(names) == 1: message += ( f" Parameter {pstr} will become a keyword-only parameter " @@ -913,8 +845,8 @@ def deprecate_positional_use( code = self.DEPRECATION_WARNING_PROTOTYPE.format( condition=condition, errcheck="", - message=wrapped_c_string_literal(message, width=64, - subsequent_indent=20), + message=libclinic.wrapped_c_string_literal(message, width=64, + subsequent_indent=20), ) return normalize_snippet(code, indent=4) @@ -963,7 +895,7 @@ def deprecate_keyword_use( else: condition = f"kwargs && PyDict_GET_SIZE(kwargs) && {condition}" names = [repr(p.name) for p in params.values()] - pstr = pprint_words(names) + pstr = libclinic.pprint_words(names) pl = 's' if len(params) != 1 else '' message = ( f"Passing keyword argument{pl} {pstr} to " @@ -974,7 +906,7 @@ def deprecate_keyword_use( params.values(), key=attrgetter("deprecated_keyword") ): names = [repr(p.name) for p in group] - pstr = pprint_words(names) + pstr = libclinic.pprint_words(names) pl = 's' if len(names) != 1 else '' message += ( f" Parameter{pl} {pstr} will become positional-only " @@ -996,31 +928,11 @@ def deprecate_keyword_use( code = self.DEPRECATION_WARNING_PROTOTYPE.format( condition=condition, errcheck=errcheck, - message=wrapped_c_string_literal(message, width=64, - subsequent_indent=20), + message=libclinic.wrapped_c_string_literal(message, width=64, + subsequent_indent=20), ) return normalize_snippet(code, indent=4) - def docstring_for_c_string( - self, - f: Function - ) -> str: - lines = [] - # turn docstring into a properly quoted C string - for line in f.docstring.split('\n'): - lines.append('"') - lines.append(quoted_for_c_string(line)) - lines.append('\\n"\n') - - if lines[-2] == sig_end_marker: - # If we only have a signature, add the blank line that the - # __text_signature__ getter expects to be there. - lines.append('"\\n"') - else: - lines.pop() - lines.append('"') - return ''.join(lines) - def output_templates( self, f: Function, @@ -1049,7 +961,7 @@ def output_templates( and not f.critical_section) new_or_init = f.kind.new_or_init - vararg: int | str = NO_VARARG + vararg: int | str = self.NO_VARARG pos_only = min_pos = max_pos = min_kw_only = pseudo_args = 0 for i, p in enumerate(parameters, 1): if p.is_keyword_only(): @@ -1057,12 +969,12 @@ def output_templates( if not p.is_optional(): min_kw_only = i - max_pos elif p.is_vararg(): - if vararg != NO_VARARG: + if vararg != self.NO_VARARG: fail("Too many var args") pseudo_args += 1 vararg = i - 1 else: - if vararg == NO_VARARG: + if vararg == self.NO_VARARG: max_pos = i if p.is_positional_only(): pos_only = i @@ -1271,7 +1183,7 @@ def parser_body( argname_fmt = 'PyTuple_GET_ITEM(args, %d)' left_args = f"{nargs} - {max_pos}" - max_args = NO_VARARG if (vararg != NO_VARARG) else max_pos + max_args = self.NO_VARARG if (vararg != self.NO_VARARG) else max_pos if limited_capi: parser_code = [] if nargs != 'nargs': @@ -1296,7 +1208,7 @@ def parser_body( }}}} """, indent=4)) - if max_args != NO_VARARG: + if max_args != self.NO_VARARG: pl = '' if max_args == 1 else 's' parser_code.append(normalize_snippet(f""" if ({nargs} > {max_args}) {{{{ @@ -1393,13 +1305,17 @@ def parser_body( if p.deprecated_keyword: deprecated_keywords[i] = p - has_optional_kw = (max(pos_only, min_pos) + min_kw_only < len(converters) - int(vararg != NO_VARARG)) + has_optional_kw = ( + max(pos_only, min_pos) + min_kw_only + < + len(converters) - int(vararg != self.NO_VARARG) + ) if limited_capi: parser_code = None fastcall = False else: - if vararg == NO_VARARG: + if vararg == self.NO_VARARG: clinic.add_include('pycore_modsupport.h', '_PyArg_UnpackKeywords()') args_declaration = "_PyArg_UnpackKeywords", "%s, %s, %s" % ( @@ -1499,7 +1415,7 @@ def parser_body( else: label = 'skip_optional_kwonly' first_opt = max_pos + min_kw_only - if vararg != NO_VARARG: + if vararg != self.NO_VARARG: first_opt += 1 if i == first_opt: add_label = label @@ -1897,8 +1813,7 @@ def render_function( template_dict['methoddef_name'] = f.c_basename.upper() + "_METHODDEF" template_dict['c_basename'] = f.c_basename - template_dict['docstring'] = self.docstring_for_c_string(f) - + template_dict['docstring'] = libclinic.docstring_for_c_string(f.docstring) template_dict['self_name'] = template_dict['self_type'] = template_dict['self_type_check'] = '' template_dict['target_critical_section'] = ', '.join(f.target_critical_section) for converter in converters: @@ -1976,9 +1891,9 @@ def render_function( s = wrap_declarations(s) if clinic.line_prefix: - s = indent_all_lines(s, clinic.line_prefix) + s = libclinic.indent_all_lines(s, clinic.line_prefix) if clinic.line_suffix: - s = suffix_all_lines(s, clinic.line_suffix) + s = libclinic.suffix_all_lines(s, clinic.line_suffix) destination.append(s) @@ -2263,6 +2178,9 @@ class BlockPrinter: language: Language f: io.StringIO = dc.field(default_factory=io.StringIO) + # '#include "header.h" // reason': column of '//' comment + INCLUDE_COMMENT_COLUMN: Final[int] = 35 + def print_block( self, block: Block, @@ -2318,7 +2236,7 @@ def print_block( line = f'#include "{include.filename}"' if include.reason: comment = f'// {include.reason}\n' - line = line.ljust(INCLUDE_COMMENT_COLUMN - 1) + comment + line = line.ljust(self.INCLUDE_COMMENT_COLUMN - 1) + comment output += line if current_condition: @@ -3406,7 +3324,7 @@ def parse_argument(self, args: list[str]) -> None: args.append(self.converter) if self.encoding: - args.append(c_repr(self.encoding)) + args.append(libclinic.c_repr(self.encoding)) elif self.subclass_of: args.append(self.subclass_of) @@ -3584,8 +3502,8 @@ def set_template_dict(self, template_dict: TemplateDict) -> None: @property def parser_name(self) -> str: - if self.name in CLINIC_PREFIXED_ARGS: # bpo-39741 - return CLINIC_PREFIX + self.name + if self.name in libclinic.CLINIC_PREFIXED_ARGS: # bpo-39741 + return libclinic.CLINIC_PREFIX + self.name else: return self.name @@ -5867,7 +5785,7 @@ def bad_node(self, node: ast.AST) -> None: if isinstance(value, (bool, NoneType)): c_default = "Py_" + py_default elif isinstance(value, str): - c_default = c_repr(value) + c_default = libclinic.c_repr(value) else: c_default = py_default @@ -6312,7 +6230,7 @@ def add_parameter(text: str) -> None: # lines.append(f.return_converter.py_default) if not f.docstring_only: - lines.append("\n" + sig_end_marker + "\n") + lines.append("\n" + libclinic.SIG_END_MARKER + "\n") signature_line = "".join(lines) diff --git a/Tools/clinic/libclinic/__init__.py b/Tools/clinic/libclinic/__init__.py new file mode 100644 index 00000000000000..45ab183e0a342f --- /dev/null +++ b/Tools/clinic/libclinic/__init__.py @@ -0,0 +1,38 @@ +from typing import Final + +from .formatting import ( + c_repr, + docstring_for_c_string, + indent_all_lines, + pprint_words, + suffix_all_lines, + wrapped_c_string_literal, + SIG_END_MARKER, +) + + +CLINIC_PREFIX: Final[str] = "__clinic_" +CLINIC_PREFIXED_ARGS: Final[frozenset[str]] = frozenset({ + "_keywords", + "_parser", + "args", + "argsbuf", + "fastargs", + "kwargs", + "kwnames", + "nargs", + "noptargs", + "return_value", +}) + + +__all__ = [ + # Formatting helpers + "c_repr", + "docstring_for_c_string", + "indent_all_lines", + "pprint_words", + "suffix_all_lines", + "wrapped_c_string_literal", + "SIG_END_MARKER", +] diff --git a/Tools/clinic/libclinic/formatting.py b/Tools/clinic/libclinic/formatting.py new file mode 100644 index 00000000000000..d95abb8d0bd76e --- /dev/null +++ b/Tools/clinic/libclinic/formatting.py @@ -0,0 +1,83 @@ +"""A collection of various string formatting helpers.""" +import textwrap + +from typing import Final + + +SIG_END_MARKER: Final[str] = '--' + + +def docstring_for_c_string(docstring: str) -> str: + lines = [] + # turn docstring into a properly quoted C string + for line in docstring.split('\n'): + lines.append('"') + lines.append(_quoted_for_c_string(line)) + lines.append('\\n"\n') + + if lines[-2] == SIG_END_MARKER: + # If we only have a signature, add the blank line that the + # __text_signature__ getter expects to be there. + lines.append('"\\n"') + else: + lines.pop() + lines.append('"') + return ''.join(lines) + + +def _quoted_for_c_string(s: str) -> str: + """Helper for docstring_for_c_string().""" + for old, new in ( + ('\\', '\\\\'), # must be first! + ('"', '\\"'), + ("'", "\\'"), + ): + s = s.replace(old, new) + return s + + +def c_repr(s: str) -> str: + return '"' + s + '"' + + +def wrapped_c_string_literal( + text: str, + *, + width: int = 72, + suffix: str = '', + initial_indent: int = 0, + subsequent_indent: int = 4 +) -> str: + wrapped = textwrap.wrap(text, width=width, replace_whitespace=False, + drop_whitespace=False, break_on_hyphens=False) + separator = c_repr(suffix + '\n' + subsequent_indent * ' ') + return initial_indent * ' ' + c_repr(separator.join(wrapped)) + + +def _add_prefix_and_suffix(s: str, prefix: str = "", suffix: str = "") -> str: + """Return 's', with 'prefix' prepended and 'suffix' appended to all lines. + + If the last line is empty, it remains unchanged. + If s is blank, returns s unchanged. + + (textwrap.indent only adds to non-blank lines.) + """ + *split, last = s.split("\n") + lines = [prefix + line + suffix + "\n" for line in split] + if last: + lines.append(prefix + last + suffix) + return "".join(lines) + + +def indent_all_lines(s: str, prefix: str) -> str: + return _add_prefix_and_suffix(s, prefix=prefix) + + +def suffix_all_lines(s: str, suffix: str) -> str: + return _add_prefix_and_suffix(s, suffix=suffix) + + +def pprint_words(items: list[str]) -> str: + if len(items) <= 2: + return " and ".join(items) + return ", ".join(items[:-1]) + " and " + items[-1] From 9e5ee9fe02b2a02487d5ba8875d5aaeba528907d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Fri, 22 Dec 2023 23:50:38 +0100 Subject: [PATCH 02/10] Address Alex's initial review comments --- Tools/clinic/clinic.py | 3 +-- Tools/clinic/libclinic/__init__.py | 4 ++-- Tools/clinic/libclinic/formatting.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index c9034d65a258aa..532c45f4b39c4e 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -1307,8 +1307,7 @@ def parser_body( has_optional_kw = ( max(pos_only, min_pos) + min_kw_only - < - len(converters) - int(vararg != self.NO_VARARG) + < len(converters) - int(vararg != self.NO_VARARG) ) if limited_capi: diff --git a/Tools/clinic/libclinic/__init__.py b/Tools/clinic/libclinic/__init__.py index 45ab183e0a342f..8ff87eae62fe1c 100644 --- a/Tools/clinic/libclinic/__init__.py +++ b/Tools/clinic/libclinic/__init__.py @@ -11,8 +11,8 @@ ) -CLINIC_PREFIX: Final[str] = "__clinic_" -CLINIC_PREFIXED_ARGS: Final[frozenset[str]] = frozenset({ +CLINIC_PREFIX: Final = "__clinic_" +CLINIC_PREFIXED_ARGS: Final = frozenset({ "_keywords", "_parser", "args", diff --git a/Tools/clinic/libclinic/formatting.py b/Tools/clinic/libclinic/formatting.py index d95abb8d0bd76e..ad4d7de0715620 100644 --- a/Tools/clinic/libclinic/formatting.py +++ b/Tools/clinic/libclinic/formatting.py @@ -4,7 +4,7 @@ from typing import Final -SIG_END_MARKER: Final[str] = '--' +SIG_END_MARKER: Final = '--' def docstring_for_c_string(docstring: str) -> str: From c94618b17a1de5e6f01a1d8e3a5a3a0467aac185 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 23 Dec 2023 00:07:04 +0100 Subject: [PATCH 03/10] Fix indent --- Tools/clinic/libclinic/formatting.py | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Tools/clinic/libclinic/formatting.py b/Tools/clinic/libclinic/formatting.py index ad4d7de0715620..d1c8ee133c3898 100644 --- a/Tools/clinic/libclinic/formatting.py +++ b/Tools/clinic/libclinic/formatting.py @@ -8,21 +8,21 @@ def docstring_for_c_string(docstring: str) -> str: - lines = [] - # turn docstring into a properly quoted C string - for line in docstring.split('\n'): - lines.append('"') - lines.append(_quoted_for_c_string(line)) - lines.append('\\n"\n') - - if lines[-2] == SIG_END_MARKER: - # If we only have a signature, add the blank line that the - # __text_signature__ getter expects to be there. - lines.append('"\\n"') - else: - lines.pop() - lines.append('"') - return ''.join(lines) + lines = [] + # turn docstring into a properly quoted C string + for line in docstring.split('\n'): + lines.append('"') + lines.append(_quoted_for_c_string(line)) + lines.append('\\n"\n') + + if lines[-2] == SIG_END_MARKER: + # If we only have a signature, add the blank line that the + # __text_signature__ getter expects to be there. + lines.append('"\\n"') + else: + lines.pop() + lines.append('"') + return ''.join(lines) def _quoted_for_c_string(s: str) -> str: From e5168ff9a2d6e304841703b37d62a173b5e976e9 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 23 Dec 2023 00:08:07 +0100 Subject: [PATCH 04/10] Fix more indents --- Tools/clinic/libclinic/formatting.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Tools/clinic/libclinic/formatting.py b/Tools/clinic/libclinic/formatting.py index d1c8ee133c3898..e2a345c1f1cb3c 100644 --- a/Tools/clinic/libclinic/formatting.py +++ b/Tools/clinic/libclinic/formatting.py @@ -41,12 +41,12 @@ def c_repr(s: str) -> str: def wrapped_c_string_literal( - text: str, - *, - width: int = 72, - suffix: str = '', - initial_indent: int = 0, - subsequent_indent: int = 4 + text: str, + *, + width: int = 72, + suffix: str = '', + initial_indent: int = 0, + subsequent_indent: int = 4 ) -> str: wrapped = textwrap.wrap(text, width=width, replace_whitespace=False, drop_whitespace=False, break_on_hyphens=False) From fe04157926e43c1e950e650d82805868c200dd57 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 23 Dec 2023 00:09:26 +0100 Subject: [PATCH 05/10] Oh well, since we're introducing a new file, let's just run black on it --- Tools/clinic/libclinic/formatting.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Tools/clinic/libclinic/formatting.py b/Tools/clinic/libclinic/formatting.py index e2a345c1f1cb3c..1609869a40e452 100644 --- a/Tools/clinic/libclinic/formatting.py +++ b/Tools/clinic/libclinic/formatting.py @@ -4,13 +4,13 @@ from typing import Final -SIG_END_MARKER: Final = '--' +SIG_END_MARKER: Final = "--" def docstring_for_c_string(docstring: str) -> str: lines = [] # turn docstring into a properly quoted C string - for line in docstring.split('\n'): + for line in docstring.split("\n"): lines.append('"') lines.append(_quoted_for_c_string(line)) lines.append('\\n"\n') @@ -22,13 +22,13 @@ def docstring_for_c_string(docstring: str) -> str: else: lines.pop() lines.append('"') - return ''.join(lines) + return "".join(lines) def _quoted_for_c_string(s: str) -> str: """Helper for docstring_for_c_string().""" for old, new in ( - ('\\', '\\\\'), # must be first! + ("\\", "\\\\"), # must be first! ('"', '\\"'), ("'", "\\'"), ): @@ -44,14 +44,19 @@ def wrapped_c_string_literal( text: str, *, width: int = 72, - suffix: str = '', + suffix: str = "", initial_indent: int = 0, subsequent_indent: int = 4 ) -> str: - wrapped = textwrap.wrap(text, width=width, replace_whitespace=False, - drop_whitespace=False, break_on_hyphens=False) - separator = c_repr(suffix + '\n' + subsequent_indent * ' ') - return initial_indent * ' ' + c_repr(separator.join(wrapped)) + wrapped = textwrap.wrap( + text, + width=width, + replace_whitespace=False, + drop_whitespace=False, + break_on_hyphens=False, + ) + separator = c_repr(suffix + "\n" + subsequent_indent * " ") + return initial_indent * " " + c_repr(separator.join(wrapped)) def _add_prefix_and_suffix(s: str, prefix: str = "", suffix: str = "") -> str: From a0c2ce5b0ad6b27c6f109377788181efa036e82a Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 23 Dec 2023 00:09:45 +0100 Subject: [PATCH 06/10] Also blacken __init__.py --- Tools/clinic/libclinic/__init__.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/Tools/clinic/libclinic/__init__.py b/Tools/clinic/libclinic/__init__.py index 8ff87eae62fe1c..2ad4d179421cce 100644 --- a/Tools/clinic/libclinic/__init__.py +++ b/Tools/clinic/libclinic/__init__.py @@ -12,18 +12,20 @@ CLINIC_PREFIX: Final = "__clinic_" -CLINIC_PREFIXED_ARGS: Final = frozenset({ - "_keywords", - "_parser", - "args", - "argsbuf", - "fastargs", - "kwargs", - "kwnames", - "nargs", - "noptargs", - "return_value", -}) +CLINIC_PREFIXED_ARGS: Final = frozenset( + { + "_keywords", + "_parser", + "args", + "argsbuf", + "fastargs", + "kwargs", + "kwnames", + "nargs", + "noptargs", + "return_value", + } +) __all__ = [ From 87e55141860d17c4d87810fa2509f4747380518a Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 23 Dec 2023 00:10:49 +0100 Subject: [PATCH 07/10] Comments --- Tools/clinic/libclinic/formatting.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tools/clinic/libclinic/formatting.py b/Tools/clinic/libclinic/formatting.py index 1609869a40e452..ba73dce392a942 100644 --- a/Tools/clinic/libclinic/formatting.py +++ b/Tools/clinic/libclinic/formatting.py @@ -1,4 +1,4 @@ -"""A collection of various string formatting helpers.""" +"""A collection of string formatting helpers.""" import textwrap from typing import Final @@ -9,7 +9,7 @@ def docstring_for_c_string(docstring: str) -> str: lines = [] - # turn docstring into a properly quoted C string + # Turn docstring into a properly quoted C string. for line in docstring.split("\n"): lines.append('"') lines.append(_quoted_for_c_string(line)) From 11cada6e980b282f599788e95a544aa2e3d53413 Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 23 Dec 2023 00:15:42 +0100 Subject: [PATCH 08/10] Nits --- Tools/clinic/libclinic/formatting.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Tools/clinic/libclinic/formatting.py b/Tools/clinic/libclinic/formatting.py index ba73dce392a942..55e6143772f2e8 100644 --- a/Tools/clinic/libclinic/formatting.py +++ b/Tools/clinic/libclinic/formatting.py @@ -59,27 +59,31 @@ def wrapped_c_string_literal( return initial_indent * " " + c_repr(separator.join(wrapped)) -def _add_prefix_and_suffix(s: str, prefix: str = "", suffix: str = "") -> str: - """Return 's', with 'prefix' prepended and 'suffix' appended to all lines. +def _add_prefix_and_suffix( + text: str, + prefix: str = "", + suffix: str = "" +) -> str: + """Return 'text' with 'prefix' prepended and 'suffix' appended to all lines. If the last line is empty, it remains unchanged. - If s is blank, returns s unchanged. + If text is blank, return text unchanged. (textwrap.indent only adds to non-blank lines.) """ - *split, last = s.split("\n") + *split, last = text.split("\n") lines = [prefix + line + suffix + "\n" for line in split] if last: lines.append(prefix + last + suffix) return "".join(lines) -def indent_all_lines(s: str, prefix: str) -> str: - return _add_prefix_and_suffix(s, prefix=prefix) +def indent_all_lines(text: str, prefix: str) -> str: + return _add_prefix_and_suffix(text, prefix=prefix) -def suffix_all_lines(s: str, suffix: str) -> str: - return _add_prefix_and_suffix(s, suffix=suffix) +def suffix_all_lines(text: str, suffix: str) -> str: + return _add_prefix_and_suffix(text, suffix=suffix) def pprint_words(items: list[str]) -> str: From 2bd7ea4fed6e6ab8ea92619063a9397cc095165d Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 23 Dec 2023 00:17:35 +0100 Subject: [PATCH 09/10] Nits --- Tools/clinic/libclinic/formatting.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tools/clinic/libclinic/formatting.py b/Tools/clinic/libclinic/formatting.py index 55e6143772f2e8..fea048bfa4b7e9 100644 --- a/Tools/clinic/libclinic/formatting.py +++ b/Tools/clinic/libclinic/formatting.py @@ -25,19 +25,19 @@ def docstring_for_c_string(docstring: str) -> str: return "".join(lines) -def _quoted_for_c_string(s: str) -> str: +def _quoted_for_c_string(text: str) -> str: """Helper for docstring_for_c_string().""" for old, new in ( ("\\", "\\\\"), # must be first! ('"', '\\"'), ("'", "\\'"), ): - s = s.replace(old, new) - return s + text = text.replace(old, new) + return text -def c_repr(s: str) -> str: - return '"' + s + '"' +def c_repr(text: str) -> str: + return '"' + text + '"' def wrapped_c_string_literal( From 6a2b9e471de0f57669a84a01ee875b993099c34e Mon Sep 17 00:00:00 2001 From: "Erlend E. Aasland" Date: Sat, 23 Dec 2023 01:19:38 +0100 Subject: [PATCH 10/10] Address review --- Tools/clinic/libclinic/__init__.py | 24 ++++++++++++------------ Tools/clinic/libclinic/formatting.py | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Tools/clinic/libclinic/__init__.py b/Tools/clinic/libclinic/__init__.py index 2ad4d179421cce..32ab2259ce4226 100644 --- a/Tools/clinic/libclinic/__init__.py +++ b/Tools/clinic/libclinic/__init__.py @@ -11,6 +11,18 @@ ) +__all__ = [ + # Formatting helpers + "c_repr", + "docstring_for_c_string", + "indent_all_lines", + "pprint_words", + "suffix_all_lines", + "wrapped_c_string_literal", + "SIG_END_MARKER", +] + + CLINIC_PREFIX: Final = "__clinic_" CLINIC_PREFIXED_ARGS: Final = frozenset( { @@ -26,15 +38,3 @@ "return_value", } ) - - -__all__ = [ - # Formatting helpers - "c_repr", - "docstring_for_c_string", - "indent_all_lines", - "pprint_words", - "suffix_all_lines", - "wrapped_c_string_literal", - "SIG_END_MARKER", -] diff --git a/Tools/clinic/libclinic/formatting.py b/Tools/clinic/libclinic/formatting.py index fea048bfa4b7e9..691a8fc47ef037 100644 --- a/Tools/clinic/libclinic/formatting.py +++ b/Tools/clinic/libclinic/formatting.py @@ -1,6 +1,6 @@ """A collection of string formatting helpers.""" -import textwrap +import textwrap from typing import Final