On Wed, 26 Feb 2014 14:46:39 +0100, Peter Otten wrote: > Steven D'Aprano wrote: > >> I have to dynamically generate some code inside a function using exec, >> but I'm not sure if it is working by accident or if I can rely on it. >> >> Here is a trivial example: >> >> >> py> def spam(): >> ... exec( """x = 23""" ) >> ... return x >> ... >> py> spam() >> 23 >> >> >> (My real example is more complex than this.) >> >> According to the documentation of exec, I don't think this should >> actually work, and yet it appears to. The documentation says: >> >> The default locals act as described for function locals() below: >> modifications to the default locals dictionary should not be >> attempted. Pass an explicit locals dictionary if you need to see >> effects of the code on locals after function exec() returns. >> >> http://docs.python.org/3.4/library/functions.html#exec >> >> >> I *think* this means that if I want to guarantee that a local variable >> x is created by exec, I need to do this instead: >> >> py> def eggs(): >> ... mylocals = {} >> ... exec( """x = 23""", globals(), mylocals) ... x = >> mylocals['x'] >> ... return x >> ... >> py> eggs() >> 23 >> >> The fact that it works in spam() above is perhaps an accident of >> implementation? Yes no maybe? > > eggs() should work in Python 2 and 3, spam() should work in Python 2, > but not in Python 3.
Aha! That explains it -- I was reading the 3.x docs and testing in Python 2.7. Thanks everyone for answering. By the way, if anyone cares what my actual use-case is, I have a function that needs to work under Python 2.4 through 3.4, and it uses a with statement. With statements are not available in 2.4 (or 2.5, unless you give a from __future__ import). So after messing about for a while with circular imports and dependency injections, I eventually settled on some code that works something like this: def factory(): blah blah blah try: exec("""def inner(): with something: return something """, globals(), mylocals) inner = mylocals['inner'] except SyntaxError: def inner(): # manually operate the context manager call context manager __enter__ try: try: return something except: # Yes, a bare except. Catch EVERYTHING. blah blah blah finally: call context manager __exit__ blah blah blah return inner (By the way, yes, I have to use a bare except, not just "except BaseException". Python 2.4 and 2.5 still have string exceptions.) -- Steven -- https://mail.python.org/mailman/listinfo/python-list