comparison piecrust/configuration.py @ 852:4850f8c21b6e

core: Start of the big refactor for PieCrust 3.0. * Everything is a `ContentSource`, including assets directories. * Most content sources are subclasses of the base file-system source. * A source is processed by a "pipeline", and there are 2 built-in pipelines, one for assets and one for pages. The asset pipeline is vaguely functional, but the page pipeline is completely broken right now. * Rewrite the baking process as just running appropriate pipelines on each content item. This should allow for better parallelization.
author Ludovic Chabant <ludovic@chabant.com>
date Wed, 17 May 2017 00:11:48 -0700
parents fd694f1297c7
children f070a4fc033c
comparison
equal deleted inserted replaced
851:2c7e57d80bba 852:4850f8c21b6e
71 other_values = other 71 other_values = other
72 elif isinstance(other, Configuration): 72 elif isinstance(other, Configuration):
73 other_values = other._values 73 other_values = other._values
74 else: 74 else:
75 raise Exception( 75 raise Exception(
76 "Unsupported value type to merge: %s" % type(other)) 76 "Unsupported value type to merge: %s" % type(other))
77 77
78 merge_dicts(self._values, other_values, 78 merge_dicts(self._values, other_values,
79 validator=self._validateValue) 79 validator=self._validateValue)
80 80
81 def validateTypes(self, allowed_types=default_allowed_types): 81 def validateTypes(self, allowed_types=default_allowed_types):
94 def _validateTypeRecursive(self, v, allowed_types): 94 def _validateTypeRecursive(self, v, allowed_types):
95 if v is None: 95 if v is None:
96 return 96 return
97 if not isinstance(v, allowed_types): 97 if not isinstance(v, allowed_types):
98 raise ConfigurationError( 98 raise ConfigurationError(
99 "Value '%s' is of forbidden type: %s" % (v, type(v))) 99 "Value '%s' is of forbidden type: %s" % (v, type(v)))
100 if isinstance(v, dict): 100 if isinstance(v, dict):
101 self._validateDictTypesRecursive(v, allowed_types) 101 self._validateDictTypesRecursive(v, allowed_types)
102 elif isinstance(v, list): 102 elif isinstance(v, list):
103 self._validateListTypesRecursive(v, allowed_types) 103 self._validateListTypesRecursive(v, allowed_types)
104 104
221 if isinstance(v, dict): 221 if isinstance(v, dict):
222 _recurse_visit_dict(v, key_path, visitor) 222 _recurse_visit_dict(v, key_path, visitor)
223 223
224 224
225 header_regex = re.compile( 225 header_regex = re.compile(
226 r'(---\s*\n)(?P<header>(.*\n)*?)^(---\s*\n)', re.MULTILINE) 226 r'(---\s*\n)(?P<header>(.*\n)*?)^(---\s*\n)', re.MULTILINE)
227 227
228 228
229 def parse_config_header(text): 229 def parse_config_header(text):
230 m = header_regex.match(text) 230 m = header_regex.match(text)
231 if m is not None: 231 if m is not None:
237 offset = 0 237 offset = 0
238 return config, offset 238 return config, offset
239 239
240 240
241 class ConfigurationLoader(SafeLoader): 241 class ConfigurationLoader(SafeLoader):
242 """ A YAML loader that loads mappings into ordered dictionaries. 242 """ A YAML loader that loads mappings into ordered dictionaries,
243 and supports sexagesimal notations for timestamps.
243 """ 244 """
244 def __init__(self, *args, **kwargs): 245 def __init__(self, *args, **kwargs):
245 super(ConfigurationLoader, self).__init__(*args, **kwargs) 246 super(ConfigurationLoader, self).__init__(*args, **kwargs)
246 247
247 self.add_constructor('tag:yaml.org,2002:map', 248 self.add_constructor('tag:yaml.org,2002:map',
248 type(self).construct_yaml_map) 249 type(self).construct_yaml_map)
249 self.add_constructor('tag:yaml.org,2002:omap', 250 self.add_constructor('tag:yaml.org,2002:omap',
250 type(self).construct_yaml_map) 251 type(self).construct_yaml_map)
251 self.add_constructor('tag:yaml.org,2002:sexagesimal', 252 self.add_constructor('tag:yaml.org,2002:sexagesimal',
252 type(self).construct_yaml_time) 253 type(self).construct_yaml_time)
253 254
254 def construct_yaml_map(self, node): 255 def construct_yaml_map(self, node):
255 data = collections.OrderedDict() 256 data = collections.OrderedDict()
256 yield data 257 yield data
257 value = self.construct_mapping(node) 258 value = self.construct_mapping(node)
258 data.update(value) 259 data.update(value)
259 260
260 def construct_mapping(self, node, deep=False): 261 def construct_mapping(self, node, deep=False):
261 if not isinstance(node, yaml.MappingNode): 262 if not isinstance(node, yaml.MappingNode):
262 raise ConstructorError(None, None, 263 raise ConstructorError(
263 "expected a mapping node, but found %s" % node.id, 264 None, None,
264 node.start_mark) 265 "expected a mapping node, but found %s" % node.id,
266 node.start_mark)
265 mapping = collections.OrderedDict() 267 mapping = collections.OrderedDict()
266 for key_node, value_node in node.value: 268 for key_node, value_node in node.value:
267 key = self.construct_object(key_node, deep=deep) 269 key = self.construct_object(key_node, deep=deep)
268 if not isinstance(key, collections.Hashable): 270 if not isinstance(key, collections.Hashable):
269 raise ConstructorError("while constructing a mapping", node.start_mark, 271 raise ConstructorError(
270 "found unhashable key", key_node.start_mark) 272 "while constructing a mapping", node.start_mark,
273 "found unhashable key", key_node.start_mark)
271 value = self.construct_object(value_node, deep=deep) 274 value = self.construct_object(value_node, deep=deep)
272 mapping[key] = value 275 mapping[key] = value
273 return mapping 276 return mapping
274 277
275 time_regexp = re.compile( 278 time_regexp = re.compile(
276 r'''^(?P<hour>[0-9][0-9]?) 279 r'''^(?P<hour>[0-9][0-9]?)
277 :(?P<minute>[0-9][0-9]) 280 :(?P<minute>[0-9][0-9])
278 (:(?P<second>[0-9][0-9]) 281 (:(?P<second>[0-9][0-9])
279 (\.(?P<fraction>[0-9]+))?)?$''', re.X) 282 (\.(?P<fraction>[0-9]+))?)?$''', re.X)
280 283
281 def construct_yaml_time(self, node): 284 def construct_yaml_time(self, node):
292 usec = float('0.' + values['fraction']) 295 usec = float('0.' + values['fraction'])
293 return second + minute * 60 + hour * 60 * 60 + usec 296 return second + minute * 60 + hour * 60 * 60 + usec
294 297
295 298
296 ConfigurationLoader.add_implicit_resolver( 299 ConfigurationLoader.add_implicit_resolver(
297 'tag:yaml.org,2002:sexagesimal', 300 'tag:yaml.org,2002:sexagesimal',
298 re.compile(r'''^[0-9][0-9]?:[0-9][0-9] 301 re.compile(r'''^[0-9][0-9]?:[0-9][0-9]
299 (:[0-9][0-9](\.[0-9]+)?)?$''', re.X), 302 (:[0-9][0-9](\.[0-9]+)?)?$''', re.X),
300 list('0123456789')) 303 list('0123456789'))
301 304
302 305
303 # We need to add our `sexagesimal` resolver before the `int` one, which 306 # We need to add our `sexagesimal` resolver before the `int` one, which
304 # already supports sexagesimal notation in YAML 1.1 (but not 1.2). However, 307 # already supports sexagesimal notation in YAML 1.1 (but not 1.2). However,
305 # because we know we pretty much always want it for representing time, we 308 # because we know we pretty much always want it for representing time, we
317 # we always load maps into `OrderedDicts` anyway. 320 # we always load maps into `OrderedDicts` anyway.
318 return self.represent_mapping('tag:yaml.org,2002:map', data) 321 return self.represent_mapping('tag:yaml.org,2002:map', data)
319 322
320 323
321 ConfigurationDumper.add_representer(collections.OrderedDict, 324 ConfigurationDumper.add_representer(collections.OrderedDict,
322 ConfigurationDumper.represent_ordered_dict) 325 ConfigurationDumper.represent_ordered_dict)
323 326