]> granicus.if.org Git - python/commitdiff
- Another big step in the right direction. All the overridable
authorGuido van Rossum <guido@python.org>
Wed, 15 Aug 2001 23:57:02 +0000 (23:57 +0000)
committerGuido van Rossum <guido@python.org>
Wed, 15 Aug 2001 23:57:02 +0000 (23:57 +0000)
  operators for which a default implementation exist now work, both in
  dynamic classes and in static classes, overridden or not.  This
  affects __repr__, __str__, __hash__, __contains__, __nonzero__,
  __cmp__, and the rich comparisons (__lt__ etc.).  For dynamic
  classes, this meant copying a lot of code from classobject!  (XXX
  There are still some holes, because the comparison code in object.c
  uses PyInstance_Check(), meaning new-style classes don't get the
  same dispensation.  This needs more thinking.)

- Add object.__hash__, object.__repr__, object.__str__.  The __str__
  dispatcher now calls the __repr__ dispatcher, as it should.

- For static classes, the tp_compare, tp_richcompare and tp_hash slots
  are now inherited together, or not at all.  (XXX I fear there are
  still some situations where you can inherit __hash__ when you
  shouldn't, but mostly it's OK now, and I think there's no way we can
  get that 100% right.)

Objects/typeobject.c

index 8f48b39c78987762d8c9b367e72bd69559b24bbd..8103205f766832c64254de562bb3e88347fc3628 100644 (file)
@@ -851,8 +851,6 @@ object_dealloc(PyObject *self)
        self->ob_type->tp_free(self);
 }
 
-#if 0
-/* XXX These should be made smarter before they can be used */
 static PyObject *
 object_repr(PyObject *self)
 {
@@ -862,12 +860,22 @@ object_repr(PyObject *self)
        return PyString_FromString(buf);
 }
 
+static PyObject *
+object_str(PyObject *self)
+{
+       unaryfunc f;
+
+       f = self->ob_type->tp_repr;
+       if (f == NULL)
+               f = object_repr;
+       return f(self);
+}
+
 static long
 object_hash(PyObject *self)
 {
        return _Py_HashPointer(self);
 }
-#endif
 
 static void
 object_free(PyObject *self)
@@ -891,13 +899,13 @@ PyTypeObject PyBaseObject_Type = {
        0,                                      /* tp_getattr */
        0,                                      /* tp_setattr */
        0,                                      /* tp_compare */
-       0,                                      /* tp_repr */
+       object_repr,                            /* tp_repr */
        0,                                      /* tp_as_number */
        0,                                      /* tp_as_sequence */
        0,                                      /* tp_as_mapping */
-       0,                                      /* tp_hash */
+       object_hash,                            /* tp_hash */
        0,                                      /* tp_call */
-       0,                                      /* tp_str */
+       object_str,                             /* tp_str */
        PyObject_GenericGetAttr,                /* tp_getattro */
        PyObject_GenericSetAttr,                /* tp_setattro */
        0,                                      /* tp_as_buffer */
@@ -1163,14 +1171,18 @@ inherit_slots(PyTypeObject *type, PyTypeObject *base)
        }
        /* tp_compare see tp_richcompare */
        COPYSLOT(tp_repr);
-       COPYSLOT(tp_hash);
+       /* tp_hash see tp_richcompare */
        COPYSLOT(tp_call);
        COPYSLOT(tp_str);
        COPYSLOT(tp_as_buffer);
        if (type->tp_flags & base->tp_flags & Py_TPFLAGS_HAVE_RICHCOMPARE) {
-               if (type->tp_compare == NULL && type->tp_richcompare == NULL) {
+               if (type->tp_compare == NULL &&
+                   type->tp_richcompare == NULL &&
+                   type->tp_hash == NULL)
+               {
                        type->tp_compare = base->tp_compare;
                        type->tp_richcompare = base->tp_richcompare;
+                       type->tp_hash = base->tp_hash;
                }
        }
        else {
@@ -2193,14 +2205,27 @@ slot_sq_ass_slice(PyObject *self, int i, int j, PyObject *value)
 static int
 slot_sq_contains(PyObject *self, PyObject *value)
 {
-       PyObject *res = PyObject_CallMethod(self, "__contains__", "O", value);
-       int r;
+       PyObject *func, *res, *args;
 
-       if (res == NULL)
-               return -1;
-       r = PyInt_AsLong(res);
-       Py_DECREF(res);
-       return r;
+       func = PyObject_GetAttrString(self, "__contains__");
+
+       if (func != NULL) {
+               args = Py_BuildValue("(O)", value);
+               if (args == NULL)
+                       res = NULL;
+               else {
+                       res = PyEval_CallObject(func, args);
+                       Py_DECREF(args);
+               }
+               Py_DECREF(func);
+               if (res == NULL)
+                       return -1;
+               return PyObject_IsTrue(res);
+       }
+       else {
+               PyErr_Clear();
+               return _PySequence_IterContains(self, value);
+       }
 }
 
 SLOT1(slot_sq_inplace_concat, "__iadd__", PyObject *, "O")
@@ -2254,13 +2279,25 @@ SLOT0(slot_nb_absolute, "__abs__")
 static int
 slot_nb_nonzero(PyObject *self)
 {
-       /* XXX This should cope with a missing __nonzero__ */
-       /* XXX Should it also look for __len__? */
-       PyObject *res = PyObject_CallMethod(self, "__nonzero__", "");
+       PyObject *func, *res;
 
-       if (res == NULL)
-               return -1;
-       return (int)PyInt_AsLong(res);
+       func = PyObject_GetAttrString(self, "__nonzero__");
+       if (func == NULL) {
+               PyErr_Clear();
+               func = PyObject_GetAttrString(self, "__len__");
+       }
+
+       if (func != NULL) {
+               res = PyEval_CallObject(func, NULL);
+               Py_DECREF(func);
+               if (res == NULL)
+                       return -1;
+               return PyObject_IsTrue(res);
+       }
+       else {
+               PyErr_Clear();
+               return 1;
+       }
 }
 
 SLOT0(slot_nb_invert, "__invert__")
@@ -2292,33 +2329,126 @@ SLOT1BIN(slot_nb_true_divide, nb_true_divide, "__truediv__", "__rtruediv__")
 SLOT1(slot_nb_inplace_floor_divide, "__ifloordiv__", PyObject *, "O")
 SLOT1(slot_nb_inplace_true_divide, "__itruediv__", PyObject *, "O")
 
+static int
+half_compare(PyObject *self, PyObject *other)
+{
+       PyObject *func, *args, *res;
+       int c;
+
+       func = PyObject_GetAttrString(self, "__cmp__");
+       if (func == NULL) {
+               PyErr_Clear();
+       }
+       else {
+               args = Py_BuildValue("(O)", other);
+               if (args == NULL)
+                       res = NULL;
+               else {
+                       res = PyObject_CallObject(func, args);
+                       Py_DECREF(args);
+               }
+               if (res != Py_NotImplemented) {
+                       if (res == NULL)
+                               return -2;
+                       c = PyInt_AsLong(res);
+                       Py_DECREF(res);
+                       if (c == -1 && PyErr_Occurred())
+                               return -2;
+                       return (c < 0) ? -1 : (c > 0) ? 1 : 0;
+               }
+               Py_DECREF(res);
+       }
+       return 2;
+}
+
 static int
 slot_tp_compare(PyObject *self, PyObject *other)
 {
-       /* XXX Should this cope with a missing __cmp__? */
-       PyObject *res = PyObject_CallMethod(self, "__cmp__", "O", other);
-       long r;
+       int c;
 
-       if (res == NULL)
-               return -1;
-       r = PyInt_AsLong(res);
-       Py_DECREF(res);
-       return (int)r;
+       if (self->ob_type->tp_compare == slot_tp_compare) {
+               c = half_compare(self, other);
+               if (c <= 1)
+                       return c;
+       }
+       if (other->ob_type->tp_compare == slot_tp_compare) {
+               c = half_compare(other, self);
+               if (c < -1)
+                       return -2;
+               if (c <= 1)
+                       return -c;
+       }
+       return (void *)self < (void *)other ? -1 :
+               (void *)self > (void *)other ? 1 : 0;
+}
+
+static PyObject *
+slot_tp_repr(PyObject *self)
+{
+       PyObject *func, *res;
+
+       func = PyObject_GetAttrString(self, "__repr__");
+       if (func != NULL) {
+               res = PyEval_CallObject(func, NULL);
+               Py_DECREF(func);
+               return res;
+       }
+       else {
+               char buf[120];
+               PyErr_Clear();
+               sprintf(buf, "<%.80s object at %p>",
+                       self->ob_type->tp_name, self);
+               return PyString_FromString(buf);
+       }
 }
 
-/* XXX This should cope with a missing __repr__, and also look for __str__ */
-SLOT0(slot_tp_repr, "__repr__")
+static PyObject *
+slot_tp_str(PyObject *self)
+{
+       PyObject *func, *res;
+
+       func = PyObject_GetAttrString(self, "__str__");
+       if (func != NULL) {
+               res = PyEval_CallObject(func, NULL);
+               Py_DECREF(func);
+               return res;
+       }
+       else {
+               PyErr_Clear();
+               return slot_tp_repr(self);
+       }
+}
 
 static long
 slot_tp_hash(PyObject *self)
 {
-       /* XXX This should cope with a missing __hash__ */
-       PyObject *res = PyObject_CallMethod(self, "__hash__", "");
+       PyObject *func, *res;
        long h;
 
-       if (res == NULL)
-               return -1;
-       h = PyInt_AsLong(res);
+       func = PyObject_GetAttrString(self, "__hash__");
+
+       if (func != NULL) {
+               res = PyEval_CallObject(func, NULL);
+               Py_DECREF(func);
+               if (res == NULL)
+                       return -1;
+               h = PyInt_AsLong(res);
+       }
+       else {
+               PyErr_Clear();
+               func = PyObject_GetAttrString(self, "__eq__");
+               if (func == NULL) {
+                       PyErr_Clear();
+                       func = PyObject_GetAttrString(self, "__cmp__");
+               }
+               if (func != NULL) {
+                       Py_DECREF(func);
+                       PyErr_SetString(PyExc_TypeError, "unhashable type");
+                       return -1;
+               }
+               PyErr_Clear();
+               h = _Py_HashPointer((void *)self);
+       }
        if (h == -1 && !PyErr_Occurred())
                h = -2;
        return h;
@@ -2337,9 +2467,6 @@ slot_tp_call(PyObject *self, PyObject *args, PyObject *kwds)
        return res;
 }
 
-/* XXX This should cope with a missing __str__, and also look for __repr__ */
-SLOT0(slot_tp_str, "__str__")
-
 static PyObject *
 slot_tp_getattro(PyObject *self, PyObject *name)
 {
@@ -2384,21 +2511,73 @@ static char *name_op[] = {
        "__ge__",
 };
 
+static PyObject *
+half_richcompare(PyObject *self, PyObject *other, int op)
+{
+       PyObject *func, *args, *res;
+
+       func = PyObject_GetAttrString(self, name_op[op]);
+       if (func == NULL) {
+               PyErr_Clear();
+               Py_INCREF(Py_NotImplemented);
+               return Py_NotImplemented;
+       }
+       args = Py_BuildValue("(O)", other);
+       if (args == NULL)
+               res = NULL;
+       else {
+               res = PyObject_CallObject(func, args);
+               Py_DECREF(args);
+       }
+       Py_DECREF(func);
+       return res;
+}
+
+/* Map rich comparison operators to their swapped version, e.g. LT --> GT */
+static int swapped_op[] = {Py_GT, Py_GE, Py_EQ, Py_NE, Py_LT, Py_LE};
+
 static PyObject *
 slot_tp_richcompare(PyObject *self, PyObject *other, int op)
 {
-       /* XXX How should this cope with missing __xx__? */
-       PyObject *meth = PyObject_GetAttrString(self, name_op[op]);
        PyObject *res;
 
-       if (meth == NULL)
-               return NULL;
-       res = PyObject_CallFunction(meth, "O", other);
-       Py_DECREF(meth);
-       return res;
+       if (self->ob_type->tp_richcompare == slot_tp_richcompare) {
+               res = half_richcompare(self, other, op);
+               if (res != Py_NotImplemented)
+                       return res;
+               Py_DECREF(res);
+       }
+       if (other->ob_type->tp_richcompare == slot_tp_richcompare) {
+               res = half_richcompare(other, self, swapped_op[op]);
+               if (res != Py_NotImplemented) {
+                       return res;
+               }
+               Py_DECREF(res);
+       }
+       Py_INCREF(Py_NotImplemented);
+       return Py_NotImplemented;
 }
 
-SLOT0(slot_tp_iter, "__iter__")
+static PyObject *
+slot_tp_iter(PyObject *self)
+{
+       PyObject *func, *res;
+
+       func = PyObject_GetAttrString(self, "__iter__");
+       if (func != NULL) {
+                res = PyObject_CallObject(func, NULL);
+                Py_DECREF(func);
+                return res;
+       }
+       PyErr_Clear();
+       func = PyObject_GetAttrString(self, "__getitem__");
+       if (func == NULL) {
+               PyErr_SetString(PyExc_TypeError, "iter() of non-sequence");
+               return NULL;
+       }
+       Py_DECREF(func);
+       return PySeqIter_New(self);
+}
 
 static PyObject *
 slot_tp_iternext(PyObject *self)