summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2003-02-12 03:58:38 (GMT)
committerGuido van Rossum <guido@python.org>2003-02-12 03:58:38 (GMT)
commita89d10edc94b3c1c62a6920d4b6b8219675f36da (patch)
tree4b614ef1cd6bc73f36397b8e3320f0f70f412ee7
parente5b130bcdb491b7d4af7cd4c9cf82ebf6ba5fc63 (diff)
downloadcpython-a89d10edc94b3c1c62a6920d4b6b8219675f36da.zip
cpython-a89d10edc94b3c1c62a6920d4b6b8219675f36da.tar.gz
cpython-a89d10edc94b3c1c62a6920d4b6b8219675f36da.tar.bz2
Implement another useful feature for proxies: in super(X, x), x may
now be a proxy for an X instance, as long as issubclass(x.__class__, X).
-rw-r--r--Lib/test/test_descr.py39
-rw-r--r--Misc/NEWS3
-rw-r--r--Objects/typeobject.c100
3 files changed, 125 insertions, 17 deletions
diff --git a/Lib/test/test_descr.py b/Lib/test/test_descr.py
index 75ad135..77bf61a 100644
--- a/Lib/test/test_descr.py
+++ b/Lib/test/test_descr.py
@@ -3791,6 +3791,13 @@ def isinst_isclass():
pa = Proxy(a)
verify(isinstance(a, C)) # Baseline
verify(isinstance(pa, C)) # Test
+ # Test with a classic subclass
+ class D(C):
+ pass
+ a = D()
+ pa = Proxy(a)
+ verify(isinstance(a, C)) # Baseline
+ verify(isinstance(pa, C)) # Test
# Test with a new-style class
class C(object):
pass
@@ -3798,6 +3805,37 @@ def isinst_isclass():
pa = Proxy(a)
verify(isinstance(a, C)) # Baseline
verify(isinstance(pa, C)) # Test
+ # Test with a new-style subclass
+ class D(C):
+ pass
+ a = D()
+ pa = Proxy(a)
+ verify(isinstance(a, C)) # Baseline
+ verify(isinstance(pa, C)) # Test
+
+def proxysuper():
+ if verbose:
+ print "Testing super() for a proxy object..."
+ class Proxy(object):
+ def __init__(self, obj):
+ self.__obj = obj
+ def __getattribute__(self, name):
+ if name.startswith("_Proxy__"):
+ return object.__getattribute__(self, name)
+ else:
+ return getattr(self.__obj, name)
+
+ class B(object):
+ def f(self):
+ return "B.f"
+
+ class C(B):
+ def f(self):
+ return super(C, self).f() + "->C.f"
+
+ obj = C()
+ p = Proxy(obj)
+ vereq(C.__dict__["f"](p), "B.f->C.f")
def test_main():
@@ -3887,6 +3925,7 @@ def test_main():
dict_type_with_metaclass()
meth_class_get()
isinst_isclass()
+ proxysuper()
if verbose: print "All OK"
diff --git a/Misc/NEWS b/Misc/NEWS
index d88f753..11521c6 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -12,6 +12,9 @@ What's New in Python 2.3 alpha 2?
Core and builtins
-----------------
+- super(X, x): x may now be a proxy for an X instance, i.e.
+ issubclass(x.__class__, X) but not issubclass(type(x), X).
+
- isinstance(x, X): if X is a new-style class, this is now equivalent
to issubclass(type(x), X) or issubclass(x.__class__, X). Previously
only type(x) was tested. (For classic classes this was already the
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index baea55a..7a8bcd9 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -5025,6 +5025,7 @@ typedef struct {
PyObject_HEAD
PyTypeObject *type;
PyObject *obj;
+ PyTypeObject *obj_type;
} superobject;
static PyMemberDef super_members[] = {
@@ -5032,6 +5033,8 @@ static PyMemberDef super_members[] = {
"the class invoking super()"},
{"__self__", T_OBJECT, offsetof(superobject, obj), READONLY,
"the instance invoking super(); may be None"},
+ {"__self_class__", T_OBJECT, offsetof(superobject, obj_type), READONLY,
+ "the type of the the instance invoking super(); may be None"},
{0}
};
@@ -5043,6 +5046,7 @@ super_dealloc(PyObject *self)
_PyObject_GC_UNTRACK(self);
Py_XDECREF(su->obj);
Py_XDECREF(su->type);
+ Py_XDECREF(su->obj_type);
self->ob_type->tp_free(self);
}
@@ -5051,11 +5055,11 @@ super_repr(PyObject *self)
{
superobject *su = (superobject *)self;
- if (su->obj)
+ if (su->obj_type)
return PyString_FromFormat(
"<super: <class '%s'>, <%s object>>",
su->type ? su->type->tp_name : "NULL",
- su->obj->ob_type->tp_name);
+ su->obj_type->tp_name);
else
return PyString_FromFormat(
"<super: <class '%s'>, NULL>",
@@ -5067,13 +5071,13 @@ super_getattro(PyObject *self, PyObject *name)
{
superobject *su = (superobject *)self;
- if (su->obj != NULL) {
+ if (su->obj_type != NULL) {
PyObject *mro, *res, *tmp, *dict;
PyTypeObject *starttype;
descrgetfunc f;
int i, n;
- starttype = su->obj->ob_type;
+ starttype = su->obj_type;
mro = starttype->tp_mro;
if (mro == NULL)
@@ -5086,6 +5090,7 @@ super_getattro(PyObject *self, PyObject *name)
if ((PyObject *)(su->type) == PyTuple_GET_ITEM(mro, i))
break;
}
+#if 0
if (i >= n && PyType_Check(su->obj)) {
starttype = (PyTypeObject *)(su->obj);
mro = starttype->tp_mro;
@@ -5101,6 +5106,7 @@ super_getattro(PyObject *self, PyObject *name)
break;
}
}
+#endif
i++;
res = NULL;
for (; i < n; i++) {
@@ -5128,19 +5134,71 @@ super_getattro(PyObject *self, PyObject *name)
return PyObject_GenericGetAttr(self, name);
}
-static int
+static PyTypeObject *
supercheck(PyTypeObject *type, PyObject *obj)
{
- if (!PyType_IsSubtype(obj->ob_type, type) &&
- !(PyType_Check(obj) &&
- PyType_IsSubtype((PyTypeObject *)obj, type))) {
- PyErr_SetString(PyExc_TypeError,
+ /* Check that a super() call makes sense. Return a type object.
+
+ obj can be a new-style class, or an instance of one:
+
+ - If it is a class, it must be a subclass of 'type'. This case is
+ used for class methods; the return value is obj.
+
+ - If it is an instance, it must be an instance of 'type'. This is
+ the normal case; the return value is obj.__class__.
+
+ But... when obj is an instance, we want to allow for the case where
+ obj->ob_type is not a subclass of type, but obj.__class__ is!
+ This will allow using super() with a proxy for obj.
+ */
+
+ if (PyType_Check(obj)) {
+ /* It's a new-style class */
+ if (PyType_IsSubtype((PyTypeObject *)obj, type)) {
+ Py_INCREF(obj);
+ return (PyTypeObject *)obj;
+ }
+ else
+ goto fail;
+ }
+ else if (PyType_IsSubtype(obj->ob_type, type)) {
+ Py_INCREF(obj->ob_type);
+ return obj->ob_type;
+ }
+ else {
+ /* Try the slow way */
+ static PyObject *class_str = NULL;
+ PyObject *class_attr;
+
+ if (class_str == NULL) {
+ class_str = PyString_FromString("__class__");
+ if (class_str == NULL)
+ return NULL;
+ }
+
+ class_attr = PyObject_GetAttr(obj, class_str);
+
+ if (class_attr != NULL &&
+ PyType_Check(class_attr) &&
+ (PyTypeObject *)class_attr != obj->ob_type)
+ {
+ int ok = PyType_IsSubtype(
+ (PyTypeObject *)class_attr, type);
+ if (ok)
+ return (PyTypeObject *)class_attr;
+ }
+
+ if (class_attr == NULL)
+ PyErr_Clear();
+ else
+ Py_DECREF(class_attr);
+ }
+
+ fail:
+ PyErr_SetString(PyExc_TypeError,
"super(type, obj): "
"obj must be an instance or subtype of type");
- return -1;
- }
- else
- return 0;
+ return NULL;
}
static PyObject *
@@ -5161,7 +5219,8 @@ super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
"OO", su->type, obj);
else {
/* Inline the common case */
- if (supercheck(su->type, obj) < 0)
+ PyTypeObject *obj_type = supercheck(su->type, obj);
+ if (obj_type == NULL)
return NULL;
new = (superobject *)PySuper_Type.tp_new(&PySuper_Type,
NULL, NULL);
@@ -5171,6 +5230,7 @@ super_descr_get(PyObject *self, PyObject *obj, PyObject *type)
Py_INCREF(obj);
new->type = su->type;
new->obj = obj;
+ new->obj_type = obj_type;
return (PyObject *)new;
}
}
@@ -5181,17 +5241,22 @@ super_init(PyObject *self, PyObject *args, PyObject *kwds)
superobject *su = (superobject *)self;
PyTypeObject *type;
PyObject *obj = NULL;
+ PyTypeObject *obj_type = NULL;
if (!PyArg_ParseTuple(args, "O!|O:super", &PyType_Type, &type, &obj))
return -1;
if (obj == Py_None)
obj = NULL;
- if (obj != NULL && supercheck(type, obj) < 0)
- return -1;
+ if (obj != NULL) {
+ obj_type = supercheck(type, obj);
+ if (obj_type == NULL)
+ return -1;
+ Py_INCREF(obj);
+ }
Py_INCREF(type);
- Py_XINCREF(obj);
su->type = type;
su->obj = obj;
+ su->obj_type = obj_type;
return 0;
}
@@ -5219,6 +5284,7 @@ super_traverse(PyObject *self, visitproc visit, void *arg)
VISIT(su->obj);
VISIT(su->type);
+ VISIT(su->obj_type);
#undef VISIT