66from abc import ABCMeta
77from collections import OrderedDict
88
9+ from yaml import ScalarNode , SequenceNode , MappingNode
10+
911try :
1012 # Python 2 only:
1113 from StringIO import StringIO as _StringIO # type: ignore # noqa
@@ -27,7 +29,7 @@ def __exit__(self, exception_type, exception_value, traceback):
2729import six
2830
2931try : # python 3.5+
30- from typing import Union , TypeVar , Dict , Any
32+ from typing import Union , TypeVar , Dict , Any , Tuple
3133
3234 Y = TypeVar ('Y' , bound = 'AbstractYamlObject' )
3335
@@ -49,6 +51,30 @@ class AbstractYamlObject(six.with_metaclass(ABCMeta, object)):
4951 Default implementation uses vars(self) and cls(**dct), but subclasses can override.
5052 """
5153
54+ # def __to_yaml_scalar__(self):
55+ # # type: (...) -> Any
56+ # """
57+ # Implementors should transform the object into a scalar containing all information necessary to decode the
58+ # object as a YAML scalar in the future.
59+ #
60+ # Default implementation raises an error.
61+ # :return:
62+ # """
63+ # raise NotImplementedError("Please override `__to_yaml_scalar__` if you wish to dump instances of `%s`"
64+ # " as yaml scalars." % type(self).__name__)
65+ #
66+ # def __to_yaml_sequence__(self):
67+ # # type: (...) -> Tuple[Any]
68+ # """
69+ # Implementors should transform the object into a tuple containing all information necessary to decode the
70+ # object as a YAML sequence in the future.
71+ #
72+ # Default implementation raises an error.
73+ # :return:
74+ # """
75+ # raise NotImplementedError("Please override `__to_yaml_sequence__` if you wish to dump instances of `%s`"
76+ # " as yaml sequences." % type(self).__name__)
77+
5278 def __to_yaml_dict__ (self ):
5379 # type: (...) -> Dict[str, Any]
5480 """
@@ -67,6 +93,52 @@ def __to_yaml_dict__(self):
6793 # Default: return vars(self) (Note: no need to make a copy, pyyaml does not modify it)
6894 return vars (self )
6995
96+ @classmethod
97+ def __from_yaml_scalar__ (cls , # type: Type[Y]
98+ scalar , # type: Any
99+ yaml_tag # type: str
100+ ):
101+ # type: (...) -> Y
102+ """
103+ Implementors should transform the given scalar (read from yaml by the pyYaml stack) into an object instance.
104+ The yaml tag associated to this object, read in the yaml document, is provided in parameter.
105+
106+ Note that for YamlAble and YamlObject2 subclasses, if this method is called the yaml tag will already have
107+ been checked so implementors do not have to validate it.
108+
109+ Default implementation returns cls(scalar)
110+
111+ :param scalar: the yaml scalar
112+ :param yaml_tag: the yaml schema id that was used for encoding the object (it has already been checked
113+ against is_json_schema_id_supported)
114+ :return:
115+ """
116+ # Default: call constructor with positional arguments
117+ return cls (scalar ) # type: ignore
118+
119+ @classmethod
120+ def __from_yaml_sequence__ (cls , # type: Type[Y]
121+ seq , # type: Tuple[Any]
122+ yaml_tag # type: str
123+ ):
124+ # type: (...) -> Y
125+ """
126+ Implementors should transform the given tuple (read from yaml by the pyYaml stack) into an object instance.
127+ The yaml tag associated to this object, read in the yaml document, is provided in parameter.
128+
129+ Note that for YamlAble and YamlObject2 subclasses, if this method is called the yaml tag will already have
130+ been checked so implementors do not have to validate it.
131+
132+ Default implementation returns cls(*seq)
133+
134+ :param seq: the yaml sequence
135+ :param yaml_tag: the yaml schema id that was used for encoding the object (it has already been checked
136+ against is_json_schema_id_supported)
137+ :return:
138+ """
139+ # Default: call constructor with positional arguments
140+ return cls (* seq ) # type: ignore
141+
70142 @classmethod
71143 def __from_yaml_dict__ (cls , # type: Type[Y]
72144 dct , # type: Dict[str, Any]
@@ -200,14 +272,72 @@ def load_yaml(cls, # type: Type[Y]
200272
201273
202274def read_yaml_node_as_dict (loader , node ):
275+ # type: (...) -> OrderedDict
203276 """
204277 Utility method to read a yaml node into a dictionary
205278
206279 :param loader:
207280 :param node:
208281 :return:
209282 """
210- loader .flatten_mapping (node )
211- pairs = loader .construct_pairs (node , deep = True ) # 'deep' allows the construction to be complete (inner seq...)
283+ # loader.flatten_mapping(node)
284+ # pairs = loader.construct_pairs(node, deep=True) # 'deep' allows the construction to be complete (inner seq...)
285+ pairs = loader .construct_mapping (node , deep = True ) # 'deep' allows the construction to be complete (inner seq...)
212286 constructor_args = OrderedDict (pairs )
213287 return constructor_args
288+
289+
290+ def read_yaml_node_as_sequence (loader , node ):
291+ # type: (...) -> Tuple
292+ """
293+ Utility method to read a yaml node into a sequence
294+
295+ :param loader:
296+ :param node:
297+ :return:
298+ """
299+ seq = loader .construct_sequence (node , deep = True ) # 'deep' allows the construction to be complete (inner seq...)
300+ return seq
301+
302+
303+ def read_yaml_node_as_scalar (loader , node ):
304+ # type: (...) -> Any
305+ """
306+ Utility method to read a yaml node into a sequence
307+
308+ :param loader:
309+ :param node:
310+ :return:
311+ """
312+ value = loader .construct_scalar (node )
313+ return value
314+
315+
316+ def read_yaml_node_as_yamlobject (
317+ cls , # type: Type[AbstractYamlObject]
318+ loader ,
319+ node , # type: MappingNode
320+ yaml_tag # type: str
321+ ):
322+ # type: (...) -> AbstractYamlObject
323+ """
324+ Default implementation: loads the node as a dictionary and calls __from_yaml_dict__ with this dictionary
325+
326+ :param loader:
327+ :param node:
328+ :return:
329+ """
330+ if isinstance (node , ScalarNode ):
331+ constructor_args = read_yaml_node_as_scalar (loader , node )
332+ return cls .__from_yaml_scalar__ (constructor_args , yaml_tag = yaml_tag ) # type: ignore
333+
334+ elif isinstance (node , SequenceNode ):
335+ constructor_args = read_yaml_node_as_sequence (loader , node )
336+ return cls .__from_yaml_sequence__ (constructor_args , yaml_tag = yaml_tag ) # type: ignore
337+
338+ elif isinstance (node , MappingNode ):
339+ constructor_args = read_yaml_node_as_dict (loader , node )
340+ return cls .__from_yaml_dict__ (constructor_args , yaml_tag = yaml_tag ) # type: ignore
341+
342+ else :
343+ raise TypeError ("Unknown type of yaml node: %r. Please report this to `yamlable` project." % type (node ))
0 commit comments