]> granicus.if.org Git - python/commitdiff
Implement another useful feature for proxies: in super(X, x), x may
authorGuido van Rossum <guido@python.org>
Wed, 12 Feb 2003 03:58:38 +0000 (03:58 +0000)
committerGuido van Rossum <guido@python.org>
Wed, 12 Feb 2003 03:58:38 +0000 (03:58 +0000)
now be a proxy for an X instance, as long as issubclass(x.__class__, X).

Lib/test/test_descr.py
Misc/NEWS
Objects/typeobject.c

index 75ad135b5a28311387f05d287d8dcdfdf26fc292..77bf61a38273330f70ee3fcce9759b3316c33cd5 100644 (file)
@@ -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"
 
index d88f7533461af5ce5b69d42b78eacafe2bf2986d..11521c6b53ab40125cfb435aa051cdff4444d37a 100644 (file)
--- 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
index baea55a7294d5dff6ab3520f5e8bf01130dbb080..7a8bcd97aefeeada610bb4a8a0185f74449106af 100644 (file)
@@ -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