source: trunk/packages/pyyaml/lib/yaml/resolver.py @ 1518

Last change on this file since 1518 was 898, checked in by hartmans, 16 years ago

Add pyyaml and libyaml packages
backported from lenny.
There is discussion about how these should go in the repository; these are added in this form
in order to make forward progress.

File size: 8.7 KB
Line 
1
2__all__ = ['BaseResolver', 'Resolver']
3
4from error import *
5from nodes import *
6
7import re
8
9class ResolverError(YAMLError):
10    pass
11
12class 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
164class Resolver(BaseResolver):
165    pass
166
167Resolver.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
174Resolver.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
182Resolver.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
191Resolver.add_implicit_resolver(
192        u'tag:yaml.org,2002:merge',
193        re.compile(ur'^(?:<<)$'),
194        ['<'])
195
196Resolver.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
203Resolver.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
212Resolver.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 '*'.
219Resolver.add_implicit_resolver(
220        u'tag:yaml.org,2002:yaml',
221        re.compile(ur'^(?:!|&|\*)$'),
222        list(u'!&*'))
223
Note: See TracBrowser for help on using the repository browser.