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

Revision 25, 12.1 KB checked in by xi, 9 years ago (diff)

Adding some docstrings.

RevLine 
[25]1"""
2syck.loaders is a high-level wrapper for the Syck YAML parser.
3Do not use it directly, use the module 'syck' instead.
4"""
[7]5
[18]6# Python 2.2 compatibility
7from __future__ import generators
[7]8
[18]9try:
10    import datetime
11except ImportError:
12    pass
13
14try:
15    import sets
16except ImportError:
17    class _sets:
18        def Set(self, items):
19            set = {}
20            for items in items:
21                set[items] = None
22            return set
23    sets = _sets()
24
[16]25import _syck
[9]26
[21]27import sys, re
[9]28
[16]29__all__ = ['GenericLoader', 'Loader',
30    'parse', 'load', 'parse_documents', 'load_documents']
[10]31
[16]32class GenericLoader(_syck.Parser):
[25]33    """
34    GenericLoader constructs primitive Python objects from YAML documents.
35    """
[8]36
[16]37    def load(self):
[25]38        """
39        Loads a YAML document from the source and return a native Python
40        object. On EOF, returns None and set the eof attribute on.
41        """
[16]42        node = self.parse()
43        if self.eof:
44            return
45        return self._convert(node, {})
[8]46
[16]47    def _convert(self, node, node_to_object):
48        if node in node_to_object:
49            return node_to_object[node]
50        value = None
51        if node.kind == 'scalar':
52            value = node.value
53        elif node.kind == 'seq':
54            value = []
55            for item_node in node.value:
56                value.append(self._convert(item_node, node_to_object))
57        elif node.kind == 'map':
58            value = {}
59            for key_node in node.value:
60                key_object = self._convert(key_node, node_to_object)
61                value_object = self._convert(node.value[key_node],
62                        node_to_object)
63                try:
[22]64                    if key_object in value:
65                        value = None
66                        break
[16]67                    value[key_object] = value_object
68                except TypeError:
69                    value = None
70                    break
71            if value is None:
72                value = []
73                for key_node in node.value:
[22]74                    key_object = self._convert(key_node, node_to_object)
[16]75                    value_object = self._convert(node.value[key_node],
76                            node_to_object)
77                value.append((key_object, value_object))
78        node.value = value
79        object = self.construct(node)
80        node_to_object[node] = object
81        return object
[7]82
[16]83    def construct(self, node):
[25]84        """Constructs a Python object by the given node."""
[16]85        return node.value
[7]86
[8]87class Merge:
[25]88    """Represents the merge key '<<'."""
[8]89    pass
90
91class Default:
[25]92    """Represents the default key '='."""
[8]93    pass
94
[16]95class Loader(GenericLoader):
[25]96    """
97    Loader constructs native Python objects from YAML documents.
98    """
[7]99
100    inf_value = 1e300000
101    nan_value = inf_value/inf_value
102
103    timestamp_expr = re.compile(r'(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)'
104            r'(?:'
105                r'(?:[Tt]|[ \t]+)(?P<hour>\d\d):(?P<minute>\d\d):(?P<second>\d\d)'
106                r'(?:\.(?P<micro>\d+)?)?'
107                r'[ \t]*(?:Z|(?P<zhour>[+-]\d\d)(?::(?P<zminute>\d\d))?)?'
108            r')?')
109
[8]110    merge_key = Merge()
111    default_key = Default()
112
[21]113    non_ascii = []
114    for i in range(256):
115        ch = chr(i)
116        if ch.isalnum():
117            non_ascii.append(ch)
118        else:
119            non_ascii.append('_')
120    non_ascii = ''.join(non_ascii)
[7]121
[21]122    python_bools = {'True': True, 'False': False}
[7]123
[21]124    class python_class:
125        pass
[7]126
[21]127    def find_constructor(self, node):
[25]128        """
129        Returns the contructor for generating a Python object for the given
130        node.
131
132        The node tags are mapped to constructors by the following rule:
133
134        Tag                             Constructor
135        ---                             -----------
136        tag:yaml.org,2002:type          construct_type
137        tag:python.yaml.org,2002:type   construct_python_type
138        x-private:type                  construct_private_type
139        tag:domain.tld,2002:type        construct_domain_tld_2002_type
140
141        See the method code for more details.
142        """
[21]143        parts = []
144        if node.tag:
145            parts = node.tag.split(':')
146        if parts:
147            if parts[0] == 'tag':
148                parts.pop(0)
149                if parts:
150                    if parts[0] == 'yaml.org,2002':
151                        parts.pop(0)
152                    elif parts[0] == 'python.yaml.org,2002':
153                        parts[0] = 'python'
154            elif parts[0] == 'x-private':
155                parts[0] = 'private'
156        parts = [part.translate(self.non_ascii) for part in parts]
157        while parts:
158            method = 'construct_'+'_'.join(parts)
159            if hasattr(self, method):
160                return getattr(self, method)
161            parts.pop()
[7]162
[16]163    def construct(self, node):
[25]164        """Constructs a Python object by the given node."""
[8]165        if node.kind == 'map' and self.merge_key in node.value:
166            self.merge_maps(node)
[21]167        constructor = self.find_constructor(node)
168        if constructor:
[16]169            return constructor(node)
[7]170        else:
171            return node.value
172
[21]173    def construct_null(self, node):
174        return None
[7]175
[21]176    def construct_bool_yes(self, node):
177        return True
[16]178
[21]179    def construct_bool_no(self, node):
180        return False
181
182    def construct_numeric_base60(self, num_type, node):
[7]183        digits = [num_type(part) for part in node.value.split(':')]
184        digits.reverse()
185        base = 1
186        value = num_type(0)
187        for digit in digits:
188            value += digit*base
189            base *= 60
190        return value
191
[21]192    def construct_int(self, node):
193        return int(node.value)
194
195    def construct_int_hex(self, node):
196        return int(node.value, 16)
197
198    def construct_int_oct(self, node):
199        return int(node.value, 8)
200
201    def construct_int_base60(self, node):
202        return self.construct_numeric_base60(int, node)
203
204    def construct_float(self, node):
205        return float(node.value)
206    construct_float_fix = construct_float
207    construct_float_exp = construct_float
208
209    def construct_float_base60(self, node):
210        return self.construct_numeric_base60(float, node)
211
212    def construct_float_inf(self, node):
213        return self.inf_value
214
215    def construct_float_neginf(self, node):
216        return -self.inf_value
217
218    def construct_float_nan(self, node):
219        return self.nan_value
220
221    def construct_binary(self, node):
222        return node.value.decode('base64')
223
[16]224    def construct_timestamp(self, node):
[7]225        match = self.timestamp_expr.match(node.value)
226        values = match.groupdict()
227        for key in values:
228            if values[key]:
229                values[key] = int(values[key])
230            else:
231                values[key] = 0
232        micro = values['micro']
233        if micro:
234            while 10*micro < 1000000:
235                micro *= 10
236        stamp = datetime.datetime(values['year'], values['month'], values['day'],
237                values['hour'], values['minute'], values['second'], micro)
238        diff = datetime.timedelta(hours=values['zhour'], minutes=values['zminute'])
239        return stamp-diff
[21]240    construct_timestamp_ymd = construct_timestamp
241    construct_timestamp_iso8601 = construct_timestamp
242    construct_timestamp_spaced = construct_timestamp
[7]243
[16]244    def construct_merge(self, node):
[8]245        return self.merge_key
246
[16]247    def construct_default(self, node):
[8]248        return self.default_key
249
250    def merge_maps(self, node):
251        maps = node.value[self.merge_key]
252        del node.value[self.merge_key]
253        if not isinstance(maps, list):
254            maps = [maps]
255        maps.reverse()
256        maps.append(node.value.copy())
257        for item in maps:
258            node.value.update(item)
259
[16]260    def construct_omap(self, node):
[8]261        omap = []
262        for mapping in node.value:
263            for key in mapping:
264                omap.append((key, mapping[key]))
265        return omap
266
[16]267    def construct_pairs(self, node): # Same as construct_omap.
[8]268        pairs = []
269        for mapping in node.value:
270            for key in mapping:
271                pairs.append((key, mapping[key]))
272        return pairs
273
[16]274    def construct_set(self, node):
[8]275        return sets.Set(node.value)
276
[21]277    def construct_python_none(self, node):
278        return None
279
280    def construct_python_bool(self, node):
281        return self.python_bools[node.value]
282
283    def construct_python_int(self, node):
284        return int(node.value)
285
286    def construct_python_long(self, node):
287        return long(node.value)
288
289    def construct_python_float(self, node):
290        return float(node.value)
291
292    def construct_python_str(self, node):
293        return str(node.value)
294
295    def construct_python_unicode(self, node):
296        return unicode(node.value, 'utf-8')
297
298    def construct_python_list(self, node):
299        return node.value
300
301    def construct_python_tuple(self, node):
302        return tuple(node.value)
303
304    def construct_python_dict(self, node):
305        return node.value
306
307    def find_python_object(self, node):
308        full_name = node.tag.split(':')[3]
309        parts = full_name.split('.')
310        object_name = parts.pop()
311        module_name = '.'.join(parts)
312        if not module_name:
313            module_name = '__builtin__'
314        else:
315            __import__(module_name)
316        return getattr(sys.modules[module_name], object_name)
317
318    def find_python_state(self, node):
319        if node.kind == 'seq':
320            args = node.value
321            kwds = {}
322            state = {}
323        else:
324            args = node.value.get('args', [])
325            kwds = node.value.get('kwds', {})
326            state = node.value.get('state', {})
327        return args, kwds, state
328
329    def set_python_state(self, object, state):
330        if hasattr(object, '__setstate__'):
331            object.__setstate__(state)
332        else:
333            slotstate = {}
334            if isinstance(state, tuple) and len(state) == 2:
335                state, slotstate = state
[25]336            if hasattr(object, '__dict__'):
337                object.__dict__.update(state)
338            elif state:
339                slotstate.update(state)
[21]340            for key, value in slotstate.items():
341                setattr(object, key, value)
342
343    def construct_python_name(self, node):
344        return self.find_python_object(node)
345
346    def construct_python_object(self, node):
347        cls = self.find_python_object(node)
348        if type(cls) is type(self.python_class):
349            if hasattr(cls, '__getnewargs__'):
350                object = cls()
351            else:
352                object = self.python_class()
353                object.__class__ = cls
354        else:
355            object = cls.__new__(cls)
356        self.set_python_state(object, node.value)
357        return object
358
359    def construct_python_new(self, node):
360        cls = self.find_python_object(node)
361        args, kwds, state = self.find_python_state(node)
362        if type(cls) is type(self.python_class):
363            object = cls(*args, **kwds)
364        else:
365            object = cls.__new__(cls, *args, **kwds)
366        self.set_python_state(object, state)
367        return object
368
369    def construct_python_apply(self, node):
370        constructor = self.find_python_object(node)
371        args, kwds, state = self.find_python_state(node)
372        object = constructor(*args, **kwds)
373        self.set_python_state(object, state)
374        return object
375
[22]376def parse(source, Loader=Loader, **parameters):
[16]377    """Parses 'source' and returns the root of the 'Node' graph."""
[22]378    loader = Loader(source, **parameters)
[16]379    return loader.parse()
380
[22]381def load(source, Loader=Loader, **parameters):
[16]382    """Parses 'source' and returns the root object."""
[22]383    loader = Loader(source, **parameters)
[16]384    return loader.load()
385
[22]386def parse_documents(source, Loader=Loader, **parameters):
[25]387    """Iterates over 'source' and yields the root 'Node' for each document."""
[22]388    loader = Loader(source, **parameters)
[16]389    while True:
390        node = loader.parse()
391        if loader.eof:
392            break
393        yield node
394
[22]395def load_documents(source, Loader=Loader, **parameters):
[25]396    """Iterates over 'source' and yields the root object for each document."""
[22]397    loader = Loader(source, **parameters)
[16]398    while True:
399        object = loader.load()
400        if loader.eof:
401            break
402        yield object
403
Note: See TracBrowser for help on using the repository browser.