PageRenderTime 48ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/src/python/WMCore/Wrappers/JsonWrapper/JSONThunker.py

https://github.com/PerilousApricot/WMCore
Python | 360 lines | 354 code | 2 blank | 4 comment | 0 complexity | c8114abd4475252695380f19bbaa8abe MD5 | raw file
  1. import sys
  2. import types
  3. class _EmptyClass:
  4. pass
  5. class JSONThunker:
  6. """
  7. _JSONThunker_
  8. Converts an arbitrary object to <-> from a jsonable object.
  9. Will, for the most part "do the right thing" about various instance objects
  10. by storing their class information along with their data in a dict. Handles
  11. a recursion limit to prevent infinite recursion.
  12. self.passThroughTypes - stores a list of types that should be passed
  13. through unchanged to the JSON parser
  14. self.blackListedModules - a list of modules that should not be stored in
  15. the JSON.
  16. """
  17. def __init__(self):
  18. self.passThroughTypes = (types.NoneType,
  19. types.BooleanType,
  20. types.IntType,
  21. types.FloatType,
  22. types.LongType,
  23. types.ComplexType,
  24. types.StringTypes,
  25. types.StringType,
  26. types.UnicodeType
  27. )
  28. # objects that inherit from dict should be treated as a dict
  29. # they don't store their data in __dict__. There was enough
  30. # of those classes that it warrented making a special case
  31. self.dictSortOfObjects = ( ('WMCore.Datastructs.Job', 'Job'),
  32. ('WMCore.WMBS.Job', 'Job'),
  33. ('WMCore.Database.CMSCouch', 'Document' ))
  34. # ditto above, but for lists
  35. self.listSortOfObjects = ( ('WMCore.DataStructs.JobPackage', 'JobPackage' ),
  36. ('WMCore.WMBS.JobPackage', 'JobPackage' ),)
  37. self.foundIDs = {}
  38. # modules we don't want JSONed
  39. self.blackListedModules = ('sqlalchemy.engine.threadlocal',
  40. 'WMCore.Database.DBCore',
  41. 'logging',
  42. 'WMCore.DAOFactory',
  43. 'WMCore.WMFactory',
  44. 'WMFactory',
  45. 'WMCore.Configuration',
  46. 'WMCore.Database.Transaction',
  47. 'threading',
  48. 'datetime')
  49. def checkRecursion(self, data):
  50. """
  51. handles checking for infinite recursion
  52. """
  53. if (id(data) in self.foundIDs):
  54. if (self.foundIDs[id(data)] > 5):
  55. self.unrecurse(data)
  56. return "**RECURSION**"
  57. else:
  58. self.foundIDs[id(data)] += 1
  59. return data
  60. else:
  61. self.foundIDs[id(data)] = 1
  62. return data
  63. def unrecurse(self, data):
  64. """
  65. backs off the recursion counter if we're returning from _thunk
  66. """
  67. self.foundIDs[id(data)] = self.foundIDs[id(data)] -1
  68. def checkBlackListed(self, data):
  69. """
  70. checks to see if a given object is from a blacklisted module
  71. """
  72. try:
  73. # special case
  74. if ((data.__class__.__module__ == 'WMCore.Database.CMSCouch') and
  75. (data.__class__.__name__ == 'Document')):
  76. data.__class__ = type({})
  77. return data
  78. if (data.__class__.__module__ in self.blackListedModules):
  79. return "Blacklisted JSON object: module %s, name %s, str() %s" %\
  80. (data.__class__.__module__,data.__class__.__name__ , str(data))
  81. else:
  82. return data
  83. except:
  84. return data
  85. def thunk(self, toThunk):
  86. """
  87. Thunk - turns an arbitrary object into a JSONable object
  88. """
  89. self.foundIDs = {}
  90. data = self._thunk(toThunk)
  91. return data
  92. def unthunk(self, data):
  93. """
  94. unthunk - turns a previously 'thunked' object back into a python object
  95. """
  96. return self._unthunk(data)
  97. def handleSetThunk(self, toThunk):
  98. toThunk = self.checkRecursion( toThunk )
  99. tempDict = {'thunker_encoded_json':True, 'type': 'set'}
  100. tempDict['set'] = self._thunk(list(toThunk))
  101. self.unrecurse(toThunk)
  102. return tempDict
  103. def handleListThunk(self, toThunk):
  104. toThunk = self.checkRecursion( toThunk )
  105. for k,v in enumerate(toThunk):
  106. toThunk[k] = self._thunk(v)
  107. self.unrecurse(toThunk)
  108. return toThunk
  109. def handleDictThunk(self, toThunk):
  110. toThunk = self.checkRecursion( toThunk )
  111. special = False
  112. tmpdict = {}
  113. for k,v in toThunk.iteritems():
  114. if type(k) == type(int):
  115. special = True
  116. tmpdict['_i:%s' % k] = self._thunk(v)
  117. elif type(k) == type(float):
  118. special = True
  119. tmpdict['_f:%s' % k] = self._thunk(v)
  120. else:
  121. tmpdict[k] = self._thunk(v)
  122. if special:
  123. toThunk['thunker_encoded_json'] = self._thunk(True)
  124. toThunk['type'] = self._thunk('dict')
  125. toThunk['dict'] = tmpdict
  126. else:
  127. toThunk.update(tmpdict)
  128. self.unrecurse(toThunk)
  129. return toThunk
  130. def handleObjectThunk(self, toThunk):
  131. toThunk = self.checkRecursion( toThunk )
  132. toThunk = self.checkBlackListed(toThunk)
  133. if (type(toThunk) == type("")):
  134. # things that got blacklisted
  135. return toThunk
  136. if (hasattr(toThunk, '__to_json__')):
  137. #Use classes own json thunker
  138. toThunk2 = toThunk.__to_json__(self)
  139. self.unrecurse(toThunk)
  140. return toThunk2
  141. elif ( isinstance(toThunk, dict) ):
  142. toThunk2 = self.handleDictObjectThunk( toThunk )
  143. self.unrecurse(toThunk)
  144. return toThunk2
  145. elif ( isinstance(toThunk, list) ):
  146. #a mother thunking list
  147. toThunk2 = self.handleListObjectThunk( toThunk )
  148. self.unrecurse(toThunk)
  149. return toThunk2
  150. else:
  151. try:
  152. thunktype = '%s.%s' % (toThunk.__class__.__module__,
  153. toThunk.__class__.__name__)
  154. tempDict = {'thunker_encoded_json':True, 'type': thunktype}
  155. tempDict[thunktype] = self._thunk(toThunk.__dict__)
  156. self.unrecurse(toThunk)
  157. return tempDict
  158. except Exception, e:
  159. tempDict = {'json_thunk_exception_' : "%s" % e }
  160. self.unrecurse(toThunk)
  161. return tempDict
  162. def handleDictObjectThunk(self, data):
  163. thunktype = '%s.%s' % (data.__class__.__module__,
  164. data.__class__.__name__)
  165. tempDict = {'thunker_encoded_json':True,
  166. 'is_dict': True,
  167. 'type': thunktype,
  168. thunktype: {}}
  169. for k,v in data.__dict__.iteritems():
  170. tempDict[k] = self._thunk(v)
  171. for k,v in data.iteritems():
  172. tempDict[thunktype][k] = self._thunk(v)
  173. return tempDict
  174. def handleDictObjectUnThunk(self, value, data):
  175. data.pop('thunker_encoded_json', False)
  176. data.pop('is_dict', False)
  177. thunktype = data.pop('type', False)
  178. for k,v in data.iteritems():
  179. if (k == thunktype):
  180. for k2,v2 in data[thunktype].iteritems():
  181. value[k2] = self._unthunk(v2)
  182. else:
  183. value.__dict__[k] = self._unthunk(v)
  184. return value
  185. def handleListObjectThunk(self, data):
  186. thunktype = '%s.%s' % (data.__class__.__module__,
  187. data.__class__.__name__)
  188. tempDict = {'thunker_encoded_json':True,
  189. 'is_list': True,
  190. 'type': thunktype,
  191. thunktype: []}
  192. for k,v in enumerate(data):
  193. tempDict['thunktype'].append(self._thunk(v))
  194. for k,v in data.__dict__.iteritems():
  195. tempDict[k] = self._thunk(v)
  196. return tempDict
  197. def handleListObjectUnThunk(self, value, data):
  198. data.pop('thunker_encoded_json', False)
  199. data.pop('is_list', False)
  200. thunktype = data.pop('type')
  201. tmpdict = {}
  202. for k,v in data[thunktype].iteritems():
  203. setattr(value, k, self._unthunk(v))
  204. for k,v in data.iteritems():
  205. if (k == thunktype):
  206. continue
  207. value.__dict__ = self._unthunk(v)
  208. return value
  209. def _thunk(self, toThunk):
  210. """
  211. helper function for thunk, does the actual work
  212. """
  213. if (type(toThunk) in self.passThroughTypes):
  214. return toThunk
  215. elif (type(toThunk) == type([])):
  216. return self.handleListThunk(toThunk)
  217. elif (type(toThunk) == type({})):
  218. return self.handleDictThunk(toThunk)
  219. elif ((type(toThunk) == type(set()))):
  220. return self.handleSetThunk(toThunk)
  221. elif (type(toThunk) == types.FunctionType):
  222. self.unrecurse(toThunk)
  223. return "function reference"
  224. elif (isinstance(toThunk, object)):
  225. return self.handleObjectThunk(toThunk)
  226. else:
  227. self.unrecurse(toThunk)
  228. raise RuntimeError, type(toThunk)
  229. def _unthunk(self, jsondata):
  230. """
  231. _unthunk - does the actual work for unthunk
  232. """
  233. if (type(jsondata) == types.UnicodeType):
  234. return str(jsondata)
  235. if (type(jsondata) == type({})):
  236. if ('thunker_encoded_json' in jsondata):
  237. # we've got a live one...
  238. if jsondata['type'] == 'set':
  239. newSet = set()
  240. for i in self._unthunk(jsondata['set']):
  241. newSet.add( self._unthunk( i ) )
  242. return newSet
  243. if jsondata['type'] == 'dict':
  244. # We have a "special" dict
  245. data = {}
  246. for k,v in jsondata['dict'].iteritems():
  247. tmp = self._unthunk(v)
  248. if k.startswith('_i:'):
  249. data[int(k.lstrip('_i:'))] = tmp
  250. elif k.startswith('_f:'):
  251. data[float(k.lstrip('_f:'))] = tmp
  252. else:
  253. data[k] = tmp
  254. return data
  255. else:
  256. # spawn up an instance.. good luck
  257. # here be monsters
  258. # inspired from python's pickle code
  259. ourClass = self.getThunkedClass(jsondata)
  260. value = _EmptyClass()
  261. if (hasattr(ourClass, '__from_json__')):
  262. # Use classes own json loader
  263. try:
  264. value.__class__ = ourClass
  265. except:
  266. value = ourClass()
  267. value = ourClass.__from_json__(value, jsondata, self)
  268. elif ('thunker_encoded_json' in jsondata and
  269. 'is_dict' in jsondata):
  270. try:
  271. value.__class__ = ourClass
  272. except:
  273. value = ourClass()
  274. value = self.handleDictObjectUnThunk( value, jsondata )
  275. elif ( 'thunker_encoded_json' in jsondata ):
  276. #print "list obj unthunk"
  277. try:
  278. value.__class__ = ourClass
  279. except:
  280. value = ourClass()
  281. value = self.handleListObjectUnThunk( value, jsondata )
  282. else:
  283. #print "did we get here"
  284. try:
  285. value.__class__ = getattr(ourClass, name).__class__
  286. #print "changed the class to %s " % value.__class__
  287. except Exception, ex:
  288. #print "Except1 in requests %s " % ex
  289. try:
  290. #value = _EmptyClass()
  291. value.__class__ = ourClass
  292. except Exception, ex2:
  293. #print "Except2 in requests %s " % ex2
  294. #print type(ourClass)
  295. try:
  296. value = ourClass();
  297. except:
  298. #print 'megafail'
  299. pass
  300. #print "name %s module %s" % (name, module)
  301. value.__dict__ = data
  302. #print "our value is %s "% value
  303. return value
  304. else:
  305. #print 'last ditch attempt'
  306. data = {}
  307. for k,v in jsondata.iteritems():
  308. data[k] = self._unthunk(v)
  309. return data
  310. else:
  311. return jsondata
  312. def getThunkedClass(self, jsondata):
  313. """
  314. Work out the class from it's thunked json representation
  315. """
  316. module = jsondata['type'].rsplit('.',1)[0]
  317. name = jsondata['type'].rsplit('.',1)[1]
  318. if (module == 'WMCore.Services.Requests') and (name == JSONThunker):
  319. raise RuntimeError, "Attempted to unthunk a JSONThunker.."
  320. __import__(module)
  321. mod = sys.modules[module]
  322. ourClass = getattr(mod, name)
  323. return ourClass