0

I'm using Python to compile another Python file. To this end, I use Template from string into which I insert, e.g., a constructed function body, e.g.,

from string import Template

s = Template('''
def main():
    ${body}
    return
''')


# body constructed bit by bit
body = ['a']
body.append('b')
body.append('c')

out = s.substitute(body='\n'.join(body))

print(out)

The output of the above is

def main():
    a
b
c
    return

which already highlights the problem: ${body} lines other than the first aren't correctly indented. I could of course manually add the spaces when inserting 'b' and 'c' into the body list, but that already assumes knowledge of the template into which the body will be inserted.

(Perhaps string.Template is not be the appropriate template engine to begin with.)

3
  • 1
    i = s.template.find('${body}'); indent = s.template[s.template.rfind('\n', 0, i) + 1:i]; out = s.substitute(body=('\n' + indent).join(body)) does not work if there are multiple ${body} with different indentation levels. Commented Apr 20, 2016 at 9:48
  • You should say what is exactly your requirement. Inserting a list of lines with proper indentation should be possible, but it is not exactly what string.Template was made for... Commented Apr 20, 2016 at 11:46
  • Thanks for the comment. What remains unclear from the description? I'll try to add more detail to that then. Commented Apr 20, 2016 at 12:24

1 Answer 1

1

Assuming you need to fix indentation for multi line replacements only when the ${} model is at the beginning of the line (except for the indentation), you could use a regex to find all the tokens in the mask and if they are preceded with only blanks repeat them on all following lines from the replacement list.

You could use code like that:

import string, re
def substitute(s, reps):
    t = string.Template(s)
    i=0; cr = {}  # prepare to iterate through the pattern string
    while True:
        # search for next replaceable token and its prefix
        m =re.search(r'^(.*?)\$\{(.*?)\}', tpl[i:], re.MULTILINE)
        if m is None: break  # no more : finished
        # the list is joined using the prefix if it contains only blanks
        sep = ('\n' + m.group(1)) if m.group(1).strip() == '' else '\n'
        cr[m.group(2)] = sep.join(rep[m.group(2)])
        i += m.end()   # continue past last processed replaceable token
    return t.substitute(cr)  # we can now substitute

With your example (slightly modified), it would give:

s = '''
def main():
    ${body}
    return ${retval}
''')
# body constructed bit by bit
body = ['a']
body.append('b')
body.append('c')

out = substitute(s, { 'body': body, 'retval': 0 }
print (out)

it gives as expected:

def main():
    a
    b
    c
    return 0
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.