-2

When I pass a bona fide dict for locals, exec() does the correct thing and falls back to the globals dictionary for missing names. However, if I pass a LazyMap (dict-like object) as locals, accesses to globals raise an AttributeError from inside the lambda given to LazyMap.

#!/usr/bin/env python3

class LazyMap:
    def __init__(self, keys, getter):
        self._keys = keys
        self._getter = getter

    def keys(self):
        return self._keys

    def __getitem__(self, k):
        return self._getter(k)

class TestObj:
    def __init__(self):
        self.field = 'foo'

obj = TestObj()

# this prints 'foo'
locs = dict((name, getattr(obj, name)) for name in dir(obj))
exec('print(field)', globals(), locs)

# this raises AttributeError
locs = LazyMap(dir(obj), lambda k,s=obj: getattr(s, k))
exec('print(field)', globals(), locs)

Why is an exception raised with LazyMap but not with a plain dict? How can I create a locals map that will only fetch/compute the value if accessed from the exec?

saul
  • 7
  • 2
  • Why are you using `globals()`, `exec()`, and ostensibly `locals()` (although I don't see that one) to do whatever it is you're trying to achieve? What exactly are you trying to achieve, anyway? – TigerhawkT3 Jan 10 '17 at 02:48
  • I'm creating a text interface programmable with simple Python one-liners. For example, `d`elete is `rows.pop(cursorRowIndex)`, where both `rows` and `cursorRowIndex` are attributes of the shown object. The problem is that some attributes are computed, and might either be invalid or take a long time to compute. There are dozens of attributes available, but it's not necessary (and will even be problematic) to compute them all just for the handful that are used for the current command. – saul Jan 10 '17 at 03:08

1 Answers1

0

I guess that exec() first tries to getitem on the locals object, and if a KeyError is raised, it then falls back to getitem on the globals object. However, in the given code, getattr will raise an AttributeError instead of a KeyError. Changing the passed lambda to vars(s)[k] will cause a missing key to raise the correct KeyError instead.

To summarize: __getitem__ should raise KeyError on missing key, while __getattr__ should raise AttributeError.

saul
  • 7
  • 2