comparison mixconfig/yaml.py @ 3:bedc4f95b9e9

Use a YAML constructor that automatically creates OrderedDict objects when an OrderedDict implementation is available
author Franz Glasner <f.glasner@feldmann-mg.com>
date Tue, 08 Mar 2016 16:25:36 +0100
parents e4c63b4f1568
children
comparison
equal deleted inserted replaced
2:9981a68040b6 3:bedc4f95b9e9
4 4
5 """ 5 """
6 6
7 from __future__ import division, print_function, absolute_import 7 from __future__ import division, print_function, absolute_import
8 8
9 try:
10 from collections import OrderedDict
11 except ImportError:
12 try:
13 from ordereddict import OrderedDict
14 except ImportError:
15 OrderedDict = None
9 import yaml 16 import yaml
17 import yaml.constructor
10 18
11 19
12 __all__ = ["safe_load", "safe_load_all", "load", "load_all"] 20 __all__ = ["safe_load", "safe_load_all", "load", "load_all"]
13 21
14 22
15 class ConfigLoader(yaml.Loader): 23 class ConfigLoader(yaml.Loader):
16 24
17 """A YAML loader, which makes all !!str strings to Unicode. Standard 25 """A YAML loader, which makes all !!str strings to Unicode. Standard
18 PyYAML does this only in the non-ASCII case. 26 PyYAML does this only in the non-ASCII case.
19 27
28 If an `OrderedDict` implementation is available then all "map" and
29 "omap" nodes are constructed as `OrderedDict`.
30 This is against YAML specs but within configuration files it seems
31 more natural.
32
20 """ 33 """
21 34
22 def construct_yaml_str(self, node): 35 def construct_yaml_str(self, node):
23 return self.construct_scalar(node) 36 return self.construct_scalar(node)
24 37
38 if OrderedDict:
39
40 #
41 # From https://pypi.python.org/pypi/yamlordereddictloader/0.1.1
42 # (MIT License)
43 #
44
45 def construct_yaml_map(self, node):
46 data = OrderedDict()
47 yield data
48 value = self.construct_mapping(node)
49 data.update(value)
50
51 def construct_mapping(self, node, deep=False):
52 if isinstance(node, yaml.MappingNode):
53 self.flatten_mapping(node)
54 else:
55 raise yaml.constructor.ConstructorError(None, None,
56 'expected a mapping node, but found %s' % node.id,
57 node.start_mark)
58
59 mapping = OrderedDict()
60 for key_node, value_node in node.value:
61 key = self.construct_object(key_node, deep=deep)
62 try:
63 hash(key)
64 except TypeError as err:
65 raise yaml.constructor.ConstructorError(
66 'while constructing a mapping', node.start_mark,
67 'found unacceptable key (%s)' % err, key_node.start_mark)
68 value = self.construct_object(value_node, deep=deep)
69 mapping[key] = value
70 return mapping
71
25 72
26 ConfigLoader.add_constructor( 73 ConfigLoader.add_constructor(
27 "tag:yaml.org,2002:str", 74 "tag:yaml.org,2002:str",
28 ConfigLoader.construct_yaml_str) 75 ConfigLoader.construct_yaml_str)
76 if OrderedDict:
77 ConfigLoader.add_constructor(
78 "tag:yaml.org,2002:map",
79 ConfigLoader.construct_yaml_map)
80 ConfigLoader.add_constructor(
81 "tag:yaml.org,2002:omap",
82 ConfigLoader.construct_yaml_map)
29 83
30 84
31 class ConfigSafeLoader(yaml.SafeLoader): 85 class ConfigSafeLoader(yaml.SafeLoader):
32 86
33 """A safe YAML loader, which makes all !!str strings to Unicode. 87 """A safe YAML loader, which makes all !!str strings to Unicode.
34 Standard PyYAML does this only in the non-ASCII case. 88 Standard PyYAML does this only in the non-ASCII case.
35 89
90 If an `OrderedDict` implementation is available then all "map" and
91 "omap" nodes are constructed as `OrderedDict`.
92 This is against YAML specs but within configuration files it seems
93 more natural.
94
36 """ 95 """
37 96
38 def construct_yaml_str(self, node): 97 def construct_yaml_str(self, node):
39 return self.construct_scalar(node) 98 return self.construct_scalar(node)
40 99
100 if OrderedDict:
101
102 #
103 # From https://pypi.python.org/pypi/yamlordereddictloader/0.1.1
104 # (MIT License)
105 #
106
107 def construct_yaml_map(self, node):
108 data = OrderedDict()
109 yield data
110 value = self.construct_mapping(node)
111 data.update(value)
112
113 def construct_mapping(self, node, deep=False):
114 if isinstance(node, yaml.MappingNode):
115 self.flatten_mapping(node)
116 else:
117 raise yaml.constructor.ConstructorError(None, None,
118 'expected a mapping node, but found %s' % node.id,
119 node.start_mark)
120
121 mapping = OrderedDict()
122 for key_node, value_node in node.value:
123 key = self.construct_object(key_node, deep=deep)
124 try:
125 hash(key)
126 except TypeError as err:
127 raise yaml.constructor.ConstructorError(
128 'while constructing a mapping', node.start_mark,
129 'found unacceptable key (%s)' % err, key_node.start_mark)
130 value = self.construct_object(value_node, deep=deep)
131 mapping[key] = value
132 return mapping
133
41 134
42 ConfigSafeLoader.add_constructor( 135 ConfigSafeLoader.add_constructor(
43 "tag:yaml.org,2002:str", 136 "tag:yaml.org,2002:str",
44 ConfigSafeLoader.construct_yaml_str) 137 ConfigSafeLoader.construct_yaml_str)
138 if OrderedDict:
139 ConfigSafeLoader.add_constructor(
140 "tag:yaml.org,2002:map",
141 ConfigSafeLoader.construct_yaml_map)
142 ConfigSafeLoader.add_constructor(
143 "tag:yaml.org,2002:omap",
144 ConfigSafeLoader.construct_yaml_map)
45 145
46 146
47 def load(stream, Loader=ConfigLoader): 147 def load(stream, Loader=ConfigLoader):
48 return yaml.load(stream, Loader) 148 return yaml.load(stream, Loader)
49 149