Jeremy Hylton : weblog : 2003-12-11

Recognizing a Descriptor

Thursday, December 11, 2003

If you ask a subclass of Persistent for its _p_oid attribute, you will get a descriptor back; the descriptor's __get__ method returns self when called for the class. I had to fix ZODB's code for recognizing persistent objects, because it wasn't prepare to get a descriptor when it asked for an oid. This caused the error installing Formulator that Sidnei reported yesterday.

The fix is to specifically check for descriptors and ignore them when looking for an oid. I had fixed the problem once before in ZODB 4 (in Python code), but forgot about it when we did the same work for ZODB 3.3 (in C code).

The problem is how do you recognize a descriptor? There is no API for this, in part because any object can be a descriptor. The best you can do is ask for an __get__ attribute. If it has one, it must be a descriptor. That ends up being a lot of work:

	    static PyObject *__get__;
	    PyObject *descr;
	    if (!__get__) {
		__get__ = PyString_InternFromString("__get__");
		if (!__get__)
		    goto err;
	    }
	    descr = PyObject_GetAttr(oid, __get__);
	    if (descr) {
                Py_DECREF(descr);
		goto return_none;
            }

	    /* XXX should check that this was an AttributeError */
	    PyErr_Clear();
You have to check whether the __get__ exists, then clear an exception that occurs if it doesn't exist. In this particular case, I know the C type of the object (PyGetSetDescr_Type), but that name isn't a part of the public API. In general, though, ZODB will support any object with an _p_oid, regardless of how it's implemented. ZODB could support persistent objects written in pure Python, where the type would be different.