Mercurial > piecrust2
comparison piecrust/configuration.py @ 107:10fc9c8bf682
Better support for times in YAML interop.
* Use our own sexagesimal parser/dumper for YAML to properly parse times.
* Better name for the custom parser/dumper classes.
* Add unit tests.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 15 Oct 2014 23:01:05 -0700 |
parents | d64e4703f5e6 |
children | b540d431f2da |
comparison
equal
deleted
inserted
replaced
106:5effaf1978d0 | 107:10fc9c8bf682 |
---|---|
114 | 114 |
115 def parse_config_header(text): | 115 def parse_config_header(text): |
116 m = header_regex.match(text) | 116 m = header_regex.match(text) |
117 if m is not None: | 117 if m is not None: |
118 header = str(m.group('header')) | 118 header = str(m.group('header')) |
119 config = yaml.load(header, Loader=OrderedDictYAMLLoader) | 119 config = yaml.load(header, Loader=ConfigurationLoader) |
120 offset = m.end() | 120 offset = m.end() |
121 else: | 121 else: |
122 config = {} | 122 config = {} |
123 offset = 0 | 123 offset = 0 |
124 return config, offset | 124 return config, offset |
125 | 125 |
126 | 126 |
127 class OrderedDictYAMLLoader(yaml.SafeLoader): | 127 class ConfigurationLoader(yaml.SafeLoader): |
128 """ A YAML loader that loads mappings into ordered dictionaries. | 128 """ A YAML loader that loads mappings into ordered dictionaries. |
129 """ | 129 """ |
130 def __init__(self, *args, **kwargs): | 130 def __init__(self, *args, **kwargs): |
131 super(OrderedDictYAMLLoader, self).__init__(*args, **kwargs) | 131 super(ConfigurationLoader, self).__init__(*args, **kwargs) |
132 | 132 |
133 self.add_constructor(u'tag:yaml.org,2002:map', | 133 self.add_constructor('tag:yaml.org,2002:map', |
134 type(self).construct_yaml_map) | 134 type(self).construct_yaml_map) |
135 self.add_constructor(u'tag:yaml.org,2002:omap', | 135 self.add_constructor('tag:yaml.org,2002:omap', |
136 type(self).construct_yaml_map) | 136 type(self).construct_yaml_map) |
137 self.add_constructor('tag:yaml.org,2002:sexagesimal', | |
138 type(self).construct_yaml_time) | |
137 | 139 |
138 def construct_yaml_map(self, node): | 140 def construct_yaml_map(self, node): |
139 data = collections.OrderedDict() | 141 data = collections.OrderedDict() |
140 yield data | 142 yield data |
141 value = self.construct_mapping(node) | 143 value = self.construct_mapping(node) |
154 "found unhashable key", key_node.start_mark) | 156 "found unhashable key", key_node.start_mark) |
155 value = self.construct_object(value_node, deep=deep) | 157 value = self.construct_object(value_node, deep=deep) |
156 mapping[key] = value | 158 mapping[key] = value |
157 return mapping | 159 return mapping |
158 | 160 |
161 time_regexp = re.compile( | |
162 r'''^(?P<hour>[0-9][0-9]?) | |
163 :(?P<minute>[0-9][0-9]) | |
164 (:(?P<second>[0-9][0-9]) | |
165 (\.(?P<fraction>[0-9]+))?)?$''', re.X) | |
166 | |
167 def construct_yaml_time(self, node): | |
168 self.construct_scalar(node) | |
169 match = self.time_regexp.match(node.value) | |
170 values = match.groupdict() | |
171 hour = int(values['hour']) | |
172 minute = int(values['minute']) | |
173 second = 0 | |
174 if values['second']: | |
175 second = int(values['second']) | |
176 usec = 0 | |
177 if values['fraction']: | |
178 usec = float('0.' + values['fraction']) | |
179 return second + minute * 60 + hour * 60 * 60 + usec | |
180 | |
181 | |
182 ConfigurationLoader.add_implicit_resolver( | |
183 'tag:yaml.org,2002:sexagesimal', | |
184 re.compile(r'''^[0-9][0-9]?:[0-9][0-9] | |
185 (:[0-9][0-9](\.[0-9]+)?)?$''', re.X), | |
186 list('0123456789')) | |
187 | |
188 | |
189 # We need to add our `sexagesimal` resolver before the `int` one, which | |
190 # already supports sexagesimal notation in YAML 1.1 (but not 1.2). However, | |
191 # because we know we pretty much always want it for representing time, we | |
192 # need a simple `12:30` to mean 45000, not 750. So that's why we override | |
193 # the default behaviour. | |
194 for ch in list('0123456789'): | |
195 ch_resolvers = ConfigurationLoader.yaml_implicit_resolvers[ch] | |
196 ch_resolvers.insert(0, ch_resolvers.pop()) | |
197 | |
198 | |
199 class ConfigurationDumper(yaml.SafeDumper): | |
200 def represent_ordered_dict(self, data): | |
201 return self.represent_mapping('tag:yaml.org,2002:omap', data) | |
202 | |
203 | |
204 ConfigurationDumper.add_representer(collections.OrderedDict, | |
205 ConfigurationDumper.represent_ordered_dict) | |
206 |