1 | |
---|
2 | __all__ = ['BaseResolver', 'Resolver'] |
---|
3 | |
---|
4 | from error import * |
---|
5 | from nodes import * |
---|
6 | |
---|
7 | import re |
---|
8 | |
---|
9 | class ResolverError(YAMLError): |
---|
10 | pass |
---|
11 | |
---|
12 | class BaseResolver(object): |
---|
13 | |
---|
14 | DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str' |
---|
15 | DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq' |
---|
16 | DEFAULT_MAPPING_TAG = u'tag:yaml.org,2002:map' |
---|
17 | |
---|
18 | yaml_implicit_resolvers = {} |
---|
19 | yaml_path_resolvers = {} |
---|
20 | |
---|
21 | def __init__(self): |
---|
22 | self.resolver_exact_paths = [] |
---|
23 | self.resolver_prefix_paths = [] |
---|
24 | |
---|
25 | def add_implicit_resolver(cls, tag, regexp, first): |
---|
26 | if not 'yaml_implicit_resolvers' in cls.__dict__: |
---|
27 | cls.yaml_implicit_resolvers = cls.yaml_implicit_resolvers.copy() |
---|
28 | if first is None: |
---|
29 | first = [None] |
---|
30 | for ch in first: |
---|
31 | cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp)) |
---|
32 | add_implicit_resolver = classmethod(add_implicit_resolver) |
---|
33 | |
---|
34 | def add_path_resolver(cls, tag, path, kind=None): |
---|
35 | # Note: `add_path_resolver` is experimental. The API could be changed. |
---|
36 | # `new_path` is a pattern that is matched against the path from the |
---|
37 | # root to the node that is being considered. `node_path` elements are |
---|
38 | # tuples `(node_check, index_check)`. `node_check` is a node class: |
---|
39 | # `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None` |
---|
40 | # matches any kind of a node. `index_check` could be `None`, a boolean |
---|
41 | # value, a string value, or a number. `None` and `False` match against |
---|
42 | # any _value_ of sequence and mapping nodes. `True` matches against |
---|
43 | # any _key_ of a mapping node. A string `index_check` matches against |
---|
44 | # a mapping value that corresponds to a scalar key which content is |
---|
45 | # equal to the `index_check` value. An integer `index_check` matches |
---|
46 | # against a sequence value with the index equal to `index_check`. |
---|
47 | if not 'yaml_path_resolvers' in cls.__dict__: |
---|
48 | cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy() |
---|
49 | new_path = [] |
---|
50 | for element in path: |
---|
51 | if isinstance(element, (list, tuple)): |
---|
52 | if len(element) == 2: |
---|
53 | node_check, index_check = element |
---|
54 | elif len(element) == 1: |
---|
55 | node_check = element[0] |
---|
56 | index_check = True |
---|
57 | else: |
---|
58 | raise ResolverError("Invalid path element: %s" % element) |
---|
59 | else: |
---|
60 | node_check = None |
---|
61 | index_check = element |
---|
62 | if node_check is str: |
---|
63 | node_check = ScalarNode |
---|
64 | elif node_check is list: |
---|
65 | node_check = SequenceNode |
---|
66 | elif node_check is dict: |
---|
67 | node_check = MappingNode |
---|
68 | elif node_check not in [ScalarNode, SequenceNode, MappingNode] \ |
---|
69 | and not isinstance(node_check, basestring) \ |
---|
70 | and node_check is not None: |
---|
71 | raise ResolverError("Invalid node checker: %s" % node_check) |
---|
72 | if not isinstance(index_check, (basestring, int)) \ |
---|
73 | and index_check is not None: |
---|
74 | raise ResolverError("Invalid index checker: %s" % index_check) |
---|
75 | new_path.append((node_check, index_check)) |
---|
76 | if kind is str: |
---|
77 | kind = ScalarNode |
---|
78 | elif kind is list: |
---|
79 | kind = SequenceNode |
---|
80 | elif kind is dict: |
---|
81 | kind = MappingNode |
---|
82 | elif kind not in [ScalarNode, SequenceNode, MappingNode] \ |
---|
83 | and kind is not None: |
---|
84 | raise ResolverError("Invalid node kind: %s" % kind) |
---|
85 | cls.yaml_path_resolvers[tuple(new_path), kind] = tag |
---|
86 | add_path_resolver = classmethod(add_path_resolver) |
---|
87 | |
---|
88 | def descend_resolver(self, current_node, current_index): |
---|
89 | if not self.yaml_path_resolvers: |
---|
90 | return |
---|
91 | exact_paths = {} |
---|
92 | prefix_paths = [] |
---|
93 | if current_node: |
---|
94 | depth = len(self.resolver_prefix_paths) |
---|
95 | for path, kind in self.resolver_prefix_paths[-1]: |
---|
96 | if self.check_resolver_prefix(depth, path, kind, |
---|
97 | current_node, current_index): |
---|
98 | if len(path) > depth: |
---|
99 | prefix_paths.append((path, kind)) |
---|
100 | else: |
---|
101 | exact_paths[kind] = self.yaml_path_resolvers[path, kind] |
---|
102 | else: |
---|
103 | for path, kind in self.yaml_path_resolvers: |
---|
104 | if not path: |
---|
105 | exact_paths[kind] = self.yaml_path_resolvers[path, kind] |
---|
106 | else: |
---|
107 | prefix_paths.append((path, kind)) |
---|
108 | self.resolver_exact_paths.append(exact_paths) |
---|
109 | self.resolver_prefix_paths.append(prefix_paths) |
---|
110 | |
---|
111 | def ascend_resolver(self): |
---|
112 | if not self.yaml_path_resolvers: |
---|
113 | return |
---|
114 | self.resolver_exact_paths.pop() |
---|
115 | self.resolver_prefix_paths.pop() |
---|
116 | |
---|
117 | def check_resolver_prefix(self, depth, path, kind, |
---|
118 | current_node, current_index): |
---|
119 | node_check, index_check = path[depth-1] |
---|
120 | if isinstance(node_check, basestring): |
---|
121 | if current_node.tag != node_check: |
---|
122 | return |
---|
123 | elif node_check is not None: |
---|
124 | if not isinstance(current_node, node_check): |
---|
125 | return |
---|
126 | if index_check is True and current_index is not None: |
---|
127 | return |
---|
128 | if (index_check is False or index_check is None) \ |
---|
129 | and current_index is None: |
---|
130 | return |
---|
131 | if isinstance(index_check, basestring): |
---|
132 | if not (isinstance(current_index, ScalarNode) |
---|
133 | and index_check == current_index.value): |
---|
134 | return |
---|
135 | elif isinstance(index_check, int) and not isinstance(index_check, bool): |
---|
136 | if index_check != current_index: |
---|
137 | return |
---|
138 | return True |
---|
139 | |
---|
140 | def resolve(self, kind, value, implicit): |
---|
141 | if kind is ScalarNode and implicit[0]: |
---|
142 | if value == u'': |
---|
143 | resolvers = self.yaml_implicit_resolvers.get(u'', []) |
---|
144 | else: |
---|
145 | resolvers = self.yaml_implicit_resolvers.get(value[0], []) |
---|
146 | resolvers += self.yaml_implicit_resolvers.get(None, []) |
---|
147 | for tag, regexp in resolvers: |
---|
148 | if regexp.match(value): |
---|
149 | return tag |
---|
150 | implicit = implicit[1] |
---|
151 | if self.yaml_path_resolvers: |
---|
152 | exact_paths = self.resolver_exact_paths[-1] |
---|
153 | if kind in exact_paths: |
---|
154 | return exact_paths[kind] |
---|
155 | if None in exact_paths: |
---|
156 | return exact_paths[None] |
---|
157 | if kind is ScalarNode: |
---|
158 | return self.DEFAULT_SCALAR_TAG |
---|
159 | elif kind is SequenceNode: |
---|
160 | return self.DEFAULT_SEQUENCE_TAG |
---|
161 | elif kind is MappingNode: |
---|
162 | return self.DEFAULT_MAPPING_TAG |
---|
163 | |
---|
164 | class Resolver(BaseResolver): |
---|
165 | pass |
---|
166 | |
---|
167 | Resolver.add_implicit_resolver( |
---|
168 | u'tag:yaml.org,2002:bool', |
---|
169 | re.compile(ur'''^(?:yes|Yes|YES|no|No|NO |
---|
170 | |true|True|TRUE|false|False|FALSE |
---|
171 | |on|On|ON|off|Off|OFF)$''', re.X), |
---|
172 | list(u'yYnNtTfFoO')) |
---|
173 | |
---|
174 | Resolver.add_implicit_resolver( |
---|
175 | u'tag:yaml.org,2002:float', |
---|
176 | re.compile(ur'''^(?:[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*(?:[eE][-+][0-9]+)? |
---|
177 | |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]* |
---|
178 | |[-+]?\.(?:inf|Inf|INF) |
---|
179 | |\.(?:nan|NaN|NAN))$''', re.X), |
---|
180 | list(u'-+0123456789.')) |
---|
181 | |
---|
182 | Resolver.add_implicit_resolver( |
---|
183 | u'tag:yaml.org,2002:int', |
---|
184 | re.compile(ur'''^(?:[-+]?0b[0-1_]+ |
---|
185 | |[-+]?0[0-7_]+ |
---|
186 | |[-+]?(?:0|[1-9][0-9_]*) |
---|
187 | |[-+]?0x[0-9a-fA-F_]+ |
---|
188 | |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X), |
---|
189 | list(u'-+0123456789')) |
---|
190 | |
---|
191 | Resolver.add_implicit_resolver( |
---|
192 | u'tag:yaml.org,2002:merge', |
---|
193 | re.compile(ur'^(?:<<)$'), |
---|
194 | ['<']) |
---|
195 | |
---|
196 | Resolver.add_implicit_resolver( |
---|
197 | u'tag:yaml.org,2002:null', |
---|
198 | re.compile(ur'''^(?: ~ |
---|
199 | |null|Null|NULL |
---|
200 | | )$''', re.X), |
---|
201 | [u'~', u'n', u'N', u'']) |
---|
202 | |
---|
203 | Resolver.add_implicit_resolver( |
---|
204 | u'tag:yaml.org,2002:timestamp', |
---|
205 | re.compile(ur'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] |
---|
206 | |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]? |
---|
207 | (?:[Tt]|[ \t]+)[0-9][0-9]? |
---|
208 | :[0-9][0-9] :[0-9][0-9] (?:\.[0-9]*)? |
---|
209 | (?:[ \t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X), |
---|
210 | list(u'0123456789')) |
---|
211 | |
---|
212 | Resolver.add_implicit_resolver( |
---|
213 | u'tag:yaml.org,2002:value', |
---|
214 | re.compile(ur'^(?:=)$'), |
---|
215 | ['=']) |
---|
216 | |
---|
217 | # The following resolver is only for documentation purposes. It cannot work |
---|
218 | # because plain scalars cannot start with '!', '&', or '*'. |
---|
219 | Resolver.add_implicit_resolver( |
---|
220 | u'tag:yaml.org,2002:yaml', |
---|
221 | re.compile(ur'^(?:!|&|\*)$'), |
---|
222 | list(u'!&*')) |
---|
223 | |
---|