[Zope3-checkins] CVS: Zope3/src/persistence - persistence.c:1.10

Jeremy Hylton jeremy@zope.com
Thu, 3 Apr 2003 19:00:20 -0500


Update of /cvs-repository/Zope3/src/persistence
In directory cvs.zope.org:/tmp/cvs-serv26419

Modified Files:
	persistence.c 
Log Message:
Don't unghostify an object for __del__.

Persistent objects are often in cycles -- they point to their data
manager and their data manager points to them.  Python 2.2 will look
for an __del__ attribute on an instance that is unreachable.  We don't
want to unghostify an object in this case.

XXX Should enforce the policy that Persistent objects can't have an
__del__. 

XXX Must backport to Zope2.


=== Zope3/src/persistence/persistence.c 1.9 => 1.10 ===
--- Zope3/src/persistence/persistence.c:1.9	Wed Apr  2 18:07:00 2003
+++ Zope3/src/persistence/persistence.c	Thu Apr  3 19:00:20 2003
@@ -15,6 +15,7 @@
 #include "structmember.h"
 #include "persistence.h"
 
+
 static char PyPersist_doc_string[] =
 "Defines Persistent mixin class for persistent objects.\n"
 "\n"
@@ -84,6 +85,7 @@
     meth = PyObject_GetAttr((PyObject *)self->po_dm, s_setstate);
     if (meth == NULL)
 	return 0;
+
     arg = PyTuple_New(1);
     if (arg == NULL) {
 	Py_DECREF(meth);
@@ -91,9 +93,12 @@
     }
     Py_INCREF(self);
     PyTuple_SET_ITEM(arg, 0, (PyObject *)self);
+
     result = PyObject_Call(meth, arg, NULL);
+
     Py_DECREF(arg);
     Py_DECREF(meth);
+
     if (result) {
 	Py_DECREF(result);
 	return 1;
@@ -367,11 +372,49 @@
    tp_setattr hooks, which allow the persistence machinery to
    automatically detect changes to and accesses of the object's state.
 
+   In general, if getattr() is called on a ghost, the data manager
+   must load the ghost's state before calling PyObject_GenericGetAttr().
+   There are several special attributes that ignore this rule.
+
    The current implemenation probably isn't right, because it doesn't
    even attempt to deal with a persistent classes that defines its own
    __getattr__ or __getattribute__.  
 */
 
+/* Returns true if the object requires unghostification.
+
+   Don't unghostify for any attribute starting with _p_.  The Python
+   special names __del__, __dict__, and __class__ are also exempt.
+*/
+
+static int
+persist_checkattr(const char *s)
+{
+    if (*s++ != '_')
+	return 1;
+    if (*s == 'p') {
+	s++;
+	if (*s == '_')  
+	    return 0; /* _p_ */
+	else
+	    return 1; 
+    }
+    else if (*s == '_') {
+	s++;
+	if (*s == 'd') {
+	    s++;
+	    if (!strncmp(s, "ict__", 5))
+		return 0; /* __dict__ */
+	    if (!strncmp(s, "el__", 4))
+		return 0; /* __del__ */
+	    return 1;
+	}
+	else if (!strncmp(s, "class__", 7))
+	    return 0; /* __class__ */
+    }
+    return 1;
+}
+
 static PyObject *
 persist_getattro(PyPersistObject *self, PyObject *name)
 {
@@ -392,10 +435,7 @@
        XXX Don't revive a ghost just to get its __class__.
     */
 
-    if ((s_name[0] != '_') ||
-	((strncmp(s_name, "_p_", 3) != 0) &&
-	 (strcmp(s_name, "__dict__") != 0) &&
-	 (strcmp(s_name, "__class__") != 0))) {
+    if (persist_checkattr(s_name)) {
 	if (self->po_state == GHOST) {
 	    /* Prevent the object from being registered as changed.
 
@@ -514,6 +554,7 @@
     }
     r = PyObject_GenericSetAttr((PyObject *)self, name, value);
     Py_DECREF(name);
+
     return r;
 }