blob: 8f0f164bc696d30692990d817cb52af23c457846 [file] [log] [blame]
Guido van Rossum2d844d11991-04-07 13:41:50 +00001# persist.py
2#
3# Implement limited persistence.
4#
5# Simple interface:
6# persist.save() save __main__ module on file (overwrite)
7# persist.load() load __main__ module from file (merge)
8#
9# These use the filename persist.defaultfile, initialized to 'wsrestore.py'.
10#
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
14#
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).
18#
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)
30
31defaultfile = 'wsrestore.py'
32
33def save():
34 import __main__
Guido van Rossum25d7caf1992-03-31 19:04:48 +000035 import os
Guido van Rossum2d844d11991-04-07 13:41:50 +000036 # XXX On SYSV, if len(defaultfile) >= 14, this is wrong!
37 backup = defaultfile + '~'
38 try:
Guido van Rossum25d7caf1992-03-31 19:04:48 +000039 os.unlink(backup)
40 except os.error:
Guido van Rossum2d844d11991-04-07 13:41:50 +000041 pass
42 try:
Guido van Rossum25d7caf1992-03-31 19:04:48 +000043 os.rename(defaultfile, backup)
44 except os.error:
Guido van Rossum2d844d11991-04-07 13:41:50 +000045 pass
46 fp = open(defaultfile, 'w')
47 writedict(__main__.__dict__, fp)
48 fp.close()
49
50def load():
51 import __main__
52 fp = open(defaultfile, 'r')
53 readdict(__main__.__dict__, fp)
54
55def 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
63
64def readdict(dict, fp):
Guido van Rossumfa0e7261991-04-21 19:33:30 +000065 contents = fp.read()
Guido van Rossum2d844d11991-04-07 13:41:50 +000066 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]
74
75
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.
79
80import sys
81
82# Name used for objects dict on output.
83#
84FUNNYNAME = FN = 'A'
85
86# Top-level function. Call with the object you want to dump.
87#
88def 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`, ']'
94
95# Generic function to dump any object.
96#
97dumpswitch = {}
98#
99def 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)
108
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.
115#
116def 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
124
125# Generic function to dump pure, simple values, except strings
126#
127def 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
135
136# Functions to dump string objects
137#
138def 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
147
148# Function to dump type objects
149#
150typeswitch = {}
Guido van Rossum18200101991-12-26 13:05:52 +0000151class some_class:
Guido van Rossum2d844d11991-04-07 13:41:50 +0000152 def method(self): pass
153some_instance = some_class()
154#
155def 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]
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000163 elif x == type(sys):
Guido van Rossum2d844d11991-04-07 13:41:50 +0000164 print 'import sys'
165 print FN, '[', `uid`, '] = type(sys)'
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000166 elif x == type(sys.stderr):
Guido van Rossum2d844d11991-04-07 13:41:50 +0000167 print 'import sys'
168 print FN, '[', `uid`, '] = type(sys.stderr)'
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000169 elif x == type(dumptype):
Guido van Rossum2d844d11991-04-07 13:41:50 +0000170 print 'def some_function(): pass'
171 print FN, '[', `uid`, '] = type(some_function)'
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000172 elif x == type(some_class):
Guido van Rossumd3166071993-05-24 14:16:22 +0000173 print 'class some_class: pass'
Guido van Rossum2d844d11991-04-07 13:41:50 +0000174 print FN, '[', `uid`, '] = type(some_class)'
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000175 elif x == type(some_instance):
Guido van Rossumd3166071993-05-24 14:16:22 +0000176 print 'class another_class: pass'
Guido van Rossum2d844d11991-04-07 13:41:50 +0000177 print 'some_instance = another_class()'
178 print FN, '[', `uid`, '] = type(some_instance)'
Guido van Rossumbdfcfcc1992-01-01 19:35:13 +0000179 elif x == type(some_instance.method):
Guido van Rossumd3166071993-05-24 14:16:22 +0000180 print 'class yet_another_class:'
Guido van Rossum2d844d11991-04-07 13:41:50 +0000181 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
188
189# Initialize the typeswitch
190#
191for x in None, 0, 0.0, '', (), [], {}:
192 typeswitch[`type(x)`] = 'type(' + `x` + ')'
193for s in 'type(0)', 'abs', '[].append':
194 typeswitch[`type(eval(s))`] = 'type(' + s + ')'
195
196# Dump a tuple object
197#
198def 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
215
216# Dump a list object
217#
218def 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
238
239# Dump a dictionary object
240#
241def 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
262
263# Dump a module object
264#
265def 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
277
278
279# Initialize dumpswitch, a table of functions to dump various objects,
280# indexed by `type(x)`.
281#
282for x in None, 0, 0.0:
283 dumpswitch[`type(x)`] = dumpvalue
284for x, f in ('', dumpstring), (type(0), dumptype), ((), dumptuple), \
285 ([], dumplist), ({}, dumpdict), (sys, dumpmodule):
286 dumpswitch[`type(x)`] = f
287
288
289# Generate the next unique id; a string consisting of digits.
290# The seed is stored as seed[0].
291#
292seed = [0]
293#
294def genuid():
295 x = seed[0]
296 seed[0] = seed[0] + 1
297 return `x`