1
# -*- test-case-name: twisted.test.test_persisted -*-
2
# Copyright (c) 2001-2004 Twisted Matrix Laboratories.
3
# See LICENSE for details.
8
Different styles of persisted objects.
17
import cStringIO as StringIO
22
from twisted.python import log
25
from new import instancemethod
27
from org.python.core import PyMethod
28
instancemethod = PyMethod
32
## First, let's register support for some stuff that really ought to
35
def pickleMethod(method):
36
'support function for copy_reg to pickle method refs'
37
return unpickleMethod, (method.im_func.__name__,
41
def unpickleMethod(im_name,
44
'support function for copy_reg to unpickle method refs'
46
unbound = getattr(im_class,im_name)
49
bound=instancemethod(unbound.im_func,
53
except AttributeError:
54
log.msg("Method",im_name,"not on class",im_class)
55
assert im_self is not None,"No recourse: no instance to guess from."
56
# Attempt a common fix before bailing -- if classes have
57
# changed around since we pickled this method, we may still be
58
# able to get it by looking on the instance's current class.
59
unbound = getattr(im_self.__class__,im_name)
60
log.msg("Attempting fixup with",unbound)
63
bound=instancemethod(unbound.im_func,
68
copy_reg.pickle(types.MethodType,
72
def pickleModule(module):
73
'support function for copy_reg to pickle module refs'
74
return unpickleModule, (module.__name__,)
76
def unpickleModule(name):
77
'support function for copy_reg to unpickle module refs'
78
if oldModules.has_key(name):
79
log.msg("Module has moved: %s" % name)
80
name = oldModules[name]
82
return __import__(name,{},{},'x')
85
copy_reg.pickle(types.ModuleType,
89
def pickleStringO(stringo):
90
'support function for copy_reg to pickle StringIO.OutputTypes'
91
return unpickleStringO, (stringo.getvalue(), stringo.tell())
93
def unpickleStringO(val, sek):
94
x = StringIO.StringIO()
99
if hasattr(StringIO, 'OutputType'):
100
copy_reg.pickle(StringIO.OutputType,
104
def pickleStringI(stringi):
105
return unpickleStringI, (stringi.getvalue(), stringi.tell())
107
def unpickleStringI(val, sek):
108
x = StringIO.StringIO(val)
113
if hasattr(StringIO, 'InputType'):
114
copy_reg.pickle(StringIO.InputType,
120
This type of object is never persisted; if possible, even references to it
124
def __getstate__(self):
125
log.msg( "WARNING: serializing ephemeral %s" % self )
127
if getattr(gc, 'get_referrers', None):
128
for r in gc.get_referrers(self):
129
log.msg( " referred to by %s" % (r,))
132
def __setstate__(self, state):
133
log.msg( "WARNING: unserializing ephemeral %s" % self.__class__ )
134
self.__class__ = Ephemeral
137
versionedsToUpgrade = {}
141
global versionedsToUpgrade, upgraded
142
for versioned in versionedsToUpgrade.values():
143
requireUpgrade(versioned)
144
versionedsToUpgrade = {}
147
def requireUpgrade(obj):
148
"""Require that a Versioned instance be upgraded completely first.
151
if objID in versionedsToUpgrade and objID not in upgraded:
156
from twisted.python import reflect
160
for b in reflect.allYourBase(c, Versioned):
161
if b not in l and b is not Versioned:
167
This type of object is persisted with versioning information.
169
I have a single class attribute, the int persistenceVersion. After I am
170
unserialized (and styles.doUpgrade() is called), self.upgradeToVersionX()
171
will be called for each version upgrade I must undergo.
173
For example, if I serialize an instance of a Foo(Versioned) at version 4
174
and then unserialize it when the code is at version 9, the calls::
176
self.upgradeToVersion5()
177
self.upgradeToVersion6()
178
self.upgradeToVersion7()
179
self.upgradeToVersion8()
180
self.upgradeToVersion9()
182
will be made. If any of these methods are undefined, a warning message
185
persistenceVersion = 0
186
persistenceForgets = ()
188
def __setstate__(self, state):
189
versionedsToUpgrade[id(self)] = self
190
self.__dict__ = state
192
def __getstate__(self, dict=None):
193
"""Get state, adding a version number to it on its way out.
195
dct = copy.copy(dict or self.__dict__)
196
bases = _aybabtu(self.__class__)
198
bases.append(self.__class__) # don't forget me!!
200
if base.__dict__.has_key('persistenceForgets'):
201
for slot in base.persistenceForgets:
202
if dct.has_key(slot):
204
if base.__dict__.has_key('persistenceVersion'):
205
dct['%s.persistenceVersion' % reflect.qual(base)] = base.persistenceVersion
208
def versionUpgrade(self):
209
"""(internal) Do a version upgrade.
211
bases = _aybabtu(self.__class__)
212
# put the bases in order so superclasses' persistenceVersion methods
213
# will be called first.
215
bases.append(self.__class__) # don't forget me!!
216
# first let's look for old-skool versioned's
217
if self.__dict__.has_key("persistenceVersion"):
219
# Hacky heuristic: if more than one class subclasses Versioned,
220
# we'll assume that the higher version number wins for the older
221
# class, so we'll consider the attribute the version of the older
222
# class. There are obviously possibly times when this will
223
# eventually be an incorrect assumption, but hopefully old-school
224
# persistenceVersion stuff won't make it that far into multiple
225
# classes inheriting from Versioned.
227
pver = self.__dict__['persistenceVersion']
228
del self.__dict__['persistenceVersion']
232
if not base.__dict__.has_key('persistenceVersion'):
234
if base.persistenceVersion > highestVersion:
236
highestVersion = base.persistenceVersion
238
self.__dict__['%s.persistenceVersion' % reflect.qual(highestBase)] = pver
240
# ugly hack, but it's what the user expects, really
241
if (Versioned not in base.__bases__ and
242
not base.__dict__.has_key('persistenceVersion')):
244
currentVers = base.persistenceVersion
245
pverName = '%s.persistenceVersion' % reflect.qual(base)
246
persistVers = (self.__dict__.get(pverName) or 0)
248
del self.__dict__[pverName]
249
assert persistVers <= currentVers, "Sorry, can't go backwards in time."
250
while persistVers < currentVers:
251
persistVers = persistVers + 1
252
method = base.__dict__.get('upgradeToVersion%s' % persistVers, None)
254
log.msg( "Upgrading %s (of %s @ %s) to version %s" % (reflect.qual(base), reflect.qual(self.__class__), id(self), persistVers) )
257
log.msg( 'Warning: cannot upgrade %s to version %s' % (base, persistVers) )