Generating nested code with context managers

Dan Goodman dg.gmane at thesamovar.net
Wed May 5 11:51:48 EDT 2010


I tried a very similar thing, but not using with statements:

http://mail.python.org/pipermail/python-list/2010-March/1239577.html

Dan

On 04/05/2010 22:36, Terry Reedy wrote:
> 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