Replacing globals in exec by custom class

J

Jonathan S

Hi all,

I wonder if anyone can explain some weird behaviour in Python.
What I'm trying to do is to execute a string of python code through
the 'exec' statement. I pass an instance of my Global class, which
acts like a dict. By overriding the __getitem__ method, the Global
should pretend that a global variable, named 'xx' does exist.

This does work for the outermost scope in the executed code, but
inside the nested function, 'q', the Global instance seems never to be
accessed, 'xx' is not found, while the globals() built-in still
returns the custom Global instance.

According to python's executing model [1], the interpreter is supposed
to look into globals(), if a variable has not been assigned in the
inner scope. What am I missing here?

[1] http://docs.python.org/reference/executionmodel.html


{{{
class Global(dict):
def __init__(self):
pass
def __getitem__(self, key):
import __builtin__
if key == 'xx':
return 'xx'

if hasattr(__builtin__, key):
return getattr(__builtin__, key)

else key in self.__dict__:
return self.__dict__[key]

def __setitem__(self, key, value):
self.__dict__[key] = value

def __str__(self):
return ' <globals> ' + unicode(self.__dict__)


code="""
print globals()
print xx # Does work, prints 'xx'

def q():
print globals().__getitem__('xx') # Does work, prints 'xx'
print globals()['xx'] # Does work, prints 'xx'
print xx # Does not work, cannot find xx
q()
"""
g = Global()

exec(compile(code, 'my code', 'exec'), g, g)
}}}
 
D

DevPlayer

Couple of things:

I don't think this is what you want:

def __getitem__(self, key):
import __builtin__
if key == 'xx':
return 'xx'
I won't return a KeyError for any string you give g[]
It will return 'xx' only if you supply key='xx' and ignore every other
key=???
With the above code you can do g['yyy'] and you would get None

For your example code to work, line 14 in your example needs to be:
elif key in self.__dict__: # (change else to elif)

for your dict (or globalscl) to act like other dicts() add
an else:
raise KeyError( 'Key not found: %s' % (key,))

__dict__ of a dict I believe is not the namespace of dict. I thought
dict[key] and dict.__dict__[key] where not the same place

__dict__ of dict in Python 2.5 and prior was rumored to be buggy and
unused. Don't know about Python 2.6 +

I thought __getitem__() did not look in __dict__ of dict().

If I copy your code into a module globalcl.py
and comment out "print xx" in the string,
and in PyCrust I "import globalcl"
I too get the results and errors you claim.

If I then do:
I get: ['__builtins__']
I get: nothing (or None)
I get: nothing (or None)

# -------------------------
# interesting part
I get: 'xx'
.... print True
....else:
.... print False

I get: False
# -------------------------
AttributeError: 'Global' object has no attribute __builtins__'
AttributeError: 'Global' object has no attribute '__builtin__'
{'q': <function q at 0x01C738B0>}
 
D

DevPlayer

Shouldn't
return 'xx'
be
return self['xx'


I don't know why precisely you're using a class as a global namespace,
not that I personally find fault with it. But here are some other
things you can do.

Idea one:
======================================================
class NS(object): """place to put junk"""

ns = NS()

ns.global_var1 = "SPAM and eggs"
ns.global_var2 = "use as you like just prefix with ns."
del ns.global_var # because I'm fickle
dir(ns)

Idea two:
======================================================
Instead of a class as a global namespace, use a module

ns.py
------
"""My global namespace"""
# do not import anything or make
# classes or run functions here, just a place to have varibles

ns_var = "var defined in ns.py"

ignore = ['__builtins__', '__class__', '__delattr__', '__dict__',
'__doc__', '__file__', '__format__', '__getattribute__', '__hash__',
'__init__', '__name__', '__new__', '__package__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__']

main.py
--------
import ns
import mod1
import mod2
import mod3

ns.main_var = "this is main var"

ns.mod3_var = mod3.mod3_var
ns.mod3_somefunc_var = mod3.somefunc.somefunc_var

mod3.show( "ns.ns_var", ns.ns_var)
mod3.show( "ns.main_var", ns.main_var)
mod3.show( "ns.mod1_var", ns.mod1_var)
mod3.show( "ns.mod2_var", ns.mod2_var)
mod3.show( "ns.somefunc_var", ns.somefunc_var)
mod3.show( "ns.mod3_var", ns.mod3_var)
mod3.show( "ns.mod3_somefunc_var", ns.mod3_somefunc_var)
mod3.show( "dir(ns)", dir(ns))
mod3.list_globals()


mod1.py
-------
import ns

# good usage; var not in mod1 global namespace and value is not copied
# from one namespace to another but just put in it.

ns.mod1_var = "this is text in mod1"

# therefore by doing this your code in mod1 is likely to use
# ns.mod1_var and not just mod1_var -is- not ns.mod1_var


mod2.py
-------
import ns

ns.mod2_var = "text in mod2"


def somefunc():
# good app globals
ns.somefunc_var = "make a var not in the global namespace of the
mod2"
ns.renamed_var = "rename this"

somefunc()



mod3.py
-------
# mod3_var is misleading; because you might use it but mod3.mod3_var
# would not be the same value as the ns.mod3_var
mod3_var = "not assigned to ns from in mod3.py but from main.py"


def somefunc():
# bad globals
somefunc.somefunc_var = "make a var not in the global namespace of
the mod3"
somefunc.renamed_var = "rename this"

somefunc() # instinate somefunc_var

def show(astring, avalue):
print astring
print ' ', str(avalue)

def list_globals():
print 'ns variable list'
import ns
print ' [',
for item in dir(ns):
if not item in ns.ignore:
print "'" + item.strip() + "', ",
print ']'
 
T

Terry Reedy

class Global(dict):
def __init__(self):
pass
def __getitem__(self, key):
import __builtin__
if key == 'xx':
return 'xx'

if hasattr(__builtin__, key):
return getattr(__builtin__, key)

else key in self.__dict__:
return self.__dict__[key]

syntax error
def __setitem__(self, key, value):
self.__dict__[key] = value

def __str__(self):
return '<globals> ' + unicode(self.__dict__)


code="""
print globals()
print xx # Does work, prints 'xx'

def q():
print globals().__getitem__('xx') # Does work, prints 'xx'
print globals()['xx'] # Does work, prints 'xx'
print xx # Does not work, cannot find xx
q()
"""
g = Global()

exec(compile(code, 'my code', 'exec'), g, g)
}}}

When asking such questions, paste both the actual code that compiled and
ran and the actual traceback you got in response.
 
J

Jonathan S

Thanks for your response! (And sorry about the syntax error, I forgot
to test my code after cleaning up some debug statements before
posting, the else should have been elif indeed.)

It's very interesing, how Python works internally. According to a
thread on the Python mailing list in 2002, it seems that the
implementation of CPython bypasses __getitem__ of the dict object,
when it is used as Global namespace. (For performance reasons.)

http://mail.python.org/pipermail/python-dev/2002-October/029753.html
 

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

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,153
Members
46,699
Latest member
AnneRosen

Latest Threads

Top