Generating nested code with context managers
Terry Reedy
tjreedy at udel.edu
Tue May 4 16:36:32 EDT 2010
In a current thread, people have claimed that generating properly
indented nested blocks is a pain because of the need to keep track of
indent levels. Someone countered with the now rather ancient
http://effbot.org/zone/python-code-generator.htm
The usage example
c = CodeGeneratorBackend()
c.begin(tab=" ")
c.write("for i in range(1000):\n")
c.indent()
c.write("print 'code generation is trivial'")
c.dedent()
illustrates three problems with the CodeGeneratorBackend class. 1) it
requires explicit \n on all lines (which the second omits, though it is
non-fatal since it is also the last) 2) the user still has to manually
match indents and dedents, and 3) the user *cannot* indent lines that
produce indented code.
The relatively new with statement and associated context managers are
designed, among other things, for this situation, where one needs to
alter and restore a global context. So here is my updated (3.1)
proof-of-concept version.
class PyCodeGen:
def __init__(self, tab=" "):
self.code = []
self.tab = tab
self.level = 0
# all attributes should be treated as read-only
def end(self):
return '\n'.join(self.code)
def line(self, string): # new line
self.code.append(self.tab * self.level + string)
class For:
def __init__(self, target, in_expression):
target.line('for ' + in_expression + ':')
self.target = target
def __enter__(self):
self.target.level += 1
def __exit__(self, t, v, tb):
self.target.level -= 1
c = PyCodeGen()
with For(c, 'i in range(1000)'):
c.line('print("Code gen is easy")')
c.line('# done')
print(c.end())
# prints
for i in range(1000):
print("Code gen is easy")
# done
Note that the absence of .indent and .dedent is intentional. In a
fleshed out system, there would be a context manager for each compound
statement and these would handle all indents and dedents.
If one really preferred to write, for instance, 'c.For(s); instead of
'For(c,s)' in the with statement, one could add a wrapper method like
def For(self, s): return For(self, s)
for each context manager. I left that out.
Similar methods can be used to auto-match C braces and other open/close
pairs.
Terry Jan Reedy
More information about the Python-list
mailing list