source: pyyaml/trunk/lib/yaml/composer.py @ 136

Revision 136, 6.7 KB checked in by xi, 8 years ago (diff)

Major refactoring.

RevLine 
[53]1
[136]2__all__ = ['BaseComposer', 'Composer', 'ComposerError']
[57]3
[53]4from error import MarkedYAMLError
5from events import *
6from nodes import *
7
8class ComposerError(MarkedYAMLError):
9    pass
10
[136]11class BaseComposer:
[53]12
[136]13    yaml_resolvers = {}
14
15    def __init__(self):
[53]16        self.all_anchors = {}
17        self.complete_anchors = {}
[136]18        self.resolver_tags = []
19        self.resolver_paths = []
[53]20
[136]21    def check_node(self):
[53]22        # If there are more documents available?
[136]23        return not self.check_event(StreamEndEvent)
[53]24
[136]25    def get_node(self):
[53]26        # Get the root node of the next document.
[136]27        if not self.check_event(StreamEndEvent):
[53]28            return self.compose_document()
29
30    def __iter__(self):
31        # Iterator protocol.
[136]32        while not self.check_event(StreamEndEvent):
[53]33            yield self.compose_document()
34
35    def compose_document(self):
[118]36
[136]37        # Drop the STREAM-START event.
38        if self.check_event(StreamStartEvent):
39            self.get_event()
40
[118]41        # Drop the DOCUMENT-START event.
[136]42        self.get_event()
[118]43
44        # Compose the root node.
[136]45        node = self.compose_node([])
[118]46
47        # Drop the DOCUMENT-END event.
[136]48        self.get_event()
[118]49
[53]50        self.all_anchors = {}
51        self.complete_anchors = {}
[136]52        self.resolver_tags = []
53        self.resolver_paths = []
[53]54        return node
55
[136]56    def increase_resolver_depth(self, path):
57        depth = len(path)
58        tag = None
59        paths = []
60        if not depth:
61            for resolver_path in self.yaml_resolvers.keys():
62                if resolver_path:
63                    paths.append(resolver_path)
64                else:
65                    tag = self.yaml_resolvers[resolver_path]
66        else:
67            base, index = path[-1]
68            if isinstance(index, ScalarNode)    \
69                    and index.tag == self.DEFAULT_SCALAR_TAG:
70                index = index.value
71            elif isinstance(index, Node):
72                index = None
73            for resolver_path in self.resolver_paths[-1]:
74                resolver_index = resolver_path[depth-1]
75                if resolver_index is None or resolver_index == index:
76                    if len(resolver_index) > depth:
77                        paths.append(resolver_path)
78                    else:
79                        tag = self.yaml_resolvers[resolver_path]
80        self.resolver_tags.append(tag)
81        self.resolver_paths.append(paths)
82
83    def decrease_resolver_depth(self):
84        del self.resolver_tags[-1]
85        del self.resolver_paths[-1]
86
87    def compose_node(self, path):
88        if self.check_event(AliasEvent):
89            event = self.get_event()
[53]90            anchor = event.anchor
91            if anchor not in self.all_anchors:
92                raise ComposerError(None, None, "found undefined alias %r"
[116]93                        % anchor.encode('utf-8'), event.start_mark)
[53]94            if anchor not in self.complete_anchors:
95                collection_event = self.all_anchors[anchor]
96                raise ComposerError("while composing a collection",
[116]97                        collection_event.start_mark,
[53]98                        "found recursive anchor %r" % anchor.encode('utf-8'),
[116]99                        event.start_mark)
[53]100            return self.complete_anchors[anchor]
[136]101        self.increase_resolver_depth(path)
102        event = self.peek_event()
[53]103        anchor = event.anchor
104        if anchor is not None:
105            if anchor in self.all_anchors:
106                raise ComposerError("found duplicate anchor %r; first occurence"
[116]107                        % anchor.encode('utf-8'), self.all_anchors[anchor].start_mark,
108                        "second occurence", event.start_mark)
[53]109            self.all_anchors[anchor] = event
[136]110        if self.check_event(ScalarEvent):
111            node = self.compose_scalar_node(path)
112        elif self.check_event(SequenceStartEvent):
113            node = self.compose_sequence_node(path)
114        elif self.check_event(MappingStartEvent):
115            node = self.compose_mapping_node(path)
[53]116        if anchor is not None:
117            self.complete_anchors[anchor] = node
[136]118        self.decrease_resolver_depth()
[53]119        return node
120
[136]121    def compose_scalar_node(self, path):
122        event = self.get_event()
123        tag = self.resolve_scalar(path, event.tag, event.implicit, event.value)
124        return ScalarNode(tag, event.value,
[133]125                event.start_mark, event.end_mark, style=event.style)
[53]126
[136]127    def compose_sequence_node(self, path):
128        start_event = self.get_event()
129        tag = self.resolve_sequence(path, start_event.tag)
130        node = SequenceNode(tag, [],
131                start_event.start_mark, None,
[133]132                flow_style=start_event.flow_style)
[136]133        index = 0
134        while not self.check_event(SequenceEndEvent):
135            node.value.append(self.compose_node(path+[(node, index)]))
136            index += 1
137        end_event = self.get_event()
138        node.end_mark = end_event.end_mark
139        return node
[53]140
[136]141    def compose_mapping_node(self, path):
142        start_event = self.get_event()
143        tag = self.resolve_mapping(path, start_event.tag)
144        node = MappingNode(tag, {},
145                start_event.start_mark, None,
146                flow_style=start_event.flow_style)
147        while not self.check_event(MappingEndEvent):
148            key_event = self.peek_event()
149            item_key = self.compose_node(path+[(node, None)])
150            item_value = self.compose_node(path+[(node, item_key)])
151            if item_key in node.value:
[116]152                raise ComposerError("while composing a mapping", start_event.start_mark,
153                        "found duplicate key", key_event.start_mark)
[136]154            node.value[item_key] = item_value
155        end_event = self.get_event()
156        node.end_mark = end_event.end_mark
157        return node
[53]158
[136]159    def resolve_scalar(self, path, tag, implicit, value):
160        if implicit:
161            tag = self.detect(value)
162        if tag is None and self.resolver_tags[-1]:
163            tag = self.resolver_tags[-1]
164        if tag is None or tag == u'!':
165            tag = self.DEFAULT_SCALAR_TAG
166        return tag
167
168    def resolve_sequence(self, path, tag):
169        if tag is None and self.resolver_tags[-1]:
170            tag = self.resolver_tags[-1]
171        if tag is None or tag == u'!':
172            tag = self.DEFAULT_SEQUENCE_TAG
173        return tag
174
175    def resolve_mapping(self, path, tag):
176        if tag is None and self.resolver_tags[-1]:
177            tag = self.resolver_tags[-1]
178        if tag is None or tag == u'!':
179            tag = self.DEFAULT_MAPPING_TAG
180        return tag
181
182    def add_resolver(self, tag, path):
183        if not 'yaml_resolvers' in cls.__dict__:
184            cls.yaml_resolvers = cls.yaml_resolvers.copy()
185        cls.yaml_resolvers[tuple(path)] = tag
186    add_resolver = classmethod(add_resolver)
187
188class Composer(BaseComposer):
189    pass
190
Note: See TracBrowser for help on using the repository browser.