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
 

_______________________________________________
Zope-Checkins maillist  -  Zope-Checkins@zope.org
http://mail.zope.org/mailman/listinfo/zope-checkins

Reply via email to