3 """Add representers to YAML to support Hooke.
5 Without introspection, YAML cannot decide how to save some
6 objects. By refusing to save these objects, we obviously loose
7 that information, so make sure the things you drop are either
8 stored somewhere else or not important.
11 >>> a = numpy.array([1,2,3])
12 >>> print yaml.dump(a)
17 The default behavior is to crash.
19 >>> yaml.Dumper.yaml_representers.pop(numpy.ndarray) # doctest: +ELLIPSIS
20 <function none_representer at 0x...>
21 >>> print yaml.dump(a)
22 !!python/object/apply:numpy.core.multiarray._reconstruct
24 - !!python/name:numpy.ndarray ''
32 - "\\x01\\0\\0\\0\\x02\\0\\0\\0\\x03\\0\\0\\0"
35 Hmm, at one point that crashed like this::
37 Traceback (most recent call last):
39 if data in [None, ()]:
40 TypeError: data type not understood
42 Must be because of the other representers I've loaded since.
44 Restore the representer for future tests.
46 >>> yaml.add_representer(numpy.ndarray, none_representer)
49 from __future__ import absolute_import
56 import yaml.constructor
57 from yaml.constructor import ConstructorError
58 import yaml.representer
60 from ..curve import Data, Curve
61 from ..playlist import FilePlaylist
64 DATA_INFO_TAG = u'!hooke.curve.DataInfo'
67 if False: # YAML dump debugging code
68 """To help isolate data types etc. that give YAML problems.
70 This is usually caused by external C modules (e.g. numpy) that
71 define new types (e.g. numpy.ndarray) which YAML cannot inspect.
73 def ignore_aliases(data):
74 print data, repr(data), type(data), repr(type(data))
76 if data in [None, ()]:
78 if isinstance(data, (str, unicode, bool, int, float)):
80 yaml.representer.SafeRepresenter.ignore_aliases = staticmethod(
84 # numpy.dtype(numpy.int32) in [None, ()]
86 # http://projects.scipy.org/numpy/ticket/1001
87 def ignore_aliases(data):
89 if data in [None, ()]:
91 if isinstance(data, (str, unicode, bool, int, float)):
95 yaml.representer.SafeRepresenter.ignore_aliases = staticmethod(
99 def none_representer(dumper, data):
100 return dumper.represent_none(None)
101 yaml.add_representer(numpy.ndarray, none_representer)
103 def bool_representer(dumper, data):
104 return dumper.represent_bool(data)
105 yaml.add_representer(numpy.bool_, bool_representer)
107 def int_representer(dumper, data):
108 return dumper.represent_int(data)
109 yaml.add_representer(numpy.int32, int_representer)
110 yaml.add_representer(numpy.dtype(numpy.int32), int_representer)
112 def long_representer(dumper, data):
113 return dumper.represent_long(data)
114 yaml.add_representer(numpy.int64, int_representer)
116 def float_representer(dumper, data):
117 return dumper.represent_float(data)
118 yaml.add_representer(numpy.float32, float_representer)
119 yaml.add_representer(numpy.float64, float_representer)
121 def data_representer(dumper, data):
122 info = dict(data.info)
123 for key in info.keys():
124 if key.startswith('raw '):
126 return dumper.represent_mapping(DATA_INFO_TAG, info)
127 yaml.add_representer(Data, data_representer)
129 def data_constructor(loader, node):
130 info = loader.construct_mapping(node)
131 return Data(shape=(0,0), dtype=numpy.float32, info=info)
132 yaml.add_constructor(DATA_INFO_TAG, data_constructor)
134 def object_representer(dumper, data):
136 if cls in copy_reg.dispatch_table:
137 reduce = copy_reg.dispatch_table[cls](data)
138 elif hasattr(data, '__reduce_ex__'):
139 reduce = data.__reduce_ex__(2)
140 elif hasattr(data, '__reduce__'):
141 reduce = data.__reduce__()
143 raise RepresenterError("cannot represent object: %r" % data)
144 reduce = (list(reduce)+[None]*5)[:5]
145 function, args, state, listitems, dictitems = reduce
149 if isinstance(state, dict) and '_default_attrs' in state:
150 for key in state['_default_attrs']:
151 if key in state and state[key] == state['_default_attrs'][key]:
153 del(state['_default_attrs'])
154 if listitems is not None:
155 listitems = list(listitems)
156 if dictitems is not None:
157 dictitems = dict(dictitems)
158 if function.__name__ == '__newobj__':
161 tag = u'tag:yaml.org,2002:python/object/new:'
164 tag = u'tag:yaml.org,2002:python/object/apply:'
166 function_name = u'%s.%s' % (function.__module__, function.__name__)
167 if not args and not listitems and not dictitems \
168 and isinstance(state, dict) and newobj:
169 return dumper.represent_mapping(
170 u'tag:yaml.org,2002:python/object:'+function_name, state)
171 if not listitems and not dictitems \
172 and isinstance(state, dict) and not state:
173 return dumper.represent_sequence(tag+function_name, args)
177 if state or not isinstance(state, dict):
178 value['state'] = state
180 value['listitems'] = listitems
182 value['dictitems'] = dictitems
183 return dumper.represent_mapping(tag+function_name, value)
184 yaml.add_representer(FilePlaylist, object_representer)
185 yaml.add_representer(Curve, object_representer)
188 # Monkey patch PyYAML bug 159.
189 # Yaml failed to restore loops in objects when __setstate__ is defined
190 # http://pyyaml.org/ticket/159
191 # With viktor.x.voroshylo@jpmchase.com's patch
192 def construct_object(self, node, deep=False):
194 old_deep = self.deep_construct
195 self.deep_construct = True
196 if node in self.constructed_objects:
197 return self.constructed_objects[node]
198 if node in self.recursive_objects:
199 obj = self.recursive_objects[node]
201 raise ConstructorError(None, None,
202 "found unconstructable recursive node", node.start_mark)
204 self.recursive_objects[node] = None
207 if node.tag in self.yaml_constructors:
208 constructor = self.yaml_constructors[node.tag]
210 for tag_prefix in self.yaml_multi_constructors:
211 if node.tag.startswith(tag_prefix):
212 tag_suffix = node.tag[len(tag_prefix):]
213 constructor = self.yaml_multi_constructors[tag_prefix]
216 if None in self.yaml_multi_constructors:
217 tag_suffix = node.tag
218 constructor = self.yaml_multi_constructors[None]
219 elif None in self.yaml_constructors:
220 constructor = self.yaml_constructors[None]
221 elif isinstance(node, ScalarNode):
222 constructor = self.__class__.construct_scalar
223 elif isinstance(node, SequenceNode):
224 constructor = self.__class__.construct_sequence
225 elif isinstance(node, MappingNode):
226 constructor = self.__class__.construct_mapping
227 if tag_suffix is None:
228 data = constructor(self, node)
230 data = constructor(self, tag_suffix, node)
231 if isinstance(data, types.GeneratorType):
233 data = generator.next()
234 if self.deep_construct:
235 self.recursive_objects[node] = data
236 for dummy in generator:
239 self.state_generators.append(generator)
240 self.constructed_objects[node] = data
241 del self.recursive_objects[node]
243 self.deep_construct = old_deep
245 yaml.constructor.BaseConstructor.construct_object = construct_object