[Zope-Checkins] SVN: Zope/trunk/lib/python/ExtensionClass/ Added a
helper function to set (or clear) the __get__ slot depending
Jim Fulton
jim at zope.com
Sun Apr 10 09:03:12 EDT 2005
Log message for revision 29933:
Added a helper function to set (or clear) the __get__ slot depending
on whether a class has an __of__ definition.
Changed:
U Zope/trunk/lib/python/ExtensionClass/_ExtensionClass.c
U Zope/trunk/lib/python/ExtensionClass/tests.py
-=-
Modified: Zope/trunk/lib/python/ExtensionClass/_ExtensionClass.c
===================================================================
--- Zope/trunk/lib/python/ExtensionClass/_ExtensionClass.c 2005-04-10 01:53:37 UTC (rev 29932)
+++ Zope/trunk/lib/python/ExtensionClass/_ExtensionClass.c 2005-04-10 13:03:12 UTC (rev 29933)
@@ -301,10 +301,42 @@
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 @@
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 @@
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 */
};
Modified: Zope/trunk/lib/python/ExtensionClass/tests.py
===================================================================
--- Zope/trunk/lib/python/ExtensionClass/tests.py 2005-04-10 01:53:37 UTC (rev 29932)
+++ Zope/trunk/lib/python/ExtensionClass/tests.py 2005-04-10 13:03:12 UTC (rev 29933)
@@ -736,7 +736,77 @@
"""
+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
More information about the Zope-Checkins
mailing list