[Zope-Checkins] CVS: ZODB3/persistent - README.txt:1.1.2.1 cPersistence.c:1.75.2.5

Jim Fulton jim at zope.com
Wed Jan 28 07:16:19 EST 2004


Update of /cvs-repository/ZODB3/persistent
In directory cvs.zope.org:/tmp/cvs-serv24413/src/persistent

Modified Files:
      Tag: zope3-zodb3-devel-branch
	cPersistence.c 
Added Files:
      Tag: zope3-zodb3-devel-branch
	README.txt 
Log Message:
Ported and extended the mechanism from ZODB 4 for supporting
overriding of __getattr__, __getattribute__, __setattr__, and
__delattr__.

Added _p_getattr, _p_setattr, and _p_delattr methods.


=== Added File ZODB3/persistent/README.txt ===
===================
Persistence support
==================

(This document is under construction. More basic documentation will
 eventually appear here.)


Overriding __getattr__, __getattribute__, __setattr__, and __delattr__
-----------------------------------------------------------------------  

Subclasses can override the attribute-management methods.  For the
__getattr__ method, the behavior is like that for regular Python
classes and for earlier versions of ZODB 3.

For __getattribute__, __setattr__, and __delattr__, it is necessary to
cal certain methods defined by persistent.Persistent.  Detailed
examples and documentation is provided in the test module,
persistent.tests.test_overriding_attrs.




  


=== ZODB3/persistent/cPersistence.c 1.75.2.4 => 1.75.2.5 ===
--- ZODB3/persistent/cPersistence.c:1.75.2.4	Fri Jan 16 16:16:35 2004
+++ ZODB3/persistent/cPersistence.c	Wed Jan 28 07:16:18 2004
@@ -219,6 +219,8 @@
     return Py_None;
 }
 
+static int Per_set_changed(cPersistentObject *self, PyObject *v);
+
 static PyObject *
 Per__p_invalidate(cPersistentObject *self)
 {
@@ -262,24 +264,6 @@
     return pickle___getstate__((PyObject*)self);
 }
 
-
-static struct PyMethodDef Per_methods[] = {
-  {"_p_deactivate", (PyCFunction)Per__p_deactivate, METH_NOARGS,
-   "_p_deactivate() -- Deactivate the object"},
-  {"_p_activate", (PyCFunction)Per__p_activate, METH_NOARGS,
-   "_p_activate() -- Activate the object"},
-  {"_p_invalidate", (PyCFunction)Per__p_invalidate, METH_NOARGS,
-   "_p_invalidate() -- Invalidate the object"},
-  {"__getstate__", (PyCFunction)Per__getstate__, METH_NOARGS,
-   pickle___getstate__doc },
-
-  PICKLE_SETSTATE_DEF
-  PICKLE_GETNEWARGS_DEF
-  PICKLE_REDUCE_DEF
-
-  {NULL,		NULL}		/* sentinel */
-};
-
 /* The Persistent base type provides a traverse function, but not a
    clear function.  An instance of a Persistent subclass will have
    its dict cleared through subtype_clear().
@@ -418,23 +402,37 @@
     return result;
 }
 
-/* We need to decide on a reasonable way for a programmer to write
-   an __setattr__() or __delattr__() hook.
+/* Exposed as _p_getattr method.  Test whether base getattr should be used */
+static PyObject *
+Per__p_getattr(cPersistentObject *self, PyObject *name)
+{
+    PyObject *result = NULL;	/* guilty until proved innocent */
+    char *s;
 
-   The ZODB3 has been that if you write a hook, it will be called if
-   the attribute is not an _p_ attribute and after doing any necessary
-   unghostifying.  AMK's guide says modification will not be tracked
-   automatically, so the hook must explicitly set _p_changed; I'm not
-   sure if I believe that.
-
-   This approach won't work with new-style classes, because type will
-   install a slot wrapper that calls the derived class's __setattr__().
-   That means Persistent's tp_setattro doesn't get a chance to be called.
-   Changing this behavior would require a metaclass.
-
-   One option for ZODB 3.3 is to require setattr hooks to know about
-   _p_ and to call a prep function before modifying the object's state.
-   That's the solution I like best at the moment.
+    name = convert_name(name);
+    if (!name)
+	goto Done;
+    s = PyString_AS_STRING(name);
+
+    if (*s != '_' || unghost_getattr(s)) 
+      {
+	if (unghostify(self) < 0)
+	    goto Done;
+	accessed(self);
+        result = Py_False;
+      }
+    else
+      result = Py_True;
+      
+    Py_INCREF(result);
+
+  Done:
+    Py_XDECREF(name);
+    return result;
+}
+
+/* 
+   XXX we should probably not allow assignment of __class__ and __dict__.
 */
 
 static int
@@ -465,6 +463,72 @@
     return result;
 }
 
+
+static int
+Per_p_set_or_delattro(cPersistentObject *self, PyObject *name, PyObject *v)
+{
+    int result = -1;	/* guilty until proved innocent */
+    char *s;
+
+    name = convert_name(name);
+    if (!name)
+	goto Done;
+    s = PyString_AS_STRING(name);
+
+    if (strncmp(s, "_p_", 3) != 0) 
+      {
+	if (unghostify(self) < 0)
+	    goto Done;
+	accessed(self);
+
+        result = 0;
+      }
+    else
+      {
+        if (PyObject_GenericSetAttr((PyObject *)self, name, v) < 0)
+          goto Done;
+        result = 1;
+      }
+
+ Done:
+    Py_XDECREF(name);
+    return result;
+}
+
+static PyObject *
+Per__p_setattr(cPersistentObject *self, PyObject *args)
+{
+  PyObject *name, *v, *result;
+  int r;
+
+  if (! PyArg_ParseTuple(args, "OO:_p_setattr", &name, &v))
+    return NULL;
+
+  r = Per_p_set_or_delattro(self, name, v);
+  if (r < 0)
+    return NULL;
+
+  result = r ? Py_True : Py_False;
+  Py_INCREF(result);
+  return result;
+}
+
+static PyObject *
+Per__p_delattr(cPersistentObject *self, PyObject *name)
+{
+  int r;
+  PyObject *result;
+
+  r = Per_p_set_or_delattro(self, name, NULL);
+  if (r < 0)
+    return NULL;
+
+  result = r ? Py_True : Py_False;
+  Py_INCREF(result);
+  return result;
+}
+
+
 static PyObject *
 Per_get_changed(cPersistentObject *self)
 {
@@ -650,6 +714,54 @@
     {"_p_serial", (getter)Per_get_serial, (setter)Per_set_serial},
     {"_p_state", (getter)Per_get_state},
     {NULL}
+};
+
+static struct PyMethodDef Per_methods[] = {
+  {"_p_deactivate", (PyCFunction)Per__p_deactivate, METH_NOARGS,
+   "_p_deactivate() -- Deactivate the object"},
+  {"_p_activate", (PyCFunction)Per__p_activate, METH_NOARGS,
+   "_p_activate() -- Activate the object"},
+  {"_p_invalidate", (PyCFunction)Per__p_invalidate, METH_NOARGS,
+   "_p_invalidate() -- Invalidate the object"},
+  {"_p_getattr", (PyCFunction)Per__p_getattr, METH_O,
+   "_p_getattr(name) -- Test whether the base class must handle the name\n"
+   "\n"
+   "The method unghostifies the object, if necessary.\n"
+   "The method records the object access, if necessary.\n"
+   "\n"
+   "This method should be called by subclass __getattribute__\n"
+   "implementations before doing anything else. If the method\n"
+   "returns True, then __getattribute__ implementations must delegate\n"
+   "to the base class, Persistent.\n"
+  },
+  {"_p_setattr", (PyCFunction)Per__p_setattr, METH_VARARGS,
+   "_p_setattr(name, value) -- Save persistent meta data\n"
+   "\n"
+   "This method should be called by subclass __setattr__ implementations\n"
+   "before doing anything else.  If it returns true, then the attribute\n"
+   "was handled by the base class.\n"
+   "\n"
+   "The method unghostifies the object, if necessary.\n"
+   "The method records the object access, if necessary.\n"
+  },
+  {"_p_delattr", (PyCFunction)Per__p_delattr, METH_O,
+   "_p_delattr(name) -- Delete persistent meta data\n"
+   "\n"
+   "This method should be called by subclass __delattr__ implementations\n"
+   "before doing anything else.  If it returns true, then the attribute\n"
+   "was handled by the base class.\n"
+   "\n"
+   "The method unghostifies the object, if necessary.\n"
+   "The method records the object access, if necessary.\n"
+  },
+  {"__getstate__", (PyCFunction)Per__getstate__, METH_NOARGS,
+   pickle___getstate__doc },
+
+  PICKLE_SETSTATE_DEF
+  PICKLE_GETNEWARGS_DEF
+  PICKLE_REDUCE_DEF
+
+  {NULL,		NULL}		/* sentinel */
 };
 
 /* This module is compiled as a shared library.  Some compilers don't




More information about the Zope-Checkins mailing list