[Zodb-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 Zodb-checkins
mailing list