mirror of https://github.com/python/cpython
gh-113317: Move more formatting helpers into libclinic (#113438)
Move the following global helpers into libclinic: - format_escape() - normalize_snippet() - wrap_declarations() Also move strip_leading_and_trailing_blank_lines() and make it internal to libclinic.
This commit is contained in:
parent
8bce593a63
commit
ca71987f4e
|
@ -3730,7 +3730,7 @@ class FormatHelperTests(unittest.TestCase):
|
|||
)
|
||||
for lines, expected in dataset:
|
||||
with self.subTest(lines=lines, expected=expected):
|
||||
out = clinic.strip_leading_and_trailing_blank_lines(lines)
|
||||
out = libclinic.normalize_snippet(lines)
|
||||
self.assertEqual(out, expected)
|
||||
|
||||
def test_normalize_snippet(self):
|
||||
|
@ -3759,7 +3759,7 @@ class FormatHelperTests(unittest.TestCase):
|
|||
expected_outputs = {0: zero_indent, 4: four_indent, 8: eight_indent}
|
||||
for indent, expected in expected_outputs.items():
|
||||
with self.subTest(indent=indent):
|
||||
actual = clinic.normalize_snippet(snippet, indent=indent)
|
||||
actual = libclinic.normalize_snippet(snippet, indent=indent)
|
||||
self.assertEqual(actual, expected)
|
||||
|
||||
def test_escaped_docstring(self):
|
||||
|
@ -3780,7 +3780,7 @@ class FormatHelperTests(unittest.TestCase):
|
|||
def test_format_escape(self):
|
||||
line = "{}, {a}"
|
||||
expected = "{{}}, {{a}}"
|
||||
out = clinic.format_escape(line)
|
||||
out = libclinic.format_escape(line)
|
||||
self.assertEqual(out, expected)
|
||||
|
||||
def test_indent_all_lines(self):
|
||||
|
|
|
@ -189,12 +189,6 @@ def ensure_legal_c_identifier(s: str) -> str:
|
|||
return s + "_value"
|
||||
return s
|
||||
|
||||
def format_escape(s: str) -> str:
|
||||
# double up curly-braces, this string will be used
|
||||
# as part of a format_map() template later
|
||||
s = s.replace('{', '{{')
|
||||
s = s.replace('}', '}}')
|
||||
return s
|
||||
|
||||
def linear_format(s: str, **kwargs: str) -> str:
|
||||
"""
|
||||
|
@ -475,34 +469,6 @@ def permute_optional_groups(
|
|||
return tuple(accumulator)
|
||||
|
||||
|
||||
def strip_leading_and_trailing_blank_lines(s: str) -> str:
|
||||
lines = s.rstrip().split('\n')
|
||||
while lines:
|
||||
line = lines[0]
|
||||
if line.strip():
|
||||
break
|
||||
del lines[0]
|
||||
return '\n'.join(lines)
|
||||
|
||||
@functools.lru_cache()
|
||||
def normalize_snippet(
|
||||
s: str,
|
||||
*,
|
||||
indent: int = 0
|
||||
) -> str:
|
||||
"""
|
||||
Reformats s:
|
||||
* removes leading and trailing blank lines
|
||||
* ensures that it does not end with a newline
|
||||
* dedents so the first nonwhite character on any line is at column "indent"
|
||||
"""
|
||||
s = strip_leading_and_trailing_blank_lines(s)
|
||||
s = textwrap.dedent(s)
|
||||
if indent:
|
||||
s = textwrap.indent(s, ' ' * indent)
|
||||
return s
|
||||
|
||||
|
||||
def declare_parser(
|
||||
f: Function,
|
||||
*,
|
||||
|
@ -573,62 +539,7 @@ def declare_parser(
|
|||
}};
|
||||
#undef KWTUPLE
|
||||
""" % (format_ or fname)
|
||||
return normalize_snippet(declarations)
|
||||
|
||||
|
||||
def wrap_declarations(
|
||||
text: str,
|
||||
length: int = 78
|
||||
) -> str:
|
||||
"""
|
||||
A simple-minded text wrapper for C function declarations.
|
||||
|
||||
It views a declaration line as looking like this:
|
||||
xxxxxxxx(xxxxxxxxx,xxxxxxxxx)
|
||||
If called with length=30, it would wrap that line into
|
||||
xxxxxxxx(xxxxxxxxx,
|
||||
xxxxxxxxx)
|
||||
(If the declaration has zero or one parameters, this
|
||||
function won't wrap it.)
|
||||
|
||||
If this doesn't work properly, it's probably better to
|
||||
start from scratch with a more sophisticated algorithm,
|
||||
rather than try and improve/debug this dumb little function.
|
||||
"""
|
||||
lines = []
|
||||
for line in text.split('\n'):
|
||||
prefix, _, after_l_paren = line.partition('(')
|
||||
if not after_l_paren:
|
||||
lines.append(line)
|
||||
continue
|
||||
in_paren, _, after_r_paren = after_l_paren.partition(')')
|
||||
if not _:
|
||||
lines.append(line)
|
||||
continue
|
||||
if ',' not in in_paren:
|
||||
lines.append(line)
|
||||
continue
|
||||
parameters = [x.strip() + ", " for x in in_paren.split(',')]
|
||||
prefix += "("
|
||||
if len(prefix) < length:
|
||||
spaces = " " * len(prefix)
|
||||
else:
|
||||
spaces = " " * 4
|
||||
|
||||
while parameters:
|
||||
line = prefix
|
||||
first = True
|
||||
while parameters:
|
||||
if (not first and
|
||||
(len(line) + len(parameters[0]) > length)):
|
||||
break
|
||||
line += parameters.pop(0)
|
||||
first = False
|
||||
if not parameters:
|
||||
line = line.rstrip(", ") + ")" + after_r_paren
|
||||
lines.append(line.rstrip())
|
||||
prefix = spaces
|
||||
return "\n".join(lines)
|
||||
return libclinic.normalize_snippet(declarations)
|
||||
|
||||
|
||||
class CLanguage(Language):
|
||||
|
@ -642,67 +553,67 @@ class CLanguage(Language):
|
|||
|
||||
NO_VARARG: Final[str] = "PY_SSIZE_T_MAX"
|
||||
|
||||
PARSER_PROTOTYPE_KEYWORD: Final[str] = normalize_snippet("""
|
||||
PARSER_PROTOTYPE_KEYWORD: Final[str] = libclinic.normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
|
||||
""")
|
||||
PARSER_PROTOTYPE_KEYWORD___INIT__: Final[str] = normalize_snippet("""
|
||||
PARSER_PROTOTYPE_KEYWORD___INIT__: Final[str] = libclinic.normalize_snippet("""
|
||||
static int
|
||||
{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs)
|
||||
""")
|
||||
PARSER_PROTOTYPE_VARARGS: Final[str] = normalize_snippet("""
|
||||
PARSER_PROTOTYPE_VARARGS: Final[str] = libclinic.normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({self_type}{self_name}, PyObject *args)
|
||||
""")
|
||||
PARSER_PROTOTYPE_FASTCALL: Final[str] = normalize_snippet("""
|
||||
PARSER_PROTOTYPE_FASTCALL: Final[str] = libclinic.normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs)
|
||||
""")
|
||||
PARSER_PROTOTYPE_FASTCALL_KEYWORDS: Final[str] = normalize_snippet("""
|
||||
PARSER_PROTOTYPE_FASTCALL_KEYWORDS: Final[str] = libclinic.normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({self_type}{self_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
""")
|
||||
PARSER_PROTOTYPE_DEF_CLASS: Final[str] = normalize_snippet("""
|
||||
PARSER_PROTOTYPE_DEF_CLASS: Final[str] = libclinic.normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({self_type}{self_name}, PyTypeObject *{defining_class_name}, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
||||
""")
|
||||
PARSER_PROTOTYPE_NOARGS: Final[str] = normalize_snippet("""
|
||||
PARSER_PROTOTYPE_NOARGS: Final[str] = libclinic.normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored))
|
||||
""")
|
||||
PARSER_PROTOTYPE_GETTER: Final[str] = normalize_snippet("""
|
||||
PARSER_PROTOTYPE_GETTER: Final[str] = libclinic.normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({self_type}{self_name}, void *Py_UNUSED(context))
|
||||
""")
|
||||
PARSER_PROTOTYPE_SETTER: Final[str] = normalize_snippet("""
|
||||
PARSER_PROTOTYPE_SETTER: Final[str] = libclinic.normalize_snippet("""
|
||||
static int
|
||||
{c_basename}({self_type}{self_name}, PyObject *value, void *Py_UNUSED(context))
|
||||
""")
|
||||
METH_O_PROTOTYPE: Final[str] = normalize_snippet("""
|
||||
METH_O_PROTOTYPE: Final[str] = libclinic.normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({impl_parameters})
|
||||
""")
|
||||
DOCSTRING_PROTOTYPE_VAR: Final[str] = normalize_snippet("""
|
||||
DOCSTRING_PROTOTYPE_VAR: Final[str] = libclinic.normalize_snippet("""
|
||||
PyDoc_VAR({c_basename}__doc__);
|
||||
""")
|
||||
DOCSTRING_PROTOTYPE_STRVAR: Final[str] = normalize_snippet("""
|
||||
DOCSTRING_PROTOTYPE_STRVAR: Final[str] = libclinic.normalize_snippet("""
|
||||
PyDoc_STRVAR({c_basename}__doc__,
|
||||
{docstring});
|
||||
""")
|
||||
GETSET_DOCSTRING_PROTOTYPE_STRVAR: Final[str] = normalize_snippet("""
|
||||
GETSET_DOCSTRING_PROTOTYPE_STRVAR: Final[str] = libclinic.normalize_snippet("""
|
||||
PyDoc_STRVAR({getset_basename}__doc__,
|
||||
{docstring});
|
||||
#define {getset_basename}_HAS_DOCSTR
|
||||
""")
|
||||
IMPL_DEFINITION_PROTOTYPE: Final[str] = normalize_snippet("""
|
||||
IMPL_DEFINITION_PROTOTYPE: Final[str] = libclinic.normalize_snippet("""
|
||||
static {impl_return_type}
|
||||
{c_basename}_impl({impl_parameters})
|
||||
""")
|
||||
METHODDEF_PROTOTYPE_DEFINE: Final[str] = normalize_snippet(r"""
|
||||
METHODDEF_PROTOTYPE_DEFINE: Final[str] = libclinic.normalize_snippet(r"""
|
||||
#define {methoddef_name} \
|
||||
{{"{name}", {methoddef_cast}{c_basename}{methoddef_cast_end}, {methoddef_flags}, {c_basename}__doc__}},
|
||||
""")
|
||||
GETTERDEF_PROTOTYPE_DEFINE: Final[str] = normalize_snippet(r"""
|
||||
GETTERDEF_PROTOTYPE_DEFINE: Final[str] = libclinic.normalize_snippet(r"""
|
||||
#if defined({getset_basename}_HAS_DOCSTR)
|
||||
# define {getset_basename}_DOCSTR {getset_basename}__doc__
|
||||
#else
|
||||
|
@ -715,7 +626,7 @@ class CLanguage(Language):
|
|||
# define {getset_name}_GETSETDEF {{"{name}", (getter){getset_basename}_get, NULL, {getset_basename}_DOCSTR}},
|
||||
#endif
|
||||
""")
|
||||
SETTERDEF_PROTOTYPE_DEFINE: Final[str] = normalize_snippet(r"""
|
||||
SETTERDEF_PROTOTYPE_DEFINE: Final[str] = libclinic.normalize_snippet(r"""
|
||||
#if defined({getset_name}_HAS_DOCSTR)
|
||||
# define {getset_basename}_DOCSTR {getset_basename}__doc__
|
||||
#else
|
||||
|
@ -728,7 +639,7 @@ class CLanguage(Language):
|
|||
# define {getset_name}_GETSETDEF {{"{name}", NULL, (setter){getset_basename}_set, NULL}},
|
||||
#endif
|
||||
""")
|
||||
METHODDEF_PROTOTYPE_IFNDEF: Final[str] = normalize_snippet("""
|
||||
METHODDEF_PROTOTYPE_IFNDEF: Final[str] = libclinic.normalize_snippet("""
|
||||
#ifndef {methoddef_name}
|
||||
#define {methoddef_name}
|
||||
#endif /* !defined({methoddef_name}) */
|
||||
|
@ -797,7 +708,7 @@ class CLanguage(Language):
|
|||
minor=minversion[1],
|
||||
message=libclinic.c_repr(message),
|
||||
)
|
||||
return normalize_snippet(code)
|
||||
return libclinic.normalize_snippet(code)
|
||||
|
||||
def deprecate_positional_use(
|
||||
self,
|
||||
|
@ -848,7 +759,7 @@ class CLanguage(Language):
|
|||
message=libclinic.wrapped_c_string_literal(message, width=64,
|
||||
subsequent_indent=20),
|
||||
)
|
||||
return normalize_snippet(code, indent=4)
|
||||
return libclinic.normalize_snippet(code, indent=4)
|
||||
|
||||
def deprecate_keyword_use(
|
||||
self,
|
||||
|
@ -931,7 +842,7 @@ class CLanguage(Language):
|
|||
message=libclinic.wrapped_c_string_literal(message, width=64,
|
||||
subsequent_indent=20),
|
||||
)
|
||||
return normalize_snippet(code, indent=4)
|
||||
return libclinic.normalize_snippet(code, indent=4)
|
||||
|
||||
def output_templates(
|
||||
self,
|
||||
|
@ -1036,14 +947,14 @@ class CLanguage(Language):
|
|||
lines.append(prototype)
|
||||
parser_body_fields = fields
|
||||
|
||||
preamble = normalize_snippet("""
|
||||
preamble = libclinic.normalize_snippet("""
|
||||
{{
|
||||
{return_value_declaration}
|
||||
{parser_declarations}
|
||||
{declarations}
|
||||
{initializers}
|
||||
""") + "\n"
|
||||
finale = normalize_snippet("""
|
||||
finale = libclinic.normalize_snippet("""
|
||||
{modifications}
|
||||
{lock}
|
||||
{return_value} = {c_basename}_impl({impl_arguments});
|
||||
|
@ -1095,7 +1006,7 @@ class CLanguage(Language):
|
|||
parser_prototype = self.PARSER_PROTOTYPE_DEF_CLASS
|
||||
return_error = ('return NULL;' if simple_return
|
||||
else 'goto exit;')
|
||||
parser_code = [normalize_snippet("""
|
||||
parser_code = [libclinic.normalize_snippet("""
|
||||
if (nargs) {{
|
||||
PyErr_SetString(PyExc_TypeError, "{name}() takes no arguments");
|
||||
%s
|
||||
|
@ -1135,7 +1046,7 @@ class CLanguage(Language):
|
|||
argname = 'arg'
|
||||
if parameters[0].name == argname:
|
||||
argname += '_'
|
||||
parser_prototype = normalize_snippet("""
|
||||
parser_prototype = libclinic.normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({self_type}{self_name}, PyObject *%s)
|
||||
""" % argname)
|
||||
|
@ -1149,7 +1060,7 @@ class CLanguage(Language):
|
|||
}}
|
||||
""" % argname
|
||||
parser_definition = parser_body(parser_prototype,
|
||||
normalize_snippet(parsearg, indent=4))
|
||||
libclinic.normalize_snippet(parsearg, indent=4))
|
||||
|
||||
elif has_option_groups:
|
||||
# positional parameters with option groups
|
||||
|
@ -1187,11 +1098,12 @@ class CLanguage(Language):
|
|||
if limited_capi:
|
||||
parser_code = []
|
||||
if nargs != 'nargs':
|
||||
parser_code.append(normalize_snippet(f'Py_ssize_t nargs = {nargs};', indent=4))
|
||||
nargs_def = f'Py_ssize_t nargs = {nargs};'
|
||||
parser_code.append(libclinic.normalize_snippet(nargs_def, indent=4))
|
||||
nargs = 'nargs'
|
||||
if min_pos == max_args:
|
||||
pl = '' if min_pos == 1 else 's'
|
||||
parser_code.append(normalize_snippet(f"""
|
||||
parser_code.append(libclinic.normalize_snippet(f"""
|
||||
if ({nargs} != {min_pos}) {{{{
|
||||
PyErr_Format(PyExc_TypeError, "{{name}} expected {min_pos} argument{pl}, got %zd", {nargs});
|
||||
goto exit;
|
||||
|
@ -1201,7 +1113,7 @@ class CLanguage(Language):
|
|||
else:
|
||||
if min_pos:
|
||||
pl = '' if min_pos == 1 else 's'
|
||||
parser_code.append(normalize_snippet(f"""
|
||||
parser_code.append(libclinic.normalize_snippet(f"""
|
||||
if ({nargs} < {min_pos}) {{{{
|
||||
PyErr_Format(PyExc_TypeError, "{{name}} expected at least {min_pos} argument{pl}, got %zd", {nargs});
|
||||
goto exit;
|
||||
|
@ -1210,7 +1122,7 @@ class CLanguage(Language):
|
|||
indent=4))
|
||||
if max_args != self.NO_VARARG:
|
||||
pl = '' if max_args == 1 else 's'
|
||||
parser_code.append(normalize_snippet(f"""
|
||||
parser_code.append(libclinic.normalize_snippet(f"""
|
||||
if ({nargs} > {max_args}) {{{{
|
||||
PyErr_Format(PyExc_TypeError, "{{name}} expected at most {max_args} argument{pl}, got %zd", {nargs});
|
||||
goto exit;
|
||||
|
@ -1220,7 +1132,7 @@ class CLanguage(Language):
|
|||
else:
|
||||
clinic.add_include('pycore_modsupport.h',
|
||||
'_PyArg_CheckPositional()')
|
||||
parser_code = [normalize_snippet(f"""
|
||||
parser_code = [libclinic.normalize_snippet(f"""
|
||||
if (!_PyArg_CheckPositional("{{name}}", {nargs}, {min_pos}, {max_args})) {{{{
|
||||
goto exit;
|
||||
}}}}
|
||||
|
@ -1230,7 +1142,7 @@ class CLanguage(Language):
|
|||
for i, p in enumerate(parameters):
|
||||
if p.is_vararg():
|
||||
if fastcall:
|
||||
parser_code.append(normalize_snippet("""
|
||||
parser_code.append(libclinic.normalize_snippet("""
|
||||
%s = PyTuple_New(%s);
|
||||
if (!%s) {{
|
||||
goto exit;
|
||||
|
@ -1247,7 +1159,7 @@ class CLanguage(Language):
|
|||
max_pos
|
||||
), indent=4))
|
||||
else:
|
||||
parser_code.append(normalize_snippet("""
|
||||
parser_code.append(libclinic.normalize_snippet("""
|
||||
%s = PyTuple_GetSlice(%d, -1);
|
||||
""" % (
|
||||
p.converter.parser_name,
|
||||
|
@ -1263,12 +1175,12 @@ class CLanguage(Language):
|
|||
break
|
||||
if has_optional or p.is_optional():
|
||||
has_optional = True
|
||||
parser_code.append(normalize_snippet("""
|
||||
parser_code.append(libclinic.normalize_snippet("""
|
||||
if (%s < %d) {{
|
||||
goto skip_optional;
|
||||
}}
|
||||
""", indent=4) % (nargs, i + 1))
|
||||
parser_code.append(normalize_snippet(parsearg, indent=4))
|
||||
parser_code.append(libclinic.normalize_snippet(parsearg, indent=4))
|
||||
|
||||
if parser_code is not None:
|
||||
if has_optional:
|
||||
|
@ -1279,7 +1191,7 @@ class CLanguage(Language):
|
|||
if fastcall:
|
||||
clinic.add_include('pycore_modsupport.h',
|
||||
'_PyArg_ParseStack()')
|
||||
parser_code = [normalize_snippet("""
|
||||
parser_code = [libclinic.normalize_snippet("""
|
||||
if (!_PyArg_ParseStack(args, nargs, "{format_units}:{name}",
|
||||
{parse_arguments})) {{
|
||||
goto exit;
|
||||
|
@ -1288,7 +1200,7 @@ class CLanguage(Language):
|
|||
else:
|
||||
flags = "METH_VARARGS"
|
||||
parser_prototype = self.PARSER_PROTOTYPE_VARARGS
|
||||
parser_code = [normalize_snippet("""
|
||||
parser_code = [libclinic.normalize_snippet("""
|
||||
if (!PyArg_ParseTuple(args, "{format_units}:{name}",
|
||||
{parse_arguments})) {{
|
||||
goto exit;
|
||||
|
@ -1343,7 +1255,7 @@ class CLanguage(Language):
|
|||
declarations += "\nPyObject *argsbuf[%s];" % len(converters)
|
||||
if has_optional_kw:
|
||||
declarations += "\nPy_ssize_t noptargs = %s + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - %d;" % (nargs, min_pos + min_kw_only)
|
||||
parser_code = [normalize_snippet("""
|
||||
parser_code = [libclinic.normalize_snippet("""
|
||||
args = %s(args, nargs, NULL, kwnames, &_parser, %s, argsbuf);
|
||||
if (!args) {{
|
||||
goto exit;
|
||||
|
@ -1361,7 +1273,7 @@ class CLanguage(Language):
|
|||
declarations += "\nPy_ssize_t nargs = PyTuple_GET_SIZE(args);"
|
||||
if has_optional_kw:
|
||||
declarations += "\nPy_ssize_t noptargs = %s + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - %d;" % (nargs, min_pos + min_kw_only)
|
||||
parser_code = [normalize_snippet("""
|
||||
parser_code = [libclinic.normalize_snippet("""
|
||||
fastargs = %s(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, %s, argsbuf);
|
||||
if (!fastargs) {{
|
||||
goto exit;
|
||||
|
@ -1394,19 +1306,19 @@ class CLanguage(Language):
|
|||
parser_code.append("%s:" % add_label)
|
||||
add_label = None
|
||||
if not p.is_optional():
|
||||
parser_code.append(normalize_snippet(parsearg, indent=4))
|
||||
parser_code.append(libclinic.normalize_snippet(parsearg, indent=4))
|
||||
elif i < pos_only:
|
||||
add_label = 'skip_optional_posonly'
|
||||
parser_code.append(normalize_snippet("""
|
||||
parser_code.append(libclinic.normalize_snippet("""
|
||||
if (nargs < %d) {{
|
||||
goto %s;
|
||||
}}
|
||||
""" % (i + 1, add_label), indent=4))
|
||||
if has_optional_kw:
|
||||
parser_code.append(normalize_snippet("""
|
||||
parser_code.append(libclinic.normalize_snippet("""
|
||||
noptargs--;
|
||||
""", indent=4))
|
||||
parser_code.append(normalize_snippet(parsearg, indent=4))
|
||||
parser_code.append(libclinic.normalize_snippet(parsearg, indent=4))
|
||||
else:
|
||||
if i < max_pos:
|
||||
label = 'skip_optional_pos'
|
||||
|
@ -1418,20 +1330,20 @@ class CLanguage(Language):
|
|||
first_opt += 1
|
||||
if i == first_opt:
|
||||
add_label = label
|
||||
parser_code.append(normalize_snippet("""
|
||||
parser_code.append(libclinic.normalize_snippet("""
|
||||
if (!noptargs) {{
|
||||
goto %s;
|
||||
}}
|
||||
""" % add_label, indent=4))
|
||||
if i + 1 == len(parameters):
|
||||
parser_code.append(normalize_snippet(parsearg, indent=4))
|
||||
parser_code.append(libclinic.normalize_snippet(parsearg, indent=4))
|
||||
else:
|
||||
add_label = label
|
||||
parser_code.append(normalize_snippet("""
|
||||
parser_code.append(libclinic.normalize_snippet("""
|
||||
if (%s) {{
|
||||
""" % (argname_fmt % i), indent=4))
|
||||
parser_code.append(normalize_snippet(parsearg, indent=8))
|
||||
parser_code.append(normalize_snippet("""
|
||||
parser_code.append(libclinic.normalize_snippet(parsearg, indent=8))
|
||||
parser_code.append(libclinic.normalize_snippet("""
|
||||
if (!--noptargs) {{
|
||||
goto %s;
|
||||
}}
|
||||
|
@ -1450,7 +1362,7 @@ class CLanguage(Language):
|
|||
assert not fastcall
|
||||
flags = "METH_VARARGS|METH_KEYWORDS"
|
||||
parser_prototype = self.PARSER_PROTOTYPE_KEYWORD
|
||||
parser_code = [normalize_snippet("""
|
||||
parser_code = [libclinic.normalize_snippet("""
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "{format_units}:{name}", _keywords,
|
||||
{parse_arguments}))
|
||||
goto exit;
|
||||
|
@ -1462,7 +1374,7 @@ class CLanguage(Language):
|
|||
elif fastcall:
|
||||
clinic.add_include('pycore_modsupport.h',
|
||||
'_PyArg_ParseStackAndKeywords()')
|
||||
parser_code = [normalize_snippet("""
|
||||
parser_code = [libclinic.normalize_snippet("""
|
||||
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser{parse_arguments_comma}
|
||||
{parse_arguments})) {{
|
||||
goto exit;
|
||||
|
@ -1471,7 +1383,7 @@ class CLanguage(Language):
|
|||
else:
|
||||
clinic.add_include('pycore_modsupport.h',
|
||||
'_PyArg_ParseTupleAndKeywordsFast()')
|
||||
parser_code = [normalize_snippet("""
|
||||
parser_code = [libclinic.normalize_snippet("""
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser,
|
||||
{parse_arguments})) {{
|
||||
goto exit;
|
||||
|
@ -1518,7 +1430,7 @@ class CLanguage(Language):
|
|||
declarations = '{base_type_ptr}'
|
||||
clinic.add_include('pycore_modsupport.h',
|
||||
'_PyArg_NoKeywords()')
|
||||
fields.insert(0, normalize_snippet("""
|
||||
fields.insert(0, libclinic.normalize_snippet("""
|
||||
if ({self_type_check}!_PyArg_NoKeywords("{name}", kwargs)) {{
|
||||
goto exit;
|
||||
}}
|
||||
|
@ -1526,7 +1438,7 @@ class CLanguage(Language):
|
|||
if not parses_positional:
|
||||
clinic.add_include('pycore_modsupport.h',
|
||||
'_PyArg_NoPositional()')
|
||||
fields.insert(0, normalize_snippet("""
|
||||
fields.insert(0, libclinic.normalize_snippet("""
|
||||
if ({self_type_check}!_PyArg_NoPositional("{name}", args)) {{
|
||||
goto exit;
|
||||
}}
|
||||
|
@ -1715,7 +1627,7 @@ class CLanguage(Language):
|
|||
out.append(' goto exit;\n')
|
||||
out.append("}")
|
||||
|
||||
template_dict['option_group_parsing'] = format_escape("".join(out))
|
||||
template_dict['option_group_parsing'] = libclinic.format_escape("".join(out))
|
||||
|
||||
def render_function(
|
||||
self,
|
||||
|
@ -1825,7 +1737,7 @@ class CLanguage(Language):
|
|||
else:
|
||||
template_dict['impl_return_type'] = f.return_converter.type
|
||||
|
||||
template_dict['declarations'] = format_escape("\n".join(data.declarations))
|
||||
template_dict['declarations'] = libclinic.format_escape("\n".join(data.declarations))
|
||||
template_dict['initializers'] = "\n\n".join(data.initializers)
|
||||
template_dict['modifications'] = '\n\n'.join(data.modifications)
|
||||
template_dict['keywords_c'] = ' '.join('"' + k + '",'
|
||||
|
@ -1841,9 +1753,11 @@ class CLanguage(Language):
|
|||
template_dict['parse_arguments_comma'] = '';
|
||||
template_dict['impl_parameters'] = ", ".join(data.impl_parameters)
|
||||
template_dict['impl_arguments'] = ", ".join(data.impl_arguments)
|
||||
template_dict['return_conversion'] = format_escape("".join(data.return_conversion).rstrip())
|
||||
template_dict['post_parsing'] = format_escape("".join(data.post_parsing).rstrip())
|
||||
template_dict['cleanup'] = format_escape("".join(data.cleanup))
|
||||
|
||||
template_dict['return_conversion'] = libclinic.format_escape("".join(data.return_conversion).rstrip())
|
||||
template_dict['post_parsing'] = libclinic.format_escape("".join(data.post_parsing).rstrip())
|
||||
template_dict['cleanup'] = libclinic.format_escape("".join(data.cleanup))
|
||||
|
||||
template_dict['return_value'] = data.return_value
|
||||
template_dict['lock'] = "\n".join(data.lock)
|
||||
template_dict['unlock'] = "\n".join(data.unlock)
|
||||
|
@ -1887,7 +1801,7 @@ class CLanguage(Language):
|
|||
# mild hack:
|
||||
# reflow long impl declarations
|
||||
if name in {"impl_prototype", "impl_definition"}:
|
||||
s = wrap_declarations(s)
|
||||
s = libclinic.wrap_declarations(s)
|
||||
|
||||
if clinic.line_prefix:
|
||||
s = libclinic.indent_all_lines(s, clinic.line_prefix)
|
||||
|
|
|
@ -1,25 +1,31 @@
|
|||
from typing import Final
|
||||
|
||||
from .formatting import (
|
||||
SIG_END_MARKER,
|
||||
c_repr,
|
||||
docstring_for_c_string,
|
||||
format_escape,
|
||||
indent_all_lines,
|
||||
normalize_snippet,
|
||||
pprint_words,
|
||||
suffix_all_lines,
|
||||
wrap_declarations,
|
||||
wrapped_c_string_literal,
|
||||
SIG_END_MARKER,
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
# Formatting helpers
|
||||
"SIG_END_MARKER",
|
||||
"c_repr",
|
||||
"docstring_for_c_string",
|
||||
"format_escape",
|
||||
"indent_all_lines",
|
||||
"normalize_snippet",
|
||||
"pprint_words",
|
||||
"suffix_all_lines",
|
||||
"wrap_declarations",
|
||||
"wrapped_c_string_literal",
|
||||
"SIG_END_MARKER",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""A collection of string formatting helpers."""
|
||||
|
||||
import functools
|
||||
import textwrap
|
||||
from typing import Final
|
||||
|
||||
|
@ -59,11 +60,7 @@ def wrapped_c_string_literal(
|
|||
return initial_indent * " " + c_repr(separator.join(wrapped))
|
||||
|
||||
|
||||
def _add_prefix_and_suffix(
|
||||
text: str,
|
||||
prefix: str = "",
|
||||
suffix: str = ""
|
||||
) -> str:
|
||||
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.
|
||||
|
@ -90,3 +87,87 @@ def pprint_words(items: list[str]) -> str:
|
|||
if len(items) <= 2:
|
||||
return " and ".join(items)
|
||||
return ", ".join(items[:-1]) + " and " + items[-1]
|
||||
|
||||
|
||||
def _strip_leading_and_trailing_blank_lines(text: str) -> str:
|
||||
lines = text.rstrip().split("\n")
|
||||
while lines:
|
||||
line = lines[0]
|
||||
if line.strip():
|
||||
break
|
||||
del lines[0]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
@functools.lru_cache()
|
||||
def normalize_snippet(text: str, *, indent: int = 0) -> str:
|
||||
"""
|
||||
Reformats 'text':
|
||||
* removes leading and trailing blank lines
|
||||
* ensures that it does not end with a newline
|
||||
* dedents so the first nonwhite character on any line is at column "indent"
|
||||
"""
|
||||
text = _strip_leading_and_trailing_blank_lines(text)
|
||||
text = textwrap.dedent(text)
|
||||
if indent:
|
||||
text = textwrap.indent(text, " " * indent)
|
||||
return text
|
||||
|
||||
|
||||
def format_escape(text: str) -> str:
|
||||
# double up curly-braces, this string will be used
|
||||
# as part of a format_map() template later
|
||||
text = text.replace("{", "{{")
|
||||
text = text.replace("}", "}}")
|
||||
return text
|
||||
|
||||
|
||||
def wrap_declarations(text: str, length: int = 78) -> str:
|
||||
"""
|
||||
A simple-minded text wrapper for C function declarations.
|
||||
|
||||
It views a declaration line as looking like this:
|
||||
xxxxxxxx(xxxxxxxxx,xxxxxxxxx)
|
||||
If called with length=30, it would wrap that line into
|
||||
xxxxxxxx(xxxxxxxxx,
|
||||
xxxxxxxxx)
|
||||
(If the declaration has zero or one parameters, this
|
||||
function won't wrap it.)
|
||||
|
||||
If this doesn't work properly, it's probably better to
|
||||
start from scratch with a more sophisticated algorithm,
|
||||
rather than try and improve/debug this dumb little function.
|
||||
"""
|
||||
lines = []
|
||||
for line in text.split("\n"):
|
||||
prefix, _, after_l_paren = line.partition("(")
|
||||
if not after_l_paren:
|
||||
lines.append(line)
|
||||
continue
|
||||
in_paren, _, after_r_paren = after_l_paren.partition(")")
|
||||
if not _:
|
||||
lines.append(line)
|
||||
continue
|
||||
if "," not in in_paren:
|
||||
lines.append(line)
|
||||
continue
|
||||
parameters = [x.strip() + ", " for x in in_paren.split(",")]
|
||||
prefix += "("
|
||||
if len(prefix) < length:
|
||||
spaces = " " * len(prefix)
|
||||
else:
|
||||
spaces = " " * 4
|
||||
|
||||
while parameters:
|
||||
line = prefix
|
||||
first = True
|
||||
while parameters:
|
||||
if not first and (len(line) + len(parameters[0]) > length):
|
||||
break
|
||||
line += parameters.pop(0)
|
||||
first = False
|
||||
if not parameters:
|
||||
line = line.rstrip(", ") + ")" + after_r_paren
|
||||
lines.append(line.rstrip())
|
||||
prefix = spaces
|
||||
return "\n".join(lines)
|
||||
|
|
Loading…
Reference in New Issue