3 # Implement limited persistence.
6 # persist.save() save __main__ module on file (overwrite)
7 # persist.load() load __main__ module from file (merge)
9 # These use the filename persist.defaultfile, initialized to 'wsrestore.py'.
11 # A raw interface also exists:
12 # persist.writedict(dict, fp) save dictionary to open file
13 # persist.readdict(dict, fp) read (merge) dictionary from open file
15 # Internally, the function dump() and a whole bunch of support of functions
16 # traverse a graph of objects and print them in a restorable form
17 # (which happens to be a Python module).
20 # - Volatile objects are dumped as strings:
21 # - open files, windows etc.
22 # - Other 'obscure' objects are dumped as strings:
23 # - classes, instances and methods
24 # - compiled regular expressions
25 # - anything else reasonably obscure (e.g., capabilities)
26 # - type objects for obscure objects
27 # - It's slow when there are many of lists or dictionaries
28 # (This could be fixed if there were a quick way to compute a hash
29 # function of any object, even if recursive)
31 defaultfile
= 'wsrestore.py'
36 # XXX On SYSV, if len(defaultfile) >= 14, this is wrong!
37 backup
= defaultfile
+ '~'
43 os
.rename(defaultfile
, backup
)
46 fp
= open(defaultfile
, 'w')
47 writedict(__main__
.__dict
__, fp
)
52 fp
= open(defaultfile
, 'r')
53 readdict(__main__
.__dict
__, fp
)
55 def writedict(dict, fp
):
57 savestdout
= sys
.stdout
60 dump(dict) # Writes to sys.stdout
62 sys
.stdout
= savestdout
64 def readdict(dict, fp
):
67 exec(contents
, globals)
69 for key
in top
.keys():
71 print 'warning:', key
, 'not overwritten'
76 # Function dump(x) prints (on sys.stdout!) a sequence of Python statements
77 # that, when executed in an empty environment, will reconstruct the
78 # contents of an arbitrary dictionary.
82 # Name used for objects dict on output.
86 # Top-level function. Call with the object you want to dump.
90 stack
= [] # Used by test for recursive objects
92 topuid
= dumpobject(x
, types
, stack
)
93 print 'top =', FN
, '[', `topuid`
, ']'
95 # Generic function to dump any object.
99 def dumpobject(x
, types
, stack
):
101 if not types
.has_key(typerepr
):
103 typedict
= types
[typerepr
]
104 if dumpswitch
.has_key(typerepr
):
105 return dumpswitch
[typerepr
](x
, typedict
, types
, stack
)
107 return dumpbadvalue(x
, typedict
, types
, stack
)
109 # Generic function to dump unknown values.
110 # This assumes that the Python interpreter prints such values as
111 # <foo object at xxxxxxxx>.
112 # The object will be read back as a string: '<foo object at xxxxxxxx>'.
113 # In some cases it may be possible to fix the dump manually;
114 # to ease the editing, these cases are labeled with an XXX comment.
116 def dumpbadvalue(x
, typedict
, types
, stack
):
118 if typedict
.has_key(xrepr
):
119 return typedict
[xrepr
]
121 typedict
[xrepr
] = uid
122 print FN
, '[', `uid`
, '] =', `xrepr`
, '# XXX'
125 # Generic function to dump pure, simple values, except strings
127 def dumpvalue(x
, typedict
, types
, stack
):
129 if typedict
.has_key(xrepr
):
130 return typedict
[xrepr
]
132 typedict
[xrepr
] = uid
133 print FN
, '[', `uid`
, '] =', `x`
136 # Functions to dump string objects
138 def dumpstring(x
, typedict
, types
, stack
):
139 # XXX This can break if strings have embedded '\0' bytes
140 # XXX because of a bug in the dictionary module
141 if typedict
.has_key(x
):
145 print FN
, '[', `uid`
, '] =', `x`
148 # Function to dump type objects
152 def method(self
): pass
153 some_instance
= some_class()
155 def dumptype(x
, typedict
, types
, stack
):
157 if typedict
.has_key(xrepr
):
158 return typedict
[xrepr
]
160 typedict
[xrepr
] = uid
161 if typeswitch
.has_key(xrepr
):
162 print FN
, '[', `uid`
, '] =', typeswitch
[xrepr
]
165 print FN
, '[', `uid`
, '] = type(sys)'
166 elif x
== type(sys
.stderr
):
168 print FN
, '[', `uid`
, '] = type(sys.stderr)'
169 elif x
== type(dumptype
):
170 print 'def some_function(): pass'
171 print FN
, '[', `uid`
, '] = type(some_function)'
172 elif x
== type(some_class
):
173 print 'class some_class: pass'
174 print FN
, '[', `uid`
, '] = type(some_class)'
175 elif x
== type(some_instance
):
176 print 'class another_class: pass'
177 print 'some_instance = another_class()'
178 print FN
, '[', `uid`
, '] = type(some_instance)'
179 elif x
== type(some_instance
.method
):
180 print 'class yet_another_class:'
181 print ' def method(): pass'
182 print 'another_instance = yet_another_class()'
183 print FN
, '[', `uid`
, '] = type(another_instance.method)'
186 print FN
, '[', `uid`
, '] =', `xrepr`
, '# XXX'
189 # Initialize the typeswitch
191 for x
in None, 0, 0.0, '', (), [], {}:
192 typeswitch
[`
type(x
)`
] = 'type(' + `x`
+ ')'
193 for s
in 'type(0)', 'abs', '[].append':
194 typeswitch
[`
type(eval(s
))`
] = 'type(' + s
+ ')'
196 # Dump a tuple object
198 def dumptuple(x
, typedict
, types
, stack
):
202 item_uid
= dumpobject(item
, types
, stack
)
203 item_uids
.append(item_uid
)
204 xrepr
= xrepr
+ ' ' + item_uid
206 if typedict
.has_key(xrepr
):
207 return typedict
[xrepr
]
209 typedict
[xrepr
] = uid
210 print FN
, '[', `uid`
, '] = (',
211 for item_uid
in item_uids
:
212 print FN
, '[', `item_uid`
, '],',
218 def dumplist(x
, typedict
, types
, stack
):
219 # Check for recursion
220 for x1
, uid1
in stack
:
221 if x
is x1
: return uid1
222 # Check for occurrence elsewhere in the typedict
223 for uid1
in typedict
.keys():
224 if x
is typedict
[uid1
]: return uid1
225 # This uses typedict differently!
228 print FN
, '[', `uid`
, '] = []'
232 item_uid
= dumpobject(item
, types
, stack
)
233 item_uids
.append(item_uid
)
235 for item_uid
in item_uids
:
236 print FN
, '[', `uid`
, '].append(', FN
, '[', `item_uid`
, '])'
239 # Dump a dictionary object
241 def dumpdict(x
, typedict
, types
, stack
):
242 # Check for recursion
243 for x1
, uid1
in stack
:
244 if x
is x1
: return uid1
245 # Check for occurrence elsewhere in the typedict
246 for uid1
in typedict
.keys():
247 if x
is typedict
[uid1
]: return uid1
248 # This uses typedict differently!
251 print FN
, '[', `uid`
, '] = {}'
255 val_uid
= dumpobject(x
[key
], types
, stack
)
256 item_uids
.append(key
, val_uid
)
258 for key
, val_uid
in item_uids
:
259 print FN
, '[', `uid`
, '][', `key`
, '] =',
260 print FN
, '[', `val_uid`
, ']'
263 # Dump a module object
265 def dumpmodule(x
, typedict
, types
, stack
):
267 if typedict
.has_key(xrepr
):
268 return typedict
[xrepr
]
269 from string
import split
270 # `x` has the form <module 'foo'>
273 typedict
[xrepr
] = uid
275 print FN
, '[', `uid`
, '] =', name
279 # Initialize dumpswitch, a table of functions to dump various objects,
280 # indexed by `type(x)`.
282 for x
in None, 0, 0.0:
283 dumpswitch
[`
type(x
)`
] = dumpvalue
284 for x
, f
in ('', dumpstring
), (type(0), dumptype
), ((), dumptuple
), \
285 ([], dumplist
), ({}, dumpdict
), (sys
, dumpmodule
):
286 dumpswitch
[`
type(x
)`
] = f
289 # Generate the next unique id; a string consisting of digits.
290 # The seed is stored as seed[0].
296 seed
[0] = seed
[0] + 1