Mercurial > piecrust2
annotate piecrust/configuration.py @ 84:b3ce11b2cf36
Don't complain about missing `pages` or `posts` directories by default.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Wed, 03 Sep 2014 17:26:38 -0700 |
parents | d64e4703f5e6 |
children | 10fc9c8bf682 |
rev | line source |
---|---|
0 | 1 import re |
2
40fa08b261b9
Added unit tests (using `py.test`) for `Configuration`.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
2 import logging |
67
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
3 import collections |
0 | 4 import yaml |
67
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
5 from yaml.constructor import ConstructorError |
0 | 6 |
7 | |
2
40fa08b261b9
Added unit tests (using `py.test`) for `Configuration`.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
8 logger = logging.getLogger(__name__) |
40fa08b261b9
Added unit tests (using `py.test`) for `Configuration`.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
9 |
40fa08b261b9
Added unit tests (using `py.test`) for `Configuration`.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
10 |
3
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
11 class ConfigurationError(Exception): |
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
12 pass |
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
13 |
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
14 |
0 | 15 class Configuration(object): |
16 def __init__(self, values=None, validate=True): | |
17 if values is not None: | |
3
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
18 self.setAll(values, validate) |
2
40fa08b261b9
Added unit tests (using `py.test`) for `Configuration`.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
19 else: |
40fa08b261b9
Added unit tests (using `py.test`) for `Configuration`.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
20 self._values = None |
0 | 21 |
3
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
22 def setAll(self, values, validate=True): |
0 | 23 if validate: |
24 self._validateAll(values) | |
25 self._values = values | |
26 | |
3
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
27 def getAll(self): |
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
28 return self.get() |
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
29 |
0 | 30 def get(self, key_path=None): |
31 self._ensureLoaded() | |
32 if key_path is None: | |
33 return self._values | |
34 bits = key_path.split('/') | |
35 cur = self._values | |
36 for b in bits: | |
37 cur = cur.get(b) | |
38 if cur is None: | |
39 return None | |
40 return cur | |
41 | |
42 def set(self, key_path, value): | |
43 self._ensureLoaded() | |
44 value = self._validateValue(key_path, value) | |
45 bits = key_path.split('/') | |
46 bitslen = len(bits) | |
47 cur = self._values | |
48 for i, b in enumerate(bits): | |
49 if i == bitslen - 1: | |
50 cur[b] = value | |
51 else: | |
52 if b not in cur: | |
53 cur[b] = {} | |
54 cur = cur[b] | |
55 | |
56 def has(self, key_path): | |
57 self._ensureLoaded() | |
58 bits = key_path.split('/') | |
59 cur = self._values | |
60 for b in bits: | |
61 cur = cur.get(b) | |
62 if cur is None: | |
63 return False | |
64 return True | |
65 | |
66 def merge(self, other): | |
67 self._ensureLoaded() | |
68 merge_dicts(self._values, other._values, | |
69 validator=self._validateValue) | |
70 | |
71 def _ensureLoaded(self): | |
72 if self._values is None: | |
73 self._load() | |
74 | |
75 def _load(self): | |
76 self._values = self._validateAll({}) | |
77 | |
78 def _validateAll(self, values): | |
79 return values | |
80 | |
81 def _validateValue(self, key_path, value): | |
82 return value | |
83 | |
84 | |
3
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
85 def merge_dicts(source, merging, validator=None, *args): |
0 | 86 if validator is None: |
87 validator = lambda k, v: v | |
2
40fa08b261b9
Added unit tests (using `py.test`) for `Configuration`.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
88 _recurse_merge_dicts(source, merging, None, validator) |
3
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
89 for other in args: |
f485ba500df3
Gigantic change to basically make PieCrust 2 vaguely functional.
Ludovic Chabant <ludovic@chabant.com>
parents:
2
diff
changeset
|
90 _recurse_merge_dicts(source, other, None, validator) |
0 | 91 |
2
40fa08b261b9
Added unit tests (using `py.test`) for `Configuration`.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
92 |
40fa08b261b9
Added unit tests (using `py.test`) for `Configuration`.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
93 def _recurse_merge_dicts(local_cur, incoming_cur, parent_path, validator): |
5 | 94 for k, v in incoming_cur.items(): |
0 | 95 key_path = k |
96 if parent_path is not None: | |
97 key_path = parent_path + '/' + k | |
98 | |
99 local_v = local_cur.get(k) | |
100 if local_v is not None: | |
101 if isinstance(v, dict) and isinstance(local_v, dict): | |
2
40fa08b261b9
Added unit tests (using `py.test`) for `Configuration`.
Ludovic Chabant <ludovic@chabant.com>
parents:
0
diff
changeset
|
102 _recurse_merge_dicts(local_v, v, key_path, validator) |
0 | 103 elif isinstance(v, list) and isinstance(local_v, list): |
104 local_cur[k] = v + local_v | |
105 else: | |
106 local_cur[k] = validator(key_path, v) | |
107 else: | |
108 local_cur[k] = validator(key_path, v) | |
109 | |
110 | |
111 header_regex = re.compile( | |
112 r'(---\s*\n)(?P<header>(.*\n)*?)^(---\s*\n)', re.MULTILINE) | |
113 | |
114 | |
115 def parse_config_header(text): | |
116 m = header_regex.match(text) | |
117 if m is not None: | |
5 | 118 header = str(m.group('header')) |
67
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
119 config = yaml.load(header, Loader=OrderedDictYAMLLoader) |
0 | 120 offset = m.end() |
121 else: | |
122 config = {} | |
123 offset = 0 | |
124 return config, offset | |
125 | |
67
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
126 |
68
d9e494df2a99
Use `SafeLoader` instead of `BaseLoader` for Yaml parsing.
Ludovic Chabant <ludovic@chabant.com>
parents:
67
diff
changeset
|
127 class OrderedDictYAMLLoader(yaml.SafeLoader): |
67
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
128 """ A YAML loader that loads mappings into ordered dictionaries. |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
129 """ |
81
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
130 def __init__(self, *args, **kwargs): |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
131 super(OrderedDictYAMLLoader, self).__init__(*args, **kwargs) |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
132 |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
133 self.add_constructor(u'tag:yaml.org,2002:map', |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
134 type(self).construct_yaml_map) |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
135 self.add_constructor(u'tag:yaml.org,2002:omap', |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
136 type(self).construct_yaml_map) |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
137 |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
138 def construct_yaml_map(self, node): |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
139 data = collections.OrderedDict() |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
140 yield data |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
141 value = self.construct_mapping(node) |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
142 data.update(value) |
d64e4703f5e6
Propertly create `OrderedDict`s when loading YAML.
Ludovic Chabant <ludovic@chabant.com>
parents:
68
diff
changeset
|
143 |
67
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
144 def construct_mapping(self, node, deep=False): |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
145 if not isinstance(node, yaml.MappingNode): |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
146 raise ConstructorError(None, None, |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
147 "expected a mapping node, but found %s" % node.id, |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
148 node.start_mark) |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
149 mapping = collections.OrderedDict() |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
150 for key_node, value_node in node.value: |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
151 key = self.construct_object(key_node, deep=deep) |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
152 if not isinstance(key, collections.Hashable): |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
153 raise ConstructorError("while constructing a mapping", node.start_mark, |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
154 "found unhashable key", key_node.start_mark) |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
155 value = self.construct_object(value_node, deep=deep) |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
156 mapping[key] = value |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
157 return mapping |
563ce5dd02af
I don't care what the YAML spec says, ordered maps are the only sane way.
Ludovic Chabant <ludovic@chabant.com>
parents:
5
diff
changeset
|
158 |