mirror of
https://github.com/python/cpython
synced 2024-10-14 09:43:55 +00:00
gh-113317: Argument Clinic: move linear_format into libclinic (#115518)
This commit is contained in:
parent
fd2bb4be3d
commit
58cb634632
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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])
|
||||
|
|
Loading…
Reference in a new issue