Given this template:
In [16]:
template = """\
Dynamically expanded example:
@EXAMPLE@
End of file.
"""
Replace @EXAMPLE@ with the following text, preserving indentation:
In [17]:
expansion = """\
def hello():
print('hellowording..')
hello()
"""
So, the result should look like:
In [18]:
test = """\
Dynamically expanded example:
def hello():
print('hellowording..')
hello()
End of file.
"""
In [21]:
try1 = template.replace('@EXAMPLE@', expansion)
print(try1)
try1 == test
Out[21]:
In [24]:
from string import Template
Template.delimiter = '@'
In [33]:
converter = Template(template)
converter
Out[33]:
In [32]:
try2 = converter.substitute(EXAMPLE=expansion)
print(try2)
Subclasssing
In [35]:
class Converter(Template):
delimiter = '@'
converter2 = Converter(template)
converter2
Out[35]:
In [36]:
converter2.substitute(EXAMPLE=expansion)
In [37]:
Converter("Try with single @EXAMPLE delimiter.").substitute(EXAMPLE='at')
Out[37]:
In [38]:
Converter("Try with double @EXAMPLE@ delimiter.").substitute(EXAMPLE='at')
In [50]:
class DoubleEnded(Template):
delimiter = '@'
pattern = r'''
%(delim)s # start with @
(?: # non-capturing group
(?P<escaped>%(delim)s) | # if next is @, both are escaped @
(?P<named>%(id)s%(delim)s) | # if next is id, it should end with @
(?P<braced>%(id)s%(delim)s) | # need to have braced in regex too
(?P<invalid>) # catch all for errors
)
'''
print DoubleEnded.pattern
In [51]:
DoubleEnded("Try with double @EXAMPLE@ delimiter.").substitute(EXAMPLE='at')
Out[51]:
^^^ This doesn't work, because if pattern is defined, delimiter won't work anymore.
In [57]:
class DoubleEndedAt(Template):
pattern = r'''
%(delim)s # start with @
(?: # non-capturing group
(?P<escaped>%(delim)s) | # if next is @, both are escaped @
(?P<named>%(id)s)%(delim)s | # if next is id, it should end with @
(?P<braced>%(id)s)%(delim)s | # need to have braced in regex too
(?P<invalid>) # catch all for errors
)
''' % dict(delim='@', id='[_a-z][_a-z0-9]*')
In [58]:
DoubleEndedAt("Try with double @EXAMPLE@ delimiter.").substitute(EXAMPLE='at')
Out[58]:
In [59]:
DoubleEndedAt("Try with single @EXAMPLE delimiter.").substitute(EXAMPLE='at')
In [60]:
DoubleEndedAt("Try with escaped @@ delimiter.").substitute(EXAMPLE='at')
Out[60]:
Finally, the real example.
In [64]:
try3 = DoubleEndedAt(template).substitute(EXAMPLE=expansion)
print(try3)
try3 == test
Out[64]:
In [106]:
def expand(text, needle, replacement):
"""replace `needle` with replacement preserving indentation"""
output = []
repl = replacement.splitlines(True)
for i, line in enumerate(text.splitlines(True)):
pos = line.find(needle)
#print i, repr(pos)
if pos == -1:
output.append(line)
else:
# add first line of replacement
outline = [line[0:pos], repl[0]]
if len(replacement) == 1:
outline.append(line[pos+len(needle):])
else:
# [ ] copy whitespace symbols
indent = ' '*pos
for rep in repl[1:]:
outline.append(indent)
outline.append(rep)
outline.append(line[pos+len(needle):])
output.append(''.join(outline))
return ''.join(output)
In [107]:
print expand(template, '@EXAMPLE@', expansion)
In [111]:
try4 = expand(template, '@EXAMPLE@', expansion.strip())
print try4
try4 == test
Out[111]:
In [112]:
print expand('replace me with something', 'me', 'something')
In [ ]: