Author: guido.van.rossum
Date: Mon Jun 11 23:19:50 2007
New Revision: 55914

Added:
   python/branches/p3yk/Lib/test/test_super.py   (contents, props changed)
Modified:
   python/branches/p3yk/Objects/typeobject.c
   python/branches/p3yk/Python/bltinmodule.c
   python/branches/p3yk/Python/compile.c
   python/branches/p3yk/Python/symtable.c
Log:
New super() implementation, for PEP 3135 (though the PEP is not yet updated
to this design, and small tweaks may still be made later).


Added: python/branches/p3yk/Lib/test/test_super.py
==============================================================================
--- (empty file)
+++ python/branches/p3yk/Lib/test/test_super.py Mon Jun 11 23:19:50 2007
@@ -0,0 +1,82 @@
+"""Unit tests for new super() implementation."""
+
+# XXX Temporarily, we use super() instead of super.  Or maybe we should
+# use this, period?
+
+import sys
+import unittest
+from test import test_support
+
+
+class A:
+    def f(self):
+        return 'A'
+    @classmethod
+    def cm(cls):
+        return (cls, 'A')
+
+class B(A):
+    def f(self):
+        return super().f() + 'B'
+    @classmethod
+    def cm(cls):
+        return (cls, super().cm(), 'B')
+
+class C(A):
+    def f(self):
+        return super().f() + 'C'
+    @classmethod
+    def cm(cls):
+        return (cls, super().cm(), 'C')
+
+class D(C, B):
+    def f(self):
+        return super().f() + 'D'
+    def cm(cls):
+        return (cls, super().cm(), 'D')
+
+class E(D):
+    pass
+
+class F(E):
+    f = E.f
+
+class G(A):
+    pass
+
+
+class TestSuper(unittest.TestCase):
+
+    def testBasicsWorking(self):
+        self.assertEqual(D().f(), 'ABCD')
+
+    def testClassGetattrWorking(self):
+        self.assertEqual(D.f(D()), 'ABCD')
+
+    def testSubclassNoOverrideWorking(self):
+        self.assertEqual(E().f(), 'ABCD')
+        self.assertEqual(E.f(E()), 'ABCD')
+
+    def testUnboundMethodTransferWorking(self):
+        self.assertEqual(F().f(), 'ABCD')
+        self.assertEqual(F.f(F()), 'ABCD')
+
+    def testClassMethodsStillWorking(self):
+        self.assertEqual(A.cm(), (A, 'A'))
+        self.assertEqual(A().cm(), (A, 'A'))
+        self.assertEqual(G.cm(), (G, 'A'))
+        self.assertEqual(G().cm(), (G, 'A'))
+
+    def testSuperInClassMethodsWorking(self):
+        d = D()
+        self.assertEqual(d.cm(), (d, (D, (D, (D, 'A'), 'B'), 'C'), 'D'))
+        e = E()
+        self.assertEqual(e.cm(), (e, (E, (E, (E, 'A'), 'B'), 'C'), 'D'))
+
+
+def test_main():
+    test_support.run_unittest(TestSuper)
+
+
+if __name__ == "__main__":
+    unittest.main()

Modified: python/branches/p3yk/Objects/typeobject.c
==============================================================================
--- python/branches/p3yk/Objects/typeobject.c   (original)
+++ python/branches/p3yk/Objects/typeobject.c   Mon Jun 11 23:19:50 2007
@@ -1,6 +1,7 @@
 /* Type object implementation */
 
 #include "Python.h"
+#include "frameobject.h"
 #include "structmember.h"
 
 #include <ctype.h>
@@ -5866,14 +5867,76 @@
 super_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
        superobject *su = (superobject *)self;
-       PyTypeObject *type;
+       PyTypeObject *type = NULL;
        PyObject *obj = NULL;
        PyTypeObject *obj_type = NULL;
 
        if (!_PyArg_NoKeywords("super", kwds))
                return -1;
-       if (!PyArg_ParseTuple(args, "O!|O:super", &PyType_Type, &type, &obj))
+       if (!PyArg_ParseTuple(args, "|O!O:super", &PyType_Type, &type, &obj))
                return -1;
+
+        if (type == NULL) {
+               /* Call super(), without args -- fill in from __class__
+                  and first local variable on the stack. */
+               PyFrameObject *f = PyThreadState_GET()->frame;
+               PyCodeObject *co = f->f_code;
+               int i, n;
+               if (co == NULL) {
+                       PyErr_SetString(PyExc_SystemError,
+                                       "super(): no code object");
+                       return -1;
+               }
+               if (co->co_argcount == 0) {
+                       PyErr_SetString(PyExc_SystemError,
+                                       "super(): no arguments");
+                       return -1;
+               }
+               obj = f->f_localsplus[0];
+               if (obj == NULL) {
+                       PyErr_SetString(PyExc_SystemError,
+                                       "super(): arg[0] deleted");
+                       return -1;
+               }
+               if (co->co_freevars == NULL)
+                       n = 0;
+               else {
+                       assert(PyTuple_Check(co->co_freevars));
+                       n = PyTuple_GET_SIZE(co->co_freevars);
+               }
+               for (i = 0; i < n; i++) {
+                       PyObject *name = PyTuple_GET_ITEM(co->co_freevars, i);
+                       assert(PyString_Check(name)); /* XXX PyUnicode? */
+                       if (!strcmp(PyString_AS_STRING(name), "__class__")) {
+                               PyObject *cell =
+                                       f->f_localsplus[co->co_nlocals + i];
+                               if (cell == NULL || !PyCell_Check(cell)) {
+                                       PyErr_SetString(PyExc_SystemError,
+                                         "super(): bad __class__ cell");
+                                       return -1;
+                               }
+                               type = (PyTypeObject *) PyCell_GET(cell);
+                               if (type == NULL) {
+                                       PyErr_SetString(PyExc_SystemError,
+                                         "super(): empty __class__ cell");
+                                       return -1;
+                               }
+                               if (!PyType_Check(type)) {
+                                   PyErr_Format(PyExc_SystemError,
+                                     "super(): __class__ is not a type (%s)",
+                                     type->ob_type->tp_name);
+                                   return -1;
+                               }
+                               break;
+                       }
+               }
+               if (type == NULL) {
+                       PyErr_SetString(PyExc_SystemError,
+                                       "super(): __class__ cell not found");
+                       return -1;
+               }
+        }
+
        if (obj == Py_None)
                obj = NULL;
        if (obj != NULL) {
@@ -5890,13 +5953,19 @@
 }
 
 PyDoc_STRVAR(super_doc,
+"super() -> same as super(__class__, <first argument>)\n"
 "super(type) -> unbound super object\n"
 "super(type, obj) -> bound super object; requires isinstance(obj, type)\n"
 "super(type, type2) -> bound super object; requires issubclass(type2, type)\n"
 "Typical use to call a cooperative superclass method:\n"
 "class C(B):\n"
 "    def meth(self, arg):\n"
-"       super(C, self).meth(arg)");
+"       super().meth(arg)\n"
+"This works for class methods too:\n"
+"class C(B):\n"
+"    @classmethod\n"
+"    def cmeth(cls, arg):\n"
+"       super().cmeth(arg)\n");
 
 static int
 super_traverse(PyObject *self, visitproc visit, void *arg)

Modified: python/branches/p3yk/Python/bltinmodule.c
==============================================================================
--- python/branches/p3yk/Python/bltinmodule.c   (original)
+++ python/branches/p3yk/Python/bltinmodule.c   Mon Jun 11 23:19:50 2007
@@ -33,7 +33,8 @@
 static PyObject *
 builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds)
 {
-       PyObject *func, *name, *bases, *mkw, *meta, *prep, *ns, *res;
+       PyObject *func, *name, *bases, *mkw, *meta, *prep, *ns, *cell;
+       PyObject *cls = NULL;
        Py_ssize_t nargs, nbases;
 
        assert(args != NULL);
@@ -114,22 +115,25 @@
                        return NULL;
                }
        }
-       res = PyObject_CallFunctionObjArgs(func, ns, NULL);
-       if (res != NULL) {
+       cell = PyObject_CallFunctionObjArgs(func, ns, NULL);
+       if (cell != NULL) {
                PyObject *margs;
-               Py_DECREF(res);
-               res = NULL;
                margs = Py_BuildValue("OOO", name, bases, ns);
                if (margs != NULL) {
-                       res = PyEval_CallObjectWithKeywords(meta, margs, mkw);
+                       cls = PyEval_CallObjectWithKeywords(meta, margs, mkw);
                        Py_DECREF(margs);
                }
+               if (cls != NULL && PyCell_Check(cell)) {
+                       Py_INCREF(cls);
+                       PyCell_SET(cell, cls);
+               }
+               Py_DECREF(cell);
        }
        Py_DECREF(ns);
        Py_DECREF(meta);
        Py_XDECREF(mkw);
        Py_DECREF(bases);
-       return res;
+       return cls;
 }
 
 PyDoc_STRVAR(build_class_doc,

Modified: python/branches/p3yk/Python/compile.c
==============================================================================
--- python/branches/p3yk/Python/compile.c       (original)
+++ python/branches/p3yk/Python/compile.c       Mon Jun 11 23:19:50 2007
@@ -373,10 +373,12 @@
 
        while (PyDict_Next(src, &pos, &k, &v)) {
                /* XXX this should probably be a macro in symtable.h */
+               long vi;
                assert(PyInt_Check(v));
-               scope = (PyInt_AS_LONG(v) >> SCOPE_OFFSET) & SCOPE_MASK;
+               vi = PyInt_AS_LONG(v);
+               scope = (vi >> SCOPE_OFFSET) & SCOPE_MASK;
 
-               if (scope == scope_type || PyInt_AS_LONG(v) & flag) {
+               if (scope == scope_type || vi & flag) {
                        PyObject *tuple, *item = PyInt_FromLong(i);
                        if (item == NULL) {
                                Py_DECREF(dest);
@@ -1252,7 +1254,8 @@
                else /* (reftype == FREE) */
                        arg = compiler_lookup_arg(c->u->u_freevars, name);
                if (arg == -1) {
-                       printf("lookup %s in %s %d %d\n"
+                       fprintf(stderr,
+                               "lookup %s in %s %d %d\n"
                                "freevars of %s: %s\n",
                                PyObject_REPR(name), 
                                PyString_AS_STRING(c->u->u_name), 
@@ -1475,7 +1478,6 @@
 static int
 compiler_class(struct compiler *c, stmt_ty s)
 {
-       static PyObject *build_class = NULL;
        static PyObject *locals = NULL;
        PyCodeObject *co;
        PyObject *str;
@@ -1486,13 +1488,7 @@
         if (!compiler_decorators(c, decos))
                 return 0;
 
-
        /* initialize statics */
-       if (build_class == NULL) {
-               build_class = PyString_FromString("__build_class__");
-               if (build_class == NULL)
-                       return 0;
-       }
        if (locals == NULL) {
                locals = PyString_FromString("__locals__");
                if (locals == NULL)
@@ -1502,14 +1498,16 @@
        /* ultimately generate code for:
             <name> = __build_class__(<func>, <name>, *<bases>, **<keywords>)
           where:
-            <func> is a function/closure created from the class body
+            <func> is a function/closure created from the class body;
+                    it has a single argument (__locals__) where the dict
+                   (or MutableSequence) representing the locals is passed
             <name> is the class name
              <bases> is the positional arguments and *varargs argument
             <keywords> is the keyword arguments and **kwds argument
           This borrows from compiler_call.
        */
 
-       /* 0. Create a fake variable named __locals__ */
+       /* 0. Create a fake argument named __locals__ */
        ste = PySymtable_Lookup(c->c_st, s);
        if (ste == NULL)
                return 0;
@@ -1529,11 +1527,11 @@
                c->u->u_private = s->v.ClassDef.name;
                /* force it to have one mandatory argument */
                c->u->u_argcount = 1;
-               /* load the first argument ... */
+               /* load the first argument (__locals__) ... */
                ADDOP_I(c, LOAD_FAST, 0);
                /* ... and store it into f_locals */
                ADDOP_IN_SCOPE(c, STORE_LOCALS);
-               /* load __name__ ... */
+               /* load (global) __name__ ... */
                str = PyString_InternFromString("__name__");
                if (!str || !compiler_nameop(c, str, Load)) {
                        Py_XDECREF(str);
@@ -1554,8 +1552,24 @@
                        compiler_exit_scope(c);
                        return 0;
                }
-               /* return None */
-               ADDOP_O(c, LOAD_CONST, Py_None, consts);
+               /* return the (empty) __class__ cell */
+               str = PyString_InternFromString("__class__");
+               if (str == NULL) {
+                       compiler_exit_scope(c);
+                       return 0;
+               }
+               i = compiler_lookup_arg(c->u->u_cellvars, str);
+               Py_DECREF(str);
+               if (i == -1) {
+                       /* This happens when nobody references the cell */
+                       PyErr_Clear();
+                       /* Return None */
+                       ADDOP_O(c, LOAD_CONST, Py_None, consts);
+                }
+               else {
+                       /* Return the cell where to store __class__ */
+                       ADDOP_I(c, LOAD_CLOSURE, i);
+               }
                ADDOP_IN_SCOPE(c, RETURN_VALUE);
                /* create the code object */
                co = assemble(c, 1);
@@ -2422,7 +2436,7 @@
                return compiler_error(c, "can not assign to __debug__");
        }
 
-mangled = _Py_Mangle(c->u->u_private, name);
+       mangled = _Py_Mangle(c->u->u_private, name);
        if (!mangled)
                return 0;
 

Modified: python/branches/p3yk/Python/symtable.c
==============================================================================
--- python/branches/p3yk/Python/symtable.c      (original)
+++ python/branches/p3yk/Python/symtable.c      Mon Jun 11 23:19:50 2007
@@ -187,7 +187,7 @@
 
 
 static identifier top = NULL, lambda = NULL, genexpr = NULL,
-    listcomp = NULL, setcomp = NULL;
+    listcomp = NULL, setcomp = NULL, __class__ = NULL;
 
 #define GET_IDENTIFIER(VAR) \
        ((VAR) ? (VAR) : ((VAR) = PyString_InternFromString(# VAR)))
@@ -321,7 +321,7 @@
 
 /* Analyze raw symbol information to determine scope of each name.
 
-   The next several functions are helpers for PySymtable_Analyze(),
+   The next several functions are helpers for symtable_analyze(),
    which determines whether a name is local, global, or free.  In addition, 
    it determines which local variables are cell variables; they provide
    bindings that are used for free variables in enclosed blocks.  
@@ -468,10 +468,13 @@
 
    Note that the current block's free variables are included in free.
    That's safe because no name can be free and local in the same scope.
+
+   The 'restrict' argument may be set to a string to restrict the analysis
+   to the one variable whose name equals that string (e.g. "__class__").
 */
 
 static int
-analyze_cells(PyObject *scopes, PyObject *free)
+analyze_cells(PyObject *scopes, PyObject *free, const char *restrict)
 {
         PyObject *name, *v, *v_cell;
        int success = 0;
@@ -488,6 +491,9 @@
                        continue;
                if (!PySet_Contains(free, name))
                        continue;
+               if (restrict != NULL &&
+                   strcmp(PyString_AS_STRING(name), restrict))
+                       continue;
                /* Replace LOCAL with CELL for this name, and remove
                   from free. It is safe to replace the value of name 
                   in the dict, because it will not cause a resize.
@@ -596,7 +602,7 @@
                                }
                                Py_DECREF(v_new);
                        }
-                       /* It's a cell, or already a free variable in this 
scope */
+                       /* It's a cell, or already free in this scope */
                        Py_DECREF(name);
                        continue;
                }
@@ -682,8 +688,7 @@
                        goto error;
        }
 
-       /* Populate global and bound sets to be passed to children.
-        */
+       /* Populate global and bound sets to be passed to children. */
        if (ste->ste_type != ClassBlock) {
                /* Add function locals to bound set */
                if (ste->ste_type == FunctionBlock) {
@@ -702,6 +707,14 @@
                        goto error;
                Py_DECREF(newglobal);
        }
+       else {
+               /* Special-case __class__ */
+               if (!GET_IDENTIFIER(__class__))
+                       goto error;
+               assert(PySet_Contains(local, __class__) == 1);
+               if (PySet_Add(newbound, __class__) < 0)
+                       goto error;
+       }
 
        /* Recursively call analyze_block() on each child block */
        for (i = 0; i < PyList_GET_SIZE(ste->ste_children); ++i) {
@@ -716,8 +729,12 @@
                        ste->ste_child_free = 1;
        }
 
-       /* Check if any local variables need to be converted to cell variables 
*/
-       if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree))
+       /* Check if any local variables must be converted to cell variables */
+       if (ste->ste_type == FunctionBlock && !analyze_cells(scopes, newfree,
+                                                            NULL))
+               goto error;
+        else if (ste->ste_type == ClassBlock && !analyze_cells(scopes, newfree,
+                                                              "__class__"))
                goto error;
        /* Records the results of the analysis in the symbol table entry */
        if (!update_symbols(ste->ste_symbols, scopes, bound, newfree,
@@ -1034,6 +1051,11 @@
                if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock, 
                                          (void *)s, s->lineno))
                        return 0;
+               if (!GET_IDENTIFIER(__class__) ||
+                   !symtable_add_def(st, __class__, DEF_LOCAL)) {
+                       symtable_exit_block(st, s);
+                       return 0;
+               }
                tmp = st->st_private;
                st->st_private = s->v.ClassDef.name;
                VISIT_SEQ_IN_BLOCK(st, stmt, s->v.ClassDef.body, s);
@@ -1301,6 +1323,14 @@
                if (!symtable_add_def(st, e->v.Name.id, 
                                      e->v.Name.ctx == Load ? USE : DEF_LOCAL))
                        return 0;
+               /* Special-case super: it counts as a use of __class__ */
+                if (e->v.Name.ctx == Load &&
+                   st->st_cur->ste_type == FunctionBlock &&
+                   !strcmp(PyString_AS_STRING(e->v.Name.id), "super")) {
+                       if (!GET_IDENTIFIER(__class__) ||
+                           !symtable_add_def(st, __class__, USE))
+                               return 0;
+                }
                break;
        /* child nodes of List and Tuple will have expr_context set */
         case List_kind:
_______________________________________________
Python-3000-checkins mailing list
[email protected]
http://mail.python.org/mailman/listinfo/python-3000-checkins

Reply via email to