[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