Generating nested code with context managers

T

Terry Reedy

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
 
A

alex23

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.

This is what I love about c.l.p, you wait 10 mins and someone proves
your point with working code :)

I had a more generic 'with indentation:' context manager in mind, but
I really like your approach. Cheers.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top