representer.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. __all__ = ['BaseRepresenter', 'SafeRepresenter', 'Representer',
  2. 'RepresenterError']
  3. from .error import *
  4. from .nodes import *
  5. import datetime, sys, copyreg, types, base64, collections
  6. class RepresenterError(YAMLError):
  7. pass
  8. class BaseRepresenter:
  9. yaml_representers = {}
  10. yaml_multi_representers = {}
  11. def __init__(self, default_style=None, default_flow_style=None):
  12. self.default_style = default_style
  13. self.default_flow_style = default_flow_style
  14. self.represented_objects = {}
  15. self.object_keeper = []
  16. self.alias_key = None
  17. def represent(self, data):
  18. node = self.represent_data(data)
  19. self.serialize(node)
  20. self.represented_objects = {}
  21. self.object_keeper = []
  22. self.alias_key = None
  23. def represent_data(self, data):
  24. if self.ignore_aliases(data):
  25. self.alias_key = None
  26. else:
  27. self.alias_key = id(data)
  28. if self.alias_key is not None:
  29. if self.alias_key in self.represented_objects:
  30. node = self.represented_objects[self.alias_key]
  31. #if node is None:
  32. # raise RepresenterError("recursive objects are not allowed: %r" % data)
  33. return node
  34. #self.represented_objects[alias_key] = None
  35. self.object_keeper.append(data)
  36. data_types = type(data).__mro__
  37. if data_types[0] in self.yaml_representers:
  38. node = self.yaml_representers[data_types[0]](self, data)
  39. else:
  40. for data_type in data_types:
  41. if data_type in self.yaml_multi_representers:
  42. node = self.yaml_multi_representers[data_type](self, data)
  43. break
  44. else:
  45. if None in self.yaml_multi_representers:
  46. node = self.yaml_multi_representers[None](self, data)
  47. elif None in self.yaml_representers:
  48. node = self.yaml_representers[None](self, data)
  49. else:
  50. node = ScalarNode(None, str(data))
  51. #if alias_key is not None:
  52. # self.represented_objects[alias_key] = node
  53. return node
  54. @classmethod
  55. def add_representer(cls, data_type, representer):
  56. if not 'yaml_representers' in cls.__dict__:
  57. cls.yaml_representers = cls.yaml_representers.copy()
  58. cls.yaml_representers[data_type] = representer
  59. @classmethod
  60. def add_multi_representer(cls, data_type, representer):
  61. if not 'yaml_multi_representers' in cls.__dict__:
  62. cls.yaml_multi_representers = cls.yaml_multi_representers.copy()
  63. cls.yaml_multi_representers[data_type] = representer
  64. def represent_scalar(self, tag, value, style=None):
  65. if style is None:
  66. style = self.default_style
  67. node = ScalarNode(tag, value, style=style)
  68. if self.alias_key is not None:
  69. self.represented_objects[self.alias_key] = node
  70. return node
  71. def represent_sequence(self, tag, sequence, flow_style=None):
  72. value = []
  73. node = SequenceNode(tag, value, flow_style=flow_style)
  74. if self.alias_key is not None:
  75. self.represented_objects[self.alias_key] = node
  76. best_style = True
  77. for item in sequence:
  78. node_item = self.represent_data(item)
  79. if not (isinstance(node_item, ScalarNode) and not node_item.style):
  80. best_style = False
  81. value.append(node_item)
  82. if flow_style is None:
  83. if self.default_flow_style is not None:
  84. node.flow_style = self.default_flow_style
  85. else:
  86. node.flow_style = best_style
  87. return node
  88. def represent_mapping(self, tag, mapping, flow_style=None):
  89. value = []
  90. node = MappingNode(tag, value, flow_style=flow_style)
  91. if self.alias_key is not None:
  92. self.represented_objects[self.alias_key] = node
  93. best_style = True
  94. if hasattr(mapping, 'items'):
  95. mapping = list(mapping.items())
  96. try:
  97. mapping = sorted(mapping)
  98. except TypeError:
  99. pass
  100. for item_key, item_value in mapping:
  101. node_key = self.represent_data(item_key)
  102. node_value = self.represent_data(item_value)
  103. if not (isinstance(node_key, ScalarNode) and not node_key.style):
  104. best_style = False
  105. if not (isinstance(node_value, ScalarNode) and not node_value.style):
  106. best_style = False
  107. value.append((node_key, node_value))
  108. if flow_style is None:
  109. if self.default_flow_style is not None:
  110. node.flow_style = self.default_flow_style
  111. else:
  112. node.flow_style = best_style
  113. return node
  114. def ignore_aliases(self, data):
  115. return False
  116. class SafeRepresenter(BaseRepresenter):
  117. def ignore_aliases(self, data):
  118. if data is None:
  119. return True
  120. if isinstance(data, tuple) and data == ():
  121. return True
  122. if isinstance(data, (str, bytes, bool, int, float)):
  123. return True
  124. def represent_none(self, data):
  125. return self.represent_scalar('tag:yaml.org,2002:null', 'null')
  126. def represent_str(self, data):
  127. return self.represent_scalar('tag:yaml.org,2002:str', data)
  128. def represent_binary(self, data):
  129. if hasattr(base64, 'encodebytes'):
  130. data = base64.encodebytes(data).decode('ascii')
  131. else:
  132. data = base64.encodestring(data).decode('ascii')
  133. return self.represent_scalar('tag:yaml.org,2002:binary', data, style='|')
  134. def represent_bool(self, data):
  135. if data:
  136. value = 'true'
  137. else:
  138. value = 'false'
  139. return self.represent_scalar('tag:yaml.org,2002:bool', value)
  140. def represent_int(self, data):
  141. return self.represent_scalar('tag:yaml.org,2002:int', str(data))
  142. inf_value = 1e300
  143. while repr(inf_value) != repr(inf_value*inf_value):
  144. inf_value *= inf_value
  145. def represent_float(self, data):
  146. if data != data or (data == 0.0 and data == 1.0):
  147. value = '.nan'
  148. elif data == self.inf_value:
  149. value = '.inf'
  150. elif data == -self.inf_value:
  151. value = '-.inf'
  152. else:
  153. value = repr(data).lower()
  154. # Note that in some cases `repr(data)` represents a float number
  155. # without the decimal parts. For instance:
  156. # >>> repr(1e17)
  157. # '1e17'
  158. # Unfortunately, this is not a valid float representation according
  159. # to the definition of the `!!float` tag. We fix this by adding
  160. # '.0' before the 'e' symbol.
  161. if '.' not in value and 'e' in value:
  162. value = value.replace('e', '.0e', 1)
  163. return self.represent_scalar('tag:yaml.org,2002:float', value)
  164. def represent_list(self, data):
  165. #pairs = (len(data) > 0 and isinstance(data, list))
  166. #if pairs:
  167. # for item in data:
  168. # if not isinstance(item, tuple) or len(item) != 2:
  169. # pairs = False
  170. # break
  171. #if not pairs:
  172. return self.represent_sequence('tag:yaml.org,2002:seq', data)
  173. #value = []
  174. #for item_key, item_value in data:
  175. # value.append(self.represent_mapping(u'tag:yaml.org,2002:map',
  176. # [(item_key, item_value)]))
  177. #return SequenceNode(u'tag:yaml.org,2002:pairs', value)
  178. def represent_dict(self, data):
  179. return self.represent_mapping('tag:yaml.org,2002:map', data)
  180. def represent_set(self, data):
  181. value = {}
  182. for key in data:
  183. value[key] = None
  184. return self.represent_mapping('tag:yaml.org,2002:set', value)
  185. def represent_date(self, data):
  186. value = data.isoformat()
  187. return self.represent_scalar('tag:yaml.org,2002:timestamp', value)
  188. def represent_datetime(self, data):
  189. value = data.isoformat(' ')
  190. return self.represent_scalar('tag:yaml.org,2002:timestamp', value)
  191. def represent_yaml_object(self, tag, data, cls, flow_style=None):
  192. if hasattr(data, '__getstate__'):
  193. state = data.__getstate__()
  194. else:
  195. state = data.__dict__.copy()
  196. return self.represent_mapping(tag, state, flow_style=flow_style)
  197. def represent_undefined(self, data):
  198. raise RepresenterError("cannot represent an object: %s" % data)
  199. SafeRepresenter.add_representer(type(None),
  200. SafeRepresenter.represent_none)
  201. SafeRepresenter.add_representer(str,
  202. SafeRepresenter.represent_str)
  203. SafeRepresenter.add_representer(bytes,
  204. SafeRepresenter.represent_binary)
  205. SafeRepresenter.add_representer(bool,
  206. SafeRepresenter.represent_bool)
  207. SafeRepresenter.add_representer(int,
  208. SafeRepresenter.represent_int)
  209. SafeRepresenter.add_representer(float,
  210. SafeRepresenter.represent_float)
  211. SafeRepresenter.add_representer(list,
  212. SafeRepresenter.represent_list)
  213. SafeRepresenter.add_representer(tuple,
  214. SafeRepresenter.represent_list)
  215. SafeRepresenter.add_representer(dict,
  216. SafeRepresenter.represent_dict)
  217. SafeRepresenter.add_representer(set,
  218. SafeRepresenter.represent_set)
  219. SafeRepresenter.add_representer(datetime.date,
  220. SafeRepresenter.represent_date)
  221. SafeRepresenter.add_representer(datetime.datetime,
  222. SafeRepresenter.represent_datetime)
  223. SafeRepresenter.add_representer(None,
  224. SafeRepresenter.represent_undefined)
  225. class Representer(SafeRepresenter):
  226. def represent_complex(self, data):
  227. if data.imag == 0.0:
  228. data = '%r' % data.real
  229. elif data.real == 0.0:
  230. data = '%rj' % data.imag
  231. elif data.imag > 0:
  232. data = '%r+%rj' % (data.real, data.imag)
  233. else:
  234. data = '%r%rj' % (data.real, data.imag)
  235. return self.represent_scalar('tag:yaml.org,2002:python/complex', data)
  236. def represent_tuple(self, data):
  237. return self.represent_sequence('tag:yaml.org,2002:python/tuple', data)
  238. def represent_name(self, data):
  239. name = '%s.%s' % (data.__module__, data.__name__)
  240. return self.represent_scalar('tag:yaml.org,2002:python/name:'+name, '')
  241. def represent_module(self, data):
  242. return self.represent_scalar(
  243. 'tag:yaml.org,2002:python/module:'+data.__name__, '')
  244. def represent_object(self, data):
  245. # We use __reduce__ API to save the data. data.__reduce__ returns
  246. # a tuple of length 2-5:
  247. # (function, args, state, listitems, dictitems)
  248. # For reconstructing, we calls function(*args), then set its state,
  249. # listitems, and dictitems if they are not None.
  250. # A special case is when function.__name__ == '__newobj__'. In this
  251. # case we create the object with args[0].__new__(*args).
  252. # Another special case is when __reduce__ returns a string - we don't
  253. # support it.
  254. # We produce a !!python/object, !!python/object/new or
  255. # !!python/object/apply node.
  256. cls = type(data)
  257. if cls in copyreg.dispatch_table:
  258. reduce = copyreg.dispatch_table[cls](data)
  259. elif hasattr(data, '__reduce_ex__'):
  260. reduce = data.__reduce_ex__(2)
  261. elif hasattr(data, '__reduce__'):
  262. reduce = data.__reduce__()
  263. else:
  264. raise RepresenterError("cannot represent object: %r" % data)
  265. reduce = (list(reduce)+[None]*5)[:5]
  266. function, args, state, listitems, dictitems = reduce
  267. args = list(args)
  268. if state is None:
  269. state = {}
  270. if listitems is not None:
  271. listitems = list(listitems)
  272. if dictitems is not None:
  273. dictitems = dict(dictitems)
  274. if function.__name__ == '__newobj__':
  275. function = args[0]
  276. args = args[1:]
  277. tag = 'tag:yaml.org,2002:python/object/new:'
  278. newobj = True
  279. else:
  280. tag = 'tag:yaml.org,2002:python/object/apply:'
  281. newobj = False
  282. function_name = '%s.%s' % (function.__module__, function.__name__)
  283. if not args and not listitems and not dictitems \
  284. and isinstance(state, dict) and newobj:
  285. return self.represent_mapping(
  286. 'tag:yaml.org,2002:python/object:'+function_name, state)
  287. if not listitems and not dictitems \
  288. and isinstance(state, dict) and not state:
  289. return self.represent_sequence(tag+function_name, args)
  290. value = {}
  291. if args:
  292. value['args'] = args
  293. if state or not isinstance(state, dict):
  294. value['state'] = state
  295. if listitems:
  296. value['listitems'] = listitems
  297. if dictitems:
  298. value['dictitems'] = dictitems
  299. return self.represent_mapping(tag+function_name, value)
  300. def represent_ordered_dict(self, data):
  301. # Provide uniform representation across different Python versions.
  302. data_type = type(data)
  303. tag = 'tag:yaml.org,2002:python/object/apply:%s.%s' \
  304. % (data_type.__module__, data_type.__name__)
  305. items = [[key, value] for key, value in data.items()]
  306. return self.represent_sequence(tag, [items])
  307. Representer.add_representer(complex,
  308. Representer.represent_complex)
  309. Representer.add_representer(tuple,
  310. Representer.represent_tuple)
  311. Representer.add_representer(type,
  312. Representer.represent_name)
  313. Representer.add_representer(collections.OrderedDict,
  314. Representer.represent_ordered_dict)
  315. Representer.add_representer(types.FunctionType,
  316. Representer.represent_name)
  317. Representer.add_representer(types.BuiltinFunctionType,
  318. Representer.represent_name)
  319. Representer.add_representer(types.ModuleType,
  320. Representer.represent_module)
  321. Representer.add_multi_representer(object,
  322. Representer.represent_object)