Commit a7f7b252 authored by Jim Fulton's avatar Jim Fulton

Added a helper function to set (or clear) the __get__ slot depending

on whether a class has an __of__ definition.
parent 113fc84a
......@@ -301,10 +301,42 @@ EC_new(PyTypeObject *self, PyObject *args, PyObject *kw)
return result;
}
/* set up __get__, if necessary */
static int
EC_init_of(PyTypeObject *self)
{
PyObject *__of__;
__of__ = PyObject_GetAttr(OBJECT(self), str__of__);
if (__of__)
{
Py_DECREF(__of__);
if (self->tp_descr_get)
{
if (self->tp_descr_get != of_get)
{
PyErr_SetString(PyExc_TypeError,
"Can't mix __of__ and descriptors");
return -1;
}
}
else
self->tp_descr_get = of_get;
}
else
{
PyErr_Clear();
if (self->tp_descr_get == of_get)
self->tp_descr_get = NULL;
}
return 0;
}
static int
EC_init(PyTypeObject *self, PyObject *args, PyObject *kw)
{
PyObject *__class_init__, *__of__, *r;
PyObject *__class_init__, *r;
if (PyType_Type.tp_init(OBJECT(self), args, kw) < 0)
return -1;
......@@ -318,24 +350,8 @@ EC_init(PyTypeObject *self, PyObject *args, PyObject *kw)
return -1;
}
/* set up __get__, if necessary */
if (self->tp_descr_get != of_get)
{
__of__ = PyObject_GetAttr(OBJECT(self), str__of__);
if (__of__)
{
Py_DECREF(__of__);
if (self->tp_descr_get)
{
PyErr_SetString(PyExc_TypeError,
"Can't mix __of__ and descriptors");
return -1;
}
self->tp_descr_get = of_get;
}
else
PyErr_Clear();
}
if (EC_init_of(self) < 0)
return -1;
/* Call __class_init__ */
__class_init__ = PyObject_GetAttr(OBJECT(self), str__class_init__);
......@@ -635,10 +651,28 @@ debug(PyObject *self, PyObject *o)
return Py_None;
}
static PyObject *
pmc_init_of(PyObject *self, PyObject *args)
{
PyObject *o;
if (! PyArg_ParseTuple(args, "O!", (PyObject *)&ExtensionClassType, &o))
return NULL;
if (EC_init_of((PyTypeObject *)o) < 0)
return NULL;
Py_INCREF(Py_None);
return Py_None;
}
/* List of methods defined in the module */
static struct PyMethodDef ec_methods[] = {
{"debug", (PyCFunction)debug, METH_O, ""},
{"pmc_init_of", (PyCFunction)pmc_init_of, METH_VARARGS,
"Initialize __get__ for classes that define __of__"},
{NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
};
......
......@@ -736,6 +736,76 @@ def test___of___w_metaclass_instance():
"""
def test___of__set_after_creation():
"""We may need to set __of__ after a class is created.
Normally, in a class's __init__, the initialization code checks for
an __of__ method and, if it isn't already set, sets __get__.
If a class is persistent and loaded from the database, we want
this to happen in __setstate__. The pmc_init_of function allws us
to do that.
We'll create an extension class without a __of__. We'll also give
it a special meta class, just to make sure that this works with
funny metaclasses too:
>>> import ExtensionClass
>>> class M(ExtensionClass.ExtensionClass):
... "A meta class"
>>> class B(ExtensionClass.Base):
... __metaclass__ = M
... def __init__(self, name):
... self.name = name
... def __repr__(self):
... return self.name
>>> B.__class__ is M
True
>>> x = B('x')
>>> x.y = B('y')
>>> x.y
y
We define a __of__ method for B after the fact:
>>> def __of__(self, other):
... print '__of__(%r, %r)' % (self, other)
... return self
>>> B.__of__ = __of__
We see that this has no effect:
>>> x.y
y
Until we use pmc_init_of:
>>> ExtensionClass.pmc_init_of(B)
>>> x.y
__of__(y, x)
y
Note that there is no harm in calling pmc_init_of multiple times:
>>> ExtensionClass.pmc_init_of(B)
>>> ExtensionClass.pmc_init_of(B)
>>> ExtensionClass.pmc_init_of(B)
>>> x.y
__of__(y, x)
y
If we remove __of__, we'll go back to the behavior we had before:
>>> del B.__of__
>>> ExtensionClass.pmc_init_of(B)
>>> x.y
y
"""
from zope.testing.doctest import DocTestSuite
import unittest
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment