Reputation: 8759
I generate bits of (C++) code using format strings such as
memfn_declaration = '''\
{doc}
{static}auto {fun}({formals}){const}
-> {result};
'''
Format strings are nice for this, but is there a way to make them keep the indentation level for {doc}
here? It's typically multiline.
I know I can just indent the string corresponding to doc
by two spaces, I know there are plenty of functions to this end, but that's not what I'm asking: I'm looking for something that would work without tweaking the strings I pass.
Upvotes: 1
Views: 618
Reputation: 123453
Now that you've posted your own answer and clarified at little more about what you want. I think it would be slightly better to implement it by defining your own str
subclass that extends the way strings can be formatted by supporting a new conversion type, 'i'
, which must be followed by a decimal number representing the level of indentation desired.
Here's an implementation that works in both Python 2 & 3:
import re
from textwrap import dedent
try:
from textwrap import indent
except ImportError:
def indent(s, prefix):
return prefix + prefix.join(s.splitlines(True))
class Indentable(str):
indent_format_spec = re.compile(r'''i([0-9]+)''')
def __format__(self, format_spec):
matches = self.indent_format_spec.search(format_spec)
if matches:
level = int(matches.group(1))
first, sep, text = dedent(self).strip().partition('\n')
return first + sep + indent(text, ' ' * level)
return super(Indentable, self).__format__(format_spec)
sample_format_string = '''\
{doc:i2}
{static}auto {fun}({formals}){const}
-> {result};
'''
specs = {
'doc': Indentable('''
// Convert a string to a float.
// Quite obsolete.
// Use something better instead.
'''),
'static': '',
'fun': 'atof',
'formals': 'const char*',
'const': '',
'result': 'float',
}
print(sample_format_string.format(**specs))
Output:
// Convert a string to a float.
// Quite obsolete.
// Use something better instead.
auto atof(const char*)
-> float;
Upvotes: 1
Reputation: 8759
I'm looking for something which is light-weight on the caller site. I can live with the following compromise: I use the fact that format strings are allowed to query for attributes (0.foo
) or items (0['foo']
) to pass the indentation level in the format string.
import textwrap
class Indent(str):
def __new__(cls, *args, **kwargs):
return super(Indent, cls).__new__(cls, *args, **kwargs)
def __getitem__(self, level):
first, _, text = textwrap.dedent(self).strip().partition('\n')
text = textwrap.indent(text, ' ' * level)
return first + '\n' + text
def indent_doc(d):
res = dict(d)
res['doc'] = Indent(d['doc'])
return res
format = '''
{doc[2]}
{static}auto {fun}({formals}){const}
-> {result};
'''
specs = {
'doc': '''
// Convert a string to a float.
// Quite obsolete.
// Use something better instead.
''',
'static': '',
'fun': 'atof',
'formals': 'const char*',
'const': '',
'result': 'float',
}
print(format.format_map(indent_doc(specs)))
that gives:
$ python /tmp/foo.py
// Convert a string to a float.
// Quite obsolete.
// Use something better instead.
auto atof(const char*)
-> float;
I'd be happy to read opinions about that.
Upvotes: 0
Reputation: 455
The issue is this:
'''\
The backslash will ignore all whitespaces up to the next character. Remove the backslash.
Upvotes: 0