home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pyth_os2.zip / python-1.0.2 / Lib / persist.py < prev    next >
Text File  |  1994-04-25  |  8KB  |  298 lines

  1. # 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.  
  31. defaultfile = 'wsrestore.py'
  32.  
  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()
  49.  
  50. def load():
  51.     import __main__
  52.     fp = open(defaultfile, 'r')
  53.     readdict(__main__.__dict__, fp)
  54.  
  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
  63.  
  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]
  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.  
  80. import sys
  81.  
  82. # Name used for objects dict on output.
  83. #
  84. FUNNYNAME = FN = 'A'
  85.  
  86. # Top-level function.  Call with the object you want to dump.
  87. #
  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`, ']'
  94.  
  95. # Generic function to dump any object.
  96. #
  97. dumpswitch = {}
  98. #
  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)
  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. #
  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
  124.  
  125. # Generic function to dump pure, simple values, except strings
  126. #
  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
  135.  
  136. # Functions to dump string objects
  137. #
  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
  147.  
  148. # Function to dump type objects
  149. #
  150. typeswitch = {}
  151. class some_class:
  152.     def method(self): pass
  153. some_instance = some_class()
  154. #
  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
  188.  
  189. # Initialize the typeswitch
  190. #
  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 + ')'
  195.  
  196. # Dump a tuple object
  197. #
  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
  215.  
  216. # Dump a list object
  217. #
  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
  238.  
  239. # Dump a dictionary object
  240. #
  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
  262.  
  263. # Dump a module object
  264. #
  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
  277.  
  278.  
  279. # Initialize dumpswitch, a table of functions to dump various objects,
  280. # indexed by `type(x)`.
  281. #
  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
  287.  
  288.  
  289. # Generate the next unique id; a string consisting of digits.
  290. # The seed is stored as seed[0].
  291. #
  292. seed = [0]
  293. #
  294. def genuid():
  295.     x = seed[0]
  296.     seed[0] = seed[0] + 1
  297.     return `x`
  298.