representer.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. __all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
  2. 'RepresenterError']
  3. from error import *
  4. from nodes import *
  5. import datetime
  6. import copy_reg, types
  7. class RepresenterError(YAMLError):
  8. pass
  9. class BaseRepresenter(object):
  10. yaml_representers = {}
  11. yaml_multi_representers = {}
  12. def __init__(self, default_style=None, default_flow_style=False, sort_keys=True):
  13. self.default_style = default_style
  14. self.default_flow_style = default_flow_style
  15. self.sort_keys = sort_keys
  16. self.represented_objects = {}
  17. self.object_keeper = []
  18. self.alias_key = None
  19. def represent(self, data):
  20. node = self.represent_data(data)
  21. self.serialize(node)
  22. self.represented_objects = {}
  23. self.object_keeper = []
  24. self.alias_key = None
  25. def get_classobj_bases(self, cls):
  26. bases = [cls]
  27. for base in cls.__bases__:
  28. bases.extend(self.get_classobj_bases(base))
  29. return bases
  30. def represent_data(self, data):
  31. if self.ignore_aliases(data):
  32. self.alias_key = None
  33. else:
  34. self.alias_key = id(data)
  35. if self.alias_key is not None:
  36. if self.alias_key in self.represented_objects:
  37. node = self.represented_objects[self.alias_key]
  38. #if node is None:
  39. # raise RepresenterError("recursive objects are not allowed: %r" % data)
  40. return node
  41. #self.represented_objects[alias_key] = None
  42. self.object_keeper.append(data)
  43. data_types = type(data).__mro__
  44. if type(data) is types.InstanceType:
  45. data_types = self.get_classobj_bases(data.__class__)+list(data_types)
  46. if data_types[0] in self.yaml_representers:
  47. node = self.yaml_representers[data_types[0]](self, data)
  48. else:
  49. for data_type in data_types:
  50. if data_type in self.yaml_multi_representers:
  51. node = self.yaml_multi_representers[data_type](self, data)
  52. break
  53. else:
  54. if None in self.yaml_multi_representers:
  55. node = self.yaml_multi_representers[None](self, data)
  56. elif None in self.yaml_representers:
  57. node = self.yaml_representers[None](self, data)
  58. else:
  59. node = ScalarNode(None, unicode(data))
  60. #if alias_key is not None:
  61. # self.represented_objects[alias_key] = node
  62. return node
  63. def add_representer(cls, data_type, representer):
  64. if not 'yaml_representers' in cls.__dict__:
  65. cls.yaml_representers = cls.yaml_representers.copy()
  66. cls.yaml_representers[data_type] = representer
  67. add_representer = classmethod(add_representer)
  68. def add_multi_representer(cls, data_type, representer):
  69. if not 'yaml_multi_representers' in cls.__dict__:
  70. cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
  71. cls.yaml_multi_representers[data_type] = representer
  72. add_multi_representer = classmethod(add_multi_representer)
  73. def represent_scalar(self, tag, value, style=None):
  74. if style is None:
  75. style = self.default_style
  76. node = ScalarNode(tag, value, style=style)
  77. if self.alias_key is not None:
  78. self.represented_objects[self.alias_key] = node
  79. return node
  80. def represent_sequence(self, tag, sequence, flow_style=None):
  81. value = []
  82. node = SequenceNode(tag, value, flow_style=flow_style)
  83. if self.alias_key is not None:
  84. self.represented_objects[self.alias_key] = node
  85. best_style = True
  86. for item in sequence:
  87. node_item = self.represent_data(item)
  88. if not (isinstance(node_item, ScalarNode) and not node_item.style):
  89. best_style = False
  90. value.append(node_item)
  91. if flow_style is None:
  92. if self.default_flow_style is not None:
  93. node.flow_style = self.default_flow_style
  94. else:
  95. node.flow_style = best_style
  96. return node
  97. def represent_mapping(self, tag, mapping, flow_style=None):
  98. value = []
  99. node = MappingNode(tag, value, flow_style=flow_style)
  100. if self.alias_key is not None:
  101. self.represented_objects[self.alias_key] = node
  102. best_style = True
  103. if hasattr(mapping, 'items'):
  104. mapping = mapping.items()
  105. if self.sort_keys:
  106. mapping.sort()
  107. for item_key, item_value in mapping:
  108. node_key = self.represent_data(item_key)
  109. node_value = self.represent_data(item_value)
  110. if not (isinstance(node_key, ScalarNode) and not node_key.style):
  111. best_style = False
  112. if not (isinstance(node_value, ScalarNode) and not node_value.style):
  113. best_style = False
  114. value.append((node_key, node_value))
  115. if flow_style is None:
  116. if self.default_flow_style is not None:
  117. node.flow_style = self.default_flow_style
  118. else:
  119. node.flow_style = best_style
  120. return node
  121. def ignore_aliases(self, data):
  122. return False
  123. class SafeRepresenter(BaseRepresenter):
  124. def ignore_aliases(self, data):
  125. if data is None:
  126. return True
  127. if isinstance(data, tuple) and data == ():
  128. return True
  129. if isinstance(data, (str, unicode, bool, int, float)):
  130. return True
  131. def represent_none(self, data):
  132. return self.represent_scalar(u'tag:yaml.org,2002:null',
  133. u'null')
  134. def represent_str(self, data):
  135. tag = None
  136. style = None
  137. try:
  138. data = unicode(data, 'ascii')
  139. tag = u'tag:yaml.org,2002:str'
  140. except UnicodeDecodeError:
  141. try:
  142. data = unicode(data, 'utf-8')
  143. tag = u'tag:yaml.org,2002:str'
  144. except UnicodeDecodeError:
  145. data = data.encode('base64')
  146. tag = u'tag:yaml.org,2002:binary'
  147. style = '|'
  148. return self.represent_scalar(tag, data, style=style)
  149. def represent_unicode(self, data):
  150. return self.represent_scalar(u'tag:yaml.org,2002:str', data)
  151. def represent_bool(self, data):
  152. if data:
  153. value = u'true'
  154. else:
  155. value = u'false'
  156. return self.represent_scalar(u'tag:yaml.org,2002:bool', value)
  157. def represent_int(self, data):
  158. return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
  159. def represent_long(self, data):
  160. return self.represent_scalar(u'tag:yaml.org,2002:int', unicode(data))
  161. inf_value = 1e300
  162. while repr(inf_value) != repr(inf_value*inf_value):
  163. inf_value *= inf_value
  164. def represent_float(self, data):
  165. if data != data or (data == 0.0 and data == 1.0):
  166. value = u'.nan'
  167. elif data == self.inf_value:
  168. value = u'.inf'
  169. elif data == -self.inf_value:
  170. value = u'-.inf'
  171. else:
  172. value = unicode(repr(data)).lower()
  173. # Note that in some cases `repr(data)` represents a float number
  174. # without the decimal parts. For instance:
  175. # >>> repr(1e17)
  176. # '1e17'
  177. # Unfortunately, this is not a valid float representation according
  178. # to the definition of the `!!float` tag. We fix this by adding
  179. # '.0' before the 'e' symbol.
  180. if u'.' not in value and u'e' in value:
  181. value = value.replace(u'e', u'.0e', 1)
  182. return self.represent_scalar(u'tag:yaml.org,2002:float', value)
  183. def represent_list(self, data):
  184. #pairs = (len(data) > 0 and isinstance(data, list))
  185. #if pairs:
  186. # for item in data:
  187. # if not isinstance(item, tuple) or len(item) != 2:
  188. # pairs = False
  189. # break
  190. #if not pairs:
  191. return self.represent_sequence(u'tag:yaml.org,2002:seq', data)
  192. #value = []
  193. #for item_key, item_value in data:
  194. # value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
  195. # [(item_key, item_value)]))
  196. #return SequenceNode(u'tag:yaml.org,2002:pairs', value)
  197. def represent_dict(self, data):
  198. return self.represent_mapping(u'tag:yaml.org,2002:map', data)
  199. def represent_set(self, data):
  200. value = {}
  201. for key in data:
  202. value[key] = None
  203. return self.represent_mapping(u'tag:yaml.org,2002:set', value)
  204. def represent_date(self, data):
  205. value = unicode(data.isoformat())
  206. return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
  207. def represent_datetime(self, data):
  208. value = unicode(data.isoformat(' '))
  209. return self.represent_scalar(u'tag:yaml.org,2002:timestamp', value)
  210. def represent_yaml_object(self, tag, data, cls, flow_style=None):
  211. if hasattr(data, '__getstate__'):
  212. state = data.__getstate__()
  213. else:
  214. state = data.__dict__.copy()
  215. return self.represent_mapping(tag, state, flow_style=flow_style)
  216. def represent_undefined(self, data):
  217. raise RepresenterError("cannot represent an object", data)
  218. SafeRepresenter.add_representer(type(None),
  219. SafeRepresenter.represent_none)
  220. SafeRepresenter.add_representer(str,
  221. SafeRepresenter.represent_str)
  222. SafeRepresenter.add_representer(unicode,
  223. SafeRepresenter.represent_unicode)
  224. SafeRepresenter.add_representer(bool,
  225. SafeRepresenter.represent_bool)
  226. SafeRepresenter.add_representer(int,
  227. SafeRepresenter.represent_int)
  228. SafeRepresenter.add_representer(long,
  229. SafeRepresenter.represent_long)
  230. SafeRepresenter.add_representer(float,
  231. SafeRepresenter.represent_float)
  232. SafeRepresenter.add_representer(list,
  233. SafeRepresenter.represent_list)
  234. SafeRepresenter.add_representer(tuple,
  235. SafeRepresenter.represent_list)
  236. SafeRepresenter.add_representer(dict,
  237. SafeRepresenter.represent_dict)
  238. SafeRepresenter.add_representer(set,
  239. SafeRepresenter.represent_set)
  240. SafeRepresenter.add_representer(datetime.date,
  241. SafeRepresenter.represent_date)
  242. SafeRepresenter.add_representer(datetime.datetime,
  243. SafeRepresenter.represent_datetime)
  244. SafeRepresenter.add_representer(None,
  245. SafeRepresenter.represent_undefined)
  246. class Representer(SafeRepresenter):
  247. def represent_str(self, data):
  248. tag = None
  249. style = None
  250. try:
  251. data = unicode(data, 'ascii')
  252. tag = u'tag:yaml.org,2002:str'
  253. except UnicodeDecodeError:
  254. try:
  255. data = unicode(data, 'utf-8')
  256. tag = u'tag:yaml.org,2002:python/str'
  257. except UnicodeDecodeError:
  258. data = data.encode('base64')
  259. tag = u'tag:yaml.org,2002:binary'
  260. style = '|'
  261. return self.represent_scalar(tag, data, style=style)
  262. def represent_unicode(self, data):
  263. tag = None
  264. try:
  265. data.encode('ascii')
  266. tag = u'tag:yaml.org,2002:python/unicode'
  267. except UnicodeEncodeError:
  268. tag = u'tag:yaml.org,2002:str'
  269. return self.represent_scalar(tag, data)
  270. def represent_long(self, data):
  271. tag = u'tag:yaml.org,2002:int'
  272. if int(data) is not data:
  273. tag = u'tag:yaml.org,2002:python/long'
  274. return self.represent_scalar(tag, unicode(data))
  275. def represent_complex(self, data):
  276. if data.imag == 0.0:
  277. data = u'%r' % data.real
  278. elif data.real == 0.0:
  279. data = u'%rj' % data.imag
  280. elif data.imag > 0:
  281. data = u'%r+%rj' % (data.real, data.imag)
  282. else:
  283. data = u'%r%rj' % (data.real, data.imag)
  284. return self.represent_scalar(u'tag:yaml.org,2002:python/complex', data)
  285. def represent_tuple(self, data):
  286. return self.represent_sequence(u'tag:yaml.org,2002:python/tuple', data)
  287. def represent_name(self, data):
  288. name = u'%s.%s' % (data.__module__, data.__name__)
  289. return self.represent_scalar(u'tag:yaml.org,2002:python/name:'+name, u'')
  290. def represent_module(self, data):
  291. return self.represent_scalar(
  292. u'tag:yaml.org,2002:python/module:'+data.__name__, u'')
  293. def represent_instance(self, data):
  294. # For instances of classic classes, we use __getinitargs__ and
  295. # __getstate__ to serialize the data.
  296. # If data.__getinitargs__ exists, the object must be reconstructed by
  297. # calling cls(**args), where args is a tuple returned by
  298. # __getinitargs__. Otherwise, the cls.__init__ method should never be
  299. # called and the class instance is created by instantiating a trivial
  300. # class and assigning to the instance's __class__ variable.
  301. # If data.__getstate__ exists, it returns the state of the object.
  302. # Otherwise, the state of the object is data.__dict__.
  303. # We produce either a !!python/object or !!python/object/new node.
  304. # If data.__getinitargs__ does not exist and state is a dictionary, we
  305. # produce a !!python/object node . Otherwise we produce a
  306. # !!python/object/new node.
  307. cls = data.__class__
  308. class_name = u'%s.%s' % (cls.__module__, cls.__name__)
  309. args = None
  310. state = None
  311. if hasattr(data, '__getinitargs__'):
  312. args = list(data.__getinitargs__())
  313. if hasattr(data, '__getstate__'):
  314. state = data.__getstate__()
  315. else:
  316. state = data.__dict__
  317. if args is None and isinstance(state, dict):
  318. return self.represent_mapping(
  319. u'tag:yaml.org,2002:python/object:'+class_name, state)
  320. if isinstance(state, dict) and not state:
  321. return self.represent_sequence(
  322. u'tag:yaml.org,2002:python/object/new:'+class_name, args)
  323. value = {}
  324. if args:
  325. value['args'] = args
  326. value['state'] = state
  327. return self.represent_mapping(
  328. u'tag:yaml.org,2002:python/object/new:'+class_name, value)
  329. def represent_object(self, data):
  330. # We use __reduce__ API to save the data. data.__reduce__ returns
  331. # a tuple of length 2-5:
  332. # (function, args, state, listitems, dictitems)
  333. # For reconstructing, we calls function(*args), then set its state,
  334. # listitems, and dictitems if they are not None.
  335. # A special case is when function.__name__ == '__newobj__'. In this
  336. # case we create the object with args[0].__new__(*args).
  337. # Another special case is when __reduce__ returns a string - we don't
  338. # support it.
  339. # We produce a !!python/object, !!python/object/new or
  340. # !!python/object/apply node.
  341. cls = type(data)
  342. if cls in copy_reg.dispatch_table:
  343. reduce = copy_reg.dispatch_table[cls](data)
  344. elif hasattr(data, '__reduce_ex__'):
  345. reduce = data.__reduce_ex__(2)
  346. elif hasattr(data, '__reduce__'):
  347. reduce = data.__reduce__()
  348. else:
  349. raise RepresenterError("cannot represent an object", data)
  350. reduce = (list(reduce)+[None]*5)[:5]
  351. function, args, state, listitems, dictitems = reduce
  352. args = list(args)
  353. if state is None:
  354. state = {}
  355. if listitems is not None:
  356. listitems = list(listitems)
  357. if dictitems is not None:
  358. dictitems = dict(dictitems)
  359. if function.__name__ == '__newobj__':
  360. function = args[0]
  361. args = args[1:]
  362. tag = u'tag:yaml.org,2002:python/object/new:'
  363. newobj = True
  364. else:
  365. tag = u'tag:yaml.org,2002:python/object/apply:'
  366. newobj = False
  367. function_name = u'%s.%s' % (function.__module__, function.__name__)
  368. if not args and not listitems and not dictitems \
  369. and isinstance(state, dict) and newobj:
  370. return self.represent_mapping(
  371. u'tag:yaml.org,2002:python/object:'+function_name, state)
  372. if not listitems and not dictitems \
  373. and isinstance(state, dict) and not state:
  374. return self.represent_sequence(tag+function_name, args)
  375. value = {}
  376. if args:
  377. value['args'] = args
  378. if state or not isinstance(state, dict):
  379. value['state'] = state
  380. if listitems:
  381. value['listitems'] = listitems
  382. if dictitems:
  383. value['dictitems'] = dictitems
  384. return self.represent_mapping(tag+function_name, value)
  385. Representer.add_representer(str,
  386. Representer.represent_str)
  387. Representer.add_representer(unicode,
  388. Representer.represent_unicode)
  389. Representer.add_representer(long,
  390. Representer.represent_long)
  391. Representer.add_representer(complex,
  392. Representer.represent_complex)
  393. Representer.add_representer(tuple,
  394. Representer.represent_tuple)
  395. Representer.add_representer(type,
  396. Representer.represent_name)
  397. Representer.add_representer(types.ClassType,
  398. Representer.represent_name)
  399. Representer.add_representer(types.FunctionType,
  400. Representer.represent_name)
  401. Representer.add_representer(types.BuiltinFunctionType,
  402. Representer.represent_name)
  403. Representer.add_representer(types.ModuleType,
  404. Representer.represent_module)
  405. Representer.add_multi_representer(types.InstanceType,
  406. Representer.represent_instance)
  407. Representer.add_multi_representer(object,
  408. Representer.represent_object)