<br><br><div class="gmail_quote">On Sat, Jan 21, 2012 at 2:35 AM, Vitja Makarov <span dir="ltr">&lt;<a href="mailto:vitja.makarov@gmail.com">vitja.makarov@gmail.com</a>&gt;</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 &lt;<a href="mailto:stefan_ml@behnel.de">stefan_ml@behnel.de</a>&gt;:<br>
<div class="im">&gt; Chris Colbert, 19.01.2012 09:18:<br>
&gt;&gt; If it doesn&#39;t pass PyDict_CheckExact you won&#39;t be able to use it as the<br>
&gt;&gt; globals to eval or exec.<br>
&gt;<br>
&gt; What makes you say that? I tried and it worked for me, all the way back to<br>
&gt; Python 2.4:<br>
&gt;<br>
&gt; --------------------<br>
&gt; Python 2.4.6 (#2, Jan 21 2010, 23:45:25)<br>
&gt; [GCC 4.4.1] on linux2<br>
&gt; Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.<br>
&gt;&gt;&gt;&gt; class MyDict(dict): pass<br>
&gt;&gt;&gt;&gt; eval(&#39;1+1&#39;, MyDict())<br>
&gt; 2<br>
&gt;&gt;&gt;&gt; exec &#39;1+1&#39; in MyDict()<br>
&gt;&gt;&gt;&gt;<br>
&gt; --------------------<br>
&gt;<br>
&gt; I only see a couple of calls to PyDict_CheckExact() in CPython&#39;s sources<br>
&gt; and they usually seem to be related to special casing for performance<br>
&gt; reasons. Nothing that should impact a module&#39;s globals.<br>
&gt;<br>
&gt; Besides, Cython controls its own language usages of eval and exec.<br>
&gt;<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&#39;t<br>
exactly the same for module lookups:<br>
<br>
# Works in Cython and doesn&#39;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 &#39;__getitem__&#39;, key<br>
        return self._dict[key]<br>
<br>
    def __setitem__(self, key, value):<br>
        print &#39;__setitem__&#39;, key, value<br>
        self._dict[key] = value<br>
<br>
    def __getattr__(self, key):<br>
        print &#39;__getattr__&#39;<br>
<br>
d = MyDict()<br>
exec(&#39;x = 1; print x&#39;, d)<br>
eval(&#39;x&#39;, 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&#39;ll have to override<br>
many dict&#39;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-&gt;f_locals) == NULL) {
                PyErr_Format(PyExc_SystemError,
                             &quot;no locals when loading %s&quot;,
                             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 &amp;&amp; PyErr_Occurred()) {
                    if (!PyErr_ExceptionMatches(
                                    PyExc_KeyError))
                        break;
                    PyErr_Clear();
                } </pre><div><span style="white-space:pre-wrap"><font face="&#39;courier new&#39;, monospace">     }</font></span></div><div><span style="white-space:pre-wrap"><font face="&#39;courier new&#39;, monospace"><br>
</font></span></div><div><span style="white-space:pre-wrap"><font face="&#39;courier new&#39;, 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 &#39;get&#39;, name
   ...:         return super(Foo, self).__getitem__(name)
   ...:     

In [2]: f = Foo(a=42)

In [3]: eval(&#39;a&#39;, f)
get a
Out[3]: 42

In [4]: eval(&#39;a&#39;, f, {})
Out[4]: 42</span></font></div><div><font face="&#39;courier new&#39;, monospace"> </font></div></div>