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