You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

389 lines
14 KiB

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