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