<br><br><div class="gmail_quote">On Sat, Jan 21, 2012 at 2:35 AM, Vitja Makarov <span dir="ltr"><<a href="mailto:vitja.makarov@gmail.com">vitja.makarov@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
2012/1/21 Stefan Behnel <<a href="mailto:stefan_ml@behnel.de">stefan_ml@behnel.de</a>>:<br>
<div class="im">> Chris Colbert, 19.01.2012 09:18:<br>
>> If it doesn't pass PyDict_CheckExact you won't be able to use it as the<br>
>> globals to eval or exec.<br>
><br>
> What makes you say that? I tried and it worked for me, all the way back to<br>
> Python 2.4:<br>
><br>
> --------------------<br>
> Python 2.4.6 (#2, Jan 21 2010, 23:45:25)<br>
> [GCC 4.4.1] on linux2<br>
> Type "help", "copyright", "credits" or "license" for more information.<br>
>>>> class MyDict(dict): pass<br>
>>>> eval('1+1', MyDict())<br>
> 2<br>
>>>> exec '1+1' in MyDict()<br>
>>>><br>
> --------------------<br>
><br>
> I only see a couple of calls to PyDict_CheckExact() in CPython's sources<br>
> and they usually seem to be related to special casing for performance<br>
> reasons. Nothing that should impact a module's globals.<br>
><br>
> Besides, Cython controls its own language usages of eval and exec.<br>
><br>
<br>
</div>Cool!<br>
It seems that python internally uses PyObject_GetItem() for module<br>
level lookups and not PyDict_GetItem().<br>
Btw we use __Pyx_GetName() that calls PyObject_GetAttr() that isn't<br>
exactly the same for module lookups:<br>
<br>
# Works in Cython and doesn't work in Python<br>
print __class__<br>
<br>
So we can override __getitem__() and __setitem__():<br>
class MyDict(dict):<br>
def __init__(self):<br>
self._dict = {}<br>
<br>
def __getitem__(self, key):<br>
print '__getitem__', key<br>
return self._dict[key]<br>
<br>
def __setitem__(self, key, value):<br>
print '__setitem__', key, value<br>
self._dict[key] = value<br>
<br>
def __getattr__(self, key):<br>
print '__getattr__'<br>
<br>
d = MyDict()<br>
exec('x = 1; print x', d)<br>
eval('x', d)<br>
$ python foo.py<br>
__setitem__ x 1<br>
__getitem__ x<br>
1<br>
__getitem__ x<br>
<br>
<br>
So we can make globals() return special dict with custom<br>
__setitem__()/__getitem__(). But it seems that we'll have to override<br>
many dict's standard methods like values(), update() and so on. That<br>
would be hard.<br>
<span class="HOEnZb"><font color="#888888"><br><br></font></span></blockquote><div><br></div><div>Be careful. That only works because your dict subclass is being used as the locals as well. The LOAD_NAME opcode does a PyDict_CheckExact on the locals and will call PyDict_GetItem if true, PyObject_GetItem if False:</div>
<div><br></div><pre style="word-wrap:break-word;white-space:pre-wrap">case LOAD_NAME:
w = GETITEM(names, oparg);
if ((v = f->f_locals) == NULL) {
PyErr_Format(PyExc_SystemError,
"no locals when loading %s",
PyObject_REPR(w));
why = WHY_EXCEPTION;
break;
}
if (PyDict_CheckExact(v)) {
x = PyDict_GetItem(v, w);
Py_XINCREF(x);
}
else {
x = PyObject_GetItem(v, w);
if (x == NULL && PyErr_Occurred()) {
if (!PyErr_ExceptionMatches(
PyExc_KeyError))
break;
PyErr_Clear();
} </pre><div><span style="white-space:pre-wrap"><font face="'courier new', monospace"> }</font></span></div><div><span style="white-space:pre-wrap"><font face="'courier new', monospace"><br>
</font></span></div><div><span style="white-space:pre-wrap"><font face="'courier new', monospace"><br></font></span></div><div><span style="white-space:pre-wrap"><font face="arial, helvetica, sans-serif">You can see that the dict subclassing breaks down when you pass an empty dict as the locals:</font></span></div>
<div><span style="white-space:pre-wrap"><font face="arial, helvetica, sans-serif"><br></font></span></div><div><font face="arial, helvetica, sans-serif"><span style="white-space:pre-wrap">In [1]: class Foo(dict):
...: def __getitem__(self, name):
...: print 'get', name
...: return super(Foo, self).__getitem__(name)
...:
In [2]: f = Foo(a=42)
In [3]: eval('a', f)
get a
Out[3]: 42
In [4]: eval('a', f, {})
Out[4]: 42</span></font></div><div><font face="'courier new', monospace"> </font></div></div>