[Python-3000-checkins] r55914 - in python/branches/p3yk: Lib/test/test_super.py Objects/typeobject.c Python/bltinmodule.c Python/compile.c Python/symtable.c

guido.van.rossum python-3000-checkins at python.org
Mon Jun 11 23:19:56 CEST 2007


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:


More information about the Python-3000-checkins mailing list