How do functions get access to builtins?

Steven D'Aprano steve+comp.lang.python at pearwood.info
Sat Jan 19 21:59:45 EST 2013


I've been playing around with ChainedMap in Python 3.3, and run into 
something which perplexes me. Let's start with an ordinary function that 
accesses one global and one builtin.


x = 42
def f():
    print(x)


If you call f(), it works as expected. But let's make a version with no 
access to builtins, and watch it break:

from types import FunctionType
g = FunctionType(f.__code__, {'x': 23})


If you call g(), you get an exception:

py> g()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in f
NameError: global name 'print' is not defined


(Don't be fooled by the traceback referring to "f" rather than g.
That's because g's code was copied from f.)

We can add support for builtins:

import builtins  # use __builtin__ with no "s" in Python 2
g.__globals__['__builtins__'] = builtins  # Note the "s" in the key.


and now calling g() prints 23, as expected.

Now let me try the same thing using Python 3.3's ChainMap. Unfortunately, 
functions insist that their __global__ is a dict, so we fool it into 
accepting a ChainMap with some multiple inheritance trickery:


from collections import ChainMap
class ChainedDict(ChainMap, dict):
    pass

d = ChainedDict({}, {'x': 23}, {'x': 42})
assert d['x'] == 23
g = FunctionType(f.__code__, d)


As before, calling g() raises NameError, "global name 'print' is not 
defined". So I expected to be able to fix it just as I did before:

g.__globals__['__builtins__'] = builtins


But it doesn't work -- I still get the same NameError. Why does this not 
work here, when it works for a regular dict?


I can fix it by adding the builtins into the ChainMap:

g.__globals__.maps.append(builtins.__dict__)


And now calling g() prints 23 as expected.



-- 
Steven



More information about the Python-list mailing list