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

Revision 18, 8.0 KB checked in by xi, 9 years ago (diff)

Python 2.2 compatibility (sort of).

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