/indico/MaKaC/common/PickleJar.py

https://github.com/davidmorrison/indico
Python | 263 lines | 179 code | 62 blank | 22 comment | 55 complexity | 54e4ca1685115c8dd49f88eea791c80e MD5 | raw file
  1. import copy
  2. from persistent.list import PersistentList
  3. from persistent.mapping import PersistentMapping
  4. from MaKaC.common.Conversion import Conversion
  5. globals()['_PickleJar__mappings_pickle'] = {}
  6. globals()['_PickleJar__mappings_unpickle'] = {}
  7. globalPickleMap = globals()['_PickleJar__mappings_pickle']
  8. globalUnpickleMap = globals()['_PickleJar__mappings_unpickle']
  9. def if_else(cond, resT, resF):
  10. if cond:
  11. return resT
  12. else:
  13. return resF
  14. class PicklerException(Exception):
  15. def __init__(self, message, inner=None):
  16. self.message = message
  17. self.inner = inner
  18. def __str__(self):
  19. return str(self.message) + "\r\n" + str(self.inner)
  20. def functional_append(list, element):
  21. newlist = copy.deepcopy(list)
  22. newlist.append(element)
  23. return newlist
  24. def stringToBool(s):
  25. if (s.lower() == 'false'):
  26. return False
  27. elif (s.lower() == 'true'):
  28. return True
  29. else:
  30. raise ValueError('Impossible to convert \'%s\' to bool' % s)
  31. def classPath(clazz):
  32. return "%s.%s" % (clazz.__module__, clazz.__name__)
  33. def setProp(root, property, method, modifier, isPicklableObject = False):
  34. if len(property) == 1:
  35. if method.__name__ == '__init__':
  36. root[property[0]] = (None, modifier, isPicklableObject)
  37. else:
  38. root[property[0]] = (method,modifier, isPicklableObject)
  39. else:
  40. if not root.has_key(property[0]):
  41. root[property[0]] = PickleTree({})
  42. setProp(root[property[0]], property[1:], method, modifier, isPicklableObject)
  43. class PropIterator:
  44. def __init__(self, root):
  45. self.__stack = [([],root)]
  46. def __findNext(self):
  47. elem = self.__stack[-1]
  48. if elem[1].__class__ != PickleTree:
  49. self.__stack.pop()
  50. return elem
  51. else:
  52. (path,root) = self.__stack.pop()
  53. self.__stack.extend(map(lambda x: (functional_append(path,x[0]), x[1]),
  54. root.iteritems()))
  55. return self.__findNext()
  56. def next(self):
  57. try:
  58. (path,elem) = self.__stack[-1]
  59. except:
  60. raise StopIteration()
  61. if type(elem) == 'tuple':
  62. return (path, elem)
  63. else:
  64. return self.__findNext()
  65. class PickleTree:
  66. def __init__(self, root):
  67. self.__root = root
  68. def __getitem__(self, key):
  69. return self.__root[key]
  70. def __setitem__(self, key, value):
  71. self.__root[key] = value
  72. def __iter__(self):
  73. return PropIterator(PickleTree(self.__root))
  74. def iteritems(self):
  75. return self.__root.iteritems()
  76. def __str__(self):
  77. txt = ""
  78. for elem in self:
  79. txt += str(elem)
  80. return txt
  81. def has_key(self,key):
  82. return self.__root.has_key(key)
  83. def Retrieves(cls, property, modifier=None, isPicklableObject=False):
  84. # This descriptor incrementally builds a tree of properties
  85. # (PickleTree)
  86. #
  87. # / size (fn, modif)
  88. # ____/
  89. # / \
  90. # /file \ name (fn, modif)
  91. # .
  92. # \
  93. # title (fn, modif)
  94. #
  95. # nested properties (like 'file.size') are supported.
  96. # This is particularly useful for formats like JSON.
  97. #
  98. def factory(method):
  99. if type(cls) == str:
  100. clsList = [cls]
  101. else:
  102. clsList = cls
  103. for clazz in clsList:
  104. if not globalPickleMap.has_key(clazz):
  105. globalPickleMap[clazz] = PickleTree({})
  106. setProp(globalPickleMap[clazz],
  107. property.split('.'),
  108. method,
  109. modifier,
  110. isPicklableObject)
  111. return method
  112. return factory
  113. def Updates(cls, property, modifier=None):
  114. #Similar to @Retrieves
  115. def factory(method):
  116. if type(cls) == str:
  117. clsList = [cls]
  118. else:
  119. clsList = cls
  120. for clazz in clsList:
  121. if not globalUnpickleMap.has_key(clazz):
  122. globalUnpickleMap[clazz] = PickleTree({})
  123. setProp(globalUnpickleMap[clazz], property.split('.'), method, modifier)
  124. return method
  125. return factory
  126. class DictPickler:
  127. @classmethod
  128. def pickle(cls, object, timezone = None):
  129. """ timezone can be a string (preferred) or a pytz.timezone object
  130. """
  131. # path mapping supported
  132. # i.e. getFileSize() ==> file.size
  133. if object is None:
  134. return None
  135. elif type(object) == list or type(object) == tuple or type(object) == set or isinstance(object, PersistentList):
  136. res = []
  137. for obj in object:
  138. res.append(DictPickler.pickle(obj, timezone))
  139. return res
  140. elif type(object) == dict or isinstance(object, PersistentMapping):
  141. res = {}
  142. for key, obj in object.iteritems():
  143. if not isinstance(key, basestring):
  144. raise Exception("Key %s cannot be pickled because it's not a string. object=%s" % (str(key), str(object)))
  145. res[key] = DictPickler.pickle(obj, timezone)
  146. return res
  147. elif isinstance(object, basestring):
  148. return object
  149. else:
  150. clazz = classPath(object.__class__)
  151. if not globalPickleMap.has_key(clazz):
  152. raise Exception('Class %s is not supposed to be pickled. object=%s' % (clazz, str(object)))
  153. return DictPickler._pickle(object, globalPickleMap[clazz], timezone)
  154. @classmethod
  155. def update(cls, object, dict):
  156. # path mapping supported
  157. # i.e. file.size ==> setFileSize()
  158. clazz = classPath(object.__class__)
  159. if not globalUnpickleMap.has_key(clazz):
  160. raise Exception('Class %s is not supposed to be pickled. object=%s' % (clazz, str(object)))
  161. return DictPickler._update(object, globalUnpickleMap[clazz], dict)
  162. @classmethod
  163. def _update(cls,object,unpickleTree,dict):
  164. def recursiveUpdate(dict, prop):
  165. for (key,elem) in dict.iteritems():
  166. nextPath = functional_append(prop, key)
  167. if type(elem) == 'dict':
  168. recursiveUpdate(object, elem, nextPath)
  169. else:
  170. for (eprop, (method, modifier,isObj)) in unpickleTree:
  171. if eprop == nextPath:
  172. if (modifier):
  173. method(object,modifier(elem))
  174. else:
  175. method(object,elem)
  176. recursiveUpdate(dict, [])
  177. @classmethod
  178. def _pickle(cls,object,pickleTree, timezone):
  179. def recursiveAttribution(obj, prop, result):
  180. if len(prop) == 1:
  181. obj[prop[0]] = result
  182. else:
  183. if not obj.has_key(prop[0]):
  184. obj[prop[0]] = {}
  185. recursiveAttribution(obj[prop[0]], prop[1:], result)
  186. resDic = {}
  187. for (prop, (method, modifier, isObj)) in pickleTree:
  188. if method == None:
  189. result = object
  190. else:
  191. result = method(object)
  192. # apply the modifier, if there is one
  193. if (modifier):
  194. if modifier == Conversion.datetime:
  195. result = modifier(result, tz = timezone)
  196. else:
  197. result = modifier(result)
  198. if isObj:
  199. result = DictPickler.pickle(result)
  200. recursiveAttribution(resDic, prop, result)
  201. return resDic