[Zope-Checkins] CVS: Zope3/lib/python/Persistence - cPersistence.c:1.5
Jeremy Hylton
jeremy@zope.com
Mon, 24 Jun 2002 14:45:45 -0400
Update of /cvs-repository/Zope3/lib/python/Persistence
In directory cvs.zope.org:/tmp/cvs-serv22733
Modified Files:
cPersistence.c
Log Message:
Add _p_setattr() as simplest possible support for custom __setattr__().
Reorganize various setattr implements into a couple of helper
functions. The tp_setattro field is set to persist_setattro. If a
class defines an __setattr__, it will override the tp_setattro. Thus,
it MUST call _p_setattr() before modifying the object state.
Also move methods, getsets, and members closer to type definitions.
=== Zope3/lib/python/Persistence/cPersistence.c 1.4 => 1.5 ===
}
-static PyMethodDef base_persist_methods[] = {
- {"_p_deactivate", (PyCFunction)persist_deactivate, METH_NOARGS, },
- {"_p_activate", (PyCFunction)_PyPersist_Load, METH_NOARGS, },
- {NULL}
-};
-
-static PyMethodDef persist_methods[] = {
- {"__getstate__", (PyCFunction)persist_getstate, METH_NOARGS, },
- {"__setstate__", persist_setstate, METH_O, },
- {"_p_deactivate", (PyCFunction)persist_deactivate, METH_NOARGS, },
- {"_p_activate", (PyCFunction)_PyPersist_Load, METH_NOARGS, },
- {NULL}
-};
-
static PyObject *
persist_get_state(PyPersistObject *self)
{
@@ -490,44 +476,13 @@
}
-static PyGetSetDef base_persist_getsets[] = {
- {"_p_changed", (getter)persist_get_state, (setter)persist_set_state},
- {NULL}
-};
-
-static PyGetSetDef persist_getsets[] = {
- {"_p_changed", (getter)persist_get_state, (setter)persist_set_state},
- {"__dict__", (getter)persist_get_dict},
- {NULL}
-};
-
-/* XXX should any of these be read-only? */
-
-static PyMemberDef persist_members[] = {
- {"_p_jar", T_OBJECT, offsetof(PyPersistObject, po_dm)},
- {"_p_oid", T_OBJECT, offsetof(PyPersistObject, po_oid)},
- {"_p_serial", T_OBJECT, offsetof(PyPersistObject, po_serial)},
- {"_p_atime", T_INT, offsetof(PyPersistObject, po_atime)},
- /* XXX should this be exposed? */
- {"_p_state", T_INT, offsetof(PyPersistObject, po_state), RO},
- {NULL}
-};
-
-/* The crucial methods for a persistent object are the tp_getattr and
- tp_setattr hooks, which allow the persistence machinery to
- automatically detect changes to and accesses of the object's state.
-
- 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__.
+/* convert_name() returns a new reference to a string name
+ or sets an exception and returns NULL.
*/
static PyObject *
-persist_getattro(PyPersistObject *self, PyObject *name)
+convert_name(PyObject *name)
{
- PyObject *attr;
- char *s_name;
-
/* The ifdef block is copied from PyObject_GenericGetAttr. */
#ifdef Py_USING_UNICODE
/* The Unicode to string conversion is done here because the
@@ -544,7 +499,25 @@
return NULL;
} else
Py_INCREF(name);
+ return name;
+}
+
+/* The crucial methods for a persistent object are the tp_getattr and
+ tp_setattr hooks, which allow the persistence machinery to
+ automatically detect changes to and accesses of the object's state.
+
+ 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__.
+*/
+
+static PyObject *
+persist_getattro(PyPersistObject *self, PyObject *name)
+{
+ PyObject *attr;
+ char *s_name;
+ name = convert_name(name);
s_name = PyString_AS_STRING(name);
/* If any attribute other than an _p_ attribute or __dict__ is
accessed, make sure that the object state is loaded.
@@ -569,41 +542,42 @@
_PyPersist_SetATime((PyPersistBaseObject *)self);
}
+ /* will invoke an __getattr__ if it exists. */
attr = PyObject_GenericGetAttr((PyObject *)self, name);
Py_DECREF(name);
return attr;
}
+/* XXX why is __dict__ special? */
+
+/* persist_setattr_setup() will load the object's state if necessary.
+ Return values:
+
+ -1 : error occurred, exception set
+ 0 : state not loaded, attribute name is _p_*, _v_*, or __dict__
+ 1 : state loaded, attribute name is normal
+*/
+
static int
-persist_setattro(PyPersistObject *self, PyObject *name, PyObject *value)
+persist_setattr_prep(PyPersistBaseObject *self, PyObject *name,
+ PyObject *value)
{
char *s_name;
- int r;
-
- /* The ifdef block is copied from PyObject_GenericGetAttr. */
-#ifdef Py_USING_UNICODE
- /* The Unicode to string conversion is done here because the
- existing tp_setattro slots expect a string object as name
- and we wouldn't want to break those. */
- if (PyUnicode_Check(name)) {
- name = PyUnicode_AsEncodedString(name, NULL, NULL);
- if (name == NULL)
- return -1;
- } else
-#endif
- if (!PyString_Check(name)) {
- PyErr_SetString(PyExc_TypeError, "attribute name must be a string");
- return -1;
- } else
- Py_INCREF(name);
+ assert(PyString_Check(name));
s_name = PyString_AS_STRING(name);
- /* If any attribute other than an _p_ or _v_ attribute or __dict__
- is being assigned to, make sure that the object state is loaded.
- Implement with simple check on s_name[0] to avoid two strncmp()
- calls for all attribute names that don't start with an
- underscore.
+ /* XXX What will go wrong if someone assigns to an _p_ or _v_
+ attribute and we have no state loaded? Is it safe?
+ The current setstate implementation will not delete old values
+ and excludes _p_ and _v_ attributes from the pickle.
+ */
+
+ /* If any attribute other than an _p_ or _v_ attribute or __dict__ is
+ being assigned to, make sure that the object state is loaded.
+
+ Implement with simple check on s_name[0] to avoid multiple strncmp()
+ calls for all attribute names that don't start with an underscore.
*/
if ((s_name[0] != '_')
|| ((strncmp(s_name, "_p_", 3) != 0)
@@ -625,17 +599,60 @@
self->po_state = CHANGED;
_PyPersist_SetATime((PyPersistBaseObject *)self);
}
+ return 1;
}
+ return 0;
+}
- /* XXX What will go wrong if someone assigns to an _p_ or _v_
- attribute and we have no state loaded? Is it safe?
- */
+static int
+persist_setattro(PyPersistBaseObject *self, PyObject *name, PyObject *value)
+{
+ int r;
+
+ name = convert_name(name);
+ if (persist_setattr_prep(self, name, value) < 0)
+ return -1;
r = PyObject_GenericSetAttr((PyObject *)self, name, value);
Py_DECREF(name);
return r;
}
+/* Exported as _p_setattr()
+
+ Returns True if the internal persistence machinery handled the setattr.
+ Returns False if it did not.
+*/
+
+static PyObject *
+persist_p_setattr(PyPersistBaseObject *self, PyObject *args)
+{
+ int r;
+ PyObject *res, *name, *value;
+
+ if (!PyArg_ParseTuple(args, "OO:_p_setattr", &name, &value))
+ return NULL;
+
+ name = convert_name(name);
+ r = persist_setattr_prep(self, name, value);
+ if (r < 0)
+ return NULL;
+ else if (r > 0)
+ res = Py_False;
+ else {
+ /* r == 0 implies the name is _p_, _v_, or __dict__,
+ and that we should handle it.
+ */
+ res = Py_True;
+ r = PyObject_GenericSetAttr((PyObject *)self, name, value);
+ if (r < 0)
+ return NULL;
+ }
+ Py_INCREF(res);
+ Py_DECREF(name);
+ return res;
+}
+
static void
persist_dealloc(PyPersistObject *self)
{
@@ -684,6 +701,45 @@
}
return 0;
}
+
+static PyMethodDef base_persist_methods[] = {
+ {"_p_activate", (PyCFunction)_PyPersist_Load, METH_NOARGS, },
+ {"_p_deactivate", (PyCFunction)persist_deactivate, METH_NOARGS, },
+ {"_p_setattr", (PyCFunction)persist_p_setattr, METH_VARARGS, },
+ {NULL}
+};
+
+static PyMethodDef persist_methods[] = {
+ {"__getstate__", (PyCFunction)persist_getstate, METH_NOARGS, },
+ {"__setstate__", persist_setstate, METH_O, },
+ {"_p_activate", (PyCFunction)_PyPersist_Load, METH_NOARGS, },
+ {"_p_deactivate", (PyCFunction)persist_deactivate, METH_NOARGS, },
+ {"_p_setattr", (PyCFunction)persist_p_setattr, METH_VARARGS, },
+ {NULL}
+};
+
+static PyGetSetDef base_persist_getsets[] = {
+ {"_p_changed", (getter)persist_get_state, (setter)persist_set_state},
+ {NULL}
+};
+
+static PyGetSetDef persist_getsets[] = {
+ {"_p_changed", (getter)persist_get_state, (setter)persist_set_state},
+ {"__dict__", (getter)persist_get_dict},
+ {NULL}
+};
+
+/* XXX should any of these be read-only? */
+
+static PyMemberDef persist_members[] = {
+ {"_p_jar", T_OBJECT, offsetof(PyPersistObject, po_dm)},
+ {"_p_oid", T_OBJECT, offsetof(PyPersistObject, po_oid)},
+ {"_p_serial", T_OBJECT, offsetof(PyPersistObject, po_serial)},
+ {"_p_atime", T_INT, offsetof(PyPersistObject, po_atime)},
+ /* XXX should this be exposed? */
+ {"_p_state", T_INT, offsetof(PyPersistObject, po_state), RO},
+ {NULL}
+};
/* We link this module statically for convenience. If compiled as a shared
library instead, some compilers don't allow addresses of Python objects