comparison planemo/lib/python3.7/site-packages/ruamel/yaml/resolver.py @ 1:56ad4e20f292 draft

"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author guerler
date Fri, 31 Jul 2020 00:32:28 -0400
parents
children
comparison
equal deleted inserted replaced
0:d30785e31577 1:56ad4e20f292
1 # coding: utf-8
2
3 from __future__ import absolute_import
4
5 import re
6
7 if False: # MYPY
8 from typing import Any, Dict, List, Union, Text, Optional # NOQA
9 from ruamel.yaml.compat import VersionType # NOQA
10
11 from ruamel.yaml.compat import string_types, _DEFAULT_YAML_VERSION # NOQA
12 from ruamel.yaml.error import * # NOQA
13 from ruamel.yaml.nodes import MappingNode, ScalarNode, SequenceNode # NOQA
14 from ruamel.yaml.util import RegExp # NOQA
15
16 __all__ = ['BaseResolver', 'Resolver', 'VersionedResolver']
17
18
19 # fmt: off
20 # resolvers consist of
21 # - a list of applicable version
22 # - a tag
23 # - a regexp
24 # - a list of first characters to match
25 implicit_resolvers = [
26 ([(1, 2)],
27 u'tag:yaml.org,2002:bool',
28 RegExp(u'''^(?:true|True|TRUE|false|False|FALSE)$''', re.X),
29 list(u'tTfF')),
30 ([(1, 1)],
31 u'tag:yaml.org,2002:bool',
32 RegExp(u'''^(?:y|Y|yes|Yes|YES|n|N|no|No|NO
33 |true|True|TRUE|false|False|FALSE
34 |on|On|ON|off|Off|OFF)$''', re.X),
35 list(u'yYnNtTfFoO')),
36 ([(1, 2)],
37 u'tag:yaml.org,2002:float',
38 RegExp(u'''^(?:
39 [-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+]?[0-9]+)?
40 |[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+)
41 |[-+]?\\.[0-9_]+(?:[eE][-+][0-9]+)?
42 |[-+]?\\.(?:inf|Inf|INF)
43 |\\.(?:nan|NaN|NAN))$''', re.X),
44 list(u'-+0123456789.')),
45 ([(1, 1)],
46 u'tag:yaml.org,2002:float',
47 RegExp(u'''^(?:
48 [-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+]?[0-9]+)?
49 |[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+)
50 |\\.[0-9_]+(?:[eE][-+][0-9]+)?
51 |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]* # sexagesimal float
52 |[-+]?\\.(?:inf|Inf|INF)
53 |\\.(?:nan|NaN|NAN))$''', re.X),
54 list(u'-+0123456789.')),
55 ([(1, 2)],
56 u'tag:yaml.org,2002:int',
57 RegExp(u'''^(?:[-+]?0b[0-1_]+
58 |[-+]?0o?[0-7_]+
59 |[-+]?[0-9_]+
60 |[-+]?0x[0-9a-fA-F_]+)$''', re.X),
61 list(u'-+0123456789')),
62 ([(1, 1)],
63 u'tag:yaml.org,2002:int',
64 RegExp(u'''^(?:[-+]?0b[0-1_]+
65 |[-+]?0?[0-7_]+
66 |[-+]?(?:0|[1-9][0-9_]*)
67 |[-+]?0x[0-9a-fA-F_]+
68 |[-+]?[1-9][0-9_]*(?::[0-5]?[0-9])+)$''', re.X), # sexagesimal int
69 list(u'-+0123456789')),
70 ([(1, 2), (1, 1)],
71 u'tag:yaml.org,2002:merge',
72 RegExp(u'^(?:<<)$'),
73 [u'<']),
74 ([(1, 2), (1, 1)],
75 u'tag:yaml.org,2002:null',
76 RegExp(u'''^(?: ~
77 |null|Null|NULL
78 | )$''', re.X),
79 [u'~', u'n', u'N', u'']),
80 ([(1, 2), (1, 1)],
81 u'tag:yaml.org,2002:timestamp',
82 RegExp(u'''^(?:[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]
83 |[0-9][0-9][0-9][0-9] -[0-9][0-9]? -[0-9][0-9]?
84 (?:[Tt]|[ \\t]+)[0-9][0-9]?
85 :[0-9][0-9] :[0-9][0-9] (?:\\.[0-9]*)?
86 (?:[ \\t]*(?:Z|[-+][0-9][0-9]?(?::[0-9][0-9])?))?)$''', re.X),
87 list(u'0123456789')),
88 ([(1, 2), (1, 1)],
89 u'tag:yaml.org,2002:value',
90 RegExp(u'^(?:=)$'),
91 [u'=']),
92 # The following resolver is only for documentation purposes. It cannot work
93 # because plain scalars cannot start with '!', '&', or '*'.
94 ([(1, 2), (1, 1)],
95 u'tag:yaml.org,2002:yaml',
96 RegExp(u'^(?:!|&|\\*)$'),
97 list(u'!&*')),
98 ]
99 # fmt: on
100
101
102 class ResolverError(YAMLError):
103 pass
104
105
106 class BaseResolver(object):
107
108 DEFAULT_SCALAR_TAG = u'tag:yaml.org,2002:str'
109 DEFAULT_SEQUENCE_TAG = u'tag:yaml.org,2002:seq'
110 DEFAULT_MAPPING_TAG = u'tag:yaml.org,2002:map'
111
112 yaml_implicit_resolvers = {} # type: Dict[Any, Any]
113 yaml_path_resolvers = {} # type: Dict[Any, Any]
114
115 def __init__(self, loadumper=None):
116 # type: (Any, Any) -> None
117 self.loadumper = loadumper
118 if self.loadumper is not None and getattr(self.loadumper, '_resolver', None) is None:
119 self.loadumper._resolver = self.loadumper
120 self._loader_version = None # type: Any
121 self.resolver_exact_paths = [] # type: List[Any]
122 self.resolver_prefix_paths = [] # type: List[Any]
123
124 @property
125 def parser(self):
126 # type: () -> Any
127 if self.loadumper is not None:
128 if hasattr(self.loadumper, 'typ'):
129 return self.loadumper.parser
130 return self.loadumper._parser
131 return None
132
133 @classmethod
134 def add_implicit_resolver_base(cls, tag, regexp, first):
135 # type: (Any, Any, Any) -> None
136 if 'yaml_implicit_resolvers' not in cls.__dict__:
137 # deepcopy doesn't work here
138 cls.yaml_implicit_resolvers = dict(
139 (k, cls.yaml_implicit_resolvers[k][:]) for k in cls.yaml_implicit_resolvers
140 )
141 if first is None:
142 first = [None]
143 for ch in first:
144 cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp))
145
146 @classmethod
147 def add_implicit_resolver(cls, tag, regexp, first):
148 # type: (Any, Any, Any) -> None
149 if 'yaml_implicit_resolvers' not in cls.__dict__:
150 # deepcopy doesn't work here
151 cls.yaml_implicit_resolvers = dict(
152 (k, cls.yaml_implicit_resolvers[k][:]) for k in cls.yaml_implicit_resolvers
153 )
154 if first is None:
155 first = [None]
156 for ch in first:
157 cls.yaml_implicit_resolvers.setdefault(ch, []).append((tag, regexp))
158 implicit_resolvers.append(([(1, 2), (1, 1)], tag, regexp, first))
159
160 # @classmethod
161 # def add_implicit_resolver(cls, tag, regexp, first):
162
163 @classmethod
164 def add_path_resolver(cls, tag, path, kind=None):
165 # type: (Any, Any, Any) -> None
166 # Note: `add_path_resolver` is experimental. The API could be changed.
167 # `new_path` is a pattern that is matched against the path from the
168 # root to the node that is being considered. `node_path` elements are
169 # tuples `(node_check, index_check)`. `node_check` is a node class:
170 # `ScalarNode`, `SequenceNode`, `MappingNode` or `None`. `None`
171 # matches any kind of a node. `index_check` could be `None`, a boolean
172 # value, a string value, or a number. `None` and `False` match against
173 # any _value_ of sequence and mapping nodes. `True` matches against
174 # any _key_ of a mapping node. A string `index_check` matches against
175 # a mapping value that corresponds to a scalar key which content is
176 # equal to the `index_check` value. An integer `index_check` matches
177 # against a sequence value with the index equal to `index_check`.
178 if 'yaml_path_resolvers' not in cls.__dict__:
179 cls.yaml_path_resolvers = cls.yaml_path_resolvers.copy()
180 new_path = [] # type: List[Any]
181 for element in path:
182 if isinstance(element, (list, tuple)):
183 if len(element) == 2:
184 node_check, index_check = element
185 elif len(element) == 1:
186 node_check = element[0]
187 index_check = True
188 else:
189 raise ResolverError('Invalid path element: %s' % (element,))
190 else:
191 node_check = None
192 index_check = element
193 if node_check is str:
194 node_check = ScalarNode
195 elif node_check is list:
196 node_check = SequenceNode
197 elif node_check is dict:
198 node_check = MappingNode
199 elif (
200 node_check not in [ScalarNode, SequenceNode, MappingNode]
201 and not isinstance(node_check, string_types)
202 and node_check is not None
203 ):
204 raise ResolverError('Invalid node checker: %s' % (node_check,))
205 if not isinstance(index_check, (string_types, int)) and index_check is not None:
206 raise ResolverError('Invalid index checker: %s' % (index_check,))
207 new_path.append((node_check, index_check))
208 if kind is str:
209 kind = ScalarNode
210 elif kind is list:
211 kind = SequenceNode
212 elif kind is dict:
213 kind = MappingNode
214 elif kind not in [ScalarNode, SequenceNode, MappingNode] and kind is not None:
215 raise ResolverError('Invalid node kind: %s' % (kind,))
216 cls.yaml_path_resolvers[tuple(new_path), kind] = tag
217
218 def descend_resolver(self, current_node, current_index):
219 # type: (Any, Any) -> None
220 if not self.yaml_path_resolvers:
221 return
222 exact_paths = {}
223 prefix_paths = []
224 if current_node:
225 depth = len(self.resolver_prefix_paths)
226 for path, kind in self.resolver_prefix_paths[-1]:
227 if self.check_resolver_prefix(depth, path, kind, current_node, current_index):
228 if len(path) > depth:
229 prefix_paths.append((path, kind))
230 else:
231 exact_paths[kind] = self.yaml_path_resolvers[path, kind]
232 else:
233 for path, kind in self.yaml_path_resolvers:
234 if not path:
235 exact_paths[kind] = self.yaml_path_resolvers[path, kind]
236 else:
237 prefix_paths.append((path, kind))
238 self.resolver_exact_paths.append(exact_paths)
239 self.resolver_prefix_paths.append(prefix_paths)
240
241 def ascend_resolver(self):
242 # type: () -> None
243 if not self.yaml_path_resolvers:
244 return
245 self.resolver_exact_paths.pop()
246 self.resolver_prefix_paths.pop()
247
248 def check_resolver_prefix(self, depth, path, kind, current_node, current_index):
249 # type: (int, Text, Any, Any, Any) -> bool
250 node_check, index_check = path[depth - 1]
251 if isinstance(node_check, string_types):
252 if current_node.tag != node_check:
253 return False
254 elif node_check is not None:
255 if not isinstance(current_node, node_check):
256 return False
257 if index_check is True and current_index is not None:
258 return False
259 if (index_check is False or index_check is None) and current_index is None:
260 return False
261 if isinstance(index_check, string_types):
262 if not (
263 isinstance(current_index, ScalarNode) and index_check == current_index.value
264 ):
265 return False
266 elif isinstance(index_check, int) and not isinstance(index_check, bool):
267 if index_check != current_index:
268 return False
269 return True
270
271 def resolve(self, kind, value, implicit):
272 # type: (Any, Any, Any) -> Any
273 if kind is ScalarNode and implicit[0]:
274 if value == "":
275 resolvers = self.yaml_implicit_resolvers.get("", [])
276 else:
277 resolvers = self.yaml_implicit_resolvers.get(value[0], [])
278 resolvers += self.yaml_implicit_resolvers.get(None, [])
279 for tag, regexp in resolvers:
280 if regexp.match(value):
281 return tag
282 implicit = implicit[1]
283 if bool(self.yaml_path_resolvers):
284 exact_paths = self.resolver_exact_paths[-1]
285 if kind in exact_paths:
286 return exact_paths[kind]
287 if None in exact_paths:
288 return exact_paths[None]
289 if kind is ScalarNode:
290 return self.DEFAULT_SCALAR_TAG
291 elif kind is SequenceNode:
292 return self.DEFAULT_SEQUENCE_TAG
293 elif kind is MappingNode:
294 return self.DEFAULT_MAPPING_TAG
295
296 @property
297 def processing_version(self):
298 # type: () -> Any
299 return None
300
301
302 class Resolver(BaseResolver):
303 pass
304
305
306 for ir in implicit_resolvers:
307 if (1, 2) in ir[0]:
308 Resolver.add_implicit_resolver_base(*ir[1:])
309
310
311 class VersionedResolver(BaseResolver):
312 """
313 contrary to the "normal" resolver, the smart resolver delays loading
314 the pattern matching rules. That way it can decide to load 1.1 rules
315 or the (default) 1.2 rules, that no longer support octal without 0o, sexagesimals
316 and Yes/No/On/Off booleans.
317 """
318
319 def __init__(self, version=None, loader=None, loadumper=None):
320 # type: (Optional[VersionType], Any, Any) -> None
321 if loader is None and loadumper is not None:
322 loader = loadumper
323 BaseResolver.__init__(self, loader)
324 self._loader_version = self.get_loader_version(version)
325 self._version_implicit_resolver = {} # type: Dict[Any, Any]
326
327 def add_version_implicit_resolver(self, version, tag, regexp, first):
328 # type: (VersionType, Any, Any, Any) -> None
329 if first is None:
330 first = [None]
331 impl_resolver = self._version_implicit_resolver.setdefault(version, {})
332 for ch in first:
333 impl_resolver.setdefault(ch, []).append((tag, regexp))
334
335 def get_loader_version(self, version):
336 # type: (Optional[VersionType]) -> Any
337 if version is None or isinstance(version, tuple):
338 return version
339 if isinstance(version, list):
340 return tuple(version)
341 # assume string
342 return tuple(map(int, version.split(u'.')))
343
344 @property
345 def versioned_resolver(self):
346 # type: () -> Any
347 """
348 select the resolver based on the version we are parsing
349 """
350 version = self.processing_version
351 if version not in self._version_implicit_resolver:
352 for x in implicit_resolvers:
353 if version in x[0]:
354 self.add_version_implicit_resolver(version, x[1], x[2], x[3])
355 return self._version_implicit_resolver[version]
356
357 def resolve(self, kind, value, implicit):
358 # type: (Any, Any, Any) -> Any
359 if kind is ScalarNode and implicit[0]:
360 if value == "":
361 resolvers = self.versioned_resolver.get("", [])
362 else:
363 resolvers = self.versioned_resolver.get(value[0], [])
364 resolvers += self.versioned_resolver.get(None, [])
365 for tag, regexp in resolvers:
366 if regexp.match(value):
367 return tag
368 implicit = implicit[1]
369 if bool(self.yaml_path_resolvers):
370 exact_paths = self.resolver_exact_paths[-1]
371 if kind in exact_paths:
372 return exact_paths[kind]
373 if None in exact_paths:
374 return exact_paths[None]
375 if kind is ScalarNode:
376 return self.DEFAULT_SCALAR_TAG
377 elif kind is SequenceNode:
378 return self.DEFAULT_SEQUENCE_TAG
379 elif kind is MappingNode:
380 return self.DEFAULT_MAPPING_TAG
381
382 @property
383 def processing_version(self):
384 # type: () -> Any
385 try:
386 version = self.parser.yaml_version
387 except AttributeError:
388 try:
389 if hasattr(self.loadumper, 'typ'):
390 version = self.loadumper.version
391 else:
392 version = self.loadumper._serializer.use_version # dumping
393 except AttributeError:
394 version = None
395 if version is None:
396 version = self._loader_version
397 if version is None:
398 version = _DEFAULT_YAML_VERSION
399 return version