Working on Constructor.
[pyyaml/python3.git] / lib / yaml / constructor.py
blob352709b3b29a376a4bad7740041ed2434e7f5f9d
2 from error import *
3 from nodes import *
5 try:
6 import datetime
7 datetime_available = True
8 except ImportError:
9 datetime_available = False
11 try:
12 set
13 except NameError:
14 from sets import Set as set
16 import binascii
18 class ConstructorError(MarkedYAMLError):
19 pass
21 class BaseConstructor:
23 def __init__(self, resolver):
24 self.resolver = resolver
25 self.constructed_objects = {}
27 def check(self):
28 # If there are more documents available?
29 return self.resolver.check()
31 def get(self):
32 # Construct and return the next document.
33 if self.resolver.check():
34 return self.construct_document(self.resolver.get())
36 def __iter__(self):
37 # Iterator protocol.
38 while self.resolver.check():
39 yield self.construct_document(self.resolver.get())
41 def construct_document(self, node):
42 native = self.construct_object(node)
43 self.constructed_objects = {}
44 return native
46 def construct_object(self, node):
47 if node in self.constructed_objects:
48 return self.constructed_objects[node]
49 if node.tag in self.yaml_constructors:
50 native = self.yaml_constructors[node.tag](self, node)
51 elif None in self.yaml_constructors:
52 native = self.yaml_constructors[None](self, node)
53 elif isinstance(node, ScalarNode):
54 native = self.construct_scalar(node)
55 elif isinstance(node, SequenceNode):
56 native = self.construct_sequence(node)
57 elif isinstance(node, MappingNode):
58 native = self.construct_mapping(node)
59 self.constructed_objects[node] = native
60 return native
62 def construct_scalar(self, node):
63 if not isinstance(node, ScalarNode):
64 if isinstance(node, MappingNode):
65 for key_node in node.value:
66 if key_node.tag == u'tag:yaml.org,2002:value':
67 return self.construct_scalar(node.value[key_node])
68 raise ConstructorError(None, None,
69 "expected a scalar node, but found %s" % node.id,
70 node.start_marker)
71 return node.value
73 def construct_sequence(self, node):
74 if not isinstance(node, SequenceNode):
75 raise ConstructorError(None, None,
76 "expected a sequence node, but found %s" % node.id,
77 node.start_marker)
78 return [self.construct_object(child) for child in node.value]
80 def construct_mapping(self, node):
81 if not isinstance(node, MappingNode):
82 raise ConstructorError(None, None,
83 "expected a mapping node, but found %s" % node.id,
84 node.start_marker)
85 mapping = {}
86 for key_node in node.value:
87 key = self.construct_object(key_node)
88 try:
89 duplicate_key = key in mapping
90 except TypeError, exc:
91 raise ConstructorError("while constructing a mapping", node.start_marker,
92 "found unacceptable key (%s)" % exc, key_node.start_marker)
93 if duplicate_key:
94 raise ConstructorError("while constructing a mapping", node.start_marker,
95 "found duplicate key", key_node.start_marker)
96 value = self.construct_object(node.value[key_node])
97 mapping[key] = value
98 return mapping
100 def construct_pairs(self, node):
101 if not isinstance(node, MappingNode):
102 raise ConstructorError(None, None,
103 "expected a mapping node, but found %s" % node.id,
104 node.start_marker)
105 pairs = []
106 for key_node in node.value:
107 key = self.construct_object(key_node)
108 value = self.construct_object(node.value[key_node])
109 pairs.append((key, value))
110 return pairs
112 def add_constructor(cls, tag, constructor):
113 if not 'yaml_constructors' in cls.__dict__:
114 cls.yaml_constructors = cls.yaml_constructors.copy()
115 cls.yaml_constructors[tag] = constructor
116 add_constructor = classmethod(add_constructor)
118 yaml_constructors = {}
120 class Constructor(BaseConstructor):
122 def construct_yaml_null(self, node):
123 self.construct_scalar(node)
124 return None
126 bool_values = {
127 u'y': True,
128 u'yes': True,
129 u'n': False,
130 u'no': False,
131 u'true': True,
132 u'false': False,
133 u'on': True,
134 u'off': False,
137 def construct_yaml_bool(self, node):
138 value = self.construct_scalar(node)
139 return self.bool_values[value.lower()]
141 def construct_yaml_int(self, node):
142 value = str(self.construct_scalar(node))
143 value = value.replace('_', '')
144 sign = +1
145 if value[0] == '-':
146 sign = -1
147 if value[0] in '+-':
148 value = value[1:]
149 if value == '0':
150 return 0
151 elif value.startswith('0b'):
152 return sign*int(value[2:], 2)
153 elif value.startswith('0x'):
154 return sign*int(value[2:], 16)
155 elif value[0] == '0':
156 return sign*int(value, 8)
157 elif ':' in value:
158 digits = [int(part) for part in value.split(':')]
159 digits.reverse()
160 base = 1
161 value = 0
162 for digit in digits:
163 value += digit*base
164 base *= 60
165 return sign*value
166 else:
167 return sign*int(value)
169 inf_value = 1e300000
170 nan_value = inf_value/inf_value
172 def construct_yaml_float(self, node):
173 value = str(self.construct_scalar(node))
174 value = value.replace('_', '')
175 sign = +1
176 if value[0] == '-':
177 value = -1
178 if value[0] in '+-':
179 value = value[1:]
180 if value.lower() == '.inf':
181 return sign*self.inf_value
182 elif value.lower() == '.nan':
183 return self.nan_value
184 elif ':' in value:
185 digits = [float(part) for part in value.split(':')]
186 digits.reverse()
187 base = 1
188 value = 0.0
189 for digit in digits:
190 value += digit*base
191 base *= 60
192 return sign*value
193 else:
194 return float(value)
196 def construct_yaml_binary(self, node):
197 value = self.construct_scalar(node)
198 try:
199 return str(value).decode('base64')
200 except (binascii.Error, UnicodeEncodeError), exc:
201 raise ConstructorError(None, None,
202 "failed to decode base64 data: %s" % exc, node.start_mark)
204 def construct_yaml_str(self, node):
205 value = self.construct_scalar(node)
206 try:
207 return str(value)
208 except UnicodeEncodeError:
209 return value
211 Constructor.add_constructor(
212 u'tag:yaml.org,2002:null',
213 Constructor.construct_yaml_null)
215 Constructor.add_constructor(
216 u'tag:yaml.org,2002:bool',
217 Constructor.construct_yaml_bool)
219 Constructor.add_constructor(
220 u'tag:yaml.org,2002:int',
221 Constructor.construct_yaml_int)
223 Constructor.add_constructor(
224 u'tag:yaml.org,2002:float',
225 Constructor.construct_yaml_float)
227 Constructor.add_constructor(
228 u'tag:yaml.org,2002:str',
229 Constructor.construct_yaml_str)
231 class YAMLObjectMetaclass(type):
233 def __init__(cls, name, bases, kwds):
234 super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
235 if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
236 cls.yaml_constructor_class.add_constructor(cls.yaml_tag, cls.from_yaml)
238 class YAMLObject(object):
240 __metaclass__ = YAMLObjectMetaclass
242 yaml_constructor_class = Constructor
244 yaml_tag = None
246 def from_yaml(cls, constructor, node):
247 raise ConstructorError(None, None,
248 "found undefined constructor for the tag %r"
249 % node.tag.encode('utf-8'), node.start_marker)
250 from_yaml = classmethod(from_yaml)
252 def to_yaml(self):
253 assert False # needs dumper