https://github.com/python/cpython/commit/58cb634632cd4d27e1348320665bcfa010e9cbb2
commit: 58cb634632cd4d27e1348320665bcfa010e9cbb2
branch: main
author: Erlend E. Aasland <[email protected]>
committer: erlend-aasland <[email protected]>
date: 2024-02-15T23:52:20+01:00
summary:
gh-113317: Argument Clinic: move linear_format into libclinic (#115518)
files:
M Lib/test/test_clinic.py
M Tools/clinic/clinic.py
M Tools/clinic/libclinic/__init__.py
M Tools/clinic/libclinic/formatting.py
diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index be35e80fb02c72..f5e9b11ad1cc8a 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -711,7 +711,7 @@ def fn():
class ClinicLinearFormatTest(TestCase):
def _test(self, input, output, **kwargs):
- computed = clinic.linear_format(input, **kwargs)
+ computed = libclinic.linear_format(input, **kwargs)
self.assertEqual(output, computed)
def test_empty_strings(self):
@@ -761,6 +761,19 @@ def test_multiline_substitution(self):
def
""", name='bingle\nbungle\n')
+ def test_text_before_block_marker(self):
+ regex = re.escape("found before '{marker}'")
+ with self.assertRaisesRegex(clinic.ClinicError, regex):
+ libclinic.linear_format("no text before marker for you! {marker}",
+ marker="not allowed!")
+
+ def test_text_after_block_marker(self):
+ regex = re.escape("found after '{marker}'")
+ with self.assertRaisesRegex(clinic.ClinicError, regex):
+ libclinic.linear_format("{marker} no text after marker for you!",
+ marker="not allowed!")
+
+
class InertParser:
def __init__(self, clinic):
pass
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index 7e657351b3f629..4925f27b2937b1 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -163,50 +163,6 @@ def ensure_legal_c_identifier(s: str) -> str:
return s
-def linear_format(s: str, **kwargs: str) -> str:
- """
- Perform str.format-like substitution, except:
- * The strings substituted must be on lines by
- themselves. (This line is the "source line".)
- * If the substitution text is empty, the source line
- is removed in the output.
- * If the field is not recognized, the original line
- is passed unmodified through to the output.
- * If the substitution text is not empty:
- * Each line of the substituted text is indented
- 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('{')
- if not curly:
- lines.extend([line, "\n"])
- continue
-
- name, curly, trailing = trailing.partition('}')
- if not curly or name not in kwargs:
- lines.extend([line, "\n"])
- continue
-
- if trailing:
- fail(f"Text found after {{{name}}} block marker! "
- "It must be on a line by itself.")
- if indent.strip():
- fail(f"Non-whitespace characters found before {{{name}}} block
marker! "
- "It must be on a line by itself.")
-
- value = kwargs[name]
- if not value:
- continue
-
- stripped = [line.rstrip() for line in value.split("\n")]
- value = textwrap.indent("\n".join(stripped), indent)
- lines.extend([value, "\n"])
-
- return "".join(lines[:-1])
-
-
class CRenderData:
def __init__(self) -> None:
@@ -915,7 +871,8 @@ def parser_body(
""")
for field in preamble, *fields, finale:
lines.append(field)
- return linear_format("\n".join(lines),
parser_declarations=declarations)
+ return libclinic.linear_format("\n".join(lines),
+ parser_declarations=declarations)
fastcall = not new_or_init
limited_capi = clinic.limited_capi
@@ -1570,7 +1527,7 @@ def render_option_group_parsing(
{group_booleans}
break;
"""
- s = linear_format(s, group_booleans=lines)
+ s = libclinic.linear_format(s, group_booleans=lines)
s = s.format_map(d)
out.append(s)
@@ -1729,9 +1686,9 @@ def render_function(
for name, destination in clinic.destination_buffers.items():
template = templates[name]
if has_option_groups:
- template = linear_format(template,
+ template = libclinic.linear_format(template,
option_group_parsing=template_dict['option_group_parsing'])
- template = linear_format(template,
+ template = libclinic.linear_format(template,
declarations=template_dict['declarations'],
return_conversion=template_dict['return_conversion'],
initializers=template_dict['initializers'],
@@ -1744,10 +1701,8 @@ def render_function(
# Only generate the "exit:" label
# if we have any gotos
- need_exit_label = "goto exit;" in template
- template = linear_format(template,
- exit_label="exit:" if need_exit_label else ''
- )
+ label = "exit:" if "goto exit;" in template else ""
+ template = libclinic.linear_format(template, exit_label=label)
s = template.format_map(template_dict)
@@ -6125,9 +6080,9 @@ def format_docstring(self) -> str:
parameters = self.format_docstring_parameters(params)
signature = self.format_docstring_signature(f, params)
docstring = "\n".join(lines)
- return linear_format(docstring,
- signature=signature,
- parameters=parameters).rstrip()
+ return libclinic.linear_format(docstring,
+ signature=signature,
+ parameters=parameters).rstrip()
def check_remaining_star(self, lineno: int | None = None) -> None:
assert isinstance(self.function, Function)
diff --git a/Tools/clinic/libclinic/__init__.py
b/Tools/clinic/libclinic/__init__.py
index 1b300b55acc21e..6237809764d9e1 100644
--- a/Tools/clinic/libclinic/__init__.py
+++ b/Tools/clinic/libclinic/__init__.py
@@ -9,6 +9,7 @@
docstring_for_c_string,
format_escape,
indent_all_lines,
+ linear_format,
normalize_snippet,
pprint_words,
suffix_all_lines,
@@ -33,6 +34,7 @@
"docstring_for_c_string",
"format_escape",
"indent_all_lines",
+ "linear_format",
"normalize_snippet",
"pprint_words",
"suffix_all_lines",
diff --git a/Tools/clinic/libclinic/formatting.py
b/Tools/clinic/libclinic/formatting.py
index 8b3ad7ba566bc8..873ece6210017a 100644
--- a/Tools/clinic/libclinic/formatting.py
+++ b/Tools/clinic/libclinic/formatting.py
@@ -4,6 +4,8 @@
import textwrap
from typing import Final
+from libclinic import ClinicError
+
SIG_END_MARKER: Final = "--"
@@ -171,3 +173,51 @@ def wrap_declarations(text: str, length: int = 78) -> str:
lines.append(line.rstrip())
prefix = spaces
return "\n".join(lines)
+
+
+def linear_format(text: str, **kwargs: str) -> str:
+ """
+ Perform str.format-like substitution, except:
+ * The strings substituted must be on lines by
+ themselves. (This line is the "source line".)
+ * If the substitution text is empty, the source line
+ is removed in the output.
+ * If the field is not recognized, the original line
+ is passed unmodified through to the output.
+ * If the substitution text is not empty:
+ * Each line of the substituted text is indented
+ by the indent of the source line.
+ * A newline will be added to the end.
+ """
+ lines = []
+ for line in text.split("\n"):
+ indent, curly, trailing = line.partition("{")
+ if not curly:
+ lines.extend([line, "\n"])
+ continue
+
+ name, curly, trailing = trailing.partition("}")
+ if not curly or name not in kwargs:
+ lines.extend([line, "\n"])
+ continue
+
+ if trailing:
+ raise ClinicError(
+ f"Text found after '{{{name}}}' block marker! "
+ "It must be on a line by itself."
+ )
+ if indent.strip():
+ raise ClinicError(
+ f"Non-whitespace characters found before '{{{name}}}' block
marker! "
+ "It must be on a line by itself."
+ )
+
+ value = kwargs[name]
+ if not value:
+ continue
+
+ stripped = [line.rstrip() for line in value.split("\n")]
+ value = textwrap.indent("\n".join(stripped), indent)
+ lines.extend([value, "\n"])
+
+ return "".join(lines[:-1])
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]