Just top-posting for clarity. :-)

<code file="directions.py">
up      = "UP"
left    = "LEFT"
down    = "DOWN"
right   = "RIGHT"
</code>


<code file="locals.py">
# This code is not guaranteed to work by the language specification.
# But it is one way to do the solution I presented earlier in the thread.

import sys

def import_from( module_name ):
    local_variables = sys._getframe( 1 ).f_locals
    m = __import__( module_name, globals(), local_variables, "*" )
    for name in local_variables:
        if not name.startswith( "_" ):
            local_variables[name] = getattr( m, name )

def move( direction ):
    print( "Moving " + str( direction ) )

def test():
    up = "outerScopeUp"
    class using_directions:
        (up, left, down, right) = 4*[None]; import_from( "directions" )
        move( up )
        move( down )
        move( left )
        move( right )
    print( "in the outer scope up is still: " + up )
    print( "this should fail:" )
    down

test()
</code>


<output pyversions="2.x and 3.x">
Moving UP
Moving DOWN
Moving LEFT
Moving RIGHT
in the outer scope up is still: outerScopeUp
this should fail:
Traceback (most recent call last):
  File "locals.py", line 29, in <module>
    test()
  File "locals.py", line 27, in test
    down
NameError: global name 'down' is not defined
</output>


Cheers & hth.,

- Alf


* George Sakkis:
On Jan 22, 8:39 pm, Martin Drautzburg <martin.drautzb...@web.de>
wrote:

Martin Drautzburg wrote:
with scope():
    # ...
    # use up, down, left, right here
# up, down, left, right no longer defined after the with block exits.
Just looked it up again. It's a cool thing. Too bad my locals() hack
would still be required. The result would be less noisy (and actually
really beautiful) than the decorator implementation though. Thanks
again for pointing this out to me.

Both in your example and by using a context manager, you can get away
with not passing locals() explicitly by introspecting the stack frame.
Here's a context manager that does the trick:

from __future__ import with_statement
from contextlib import contextmanager
import sys

@contextmanager
def enums(*consts):
    # 2 levels up the stack to bypass the contextmanager frame
    f_locals = sys._getframe(2).f_locals
    new_names = set()
    reset_locals, updated_locals = {}, {}
    for const in consts:
        updated_locals[const] = const
        if const in f_locals:
            reset_locals[const] = f_locals[const]
        else:
            new_names.add(const)
    f_locals.update(updated_locals)
    try:
        yield
    finally:
        for name in new_names:
            del f_locals[name]
        f_locals.update(reset_locals)


if __name__ == '__main__':
    def move(aDirection):
        print "moving " + aDirection

    up = "outerScopeUp"
    with enums("up", "down", "left", "right"):
        move(up)
        move(down)
        move(left)
        move(right)
    print "in the outer scope up is still:", up
    print "this should fail:"
    down


Of course, as all other attempts to mess with locals() shown in this
thread, this only "works" when locals() is globals(). If you try it
within a function, it fails:

    def test():
        up = "outerScopeUp"
        with enums("up", "down", "left", "right"):
            move(up)
            move(down)
            move(left)
            move(right)
        print "in the outer scope up is still:", up
        print "this should fail:"
        down

    ## XXX: doesn't work within a function
    test()

So it's utility is pretty limited; a custom DSL is probably better
suited to your problem.
--
http://mail.python.org/mailman/listinfo/python-list

Reply via email to