| [7] | 1 | |
|---|
| [18] | 2 | # Python 2.2 compatibility |
|---|
| 3 | from __future__ import generators |
|---|
| [7] | 4 | |
|---|
| [18] | 5 | try: |
|---|
| 6 | import datetime |
|---|
| 7 | except ImportError: |
|---|
| 8 | pass |
|---|
| 9 | |
|---|
| 10 | try: |
|---|
| 11 | import sets |
|---|
| 12 | except 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 | |
|---|
| [16] | 21 | import _syck |
|---|
| [9] | 22 | |
|---|
| [18] | 23 | import re |
|---|
| [9] | 24 | |
|---|
| [16] | 25 | __all__ = ['GenericLoader', 'Loader', |
|---|
| 26 | 'parse', 'load', 'parse_documents', 'load_documents'] |
|---|
| [10] | 27 | |
|---|
| [16] | 28 | class GenericLoader(_syck.Parser): |
|---|
| [8] | 29 | |
|---|
| [16] | 30 | def load(self): |
|---|
| 31 | node = self.parse() |
|---|
| 32 | if self.eof: |
|---|
| 33 | return |
|---|
| 34 | return self._convert(node, {}) |
|---|
| [8] | 35 | |
|---|
| [16] | 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 |
|---|
| [7] | 71 | |
|---|
| [16] | 72 | def construct(self, node): |
|---|
| 73 | return node.value |
|---|
| [7] | 74 | |
|---|
| [8] | 75 | class Merge: |
|---|
| 76 | pass |
|---|
| 77 | |
|---|
| 78 | class Default: |
|---|
| 79 | pass |
|---|
| 80 | |
|---|
| [16] | 81 | class Loader(GenericLoader): |
|---|
| [7] | 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 | |
|---|
| [8] | 94 | merge_key = Merge() |
|---|
| 95 | default_key = Default() |
|---|
| 96 | |
|---|
| [16] | 97 | def __init__(self, *args, **kwds): |
|---|
| 98 | super(Loader, self).__init__(*args, **kwds) |
|---|
| [7] | 99 | self.tags = {} |
|---|
| [8] | 100 | self.add_builtin_types() |
|---|
| [7] | 101 | |
|---|
| [8] | 102 | def add_builtin_types(self): |
|---|
| [7] | 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)) |
|---|
| [16] | 108 | self.add_builtin_type('float#base60', 'construct_base60_float') |
|---|
| [7] | 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)) |
|---|
| [16] | 115 | self.add_builtin_type('int#base60', 'construct_base60_int') |
|---|
| [7] | 116 | self.add_builtin_type('binary', lambda node: node.value.decode('base64')) |
|---|
| [16] | 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') |
|---|
| [7] | 126 | |
|---|
| [16] | 127 | def add_type(self, type_tag, constuctor): |
|---|
| 128 | self.tags[type_tag] = constructor |
|---|
| [7] | 129 | |
|---|
| [16] | 130 | def add_domain_type(self, domain, type_tag, constructor): |
|---|
| 131 | self.tags['tag:%s:%s' % (domain, type_tag)] = constructor |
|---|
| [7] | 132 | |
|---|
| [16] | 133 | def add_builtin_type(self, type_tag, constructor): |
|---|
| 134 | self.tags['tag:yaml.org,2002:'+type_tag] = constructor |
|---|
| [7] | 135 | |
|---|
| [16] | 136 | def add_python_type(self, type_tag, constructor): |
|---|
| 137 | self.tags['tag:python.yaml.org,2002:'+type_tag] = constructor |
|---|
| [7] | 138 | |
|---|
| [16] | 139 | def add_private_type(self, type_tag, constructor): |
|---|
| 140 | self.tags['x-private:'+type_tag] = constructor |
|---|
| [7] | 141 | |
|---|
| [16] | 142 | def construct(self, node): |
|---|
| [8] | 143 | if node.kind == 'map' and self.merge_key in node.value: |
|---|
| 144 | self.merge_maps(node) |
|---|
| [16] | 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) |
|---|
| [7] | 150 | else: |
|---|
| 151 | return node.value |
|---|
| 152 | |
|---|
| [16] | 153 | def construct_base60_float(self, node): |
|---|
| 154 | return self.construct_base60(float, node) |
|---|
| [7] | 155 | |
|---|
| [16] | 156 | def construct_base60_int(self, node): |
|---|
| 157 | return self.construct_base60(int, node) |
|---|
| 158 | |
|---|
| 159 | def construct_base60(self, num_type, node): |
|---|
| [7] | 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 | |
|---|
| [16] | 169 | def construct_timestamp(self, node): |
|---|
| [7] | 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 | |
|---|
| [16] | 186 | def construct_merge(self, node): |
|---|
| [8] | 187 | return self.merge_key |
|---|
| 188 | |
|---|
| [16] | 189 | def construct_default(self, node): |
|---|
| [8] | 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 | |
|---|
| [16] | 202 | def construct_omap(self, node): |
|---|
| [8] | 203 | omap = [] |
|---|
| 204 | for mapping in node.value: |
|---|
| 205 | for key in mapping: |
|---|
| 206 | omap.append((key, mapping[key])) |
|---|
| 207 | return omap |
|---|
| 208 | |
|---|
| [16] | 209 | def construct_pairs(self, node): # Same as construct_omap. |
|---|
| [8] | 210 | pairs = [] |
|---|
| 211 | for mapping in node.value: |
|---|
| 212 | for key in mapping: |
|---|
| 213 | pairs.append((key, mapping[key])) |
|---|
| 214 | return pairs |
|---|
| 215 | |
|---|
| [16] | 216 | def construct_set(self, node): |
|---|
| [8] | 217 | return sets.Set(node.value) |
|---|
| 218 | |
|---|
| [16] | 219 | def parse(source): |
|---|
| 220 | """Parses 'source' and returns the root of the 'Node' graph.""" |
|---|
| 221 | loader = Loader(source) |
|---|
| 222 | return loader.parse() |
|---|
| 223 | |
|---|
| 224 | def load(source): |
|---|
| 225 | """Parses 'source' and returns the root object.""" |
|---|
| 226 | loader = Loader(source) |
|---|
| 227 | return loader.load() |
|---|
| 228 | |
|---|
| 229 | def 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 | |
|---|
| 238 | def 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 | |
|---|