sob.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. # -*- test-case-name: twisted.test.test_sob -*-
  2. # Copyright (c) Twisted Matrix Laboratories.
  3. # See LICENSE for details.
  4. #
  5. """
  6. Save and load Small OBjects to and from files, using various formats.
  7. Maintainer: Moshe Zadka
  8. """
  9. import os
  10. import pickle
  11. import sys
  12. from zope.interface import Interface, implementer
  13. from twisted.persisted import styles
  14. from twisted.python import log, runtime
  15. class IPersistable(Interface):
  16. """An object which can be saved in several formats to a file"""
  17. def setStyle(style):
  18. """Set desired format.
  19. @type style: string (one of 'pickle' or 'source')
  20. """
  21. def save(tag=None, filename=None, passphrase=None):
  22. """Save object to file.
  23. @type tag: string
  24. @type filename: string
  25. @type passphrase: string
  26. """
  27. @implementer(IPersistable)
  28. class Persistent:
  29. style = "pickle"
  30. def __init__(self, original, name):
  31. self.original = original
  32. self.name = name
  33. def setStyle(self, style):
  34. """Set desired format.
  35. @type style: string (one of 'pickle' or 'source')
  36. """
  37. self.style = style
  38. def _getFilename(self, filename, ext, tag):
  39. if filename:
  40. finalname = filename
  41. filename = finalname + "-2"
  42. elif tag:
  43. filename = f"{self.name}-{tag}-2.{ext}"
  44. finalname = f"{self.name}-{tag}.{ext}"
  45. else:
  46. filename = f"{self.name}-2.{ext}"
  47. finalname = f"{self.name}.{ext}"
  48. return finalname, filename
  49. def _saveTemp(self, filename, dumpFunc):
  50. with open(filename, "wb") as f:
  51. dumpFunc(self.original, f)
  52. def _getStyle(self):
  53. if self.style == "source":
  54. from twisted.persisted.aot import jellyToSource as dumpFunc
  55. ext = "tas"
  56. else:
  57. def dumpFunc(obj, file=None):
  58. pickle.dump(obj, file, 2)
  59. ext = "tap"
  60. return ext, dumpFunc
  61. def save(self, tag=None, filename=None, passphrase=None):
  62. """Save object to file.
  63. @type tag: string
  64. @type filename: string
  65. @type passphrase: string
  66. """
  67. ext, dumpFunc = self._getStyle()
  68. if passphrase is not None:
  69. raise TypeError("passphrase must be None")
  70. finalname, filename = self._getFilename(filename, ext, tag)
  71. log.msg("Saving " + self.name + " application to " + finalname + "...")
  72. self._saveTemp(filename, dumpFunc)
  73. if runtime.platformType == "win32" and os.path.isfile(finalname):
  74. os.remove(finalname)
  75. os.rename(filename, finalname)
  76. log.msg("Saved.")
  77. # "Persistant" has been present since 1.0.7, so retain it for compatibility
  78. Persistant = Persistent
  79. class _EverythingEphemeral(styles.Ephemeral):
  80. initRun = 0
  81. def __init__(self, mainMod):
  82. """
  83. @param mainMod: The '__main__' module that this class will proxy.
  84. """
  85. self.mainMod = mainMod
  86. def __getattr__(self, key):
  87. try:
  88. return getattr(self.mainMod, key)
  89. except AttributeError:
  90. if self.initRun:
  91. raise
  92. else:
  93. log.msg("Warning! Loading from __main__: %s" % key)
  94. return styles.Ephemeral()
  95. def load(filename, style):
  96. """Load an object from a file.
  97. Deserialize an object from a file. The file can be encrypted.
  98. @param filename: string
  99. @param style: string (one of 'pickle' or 'source')
  100. """
  101. mode = "r"
  102. if style == "source":
  103. from twisted.persisted.aot import unjellyFromSource as _load
  104. else:
  105. _load, mode = pickle.load, "rb"
  106. fp = open(filename, mode)
  107. ee = _EverythingEphemeral(sys.modules["__main__"])
  108. sys.modules["__main__"] = ee
  109. ee.initRun = 1
  110. with fp:
  111. try:
  112. value = _load(fp)
  113. finally:
  114. # restore __main__ if an exception is raised.
  115. sys.modules["__main__"] = ee.mainMod
  116. styles.doUpgrade()
  117. ee.initRun = 0
  118. persistable = IPersistable(value, None)
  119. if persistable is not None:
  120. persistable.setStyle(style)
  121. return value
  122. def loadValueFromFile(filename, variable):
  123. """Load the value of a variable in a Python file.
  124. Run the contents of the file in a namespace and return the result of the
  125. variable named C{variable}.
  126. @param filename: string
  127. @param variable: string
  128. """
  129. with open(filename) as fileObj:
  130. data = fileObj.read()
  131. d = {"__file__": filename}
  132. codeObj = compile(data, filename, "exec")
  133. eval(codeObj, d, d)
  134. value = d[variable]
  135. return value
  136. def guessType(filename):
  137. ext = os.path.splitext(filename)[1]
  138. return {
  139. ".tac": "python",
  140. ".etac": "python",
  141. ".py": "python",
  142. ".tap": "pickle",
  143. ".etap": "pickle",
  144. ".tas": "source",
  145. ".etas": "source",
  146. }[ext]
  147. __all__ = [
  148. "loadValueFromFile",
  149. "load",
  150. "Persistent",
  151. "Persistant",
  152. "IPersistable",
  153. "guessType",
  154. ]