File contents
/*****************************************************************************
Zope Public License (ZPL) Version 1.0
-------------------------------------
Copyright (c) Digital Creations. All rights reserved.
This license has been certified as Open Source(tm).
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions in source code must retain the above copyright
notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions, and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
3. Digital Creations requests that attribution be given to Zope
in any manner possible. Zope includes a "Powered by Zope"
button that is installed by default. While it is not a license
violation to remove this button, it is requested that the
attribution remain. A significant investment has been put
into Zope, and this effort will continue if the Zope community
continues to grow. This is one way to assure that growth.
4. All advertising materials and documentation mentioning
features derived from or use of this software must display
the following acknowledgement:
"This product includes software developed by Digital Creations
for use in the Z Object Publishing Environment
(http://www.zope.org/)."
In the event that the product being advertised includes an
intact Zope distribution (with copyright and license included)
then this clause is waived.
5. Names associated with Zope or Digital Creations must not be used to
endorse or promote products derived from this software without
prior written permission from Digital Creations.
6. Modified redistributions of any form whatsoever must retain
the following acknowledgment:
"This product includes software developed by Digital Creations
for use in the Z Object Publishing Environment
(http://www.zope.org/)."
Intact (re-)distributions of any official Zope release do not
require an external acknowledgement.
7. Modifications are encouraged but must be packaged separately as
patches to official Zope releases. Distributions that do not
clearly separate the patches from the original work must be clearly
labeled as unofficial distributions. Modifications which do not
carry the name Zope may be packaged in any form, as long as they
conform to all of the clauses above.
Disclaimer
THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
This software consists of contributions made by Digital Creations and
many individuals on behalf of Digital Creations. Specific
attributions are listed in the accompanying credits file.
****************************************************************************/
static char cPersistence_doc_string[] =
"Defines Persistent mixin class for persistent objects.\n"
"\n"
"$Id$\n";
#include <string.h>
#include "cPersistence.h"
/* the layout of this struct is the same as the start of ccobject in cPickleCache.c */
struct ccobject_head_struct {
PyObject_HEAD
CPersistentRing ring_home;
int non_ghost_count;
};
#define HOME(O) ((!((O)->cache))?(NULL): (&(((struct ccobject_head_struct *)((O)->cache))->ring_home)) )
#define NON_GHOST_COUNT(O) ((!((O)->cache))?(NULL): (&(((struct ccobject_head_struct *)((O)->cache))->non_ghost_count)) )
#define ASSIGN(V,E) {PyObject *__e; __e=(E); Py_XDECREF(V); (V)=__e;}
#define UNLESS(E) if(!(E))
#define UNLESS_ASSIGN(V,E) ASSIGN(V,E) UNLESS(V)
#define OBJECT(V) ((PyObject*)(V))
static PyObject *py_keys, *py_setstate, *py___dict__, *py_timeTime;
static PyObject *py__p_changed, *py__p_deactivate;
static PyObject *py___getattr__, *py___setattr__, *py___delattr__;
static PyObject *TimeStamp;
#ifdef DEBUG_LOG
static PyObject *debug_log=0;
static int idebug_log=0;
static void *
call_debug(char *event, cPersistentObject *self)
{
PyObject *r;
/*
printf("%s %p\n",event,self->ob_type->tp_name);
*/
r=PyObject_CallFunction(debug_log,"s(sOi)",event,
self->ob_type->tp_name, self->oid,
self->state);
Py_XDECREF(r);
}
#endif
static int
init_strings(void)
{
#define INIT_STRING(S) if (! (py_ ## S = PyString_FromString(#S))) return -1;
INIT_STRING(keys);
INIT_STRING(setstate);
INIT_STRING(timeTime);
INIT_STRING(__dict__);
INIT_STRING(_p_changed);
INIT_STRING(_p_deactivate);
INIT_STRING(__getattr__);
INIT_STRING(__setattr__);
INIT_STRING(__delattr__);
#undef INIT_STRING
return 0;
}
static PyObject *
callmethod(PyObject *self, PyObject *name)
{
self=PyObject_GetAttr(self,name);
if(self)
ASSIGN(self,PyObject_CallObject(self,NULL));
return self;
}
static PyObject *
callmethod1(PyObject *self, PyObject *name, PyObject *arg)
{
if((self=PyObject_GetAttr(self,name)) && (name=PyTuple_New(1)))
{
PyTuple_SET_ITEM(name, 0, arg);
ASSIGN(self,PyObject_CallObject(self,name));
PyTuple_SET_ITEM(name, 0, NULL);
Py_DECREF(name);
}
return self;
}
#define UPDATE_STATE_IF_NECESSARY(self, ER) \
if(self->state < 0 && self->jar) \
{ \
PyObject *r; \
\
int *count = NON_GHOST_COUNT(self); \
if(count) \
{ \
(*count)++; \
self->ring.next = HOME(self); \
self->ring.prev = HOME(self)->prev; \
HOME(self)->prev->next = &self->ring; \
HOME(self)->prev = &self->ring; \
} \
self->state=cPersistent_CHANGED_STATE; \
UNLESS(r=callmethod1(self->jar,py_setstate,(PyObject*)self)) \
{ \
ghostify(self); \
return ER; \
} \
self->state=cPersistent_UPTODATE_STATE; \
Py_DECREF(r); \
}
#define KEEP_THIS_ONE_AROUND_FOR_A_WHILE(self) \
if(HOME(self) && self->state>=0) { \
self->ring.prev->next = self->ring.next; \
self->ring.next->prev = self->ring.prev; \
self->ring.next = HOME(self); \
self->ring.prev = HOME(self)->prev; \
HOME(self)->prev->next = &self->ring; \
HOME(self)->prev = &self->ring; }
/****************************************************************************/
staticforward PyExtensionClass Pertype;
static void
accessed(cPersistentObject *self)
{
KEEP_THIS_ONE_AROUND_FOR_A_WHILE(self);
}
static void
ghostify(cPersistentObject *self)
{
int *count;
count = NON_GHOST_COUNT(self);
if(count && (self->state>=0))
{
(*count)--;
self->ring.next->prev = self->ring.prev;
self->ring.prev->next = self->ring.next;
self->ring.prev = NULL;
self->ring.next = NULL;
}
self->state = cPersistent_GHOST_STATE;
}
static void
deallocated(cPersistentObject *self)
{
if(self->state>=0) ghostify(self);
if(self->cache)
{
PyObject *v=PyObject_CallMethod(self->cache,"_oid_unreferenced","O",self->oid);
if(!v) PyErr_Clear(); /* and explode later */
Py_XDECREF(v);
}
Py_XDECREF(self->jar);
Py_XDECREF(self->oid);
}
static int
changed(cPersistentObject *self)
{
static PyObject *builtins=0, *get_transaction=0, *py_register=0;
PyObject *T;
if ((self->state == cPersistent_UPTODATE_STATE ||
self->state == cPersistent_STICKY_STATE)
&& self->jar)
{
UNLESS (get_transaction)
{
UNLESS (py_register=PyString_FromString("register")) return -1;
UNLESS (T=PyImport_ImportModule("__main__")) return -1;
ASSIGN(T,PyObject_GetAttrString(T,"__builtins__"));
UNLESS (T) return -1;
builtins=T;
UNLESS (get_transaction=PyObject_GetAttrString(builtins,
"get_transaction"))
PyErr_Clear();
}
if (get_transaction)
{
UNLESS (T=PyObject_CallObject(get_transaction,NULL)) return -1;
ASSIGN(T,PyObject_GetAttr(T,py_register));
UNLESS (T) return -1;
ASSIGN(T, PyObject_CallFunction(T,"O",self));
if (T) Py_DECREF(T);
else return -1;
}
self->state=cPersistent_CHANGED_STATE;
}
return 0;
}
static PyObject *
Per___changed__(cPersistentObject *self, PyObject *args)
{
PyObject *v=0;
if (args && ! PyArg_ParseTuple(args, "|O",&v)) return NULL;
if (! v) return PyObject_GetAttr(OBJECT(self), py__p_changed);
if (PyObject_IsTrue(v))
{
if (changed(self) < 0) return NULL;
}
else if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
Per__p_deactivate(cPersistentObject *self, PyObject *args)
{
PyObject *dict,*dict2=NULL;
#ifdef DEBUG_LOG
if (idebug_log < 0) call_debug("reinit",self);
#endif
if (args && ! PyArg_ParseTuple(args,"")) return NULL;
if (self->state==cPersistent_UPTODATE_STATE && self->jar &&
HasInstDict(self) && (dict=INSTANCE_DICT(self)))
{
dict2 = PyDict_Copy(dict);
PyDict_Clear(dict);
/* Note that we need to set to ghost state unless we are
called directly. Methods that override this need to
do the same! */
ghostify(self);
}
/* need to delay releasing the last reference on instance attributes
until after we have finished accounting for losing our state */
if(dict2)
{
PyDict_Clear(dict2);
Py_DECREF(dict2);
}
Py_INCREF(Py_None);
return Py_None;
}
/* Load the object's state if necessary and become sticky */
static int
Per_setstate(cPersistentObject *self)
{
UPDATE_STATE_IF_NECESSARY(self, -1);
self->state=cPersistent_STICKY_STATE;
return 0;
}
static PyObject *
Per__getstate__(cPersistentObject *self, PyObject *args)
{
PyObject *__dict__, *d=0;
UNLESS(PyArg_ParseTuple(args, "")) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("get",self);
#endif
UPDATE_STATE_IF_NECESSARY(self, NULL);
if(HasInstDict(self) && (__dict__=INSTANCE_DICT(self)))
{
PyObject *k, *v;
int pos;
char *ck;
for(pos=0; PyDict_Next(__dict__, &pos, &k, &v); )
{
if(PyString_Check(k) && (ck=PyString_AsString(k)) &&
(*ck=='_' && ck[1]=='v' && ck[2]=='_'))
{
UNLESS(d=PyDict_New()) goto err;
for(pos=0; PyDict_Next(__dict__, &pos, &k, &v); )
UNLESS(PyString_Check(k) && (ck=PyString_AsString(k)) &&
(*ck=='_' && ck[1]=='v' && ck[2]=='_'))
if(PyDict_SetItem(d,k,v) < 0) goto err;
return d;
}
}
}
else
__dict__=Py_None;
Py_INCREF(__dict__);
return __dict__;
err:
Py_XDECREF(d);
return NULL;
}
static PyObject *
Per__setstate__(cPersistentObject *self, PyObject *args)
{
PyObject *__dict__, *v, *keys=0, *key=0, *e=0;
int l, i;
if(HasInstDict(self))
{
UNLESS(PyArg_ParseTuple(args, "O", &v)) return NULL;
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("set",self);
#endif
if(v!=Py_None)
{
__dict__=INSTANCE_DICT(self);
if(PyDict_Check(v))
{
for(i=0; PyDict_Next(v,&i,&key,&e);)
if(PyObject_SetItem(__dict__,key,e) < 0)
return NULL;
}
else
{
UNLESS(keys=callmethod(v,py_keys)) goto err;
UNLESS(-1 != (l=PyObject_Length(keys))) goto err;
for(i=0; i < l; i++)
{
UNLESS_ASSIGN(key,PySequence_GetItem(keys,i)) goto err;
UNLESS_ASSIGN(e,PyObject_GetItem(v,key)) goto err;
UNLESS(-1 != PyObject_SetItem(__dict__,key,e)) goto err;
}
Py_XDECREF(key);
Py_XDECREF(e);
Py_DECREF(keys);
}
}
}
Py_INCREF(Py_None);
return Py_None;
err:
Py_XDECREF(key);
Py_XDECREF(e);
Py_XDECREF(keys);
return NULL;
}
static struct PyMethodDef Per_methods[] = {
{"__changed__", (PyCFunction)Per___changed__, METH_VARARGS,
"DEPRECATED: use self._p_changed=1"},
{"_p_deactivate", (PyCFunction)Per__p_deactivate, METH_VARARGS,
"_p_deactivate(oid) -- Deactivate the object"},
{"__getstate__", (PyCFunction)Per__getstate__, METH_VARARGS,
"__getstate__() -- Return the state of the object" },
{"__setstate__", (PyCFunction)Per__setstate__, METH_VARARGS,
"__setstate__(v) -- Restore the saved state of the object from v" },
{NULL, NULL} /* sentinel */
};
/* ---------- */
static void
Per_dealloc(cPersistentObject *self)
{
#ifdef DEBUG_LOG
if(idebug_log < 0) call_debug("del",self);
#endif
deallocated(self);
Py_XDECREF(self->cache);
Py_DECREF(self->ob_type);
/* needed when we have weakrefs
if(HOME(self))
*{
* self->ring.next->prev = self->ring.prev;
* self->ring.prev->next = self->ring.next;
}*/
PyMem_DEL(self);
}
static PyObject *
orNothing(PyObject *v)
{
if (! v) v=Py_None;
Py_INCREF(v);
return v;
}
static PyObject *
Per_getattr(cPersistentObject *self, PyObject *oname, char *name,
PyObject *(*getattrf)(PyObject *, PyObject*))
{
char *n=name;
if(n && *n++=='_')
if(*n++=='p' && *n++=='_')
{
switch(*n++)
{
case 'o':
if(*n++=='i' && *n++=='d' && ! *n) return orNothing(self->oid);
break;
case 'j':
if(*n++=='a' && *n++=='r' && ! *n) return orNothing(self->jar);
break;
case 'c':
if(strcmp(n,"hanged")==0)
{
if(self->state < 0)
{
Py_INCREF(Py_None);
return Py_None;
}
return PyInt_FromLong(self->state ==
cPersistent_CHANGED_STATE);
}
break;
case 's':
if(strcmp(n,"erial")==0)
return PyString_FromStringAndSize(self->serial, 8);
if(strcmp(n,"elf")==0)
return orNothing(OBJECT(self));
break;
case 'm':
if(strcmp(n,"time")==0)
{
UPDATE_STATE_IF_NECESSARY(self, NULL);
KEEP_THIS_ONE_AROUND_FOR_A_WHILE(self);
if (self->serial[7]=='\0' && self->serial[6]=='\0' &&
self->serial[5]=='\0' && self->serial[4]=='\0' &&
self->serial[3]=='\0' && self->serial[2]=='\0' &&
self->serial[1]=='\0' && self->serial[0]=='\0')
{
Py_INCREF(Py_None);
return Py_None;
}
oname=PyString_FromStringAndSize(self->serial, 8);
if (! oname) return oname;
ASSIGN(oname, PyObject_CallFunction(TimeStamp, "O", oname));
if (! oname) return oname;
ASSIGN(oname, PyObject_GetAttr(oname, py_timeTime));
if (! oname) return oname;
ASSIGN(oname, PyObject_CallObject(oname, NULL));
return oname;
}
break;
}
return getattrf((PyObject *)self, oname);
}
if(! (name && *name++=='_' && *name++=='_' &&
(strcmp(name,"dict__")==0 || strcmp(name,"class__")==0
|| strcmp(name, "of__")==0)))
{
UPDATE_STATE_IF_NECESSARY(self, NULL);
KEEP_THIS_ONE_AROUND_FOR_A_WHILE(self);
}
return getattrf((PyObject *)self, oname);
}
static PyObject*
Per_getattro(cPersistentObject *self, PyObject *name)
{
char *s=NULL;
PyObject *r;
if (PyString_Check(name))
UNLESS(s=PyString_AsString(name)) return NULL;
r = Per_getattr(self, name, s, PyExtensionClassCAPI->getattro);
if (! r && self->state != cPersistent_GHOST_STATE &&
(((PyExtensionClass*)(self->ob_type))->class_flags
& EXTENSIONCLASS_USERGETATTR_FLAG)
)
{
PyErr_Clear();
r=PyObject_GetAttr(OBJECT(self), py___getattr__);
if (r)
{
ASSIGN(r, PyObject_CallFunction(r, "O", name));
}
else PyErr_SetObject(PyExc_AttributeError, name);
}
return r;
}
static int
_setattro(cPersistentObject *self, PyObject *oname, PyObject *v,
int (*setattrf)(PyObject *, PyObject*, PyObject*))
{
char *name="";
UNLESS(oname) return -1;
if(PyString_Check(oname)) UNLESS(name=PyString_AsString(oname)) return -1;
if(*name=='_' && name[1]=='p' && name[2]=='_')
{
if(name[3]=='o' && name[4]=='i' && name[5]=='d' && ! name[6])
{
if(HOME(self))
{
/* maybe we should check that the oid is actually different */
PyErr_SetString(PyExc_ValueError,"can not change the oid of a cached object");
return -1;
}
Py_XINCREF(v);
ASSIGN(self->oid, v);
return 0;
}
if(name[3]=='j' && name[4]=='a' && name[5]=='r' && ! name[6])
{
Py_XINCREF(v);
ASSIGN(self->jar, v);
return 0;
}
if(name[3]=='s' && strcmp(name+4,"erial")==0)
{
if (v)
{
if (PyString_Check(v) && PyString_Size(v)==8)
memcpy(self->serial, PyString_AS_STRING(v), 8);
else
{
PyErr_SetString(PyExc_ValueError,
"_p_serial must be an 8-character string");
return -1;
}
}
else
memset(self->serial, 0, 8);
return 0;
}
if(name[3]=='c' && strcmp(name+4,"hanged")==0)
{
if (! v)
{
/* delatter is used to invalidate the object
*even* if it has changed.
*/
if (self->state != cPersistent_GHOST_STATE)
self->state = cPersistent_UPTODATE_STATE;
v=Py_None;
}
if (v==Py_None)
{
v=PyObject_GetAttr(OBJECT(self), py__p_deactivate);
if (v) { ASSIGN(v, PyObject_CallObject(v, NULL)); }
if (v) { Py_DECREF(v); }
self->state=cPersistent_GHOST_STATE; /* is this assignment redundant ; deactivate() will do it too? */
return 0;
}
if (PyObject_IsTrue(v)) return changed(self);
if (self->state >= 0) self->state=cPersistent_UPTODATE_STATE;
return 0;
}
}
else
{
UPDATE_STATE_IF_NECESSARY(self, -1);
KEEP_THIS_ONE_AROUND_FOR_A_WHILE(self);
if((! (*name=='_' && name[1]=='v' && name[2]=='_'))
&& (self->state != cPersistent_CHANGED_STATE && self->jar)
&& setattrf
)
if(changed(self) < 0) return -1;
}
if (setattrf)
return setattrf((PyObject*)self,oname,v);
return 1; /* Ready for user setattr */
}
static int
Per_setattro(cPersistentObject *self, PyObject *oname, PyObject *v)
{
int r;
PyObject *m;
if (v && (((PyExtensionClass*)self->ob_type)->class_flags
& EXTENSIONCLASS_USERSETATTR_FLAG)
)
{
r=_setattro(self,oname, v, NULL);
if (r < 1) return r;
m=PyObject_GetAttr(OBJECT(self), py___setattr__);
if (m)
{
ASSIGN(m, PyObject_CallFunction(m, "OO", oname, v));
}
else PyErr_SetObject(PyExc_AttributeError, oname);
}
else if (!v && (((PyExtensionClass*)self->ob_type)->class_flags
& EXTENSIONCLASS_USERDELATTR_FLAG)
)
{
r=_setattro(self,oname, v, NULL);
if (r < 1) return r;
m=PyObject_GetAttr(OBJECT(self), py___delattr__);
if (m)
{
ASSIGN(m, PyObject_CallFunction(m, "O", oname));
}
else PyErr_SetObject(PyExc_AttributeError, oname);
}
else
return _setattro(self,oname, v, PyExtensionClassCAPI->setattro);
if (m)
{
Py_DECREF(m);
return 0;
}
return -1;
}
static PyExtensionClass Pertype = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"Persistent", /*tp_name*/
sizeof(cPersistentObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)Per_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)Per_getattro, /*tp_getattr with object key*/
(setattrofunc)Per_setattro, /*tp_setattr with object key*/
/* Space for future expansion */
0L,0L,
"Base class for objects that are stored in their own database records",
METHOD_CHAIN(Per_methods),
PERSISTENCE_FLAGS,
};
static PyExtensionClass Overridable = {
PyObject_HEAD_INIT(NULL)
0, /*ob_size*/
"Overridable", /*tp_name*/
sizeof(cPersistentObject), /*tp_basicsize*/
0, /*tp_itemsize*/
/* methods */
(destructor)Per_dealloc, /*tp_dealloc*/
(printfunc)0, /*tp_print*/
(getattrfunc)0, /*tp_getattr*/
(setattrfunc)0, /*tp_setattr*/
(cmpfunc)0, /*tp_compare*/
(reprfunc)0, /*tp_repr*/
0, /*tp_as_number*/
0, /*tp_as_sequence*/
0, /*tp_as_mapping*/
(hashfunc)0, /*tp_hash*/
(ternaryfunc)0, /*tp_call*/
(reprfunc)0, /*tp_str*/
(getattrofunc)Per_getattro, /*tp_getattr with object key*/
(setattrofunc)Per_setattro, /*tp_setattr with object key*/
/* Space for future expansion */
0L,0L,
"Hacked base class used in Zope's App.Uninstalled.BrokenClass\n\n"
"Not sure if this is still needed",
METHOD_CHAIN(Per_methods),
EXTENSIONCLASS_BASICNEW_FLAG | PERSISTENT_TYPE_FLAG
};
/* End of code for Persistent objects */
/* -------------------------------------------------------- */
/* List of methods defined in the module */
#ifdef DEBUG_LOG
static PyObject *
set_debug_log(PyObject *ignored, PyObject *args)
{
Py_INCREF(args);
ASSIGN(debug_log, args);
if(debug_log) idebug_log=-1;
else idebug_log=0;
Py_INCREF(Py_None);
return Py_None;
}
#endif
static struct PyMethodDef cP_methods[] = {
#ifdef DEBUG_LOG
{"set_debug_log", (PyCFunction)set_debug_log, METH_VARARGS,
"set_debug_log(callable) -- Provide a function to log events\n"
"\n"
"The function will be called with an event name and a persistent object.\n"
},
#endif
{NULL, NULL} /* sentinel */
};
/* Initialization function for the module (*must* be called initcPersistence) */
typedef int (*intfunctionwithpythonarg)(PyObject*);
static cPersistenceCAPIstruct
truecPersistenceCAPI = {
&(Pertype.methods),
(getattrofunc)Per_getattro, /*tp_getattr with object key*/
(setattrofunc)Per_setattro, /*tp_setattr with object key*/
changed,
accessed,
ghostify,
deallocated,
(intfunctionwithpythonarg)Per_setstate,
(pergetattr)Per_getattr,
(persetattr)_setattro
};
void
initcPersistence(void)
{
PyObject *m, *d, *s;
char *rev="$Revision$";
s = PyString_FromString("TimeStamp");
if (s == NULL)
return;
m = PyImport_Import(s);
if (m == NULL) {
Py_DECREF(s);
return;
}
TimeStamp = PyObject_GetAttr(m, s);
Py_DECREF(m);
if (TimeStamp == NULL) {
Py_DECREF(s);
}
if (init_strings() < 0) return;
m = Py_InitModule4("cPersistence", cP_methods, cPersistence_doc_string,
(PyObject*)NULL, PYTHON_API_VERSION);
d = PyModule_GetDict(m);
if(!strncmp(rev,"$Revision: ",11))
PyDict_SetItemString(d,"__version__",
PyString_FromStringAndSize(rev+11,strlen(rev+11)-2));
else
PyDict_SetItemString(d,"__version__",
PyString_FromString("0.0"));
PyExtensionClass_Export(d, "Persistent", Pertype);
PyExtensionClass_Export(d, "Overridable", Overridable);
cPersistenceCAPI=&truecPersistenceCAPI;
PyDict_SetItemString(d, "CAPI",
PyCObject_FromVoidPtr(cPersistenceCAPI,NULL));
}