changes by Barry, e.g. font lock & email addresses
[python/dscho.git] / Lib / persist.py
blob8f0f164bc696d30692990d817cb52af23c457846
1 # persist.py
3 # Implement limited persistence.
5 # Simple interface:
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).
19 # XXX Limitations:
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'
33 def save():
34 import __main__
35 import os
36 # XXX On SYSV, if len(defaultfile) >= 14, this is wrong!
37 backup = defaultfile + '~'
38 try:
39 os.unlink(backup)
40 except os.error:
41 pass
42 try:
43 os.rename(defaultfile, backup)
44 except os.error:
45 pass
46 fp = open(defaultfile, 'w')
47 writedict(__main__.__dict__, fp)
48 fp.close()
50 def load():
51 import __main__
52 fp = open(defaultfile, 'r')
53 readdict(__main__.__dict__, fp)
55 def writedict(dict, fp):
56 import sys
57 savestdout = sys.stdout
58 try:
59 sys.stdout = fp
60 dump(dict) # Writes to sys.stdout
61 finally:
62 sys.stdout = savestdout
64 def readdict(dict, fp):
65 contents = fp.read()
66 globals = {}
67 exec(contents, globals)
68 top = globals['top']
69 for key in top.keys():
70 if dict.has_key(key):
71 print 'warning:', key, 'not overwritten'
72 else:
73 dict[key] = top[key]
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.
80 import sys
82 # Name used for objects dict on output.
84 FUNNYNAME = FN = 'A'
86 # Top-level function. Call with the object you want to dump.
88 def dump(x):
89 types = {}
90 stack = [] # Used by test for recursive objects
91 print FN, '= {}'
92 topuid = dumpobject(x, types, stack)
93 print 'top =', FN, '[', `topuid`, ']'
95 # Generic function to dump any object.
97 dumpswitch = {}
99 def dumpobject(x, types, stack):
100 typerepr = `type(x)`
101 if not types.has_key(typerepr):
102 types[typerepr] = {}
103 typedict = types[typerepr]
104 if dumpswitch.has_key(typerepr):
105 return dumpswitch[typerepr](x, typedict, types, stack)
106 else:
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):
117 xrepr = `x`
118 if typedict.has_key(xrepr):
119 return typedict[xrepr]
120 uid = genuid()
121 typedict[xrepr] = uid
122 print FN, '[', `uid`, '] =', `xrepr`, '# XXX'
123 return uid
125 # Generic function to dump pure, simple values, except strings
127 def dumpvalue(x, typedict, types, stack):
128 xrepr = `x`
129 if typedict.has_key(xrepr):
130 return typedict[xrepr]
131 uid = genuid()
132 typedict[xrepr] = uid
133 print FN, '[', `uid`, '] =', `x`
134 return uid
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):
142 return typedict[x]
143 uid = genuid()
144 typedict[x] = uid
145 print FN, '[', `uid`, '] =', `x`
146 return uid
148 # Function to dump type objects
150 typeswitch = {}
151 class some_class:
152 def method(self): pass
153 some_instance = some_class()
155 def dumptype(x, typedict, types, stack):
156 xrepr = `x`
157 if typedict.has_key(xrepr):
158 return typedict[xrepr]
159 uid = genuid()
160 typedict[xrepr] = uid
161 if typeswitch.has_key(xrepr):
162 print FN, '[', `uid`, '] =', typeswitch[xrepr]
163 elif x == type(sys):
164 print 'import sys'
165 print FN, '[', `uid`, '] = type(sys)'
166 elif x == type(sys.stderr):
167 print 'import sys'
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)'
184 else:
185 # Unknown type
186 print FN, '[', `uid`, '] =', `xrepr`, '# XXX'
187 return uid
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):
199 item_uids = []
200 xrepr = ''
201 for item in x:
202 item_uid = dumpobject(item, types, stack)
203 item_uids.append(item_uid)
204 xrepr = xrepr + ' ' + item_uid
205 del stack[-1:]
206 if typedict.has_key(xrepr):
207 return typedict[xrepr]
208 uid = genuid()
209 typedict[xrepr] = uid
210 print FN, '[', `uid`, '] = (',
211 for item_uid in item_uids:
212 print FN, '[', `item_uid`, '],',
213 print ')'
214 return uid
216 # Dump a list object
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!
226 uid = genuid()
227 typedict[uid] = x
228 print FN, '[', `uid`, '] = []'
229 stack.append(x, uid)
230 item_uids = []
231 for item in x:
232 item_uid = dumpobject(item, types, stack)
233 item_uids.append(item_uid)
234 del stack[-1:]
235 for item_uid in item_uids:
236 print FN, '[', `uid`, '].append(', FN, '[', `item_uid`, '])'
237 return 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!
249 uid = genuid()
250 typedict[uid] = x
251 print FN, '[', `uid`, '] = {}'
252 stack.append(x, uid)
253 item_uids = []
254 for key in x.keys():
255 val_uid = dumpobject(x[key], types, stack)
256 item_uids.append(key, val_uid)
257 del stack[-1:]
258 for key, val_uid in item_uids:
259 print FN, '[', `uid`, '][', `key`, '] =',
260 print FN, '[', `val_uid`, ']'
261 return uid
263 # Dump a module object
265 def dumpmodule(x, typedict, types, stack):
266 xrepr = `x`
267 if typedict.has_key(xrepr):
268 return typedict[xrepr]
269 from string import split
270 # `x` has the form <module 'foo'>
271 name = xrepr[9:-2]
272 uid = genuid()
273 typedict[xrepr] = uid
274 print 'import', name
275 print FN, '[', `uid`, '] =', name
276 return uid
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].
292 seed = [0]
294 def genuid():
295 x = seed[0]
296 seed[0] = seed[0] + 1
297 return `x`