diff options
Diffstat (limited to 'Lib/persist.py')
-rwxr-xr-x | Lib/persist.py | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/Lib/persist.py b/Lib/persist.py new file mode 100755 index 0000000..bc91e4d --- /dev/null +++ b/Lib/persist.py @@ -0,0 +1,297 @@ +# persist.py +# +# Implement limited persistence. +# +# Simple interface: +# persist.save() save __main__ module on file (overwrite) +# persist.load() load __main__ module from file (merge) +# +# These use the filename persist.defaultfile, initialized to 'wsrestore.py'. +# +# A raw interface also exists: +# persist.writedict(dict, fp) save dictionary to open file +# persist.readdict(dict, fp) read (merge) dictionary from open file +# +# Internally, the function dump() and a whole bunch of support of functions +# traverse a graph of objects and print them in a restorable form +# (which happens to be a Python module). +# +# XXX Limitations: +# - Volatile objects are dumped as strings: +# - open files, windows etc. +# - Other 'obscure' objects are dumped as strings: +# - classes, instances and methods +# - compiled regular expressions +# - anything else reasonably obscure (e.g., capabilities) +# - type objects for obscure objects +# - It's slow when there are many of lists or dictionaries +# (This could be fixed if there were a quick way to compute a hash +# function of any object, even if recursive) + +defaultfile = 'wsrestore.py' + +def save(): + import __main__ + import posix + # XXX On SYSV, if len(defaultfile) >= 14, this is wrong! + backup = defaultfile + '~' + try: + posix.unlink(backup) + except posix.error: + pass + try: + posix.rename(defaultfile, backup) + except posix.error: + pass + fp = open(defaultfile, 'w') + writedict(__main__.__dict__, fp) + fp.close() + +def load(): + import __main__ + fp = open(defaultfile, 'r') + readdict(__main__.__dict__, fp) + +def writedict(dict, fp): + import sys + savestdout = sys.stdout + try: + sys.stdout = fp + dump(dict) # Writes to sys.stdout + finally: + sys.stdout = savestdout + +def readdict(dict, fp): + contents = fp.read() # Or: util.readopenfile(fp) + globals = {} + exec(contents, globals) + top = globals['top'] + for key in top.keys(): + if dict.has_key(key): + print 'warning:', key, 'not overwritten' + else: + dict[key] = top[key] + + +# Function dump(x) prints (on sys.stdout!) a sequence of Python statements +# that, when executed in an empty environment, will reconstruct the +# contents of an arbitrary dictionary. + +import sys + +# Name used for objects dict on output. +# +FUNNYNAME = FN = 'A' + +# Top-level function. Call with the object you want to dump. +# +def dump(x): + types = {} + stack = [] # Used by test for recursive objects + print FN, '= {}' + topuid = dumpobject(x, types, stack) + print 'top =', FN, '[', `topuid`, ']' + +# Generic function to dump any object. +# +dumpswitch = {} +# +def dumpobject(x, types, stack): + typerepr = `type(x)` + if not types.has_key(typerepr): + types[typerepr] = {} + typedict = types[typerepr] + if dumpswitch.has_key(typerepr): + return dumpswitch[typerepr](x, typedict, types, stack) + else: + return dumpbadvalue(x, typedict, types, stack) + +# Generic function to dump unknown values. +# This assumes that the Python interpreter prints such values as +# <foo object at xxxxxxxx>. +# The object will be read back as a string: '<foo object at xxxxxxxx>'. +# In some cases it may be possible to fix the dump manually; +# to ease the editing, these cases are labeled with an XXX comment. +# +def dumpbadvalue(x, typedict, types, stack): + xrepr = `x` + if typedict.has_key(xrepr): + return typedict[xrepr] + uid = genuid() + typedict[xrepr] = uid + print FN, '[', `uid`, '] =', `xrepr`, '# XXX' + return uid + +# Generic function to dump pure, simple values, except strings +# +def dumpvalue(x, typedict, types, stack): + xrepr = `x` + if typedict.has_key(xrepr): + return typedict[xrepr] + uid = genuid() + typedict[xrepr] = uid + print FN, '[', `uid`, '] =', `x` + return uid + +# Functions to dump string objects +# +def dumpstring(x, typedict, types, stack): + # XXX This can break if strings have embedded '\0' bytes + # XXX because of a bug in the dictionary module + if typedict.has_key(x): + return typedict[x] + uid = genuid() + typedict[x] = uid + print FN, '[', `uid`, '] =', `x` + return uid + +# Function to dump type objects +# +typeswitch = {} +class some_class(): + def method(self): pass +some_instance = some_class() +# +def dumptype(x, typedict, types, stack): + xrepr = `x` + if typedict.has_key(xrepr): + return typedict[xrepr] + uid = genuid() + typedict[xrepr] = uid + if typeswitch.has_key(xrepr): + print FN, '[', `uid`, '] =', typeswitch[xrepr] + elif x = type(sys): + print 'import sys' + print FN, '[', `uid`, '] = type(sys)' + elif x = type(sys.stderr): + print 'import sys' + print FN, '[', `uid`, '] = type(sys.stderr)' + elif x = type(dumptype): + print 'def some_function(): pass' + print FN, '[', `uid`, '] = type(some_function)' + elif x = type(some_class): + print 'class some_class(): pass' + print FN, '[', `uid`, '] = type(some_class)' + elif x = type(some_instance): + print 'class another_class(): pass' + print 'some_instance = another_class()' + print FN, '[', `uid`, '] = type(some_instance)' + elif x = type(some_instance.method): + print 'class yet_another_class():' + print ' def method(): pass' + print 'another_instance = yet_another_class()' + print FN, '[', `uid`, '] = type(another_instance.method)' + else: + # Unknown type + print FN, '[', `uid`, '] =', `xrepr`, '# XXX' + return uid + +# Initialize the typeswitch +# +for x in None, 0, 0.0, '', (), [], {}: + typeswitch[`type(x)`] = 'type(' + `x` + ')' +for s in 'type(0)', 'abs', '[].append': + typeswitch[`type(eval(s))`] = 'type(' + s + ')' + +# Dump a tuple object +# +def dumptuple(x, typedict, types, stack): + item_uids = [] + xrepr = '' + for item in x: + item_uid = dumpobject(item, types, stack) + item_uids.append(item_uid) + xrepr = xrepr + ' ' + item_uid + del stack[-1:] + if typedict.has_key(xrepr): + return typedict[xrepr] + uid = genuid() + typedict[xrepr] = uid + print FN, '[', `uid`, '] = (', + for item_uid in item_uids: + print FN, '[', `item_uid`, '],', + print ')' + return uid + +# Dump a list object +# +def dumplist(x, typedict, types, stack): + # Check for recursion + for x1, uid1 in stack: + if x is x1: return uid1 + # Check for occurrence elsewhere in the typedict + for uid1 in typedict.keys(): + if x is typedict[uid1]: return uid1 + # This uses typedict differently! + uid = genuid() + typedict[uid] = x + print FN, '[', `uid`, '] = []' + stack.append(x, uid) + item_uids = [] + for item in x: + item_uid = dumpobject(item, types, stack) + item_uids.append(item_uid) + del stack[-1:] + for item_uid in item_uids: + print FN, '[', `uid`, '].append(', FN, '[', `item_uid`, '])' + return uid + +# Dump a dictionary object +# +def dumpdict(x, typedict, types, stack): + # Check for recursion + for x1, uid1 in stack: + if x is x1: return uid1 + # Check for occurrence elsewhere in the typedict + for uid1 in typedict.keys(): + if x is typedict[uid1]: return uid1 + # This uses typedict differently! + uid = genuid() + typedict[uid] = x + print FN, '[', `uid`, '] = {}' + stack.append(x, uid) + item_uids = [] + for key in x.keys(): + val_uid = dumpobject(x[key], types, stack) + item_uids.append(key, val_uid) + del stack[-1:] + for key, val_uid in item_uids: + print FN, '[', `uid`, '][', `key`, '] =', + print FN, '[', `val_uid`, ']' + return uid + +# Dump a module object +# +def dumpmodule(x, typedict, types, stack): + xrepr = `x` + if typedict.has_key(xrepr): + return typedict[xrepr] + from string import split + # `x` has the form <module 'foo'> + name = xrepr[9:-2] + uid = genuid() + typedict[xrepr] = uid + print 'import', name + print FN, '[', `uid`, '] =', name + return uid + + +# Initialize dumpswitch, a table of functions to dump various objects, +# indexed by `type(x)`. +# +for x in None, 0, 0.0: + dumpswitch[`type(x)`] = dumpvalue +for x, f in ('', dumpstring), (type(0), dumptype), ((), dumptuple), \ + ([], dumplist), ({}, dumpdict), (sys, dumpmodule): + dumpswitch[`type(x)`] = f + + +# Generate the next unique id; a string consisting of digits. +# The seed is stored as seed[0]. +# +seed = [0] +# +def genuid(): + x = seed[0] + seed[0] = seed[0] + 1 + return `x` |