Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/boto/sdb/db/manager/xmlmanager.py @ 0:d30785e31577 draft
"planemo upload commit 6eee67778febed82ddd413c3ca40b3183a3898f1"
author | guerler |
---|---|
date | Fri, 31 Jul 2020 00:18:57 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:d30785e31577 |
---|---|
1 # Copyright (c) 2006-2008 Mitch Garnaat http://garnaat.org/ | |
2 # | |
3 # Permission is hereby granted, free of charge, to any person obtaining a | |
4 # copy of this software and associated documentation files (the | |
5 # "Software"), to deal in the Software without restriction, including | |
6 # without limitation the rights to use, copy, modify, merge, publish, dis- | |
7 # tribute, sublicense, and/or sell copies of the Software, and to permit | |
8 # persons to whom the Software is furnished to do so, subject to the fol- | |
9 # lowing conditions: | |
10 # | |
11 # The above copyright notice and this permission notice shall be included | |
12 # in all copies or substantial portions of the Software. | |
13 # | |
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | |
16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | |
17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
20 # IN THE SOFTWARE. | |
21 import boto | |
22 from boto.utils import find_class, Password | |
23 from boto.sdb.db.key import Key | |
24 from boto.sdb.db.model import Model | |
25 from boto.compat import six, encodebytes | |
26 from datetime import datetime | |
27 from xml.dom.minidom import getDOMImplementation, parse, parseString, Node | |
28 | |
29 ISO8601 = '%Y-%m-%dT%H:%M:%SZ' | |
30 | |
31 class XMLConverter(object): | |
32 """ | |
33 Responsible for converting base Python types to format compatible with underlying | |
34 database. For SimpleDB, that means everything needs to be converted to a string | |
35 when stored in SimpleDB and from a string when retrieved. | |
36 | |
37 To convert a value, pass it to the encode or decode method. The encode method | |
38 will take a Python native value and convert to DB format. The decode method will | |
39 take a DB format value and convert it to Python native format. To find the appropriate | |
40 method to call, the generic encode/decode methods will look for the type-specific | |
41 method by searching for a method called "encode_<type name>" or "decode_<type name>". | |
42 """ | |
43 def __init__(self, manager): | |
44 self.manager = manager | |
45 self.type_map = { bool : (self.encode_bool, self.decode_bool), | |
46 int : (self.encode_int, self.decode_int), | |
47 Model : (self.encode_reference, self.decode_reference), | |
48 Key : (self.encode_reference, self.decode_reference), | |
49 Password : (self.encode_password, self.decode_password), | |
50 datetime : (self.encode_datetime, self.decode_datetime)} | |
51 if six.PY2: | |
52 self.type_map[long] = (self.encode_long, self.decode_long) | |
53 | |
54 def get_text_value(self, parent_node): | |
55 value = '' | |
56 for node in parent_node.childNodes: | |
57 if node.nodeType == node.TEXT_NODE: | |
58 value += node.data | |
59 return value | |
60 | |
61 def encode(self, item_type, value): | |
62 if item_type in self.type_map: | |
63 encode = self.type_map[item_type][0] | |
64 return encode(value) | |
65 return value | |
66 | |
67 def decode(self, item_type, value): | |
68 if item_type in self.type_map: | |
69 decode = self.type_map[item_type][1] | |
70 return decode(value) | |
71 else: | |
72 value = self.get_text_value(value) | |
73 return value | |
74 | |
75 def encode_prop(self, prop, value): | |
76 if isinstance(value, list): | |
77 if hasattr(prop, 'item_type'): | |
78 new_value = [] | |
79 for v in value: | |
80 item_type = getattr(prop, "item_type") | |
81 if Model in item_type.mro(): | |
82 item_type = Model | |
83 new_value.append(self.encode(item_type, v)) | |
84 return new_value | |
85 else: | |
86 return value | |
87 else: | |
88 return self.encode(prop.data_type, value) | |
89 | |
90 def decode_prop(self, prop, value): | |
91 if prop.data_type == list: | |
92 if hasattr(prop, 'item_type'): | |
93 item_type = getattr(prop, "item_type") | |
94 if Model in item_type.mro(): | |
95 item_type = Model | |
96 values = [] | |
97 for item_node in value.getElementsByTagName('item'): | |
98 value = self.decode(item_type, item_node) | |
99 values.append(value) | |
100 return values | |
101 else: | |
102 return self.get_text_value(value) | |
103 else: | |
104 return self.decode(prop.data_type, value) | |
105 | |
106 def encode_int(self, value): | |
107 value = int(value) | |
108 return '%d' % value | |
109 | |
110 def decode_int(self, value): | |
111 value = self.get_text_value(value) | |
112 if value: | |
113 value = int(value) | |
114 else: | |
115 value = None | |
116 return value | |
117 | |
118 def encode_long(self, value): | |
119 value = long(value) | |
120 return '%d' % value | |
121 | |
122 def decode_long(self, value): | |
123 value = self.get_text_value(value) | |
124 return long(value) | |
125 | |
126 def encode_bool(self, value): | |
127 if value == True: | |
128 return 'true' | |
129 else: | |
130 return 'false' | |
131 | |
132 def decode_bool(self, value): | |
133 value = self.get_text_value(value) | |
134 if value.lower() == 'true': | |
135 return True | |
136 else: | |
137 return False | |
138 | |
139 def encode_datetime(self, value): | |
140 return value.strftime(ISO8601) | |
141 | |
142 def decode_datetime(self, value): | |
143 value = self.get_text_value(value) | |
144 try: | |
145 return datetime.strptime(value, ISO8601) | |
146 except: | |
147 return None | |
148 | |
149 def encode_reference(self, value): | |
150 if isinstance(value, six.string_types): | |
151 return value | |
152 if value is None: | |
153 return '' | |
154 else: | |
155 val_node = self.manager.doc.createElement("object") | |
156 val_node.setAttribute('id', value.id) | |
157 val_node.setAttribute('class', '%s.%s' % (value.__class__.__module__, value.__class__.__name__)) | |
158 return val_node | |
159 | |
160 def decode_reference(self, value): | |
161 if not value: | |
162 return None | |
163 try: | |
164 value = value.childNodes[0] | |
165 class_name = value.getAttribute("class") | |
166 id = value.getAttribute("id") | |
167 cls = find_class(class_name) | |
168 return cls.get_by_ids(id) | |
169 except: | |
170 return None | |
171 | |
172 def encode_password(self, value): | |
173 if value and len(value) > 0: | |
174 return str(value) | |
175 else: | |
176 return None | |
177 | |
178 def decode_password(self, value): | |
179 value = self.get_text_value(value) | |
180 return Password(value) | |
181 | |
182 | |
183 class XMLManager(object): | |
184 | |
185 def __init__(self, cls, db_name, db_user, db_passwd, | |
186 db_host, db_port, db_table, ddl_dir, enable_ssl): | |
187 self.cls = cls | |
188 if not db_name: | |
189 db_name = cls.__name__.lower() | |
190 self.db_name = db_name | |
191 self.db_user = db_user | |
192 self.db_passwd = db_passwd | |
193 self.db_host = db_host | |
194 self.db_port = db_port | |
195 self.db_table = db_table | |
196 self.ddl_dir = ddl_dir | |
197 self.s3 = None | |
198 self.converter = XMLConverter(self) | |
199 self.impl = getDOMImplementation() | |
200 self.doc = self.impl.createDocument(None, 'objects', None) | |
201 | |
202 self.connection = None | |
203 self.enable_ssl = enable_ssl | |
204 self.auth_header = None | |
205 if self.db_user: | |
206 base64string = encodebytes('%s:%s' % (self.db_user, self.db_passwd))[:-1] | |
207 authheader = "Basic %s" % base64string | |
208 self.auth_header = authheader | |
209 | |
210 def _connect(self): | |
211 if self.db_host: | |
212 if self.enable_ssl: | |
213 from httplib import HTTPSConnection as Connection | |
214 else: | |
215 from httplib import HTTPConnection as Connection | |
216 | |
217 self.connection = Connection(self.db_host, self.db_port) | |
218 | |
219 def _make_request(self, method, url, post_data=None, body=None): | |
220 """ | |
221 Make a request on this connection | |
222 """ | |
223 if not self.connection: | |
224 self._connect() | |
225 try: | |
226 self.connection.close() | |
227 except: | |
228 pass | |
229 self.connection.connect() | |
230 headers = {} | |
231 if self.auth_header: | |
232 headers["Authorization"] = self.auth_header | |
233 self.connection.request(method, url, body, headers) | |
234 resp = self.connection.getresponse() | |
235 return resp | |
236 | |
237 def new_doc(self): | |
238 return self.impl.createDocument(None, 'objects', None) | |
239 | |
240 def _object_lister(self, cls, doc): | |
241 for obj_node in doc.getElementsByTagName('object'): | |
242 if not cls: | |
243 class_name = obj_node.getAttribute('class') | |
244 cls = find_class(class_name) | |
245 id = obj_node.getAttribute('id') | |
246 obj = cls(id) | |
247 for prop_node in obj_node.getElementsByTagName('property'): | |
248 prop_name = prop_node.getAttribute('name') | |
249 prop = obj.find_property(prop_name) | |
250 if prop: | |
251 if hasattr(prop, 'item_type'): | |
252 value = self.get_list(prop_node, prop.item_type) | |
253 else: | |
254 value = self.decode_value(prop, prop_node) | |
255 value = prop.make_value_from_datastore(value) | |
256 setattr(obj, prop.name, value) | |
257 yield obj | |
258 | |
259 def reset(self): | |
260 self._connect() | |
261 | |
262 def get_doc(self): | |
263 return self.doc | |
264 | |
265 def encode_value(self, prop, value): | |
266 return self.converter.encode_prop(prop, value) | |
267 | |
268 def decode_value(self, prop, value): | |
269 return self.converter.decode_prop(prop, value) | |
270 | |
271 def get_s3_connection(self): | |
272 if not self.s3: | |
273 self.s3 = boto.connect_s3(self.aws_access_key_id, self.aws_secret_access_key) | |
274 return self.s3 | |
275 | |
276 def get_list(self, prop_node, item_type): | |
277 values = [] | |
278 try: | |
279 items_node = prop_node.getElementsByTagName('items')[0] | |
280 except: | |
281 return [] | |
282 for item_node in items_node.getElementsByTagName('item'): | |
283 value = self.converter.decode(item_type, item_node) | |
284 values.append(value) | |
285 return values | |
286 | |
287 def get_object_from_doc(self, cls, id, doc): | |
288 obj_node = doc.getElementsByTagName('object')[0] | |
289 if not cls: | |
290 class_name = obj_node.getAttribute('class') | |
291 cls = find_class(class_name) | |
292 if not id: | |
293 id = obj_node.getAttribute('id') | |
294 obj = cls(id) | |
295 for prop_node in obj_node.getElementsByTagName('property'): | |
296 prop_name = prop_node.getAttribute('name') | |
297 prop = obj.find_property(prop_name) | |
298 value = self.decode_value(prop, prop_node) | |
299 value = prop.make_value_from_datastore(value) | |
300 if value is not None: | |
301 try: | |
302 setattr(obj, prop.name, value) | |
303 except: | |
304 pass | |
305 return obj | |
306 | |
307 def get_props_from_doc(self, cls, id, doc): | |
308 """ | |
309 Pull out the properties from this document | |
310 Returns the class, the properties in a hash, and the id if provided as a tuple | |
311 :return: (cls, props, id) | |
312 """ | |
313 obj_node = doc.getElementsByTagName('object')[0] | |
314 if not cls: | |
315 class_name = obj_node.getAttribute('class') | |
316 cls = find_class(class_name) | |
317 if not id: | |
318 id = obj_node.getAttribute('id') | |
319 props = {} | |
320 for prop_node in obj_node.getElementsByTagName('property'): | |
321 prop_name = prop_node.getAttribute('name') | |
322 prop = cls.find_property(prop_name) | |
323 value = self.decode_value(prop, prop_node) | |
324 value = prop.make_value_from_datastore(value) | |
325 if value is not None: | |
326 props[prop.name] = value | |
327 return (cls, props, id) | |
328 | |
329 | |
330 def get_object(self, cls, id): | |
331 if not self.connection: | |
332 self._connect() | |
333 | |
334 if not self.connection: | |
335 raise NotImplementedError("Can't query without a database connection") | |
336 url = "/%s/%s" % (self.db_name, id) | |
337 resp = self._make_request('GET', url) | |
338 if resp.status == 200: | |
339 doc = parse(resp) | |
340 else: | |
341 raise Exception("Error: %s" % resp.status) | |
342 return self.get_object_from_doc(cls, id, doc) | |
343 | |
344 def query(self, cls, filters, limit=None, order_by=None): | |
345 if not self.connection: | |
346 self._connect() | |
347 | |
348 if not self.connection: | |
349 raise NotImplementedError("Can't query without a database connection") | |
350 | |
351 from urllib import urlencode | |
352 | |
353 query = str(self._build_query(cls, filters, limit, order_by)) | |
354 if query: | |
355 url = "/%s?%s" % (self.db_name, urlencode({"query": query})) | |
356 else: | |
357 url = "/%s" % self.db_name | |
358 resp = self._make_request('GET', url) | |
359 if resp.status == 200: | |
360 doc = parse(resp) | |
361 else: | |
362 raise Exception("Error: %s" % resp.status) | |
363 return self._object_lister(cls, doc) | |
364 | |
365 def _build_query(self, cls, filters, limit, order_by): | |
366 import types | |
367 if len(filters) > 4: | |
368 raise Exception('Too many filters, max is 4') | |
369 parts = [] | |
370 properties = cls.properties(hidden=False) | |
371 for filter, value in filters: | |
372 name, op = filter.strip().split() | |
373 found = False | |
374 for property in properties: | |
375 if property.name == name: | |
376 found = True | |
377 if types.TypeType(value) == list: | |
378 filter_parts = [] | |
379 for val in value: | |
380 val = self.encode_value(property, val) | |
381 filter_parts.append("'%s' %s '%s'" % (name, op, val)) | |
382 parts.append("[%s]" % " OR ".join(filter_parts)) | |
383 else: | |
384 value = self.encode_value(property, value) | |
385 parts.append("['%s' %s '%s']" % (name, op, value)) | |
386 if not found: | |
387 raise Exception('%s is not a valid field' % name) | |
388 if order_by: | |
389 if order_by.startswith("-"): | |
390 key = order_by[1:] | |
391 type = "desc" | |
392 else: | |
393 key = order_by | |
394 type = "asc" | |
395 parts.append("['%s' starts-with ''] sort '%s' %s" % (key, key, type)) | |
396 return ' intersection '.join(parts) | |
397 | |
398 def query_gql(self, query_string, *args, **kwds): | |
399 raise NotImplementedError("GQL queries not supported in XML") | |
400 | |
401 def save_list(self, doc, items, prop_node): | |
402 items_node = doc.createElement('items') | |
403 prop_node.appendChild(items_node) | |
404 for item in items: | |
405 item_node = doc.createElement('item') | |
406 items_node.appendChild(item_node) | |
407 if isinstance(item, Node): | |
408 item_node.appendChild(item) | |
409 else: | |
410 text_node = doc.createTextNode(item) | |
411 item_node.appendChild(text_node) | |
412 | |
413 def save_object(self, obj, expected_value=None): | |
414 """ | |
415 Marshal the object and do a PUT | |
416 """ | |
417 doc = self.marshal_object(obj) | |
418 if obj.id: | |
419 url = "/%s/%s" % (self.db_name, obj.id) | |
420 else: | |
421 url = "/%s" % (self.db_name) | |
422 resp = self._make_request("PUT", url, body=doc.toxml()) | |
423 new_obj = self.get_object_from_doc(obj.__class__, None, parse(resp)) | |
424 obj.id = new_obj.id | |
425 for prop in obj.properties(): | |
426 try: | |
427 propname = prop.name | |
428 except AttributeError: | |
429 propname = None | |
430 if propname: | |
431 value = getattr(new_obj, prop.name) | |
432 if value: | |
433 setattr(obj, prop.name, value) | |
434 return obj | |
435 | |
436 | |
437 def marshal_object(self, obj, doc=None): | |
438 if not doc: | |
439 doc = self.new_doc() | |
440 if not doc: | |
441 doc = self.doc | |
442 obj_node = doc.createElement('object') | |
443 | |
444 if obj.id: | |
445 obj_node.setAttribute('id', obj.id) | |
446 | |
447 obj_node.setAttribute('class', '%s.%s' % (obj.__class__.__module__, | |
448 obj.__class__.__name__)) | |
449 root = doc.documentElement | |
450 root.appendChild(obj_node) | |
451 for property in obj.properties(hidden=False): | |
452 prop_node = doc.createElement('property') | |
453 prop_node.setAttribute('name', property.name) | |
454 prop_node.setAttribute('type', property.type_name) | |
455 value = property.get_value_for_datastore(obj) | |
456 if value is not None: | |
457 value = self.encode_value(property, value) | |
458 if isinstance(value, list): | |
459 self.save_list(doc, value, prop_node) | |
460 elif isinstance(value, Node): | |
461 prop_node.appendChild(value) | |
462 else: | |
463 text_node = doc.createTextNode(six.text_type(value).encode("ascii", "ignore")) | |
464 prop_node.appendChild(text_node) | |
465 obj_node.appendChild(prop_node) | |
466 | |
467 return doc | |
468 | |
469 def unmarshal_object(self, fp, cls=None, id=None): | |
470 if isinstance(fp, six.string_types): | |
471 doc = parseString(fp) | |
472 else: | |
473 doc = parse(fp) | |
474 return self.get_object_from_doc(cls, id, doc) | |
475 | |
476 def unmarshal_props(self, fp, cls=None, id=None): | |
477 """ | |
478 Same as unmarshalling an object, except it returns | |
479 from "get_props_from_doc" | |
480 """ | |
481 if isinstance(fp, six.string_types): | |
482 doc = parseString(fp) | |
483 else: | |
484 doc = parse(fp) | |
485 return self.get_props_from_doc(cls, id, doc) | |
486 | |
487 def delete_object(self, obj): | |
488 url = "/%s/%s" % (self.db_name, obj.id) | |
489 return self._make_request("DELETE", url) | |
490 | |
491 def set_key_value(self, obj, name, value): | |
492 self.domain.put_attributes(obj.id, {name: value}, replace=True) | |
493 | |
494 def delete_key_value(self, obj, name): | |
495 self.domain.delete_attributes(obj.id, name) | |
496 | |
497 def get_key_value(self, obj, name): | |
498 a = self.domain.get_attributes(obj.id, name) | |
499 if name in a: | |
500 return a[name] | |
501 else: | |
502 return None | |
503 | |
504 def get_raw_item(self, obj): | |
505 return self.domain.get_item(obj.id) | |
506 | |
507 def set_property(self, prop, obj, name, value): | |
508 pass | |
509 | |
510 def get_property(self, prop, obj, name): | |
511 pass | |
512 | |
513 def load_object(self, obj): | |
514 if not obj._loaded: | |
515 obj = obj.get_by_id(obj.id) | |
516 obj._loaded = True | |
517 return obj |