Commit f024924f authored by Tim Peters's avatar Tim Peters

Move the Persistence package, from ZODB trunk to Zope trunk.

parent 116f06f6
/*
Copyright (c) 2003 Zope Corporation and Contributors.
All Rights Reserved.
This software is subject to the provisions of the Zope Public License,
Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
FOR A PARTICULAR PURPOSE.
*/
static char _Persistence_module_documentation[] =
"Persistent ExtensionClass\n"
"\n"
"$Id$\n"
;
#include "ExtensionClass.h"
#include "cPersistence.h"
/* convert_name() returns a new reference to a string name
or sets an exception and returns NULL.
*/
static PyObject *
convert_name(PyObject *name)
{
#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);
}
else
#endif
if (!PyString_Check(name)) {
PyErr_SetString(PyExc_TypeError, "attribute name must be a string");
return NULL;
} else
Py_INCREF(name);
return name;
}
/* Returns true if the object requires unghostification.
There are several special attributes that we allow access to without
requiring that the object be unghostified:
__class__
__del__
__dict__
__of__
__setstate__
*/
static int
unghost_getattr(const char *s)
{
if (*s++ != '_')
return 1;
if (*s == 'p') {
s++;
if (*s == '_')
return 0; /* _p_ */
else
return 1;
}
else if (*s == '_') {
s++;
switch (*s) {
case 'c':
return strcmp(s, "class__");
case 'd':
s++;
if (!strcmp(s, "el__"))
return 0; /* __del__ */
if (!strcmp(s, "ict__"))
return 0; /* __dict__ */
return 1;
case 'o':
return strcmp(s, "of__");
case 's':
return strcmp(s, "setstate__");
default:
return 1;
}
}
return 1;
}
static PyObject *
P_getattr(cPersistentObject *self, PyObject *name)
{
PyObject *v=NULL;
char *s;
name = convert_name(name);
if (!name)
return NULL;
s = PyString_AS_STRING(name);
if (*s != '_' || unghost_getattr(s))
{
if (PER_USE(self))
{
v = Py_FindAttr((PyObject*)self, name);
PER_ALLOW_DEACTIVATION(self);
PER_ACCESSED(self);
}
}
else
v = Py_FindAttr((PyObject*)self, name);
Py_DECREF(name);
return v;
}
static PyTypeObject Ptype = {
PyObject_HEAD_INIT(NULL)
/* ob_size */ 0,
/* tp_name */ "Persistence.Persistent",
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
/* tp_getattro */ (getattrofunc)P_getattr,
0, 0,
/* tp_flags */ Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_BASETYPE ,
/* tp_doc */ "Persistent ExtensionClass",
};
static struct PyMethodDef _Persistence_methods[] = {
{NULL, (PyCFunction)NULL, 0, NULL} /* sentinel */
};
#ifndef PyMODINIT_FUNC /* declarations for DLL import/export */
#define PyMODINIT_FUNC void
#endif
PyMODINIT_FUNC
init_Persistence(void)
{
PyObject *m;
if (! ExtensionClassImported)
return;
cPersistenceCAPI = PyCObject_Import("persistent.cPersistence", "CAPI");
if (cPersistenceCAPI == NULL)
return;
Ptype.tp_bases = Py_BuildValue("OO", cPersistenceCAPI->pertype, ECBaseType);
if (Ptype.tp_bases == NULL)
return;
Ptype.tp_base = cPersistenceCAPI->pertype;
Ptype.ob_type = ECExtensionClassType;
if (PyType_Ready(&Ptype) < 0)
return;
/* Create the module and add the functions */
m = Py_InitModule3("_Persistence", _Persistence_methods,
_Persistence_module_documentation);
if (m == NULL)
return;
/* Add types: */
if (PyModule_AddObject(m, "Persistent", (PyObject *)&Ptype) < 0)
return;
}
##############################################################################
#
# Copyright (c) 2001, 2002, 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Persistence and ExtensionClass combined
$Id$
"""
from persistent import PickleCache
try:
from _Persistence import Persistent
except:
from warnings import warn
warn("""Couldn't import the ExtensionClass-based base class
There are two possibilities:
1. You don't care about ExtensionClass. You are importing
Persistence because that's what you imported in the past.
In this case, you should really use the persistent package
instead:
>>> from persistent import Persistent
>>> from persistent.list import PersistentList
>>> from persistent.mapping import PersistentMapping
2. You want your classes to be ExtensionClasses. In this case,
you need to install the ExtensionClass package
separately. ExtensionClass is no-longer included with ZODB3.
""")
from persistent import Persistent
Overridable = Persistent
from Persistence.mapping import PersistentMapping
# This is a travesty. Whimper. The Data.fs.in used in Zope 2 have
# ancient pickles refering to BoboPOS. Waaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!
import sys
sys.modules['BoboPOS'] = sys.modules['Persistence']
sys.modules['BoboPOS.PersistentMapping'] = sys.modules['Persistence.mapping']
del sys
##############################################################################
#
# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
"""Python implementation of persistent base types
$Id$"""
import Persistence
import persistent
from persistent.mapping import PersistentMapping
if Persistence.Persistent is not persistent.Persistent:
class PersistentMapping(Persistence.Persistent, PersistentMapping):
"""Legacy persistent mapping class
This class mixes in ExtensionClass Base if it is present.
Unless you actually want ExtensionClass semantics, use
persistent.mapping.PersistentMapping instead.
"""
def __setstate__(self, state):
if 'data' not in state:
state['data'] = state['_container']
del state['_container']
self.__dict__.update(state)
#############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
import pickle
import time
import unittest
from Persistence import Persistent
from persistent.cPickleCache import PickleCache
from persistent.TimeStamp import TimeStamp
from ZODB.utils import p64
class Jar(object):
"""Testing stub for _p_jar attribute."""
def __init__(self):
self.cache = PickleCache(self)
self.oid = 1
self.registered = {}
def add(self, obj):
obj._p_oid = p64(self.oid)
self.oid += 1
obj._p_jar = self
self.cache[obj._p_oid] = obj
def close(self):
pass
# the following methods must be implemented to be a jar
def setklassstate(self):
# I don't know what this method does, but the pickle cache
# constructor calls it.
pass
def register(self, obj):
self.registered[obj] = 1
def setstate(self, obj):
# Trivial setstate() implementation that just re-initializes
# the object. This isn't what setstate() is supposed to do,
# but it suffices for the tests.
obj.__class__.__init__(obj)
class P(Persistent):
pass
class H1(Persistent):
def __init__(self):
self.n = 0
def __getattr__(self, attr):
self.n += 1
return self.n
class H2(Persistent):
def __init__(self):
self.n = 0
def __getattribute__(self, attr):
supergetattr = super(H2, self).__getattribute__
try:
return supergetattr(attr)
except AttributeError:
n = supergetattr("n")
self.n = n + 1
return n + 1
class PersistenceTest(unittest.TestCase):
def setUp(self):
self.jar = Jar()
def tearDown(self):
self.jar.close()
def testOidAndJarAttrs(self):
obj = P()
self.assertEqual(obj._p_oid, None)
obj._p_oid = 12
self.assertEqual(obj._p_oid, 12)
del obj._p_oid
self.jar.add(obj)
# Can't change oid of cache object.
def deloid():
del obj._p_oid
self.assertRaises(ValueError, deloid)
def setoid():
obj._p_oid = 12
self.assertRaises(ValueError, setoid)
def deloid():
del obj._p_jar
self.assertRaises(ValueError, deloid)
def setoid():
obj._p_jar = 12
self.assertRaises(ValueError, setoid)
def testChanged(self):
obj = P()
self.jar.add(obj)
# The value returned for _p_changed can be one of:
# 0 -- it is not changed
# 1 -- it is changed
# None -- it is a ghost
obj.x = 1
self.assertEqual(obj._p_changed, 1)
self.assert_(obj in self.jar.registered)
obj._p_changed = 0
self.assertEqual(obj._p_changed, 0)
self.jar.registered.clear()
obj._p_changed = 1
self.assertEqual(obj._p_changed, 1)
self.assert_(obj in self.jar.registered)
# setting obj._p_changed to None ghostifies if the
# object is in the up-to-date state, but not otherwise.
obj._p_changed = None
self.assertEqual(obj._p_changed, 1)
obj._p_changed = 0
# Now it's a ghost.
obj._p_changed = None
self.assertEqual(obj._p_changed, None)
obj = P()
self.jar.add(obj)
obj._p_changed = 1
# You can transition directly from modified to ghost if
# you delete the _p_changed attribute.
del obj._p_changed
self.assertEqual(obj._p_changed, None)
def testSerial(self):
noserial = "\000" * 8
obj = P()
self.assertEqual(obj._p_serial, noserial)
def set(val):
obj._p_serial = val
self.assertRaises(ValueError, set, 1)
self.assertRaises(ValueError, set, "0123")
self.assertRaises(ValueError, set, "012345678")
self.assertRaises(ValueError, set, u"01234567")
obj._p_serial = "01234567"
del obj._p_serial
self.assertEqual(obj._p_serial, noserial)
def testMTime(self):
obj = P()
self.assertEqual(obj._p_mtime, None)
t = int(time.time())
ts = TimeStamp(*time.gmtime(t)[:6])
obj._p_serial = repr(ts)
self.assertEqual(obj._p_mtime, t)
self.assert_(isinstance(obj._p_mtime, float))
def testPicklable(self):
obj = P()
obj.attr = "test"
s = pickle.dumps(obj)
obj2 = pickle.loads(s)
self.assertEqual(obj.attr, obj2.attr)
def testGetattr(self):
obj = H1()
self.assertEqual(obj.larry, 1)
self.assertEqual(obj.curly, 2)
self.assertEqual(obj.moe, 3)
self.jar.add(obj)
obj._p_deactivate()
# The simple Jar used for testing re-initializes the object.
self.assertEqual(obj.larry, 1)
# The getattr hook modified the object, so it should now be
# in the changed state.
self.assertEqual(obj._p_changed, 1)
self.assertEqual(obj.curly, 2)
self.assertEqual(obj.moe, 3)
def testGetattribute(self):
obj = H2()
self.assertEqual(obj.larry, 1)
self.assertEqual(obj.curly, 2)
self.assertEqual(obj.moe, 3)
self.jar.add(obj)
obj._p_deactivate()
# The simple Jar used for testing re-initializes the object.
self.assertEqual(obj.larry, 1)
# The getattr hook modified the object, so it should now be
# in the changed state.
self.assertEqual(obj._p_changed, 1)
self.assertEqual(obj.curly, 2)
self.assertEqual(obj.moe, 3)
# TODO: Need to decide how __setattr__ and __delattr__ should work,
# then write tests.
def test_suite():
return unittest.makeSuite(PersistenceTest)
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2003 Zope Corporation and Contributors.
# All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
"""
$Id$
"""
import unittest
from zope.testing.doctest import DocTestSuite
from Persistence import PersistentMapping
def test_basic_functionality():
"""
>>> m = PersistentMapping({'x': 1}, a=2, b=3)
>>> m['name'] = 'bob'
>>> m['fred']
Traceback (most recent call last):
...
KeyError: 'fred'
>>> m.get('fred')
>>> m.get('fred', 42)
42
>>> m.get('name', 42)
'bob'
>>> m.get('name')
'bob'
>>> m['name']
'bob'
>>> keys = m.keys()
>>> keys.sort()
>>> keys
['a', 'b', 'name', 'x']
>>> values = m.values()
>>> values.sort()
>>> values
[1, 2, 3, 'bob']
>>> items = m.items()
>>> items.sort()
>>> items
[('a', 2), ('b', 3), ('name', 'bob'), ('x', 1)]
>>> keys = list(m.iterkeys())
>>> keys.sort()
>>> keys
['a', 'b', 'name', 'x']
>>> values = list(m.itervalues())
>>> values.sort()
>>> values
[1, 2, 3, 'bob']
>>> items = list(m.iteritems())
>>> items.sort()
>>> items
[('a', 2), ('b', 3), ('name', 'bob'), ('x', 1)]
>>> 'name' in m
True
"""
def test_old_pickles():
"""
>>> m = PersistentMapping()
>>> m.__setstate__({'_container': {'x': 1, 'y': 2}})
>>> items = m.items()
>>> items.sort()
>>> items
[('x', 1), ('y', 2)]
"""
def test_suite():
return unittest.TestSuite((
DocTestSuite(),
))
if __name__ == '__main__': unittest.main()
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment