mirror of
https://github.com/python/cpython.git
synced 2024-11-29 12:54:02 +08:00
47b9ff6ba1
*ordering* between objects; there is only a default equality test (defined by an object being equal to itself only). Read the comment in object.c. The current implementation never uses a three-way comparison to compute a rich comparison, but it does use a rich comparison to compute a three-way comparison. I'm not quite done ripping out all the calls to PyObject_Compare/Cmp, or replacing tp_compare implementations with tp_richcompare implementations; but much of that has happened (to make most unit tests pass). The following tests still fail, because I need help deciding or understanding: test_codeop -- depends on comparing code objects test_datetime -- need Tim Peters' opinion test_marshal -- depends on comparing code objects test_mutants -- need help understanding it The problem with test_codeop and test_marshal is this: these tests compare two different code objects and expect them to be equal. Is that still a feature we'd like to support? I've temporarily removed the comparison and hash code from code objects, so they use the default (equality by pointer only) comparison. For the other two tests, run them to see for yourself. (There may be more failing test with "-u all".) A general problem with getting lots of these tests to pass is the reality that for object types that have a natural total ordering, implementing __cmp__ is much more convenient than implementing __eq__, __ne__, __lt__, and so on. Should we go back to allowing __cmp__ to provide a total ordering? Should we provide some other way to implement rich comparison with a single method override? Alex proposed a __key__() method; I've considered a __richcmp__() method. Or perhaps __cmp__() just shouldn't be killed off...
491 lines
11 KiB
C
491 lines
11 KiB
C
/* Class object implementation (dead now except for methods) */
|
|
|
|
#include "Python.h"
|
|
#include "structmember.h"
|
|
|
|
#define TP_DESCR_GET(t) ((t)->tp_descr_get)
|
|
|
|
|
|
PyObject *
|
|
PyMethod_Function(PyObject *im)
|
|
{
|
|
if (!PyMethod_Check(im)) {
|
|
PyErr_BadInternalCall();
|
|
return NULL;
|
|
}
|
|
return ((PyMethodObject *)im)->im_func;
|
|
}
|
|
|
|
PyObject *
|
|
PyMethod_Self(PyObject *im)
|
|
{
|
|
if (!PyMethod_Check(im)) {
|
|
PyErr_BadInternalCall();
|
|
return NULL;
|
|
}
|
|
return ((PyMethodObject *)im)->im_self;
|
|
}
|
|
|
|
PyObject *
|
|
PyMethod_Class(PyObject *im)
|
|
{
|
|
if (!PyMethod_Check(im)) {
|
|
PyErr_BadInternalCall();
|
|
return NULL;
|
|
}
|
|
return ((PyMethodObject *)im)->im_class;
|
|
}
|
|
|
|
|
|
/* Method objects are used for two purposes:
|
|
(a) as bound instance methods (returned by instancename.methodname)
|
|
(b) as unbound methods (returned by ClassName.methodname)
|
|
In case (b), im_self is NULL
|
|
*/
|
|
|
|
static PyMethodObject *free_list;
|
|
|
|
PyObject *
|
|
PyMethod_New(PyObject *func, PyObject *self, PyObject *klass)
|
|
{
|
|
register PyMethodObject *im;
|
|
if (!PyCallable_Check(func)) {
|
|
PyErr_BadInternalCall();
|
|
return NULL;
|
|
}
|
|
im = free_list;
|
|
if (im != NULL) {
|
|
free_list = (PyMethodObject *)(im->im_self);
|
|
PyObject_INIT(im, &PyMethod_Type);
|
|
}
|
|
else {
|
|
im = PyObject_GC_New(PyMethodObject, &PyMethod_Type);
|
|
if (im == NULL)
|
|
return NULL;
|
|
}
|
|
im->im_weakreflist = NULL;
|
|
Py_INCREF(func);
|
|
im->im_func = func;
|
|
Py_XINCREF(self);
|
|
im->im_self = self;
|
|
Py_XINCREF(klass);
|
|
im->im_class = klass;
|
|
_PyObject_GC_TRACK(im);
|
|
return (PyObject *)im;
|
|
}
|
|
|
|
/* Descriptors for PyMethod attributes */
|
|
|
|
/* im_class, im_func and im_self are stored in the PyMethod object */
|
|
|
|
#define OFF(x) offsetof(PyMethodObject, x)
|
|
|
|
static PyMemberDef method_memberlist[] = {
|
|
{"im_class", T_OBJECT, OFF(im_class), READONLY|RESTRICTED,
|
|
"the class associated with a method"},
|
|
{"im_func", T_OBJECT, OFF(im_func), READONLY|RESTRICTED,
|
|
"the function (or other callable) implementing a method"},
|
|
{"im_self", T_OBJECT, OFF(im_self), READONLY|RESTRICTED,
|
|
"the instance to which a method is bound; None for unbound methods"},
|
|
{NULL} /* Sentinel */
|
|
};
|
|
|
|
/* Christian Tismer argued convincingly that method attributes should
|
|
(nearly) always override function attributes.
|
|
The one exception is __doc__; there's a default __doc__ which
|
|
should only be used for the class, not for instances */
|
|
|
|
static PyObject *
|
|
method_get_doc(PyMethodObject *im, void *context)
|
|
{
|
|
static PyObject *docstr;
|
|
if (docstr == NULL) {
|
|
docstr= PyString_InternFromString("__doc__");
|
|
if (docstr == NULL)
|
|
return NULL;
|
|
}
|
|
return PyObject_GetAttr(im->im_func, docstr);
|
|
}
|
|
|
|
static PyGetSetDef method_getset[] = {
|
|
{"__doc__", (getter)method_get_doc, NULL, NULL},
|
|
{0}
|
|
};
|
|
|
|
static PyObject *
|
|
method_getattro(PyObject *obj, PyObject *name)
|
|
{
|
|
PyMethodObject *im = (PyMethodObject *)obj;
|
|
PyTypeObject *tp = obj->ob_type;
|
|
PyObject *descr = NULL;
|
|
|
|
{
|
|
if (tp->tp_dict == NULL) {
|
|
if (PyType_Ready(tp) < 0)
|
|
return NULL;
|
|
}
|
|
descr = _PyType_Lookup(tp, name);
|
|
}
|
|
|
|
if (descr != NULL) {
|
|
descrgetfunc f = TP_DESCR_GET(descr->ob_type);
|
|
if (f != NULL)
|
|
return f(descr, obj, (PyObject *)obj->ob_type);
|
|
else {
|
|
Py_INCREF(descr);
|
|
return descr;
|
|
}
|
|
}
|
|
|
|
return PyObject_GetAttr(im->im_func, name);
|
|
}
|
|
|
|
PyDoc_STRVAR(method_doc,
|
|
"method(function, instance, class)\n\
|
|
\n\
|
|
Create an instance method object.");
|
|
|
|
static PyObject *
|
|
method_new(PyTypeObject* type, PyObject* args, PyObject *kw)
|
|
{
|
|
PyObject *func;
|
|
PyObject *self;
|
|
PyObject *classObj = NULL;
|
|
|
|
if (!PyArg_UnpackTuple(args, "method", 2, 3,
|
|
&func, &self, &classObj))
|
|
return NULL;
|
|
if (!PyCallable_Check(func)) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"first argument must be callable");
|
|
return NULL;
|
|
}
|
|
if (self == Py_None)
|
|
self = NULL;
|
|
if (self == NULL && classObj == NULL) {
|
|
PyErr_SetString(PyExc_TypeError,
|
|
"unbound methods must have non-NULL im_class");
|
|
return NULL;
|
|
}
|
|
|
|
return PyMethod_New(func, self, classObj);
|
|
}
|
|
|
|
static void
|
|
method_dealloc(register PyMethodObject *im)
|
|
{
|
|
_PyObject_GC_UNTRACK(im);
|
|
if (im->im_weakreflist != NULL)
|
|
PyObject_ClearWeakRefs((PyObject *)im);
|
|
Py_DECREF(im->im_func);
|
|
Py_XDECREF(im->im_self);
|
|
Py_XDECREF(im->im_class);
|
|
im->im_self = (PyObject *)free_list;
|
|
free_list = im;
|
|
}
|
|
|
|
static PyObject *
|
|
method_richcompare(PyObject *self, PyObject *other, int op)
|
|
{
|
|
PyMethodObject *a, *b;
|
|
PyObject *res;
|
|
int eq;
|
|
|
|
if ((op != Py_EQ && op != Py_NE) ||
|
|
!PyMethod_Check(self) ||
|
|
!PyMethod_Check(other))
|
|
{
|
|
Py_INCREF(Py_NotImplemented);
|
|
return Py_NotImplemented;
|
|
}
|
|
a = (PyMethodObject *)self;
|
|
b = (PyMethodObject *)other;
|
|
eq = PyObject_RichCompareBool(a->im_func, b->im_func, Py_EQ);
|
|
if (eq == 1) {
|
|
if (a->im_self == NULL || b->im_self == NULL)
|
|
eq = a->im_self == b->im_self;
|
|
else
|
|
eq = PyObject_RichCompareBool(a->im_self, b->im_self,
|
|
Py_EQ);
|
|
}
|
|
if (eq < 0)
|
|
return NULL;
|
|
if (op == Py_EQ)
|
|
res = eq ? Py_True : Py_False;
|
|
else
|
|
res = eq ? Py_False : Py_True;
|
|
Py_INCREF(res);
|
|
return res;
|
|
}
|
|
|
|
static PyObject *
|
|
method_repr(PyMethodObject *a)
|
|
{
|
|
PyObject *self = a->im_self;
|
|
PyObject *func = a->im_func;
|
|
PyObject *klass = a->im_class;
|
|
PyObject *funcname = NULL, *klassname = NULL, *result = NULL;
|
|
char *sfuncname = "?", *sklassname = "?";
|
|
|
|
funcname = PyObject_GetAttrString(func, "__name__");
|
|
if (funcname == NULL) {
|
|
if (!PyErr_ExceptionMatches(PyExc_AttributeError))
|
|
return NULL;
|
|
PyErr_Clear();
|
|
}
|
|
else if (!PyString_Check(funcname)) {
|
|
Py_DECREF(funcname);
|
|
funcname = NULL;
|
|
}
|
|
else
|
|
sfuncname = PyString_AS_STRING(funcname);
|
|
if (klass == NULL)
|
|
klassname = NULL;
|
|
else {
|
|
klassname = PyObject_GetAttrString(klass, "__name__");
|
|
if (klassname == NULL) {
|
|
if (!PyErr_ExceptionMatches(PyExc_AttributeError))
|
|
return NULL;
|
|
PyErr_Clear();
|
|
}
|
|
else if (!PyString_Check(klassname)) {
|
|
Py_DECREF(klassname);
|
|
klassname = NULL;
|
|
}
|
|
else
|
|
sklassname = PyString_AS_STRING(klassname);
|
|
}
|
|
if (self == NULL)
|
|
result = PyString_FromFormat("<unbound method %s.%s>",
|
|
sklassname, sfuncname);
|
|
else {
|
|
/* XXX Shouldn't use repr() here! */
|
|
PyObject *selfrepr = PyObject_Repr(self);
|
|
if (selfrepr == NULL)
|
|
goto fail;
|
|
if (!PyString_Check(selfrepr)) {
|
|
Py_DECREF(selfrepr);
|
|
goto fail;
|
|
}
|
|
result = PyString_FromFormat("<bound method %s.%s of %s>",
|
|
sklassname, sfuncname,
|
|
PyString_AS_STRING(selfrepr));
|
|
Py_DECREF(selfrepr);
|
|
}
|
|
fail:
|
|
Py_XDECREF(funcname);
|
|
Py_XDECREF(klassname);
|
|
return result;
|
|
}
|
|
|
|
static long
|
|
method_hash(PyMethodObject *a)
|
|
{
|
|
long x, y;
|
|
if (a->im_self == NULL)
|
|
x = PyObject_Hash(Py_None);
|
|
else
|
|
x = PyObject_Hash(a->im_self);
|
|
if (x == -1)
|
|
return -1;
|
|
y = PyObject_Hash(a->im_func);
|
|
if (y == -1)
|
|
return -1;
|
|
x = x ^ y;
|
|
if (x == -1)
|
|
x = -2;
|
|
return x;
|
|
}
|
|
|
|
static int
|
|
method_traverse(PyMethodObject *im, visitproc visit, void *arg)
|
|
{
|
|
Py_VISIT(im->im_func);
|
|
Py_VISIT(im->im_self);
|
|
Py_VISIT(im->im_class);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
getclassname(PyObject *klass, char *buf, int bufsize)
|
|
{
|
|
PyObject *name;
|
|
|
|
assert(bufsize > 1);
|
|
strcpy(buf, "?"); /* Default outcome */
|
|
if (klass == NULL)
|
|
return;
|
|
name = PyObject_GetAttrString(klass, "__name__");
|
|
if (name == NULL) {
|
|
/* This function cannot return an exception */
|
|
PyErr_Clear();
|
|
return;
|
|
}
|
|
if (PyString_Check(name)) {
|
|
strncpy(buf, PyString_AS_STRING(name), bufsize);
|
|
buf[bufsize-1] = '\0';
|
|
}
|
|
Py_DECREF(name);
|
|
}
|
|
|
|
static void
|
|
getinstclassname(PyObject *inst, char *buf, int bufsize)
|
|
{
|
|
PyObject *klass;
|
|
|
|
if (inst == NULL) {
|
|
assert(bufsize > 0 && (size_t)bufsize > strlen("nothing"));
|
|
strcpy(buf, "nothing");
|
|
return;
|
|
}
|
|
|
|
klass = PyObject_GetAttrString(inst, "__class__");
|
|
if (klass == NULL) {
|
|
/* This function cannot return an exception */
|
|
PyErr_Clear();
|
|
klass = (PyObject *)(inst->ob_type);
|
|
Py_INCREF(klass);
|
|
}
|
|
getclassname(klass, buf, bufsize);
|
|
Py_XDECREF(klass);
|
|
}
|
|
|
|
static PyObject *
|
|
method_call(PyObject *func, PyObject *arg, PyObject *kw)
|
|
{
|
|
PyObject *self = PyMethod_GET_SELF(func);
|
|
PyObject *klass = PyMethod_GET_CLASS(func);
|
|
PyObject *result;
|
|
|
|
func = PyMethod_GET_FUNCTION(func);
|
|
if (self == NULL) {
|
|
/* Unbound methods must be called with an instance of
|
|
the class (or a derived class) as first argument */
|
|
int ok;
|
|
if (PyTuple_Size(arg) >= 1)
|
|
self = PyTuple_GET_ITEM(arg, 0);
|
|
if (self == NULL)
|
|
ok = 0;
|
|
else {
|
|
ok = PyObject_IsInstance(self, klass);
|
|
if (ok < 0)
|
|
return NULL;
|
|
}
|
|
if (!ok) {
|
|
char clsbuf[256];
|
|
char instbuf[256];
|
|
getclassname(klass, clsbuf, sizeof(clsbuf));
|
|
getinstclassname(self, instbuf, sizeof(instbuf));
|
|
PyErr_Format(PyExc_TypeError,
|
|
"unbound method %s%s must be called with "
|
|
"%s instance as first argument "
|
|
"(got %s%s instead)",
|
|
PyEval_GetFuncName(func),
|
|
PyEval_GetFuncDesc(func),
|
|
clsbuf,
|
|
instbuf,
|
|
self == NULL ? "" : " instance");
|
|
return NULL;
|
|
}
|
|
Py_INCREF(arg);
|
|
}
|
|
else {
|
|
Py_ssize_t argcount = PyTuple_Size(arg);
|
|
PyObject *newarg = PyTuple_New(argcount + 1);
|
|
int i;
|
|
if (newarg == NULL)
|
|
return NULL;
|
|
Py_INCREF(self);
|
|
PyTuple_SET_ITEM(newarg, 0, self);
|
|
for (i = 0; i < argcount; i++) {
|
|
PyObject *v = PyTuple_GET_ITEM(arg, i);
|
|
Py_XINCREF(v);
|
|
PyTuple_SET_ITEM(newarg, i+1, v);
|
|
}
|
|
arg = newarg;
|
|
}
|
|
result = PyObject_Call((PyObject *)func, arg, kw);
|
|
Py_DECREF(arg);
|
|
return result;
|
|
}
|
|
|
|
static PyObject *
|
|
method_descr_get(PyObject *meth, PyObject *obj, PyObject *cls)
|
|
{
|
|
/* Don't rebind an already bound method, or an unbound method
|
|
of a class that's not a base class of cls. */
|
|
|
|
if (PyMethod_GET_SELF(meth) != NULL) {
|
|
/* Already bound */
|
|
Py_INCREF(meth);
|
|
return meth;
|
|
}
|
|
/* No, it is an unbound method */
|
|
if (PyMethod_GET_CLASS(meth) != NULL && cls != NULL) {
|
|
/* Do subclass test. If it fails, return meth unchanged. */
|
|
int ok = PyObject_IsSubclass(cls, PyMethod_GET_CLASS(meth));
|
|
if (ok < 0)
|
|
return NULL;
|
|
if (!ok) {
|
|
Py_INCREF(meth);
|
|
return meth;
|
|
}
|
|
}
|
|
/* Bind it to obj */
|
|
return PyMethod_New(PyMethod_GET_FUNCTION(meth), obj, cls);
|
|
}
|
|
|
|
PyTypeObject PyMethod_Type = {
|
|
PyObject_HEAD_INIT(&PyType_Type)
|
|
0,
|
|
"method",
|
|
sizeof(PyMethodObject),
|
|
0,
|
|
(destructor)method_dealloc, /* tp_dealloc */
|
|
0, /* tp_print */
|
|
0, /* tp_getattr */
|
|
0, /* tp_setattr */
|
|
0, /* tp_compare */
|
|
(reprfunc)method_repr, /* tp_repr */
|
|
0, /* tp_as_number */
|
|
0, /* tp_as_sequence */
|
|
0, /* tp_as_mapping */
|
|
(hashfunc)method_hash, /* tp_hash */
|
|
method_call, /* tp_call */
|
|
0, /* tp_str */
|
|
method_getattro, /* tp_getattro */
|
|
PyObject_GenericSetAttr, /* tp_setattro */
|
|
0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
|
|
method_doc, /* tp_doc */
|
|
(traverseproc)method_traverse, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
method_richcompare, /* tp_richcompare */
|
|
offsetof(PyMethodObject, im_weakreflist), /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
0, /* tp_methods */
|
|
method_memberlist, /* tp_members */
|
|
method_getset, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
method_descr_get, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
0, /* tp_init */
|
|
0, /* tp_alloc */
|
|
method_new, /* tp_new */
|
|
};
|
|
|
|
/* Clear out the free list */
|
|
|
|
void
|
|
PyMethod_Fini(void)
|
|
{
|
|
while (free_list) {
|
|
PyMethodObject *im = free_list;
|
|
free_list = (PyMethodObject *)(im->im_self);
|
|
PyObject_GC_Del(im);
|
|
}
|
|
}
|