source: trunk/lib/syck/loaders.py @ 16

Revision 16, 7.8 KB checked in by xi, 9 years ago (diff)

syck.Loader is refactored (see #19).

Line 
1
2"""
3sdgsdf gsfdg sfdg sfd gsdfg
4"""
5
6import _syck
7
8import re, datetime, sets
9
10__all__ = ['GenericLoader', 'Loader',
11    'parse', 'load', 'parse_documents', 'load_documents']
12
13class GenericLoader(_syck.Parser):
14    """
15    ssdfg sfdgs dfgsdfgs
16    sdfgs dfgsfdgsfgsfdgsfdg sdfg
17    """
18
19    def load(self):
20        node = self.parse()
21        if self.eof:
22            return
23        return self._convert(node, {})
24
25    def _convert(self, node, node_to_object):
26        if node in node_to_object:
27            return node_to_object[node]
28        value = None
29        if node.kind == 'scalar':
30            value = node.value
31        elif node.kind == 'seq':
32            value = []
33            for item_node in node.value:
34                value.append(self._convert(item_node, node_to_object))
35        elif node.kind == 'map':
36            value = {}
37            for key_node in node.value:
38                key_object = self._convert(key_node, node_to_object)
39                value_object = self._convert(node.value[key_node],
40                        node_to_object)
41                if key_object in value:
42                    value = None
43                    break
44                try:
45                    value[key_object] = value_object
46                except TypeError:
47                    value = None
48                    break
49            if value is None:
50                value = []
51                for key_node in node.value:
52                    key_object = self_convert(key_node, node_to_object)
53                    value_object = self._convert(node.value[key_node],
54                            node_to_object)
55                value.append((key_object, value_object))
56        node.value = value
57        object = self.construct(node)
58        node_to_object[node] = object
59        return object
60
61    def construct(self, node):
62        return node.value
63
64class Merge:
65    pass
66
67class Default:
68    pass
69
70class Loader(GenericLoader):
71
72    inf_value = 1e300000
73    nan_value = inf_value/inf_value
74
75    ymd_expr = re.compile(r'(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)')
76    timestamp_expr = re.compile(r'(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)'
77            r'(?:'
78                r'(?:[Tt]|[ \t]+)(?P<hour>\d\d):(?P<minute>\d\d):(?P<second>\d\d)'
79                r'(?:\.(?P<micro>\d+)?)?'
80                r'[ \t]*(?:Z|(?P<zhour>[+-]\d\d)(?::(?P<zminute>\d\d))?)?'
81            r')?')
82
83    merge_key = Merge()
84    default_key = Default()
85
86    def __init__(self, *args, **kwds):
87        super(Loader, self).__init__(*args, **kwds)
88        self.tags = {}
89        self.add_builtin_types()
90
91    def add_builtin_types(self):
92        self.add_builtin_type('null', lambda node: None)
93        self.add_builtin_type('bool#yes', lambda node: True)
94        self.add_builtin_type('bool#no', lambda node: False)
95        self.add_builtin_type('float#fix', lambda node: float(node.value))
96        self.add_builtin_type('float#exp', lambda node: float(node.value))
97        self.add_builtin_type('float#base60', 'construct_base60_float')
98        self.add_builtin_type('float#inf', lambda node: self.inf_value)
99        self.add_builtin_type('float#neginf', lambda node: -self.inf_value)
100        self.add_builtin_type('float#nan', lambda node: self.nan_value)
101        self.add_builtin_type('int', lambda node: int(node.value))
102        self.add_builtin_type('int#hex', lambda node: int(node.value, 16))
103        self.add_builtin_type('int#oct', lambda node: int(node.value, 8))
104        self.add_builtin_type('int#base60', 'construct_base60_int')
105        self.add_builtin_type('binary', lambda node: node.value.decode('base64'))
106        self.add_builtin_type('timestamp#ymd', 'construct_timestamp')
107        self.add_builtin_type('timestamp#iso8601', 'construct_timestamp')
108        self.add_builtin_type('timestamp#spaced', 'construct_timestamp')
109        self.add_builtin_type('timestamp', 'construct_timestamp')
110        self.add_builtin_type('merge', 'construct_merge')
111        self.add_builtin_type('default', 'construct_default')
112        self.add_builtin_type('omap', 'construct_omap')
113        self.add_builtin_type('pairs', 'construct_pairs')
114        self.add_builtin_type('set', 'construct_set')
115
116    def add_type(self, type_tag, constuctor):
117        self.tags[type_tag] = constructor
118
119    def add_domain_type(self, domain, type_tag, constructor):
120        self.tags['tag:%s:%s' % (domain, type_tag)] = constructor
121
122    def add_builtin_type(self, type_tag, constructor):
123        self.tags['tag:yaml.org,2002:'+type_tag] = constructor
124
125    def add_python_type(self, type_tag, constructor):
126        self.tags['tag:python.yaml.org,2002:'+type_tag] = constructor
127
128    def add_private_type(self, type_tag, constructor):
129        self.tags['x-private:'+type_tag] = constructor
130
131    def construct(self, node):
132        if node.kind == 'map' and self.merge_key in node.value:
133            self.merge_maps(node)
134        if node.tag in self.tags:
135            constructor = self.tags[node.tag]
136            if isinstance(constructor, str):
137                constructor = getattr(self, constructor)
138            return constructor(node)
139        else:
140            return node.value
141
142    def construct_base60_float(self, node):
143        return self.construct_base60(float, node)
144
145    def construct_base60_int(self, node):
146        return self.construct_base60(int, node)
147
148    def construct_base60(self, num_type, node):
149        digits = [num_type(part) for part in node.value.split(':')]
150        digits.reverse()
151        base = 1
152        value = num_type(0)
153        for digit in digits:
154            value += digit*base
155            base *= 60
156        return value
157
158    def construct_timestamp(self, node):
159        match = self.timestamp_expr.match(node.value)
160        values = match.groupdict()
161        for key in values:
162            if values[key]:
163                values[key] = int(values[key])
164            else:
165                values[key] = 0
166        micro = values['micro']
167        if micro:
168            while 10*micro < 1000000:
169                micro *= 10
170        stamp = datetime.datetime(values['year'], values['month'], values['day'],
171                values['hour'], values['minute'], values['second'], micro)
172        diff = datetime.timedelta(hours=values['zhour'], minutes=values['zminute'])
173        return stamp-diff
174
175    def construct_merge(self, node):
176        return self.merge_key
177
178    def construct_default(self, node):
179        return self.default_key
180
181    def merge_maps(self, node):
182        maps = node.value[self.merge_key]
183        del node.value[self.merge_key]
184        if not isinstance(maps, list):
185            maps = [maps]
186        maps.reverse()
187        maps.append(node.value.copy())
188        for item in maps:
189            node.value.update(item)
190
191    def construct_omap(self, node):
192        omap = []
193        for mapping in node.value:
194            for key in mapping:
195                omap.append((key, mapping[key]))
196        return omap
197
198    def construct_pairs(self, node): # Same as construct_omap.
199        pairs = []
200        for mapping in node.value:
201            for key in mapping:
202                pairs.append((key, mapping[key]))
203        return pairs
204
205    def construct_set(self, node):
206        return sets.Set(node.value)
207
208def parse(source):
209    """Parses 'source' and returns the root of the 'Node' graph."""
210    loader = Loader(source)
211    return loader.parse()
212
213def load(source):
214    """Parses 'source' and returns the root object."""
215    loader = Loader(source)
216    return loader.load()
217
218def parse_documents(source):
219    """Iterates over 'source' and yields the root node of each document."""
220    loader = Loader(source)
221    while True:
222        node = loader.parse()
223        if loader.eof:
224            break
225        yield node
226
227def load_documents(source):
228    """Iterates over 'source' and yields the root object of each document."""
229    loader = Loader(source)
230    while True:
231        object = loader.load()
232        if loader.eof:
233            break
234        yield object
235
Note: See TracBrowser for help on using the repository browser.