Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Side by Side Diff: third_party/gsutil/boto/sdb/connection.py

Issue 12042069: Scripts to download files from google storage based on sha1 sums (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Removed gsutil/tests and gsutil/docs Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Copyright (c) 2006,2007 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
22 import xml.sax
23 import threading
24 import boto
25 from boto import handler
26 from boto.connection import AWSQueryConnection
27 from boto.sdb.domain import Domain, DomainMetaData
28 from boto.sdb.item import Item
29 from boto.sdb.regioninfo import SDBRegionInfo
30 from boto.exception import SDBResponseError
31
32 class ItemThread(threading.Thread):
33 """
34 A threaded :class:`Item <boto.sdb.item.Item>` retriever utility class.
35 Retrieved :class:`Item <boto.sdb.item.Item>` objects are stored in the
36 ``items`` instance variable after :py:meth:`run() <run>` is called.
37
38 .. tip:: The item retrieval will not start until
39 the :func:`run() <boto.sdb.connection.ItemThread.run>` method is called.
40 """
41 def __init__(self, name, domain_name, item_names):
42 """
43 :param str name: A thread name. Used for identification.
44 :param str domain_name: The name of a SimpleDB
45 :class:`Domain <boto.sdb.domain.Domain>`
46 :type item_names: string or list of strings
47 :param item_names: The name(s) of the items to retrieve from the specifi ed
48 :class:`Domain <boto.sdb.domain.Domain>`.
49 :ivar list items: A list of items retrieved. Starts as empty list.
50 """
51 threading.Thread.__init__(self, name=name)
52 #print 'starting %s with %d items' % (name, len(item_names))
53 self.domain_name = domain_name
54 self.conn = SDBConnection()
55 self.item_names = item_names
56 self.items = []
57
58 def run(self):
59 """
60 Start the threaded retrieval of items. Populates the
61 ``items`` list with :class:`Item <boto.sdb.item.Item>` objects.
62 """
63 for item_name in self.item_names:
64 item = self.conn.get_attributes(self.domain_name, item_name)
65 self.items.append(item)
66
67 #boto.set_stream_logger('sdb')
68
69 class SDBConnection(AWSQueryConnection):
70 """
71 This class serves as a gateway to your SimpleDB region (defaults to
72 us-east-1). Methods within allow access to SimpleDB
73 :class:`Domain <boto.sdb.domain.Domain>` objects and their associated
74 :class:`Item <boto.sdb.item.Item>` objects.
75
76 .. tip::
77 While you may instantiate this class directly, it may be easier to
78 go through :py:func:`boto.connect_sdb`.
79 """
80 DefaultRegionName = 'us-east-1'
81 DefaultRegionEndpoint = 'sdb.us-east-1.amazonaws.com'
82 APIVersion = '2009-04-15'
83 ResponseError = SDBResponseError
84
85 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
86 is_secure=True, port=None, proxy=None, proxy_port=None,
87 proxy_user=None, proxy_pass=None, debug=0,
88 https_connection_factory=None, region=None, path='/',
89 converter=None, security_token=None, validate_certs=True):
90 """
91 For any keywords that aren't documented, refer to the parent class,
92 :py:class:`boto.connection.AWSAuthConnection`. You can avoid having
93 to worry about these keyword arguments by instantiating these objects
94 via :py:func:`boto.connect_sdb`.
95
96 :type region: :class:`boto.sdb.regioninfo.SDBRegionInfo`
97 :keyword region: Explicitly specify a region. Defaults to ``us-east-1``
98 if not specified. You may also specify the region in your ``boto.cfg ``:
99
100 .. code-block:: cfg
101
102 [SDB]
103 region = eu-west-1
104
105 """
106 if not region:
107 region_name = boto.config.get('SDB', 'region', self.DefaultRegionNam e)
108 for reg in boto.sdb.regions():
109 if reg.name == region_name:
110 region = reg
111 break
112
113 self.region = region
114 AWSQueryConnection.__init__(self, aws_access_key_id,
115 aws_secret_access_key,
116 is_secure, port, proxy,
117 proxy_port, proxy_user, proxy_pass,
118 self.region.endpoint, debug,
119 https_connection_factory, path,
120 security_token=security_token,
121 validate_certs=validate_certs)
122 self.box_usage = 0.0
123 self.converter = converter
124 self.item_cls = Item
125
126 def _required_auth_capability(self):
127 return ['sdb']
128
129 def set_item_cls(self, cls):
130 """
131 While the default item class is :py:class:`boto.sdb.item.Item`, this
132 default may be overridden. Use this method to change a connection's
133 item class.
134
135 :param object cls: The new class to set as this connection's item
136 class. See the default item class for inspiration as to what your
137 replacement should/could look like.
138 """
139 self.item_cls = cls
140
141 def _build_name_value_list(self, params, attributes, replace=False,
142 label='Attribute'):
143 keys = sorted(attributes.keys())
144 i = 1
145 for key in keys:
146 value = attributes[key]
147 if isinstance(value, list):
148 for v in value:
149 params['%s.%d.Name' % (label, i)] = key
150 if self.converter:
151 v = self.converter.encode(v)
152 params['%s.%d.Value' % (label, i)] = v
153 if replace:
154 params['%s.%d.Replace' % (label, i)] = 'true'
155 i += 1
156 else:
157 params['%s.%d.Name' % (label, i)] = key
158 if self.converter:
159 value = self.converter.encode(value)
160 params['%s.%d.Value' % (label, i)] = value
161 if replace:
162 params['%s.%d.Replace' % (label, i)] = 'true'
163 i += 1
164
165 def _build_expected_value(self, params, expected_value):
166 params['Expected.1.Name'] = expected_value[0]
167 if expected_value[1] is True:
168 params['Expected.1.Exists'] = 'true'
169 elif expected_value[1] is False:
170 params['Expected.1.Exists'] = 'false'
171 else:
172 params['Expected.1.Value'] = expected_value[1]
173
174 def _build_batch_list(self, params, items, replace=False):
175 item_names = items.keys()
176 i = 0
177 for item_name in item_names:
178 params['Item.%d.ItemName' % i] = item_name
179 j = 0
180 item = items[item_name]
181 if item is not None:
182 attr_names = item.keys()
183 for attr_name in attr_names:
184 value = item[attr_name]
185 if isinstance(value, list):
186 for v in value:
187 if self.converter:
188 v = self.converter.encode(v)
189 params['Item.%d.Attribute.%d.Name' % (i, j)] = attr_ name
190 params['Item.%d.Attribute.%d.Value' % (i, j)] = v
191 if replace:
192 params['Item.%d.Attribute.%d.Replace' % (i, j)] = 'true'
193 j += 1
194 else:
195 params['Item.%d.Attribute.%d.Name' % (i, j)] = attr_name
196 if self.converter:
197 value = self.converter.encode(value)
198 params['Item.%d.Attribute.%d.Value' % (i, j)] = value
199 if replace:
200 params['Item.%d.Attribute.%d.Replace' % (i, j)] = 't rue'
201 j += 1
202 i += 1
203
204 def _build_name_list(self, params, attribute_names):
205 i = 1
206 attribute_names.sort()
207 for name in attribute_names:
208 params['Attribute.%d.Name' % i] = name
209 i += 1
210
211 def get_usage(self):
212 """
213 Returns the BoxUsage (in USD) accumulated on this specific SDBConnection
214 instance.
215
216 .. tip:: This can be out of date, and should only be treated as a
217 rough estimate. Also note that this estimate only applies to the
218 requests made on this specific connection instance. It is by
219 no means an account-wide estimate.
220
221 :rtype: float
222 :return: The accumulated BoxUsage of all requests made on the connection .
223 """
224 return self.box_usage
225
226 def print_usage(self):
227 """
228 Print the BoxUsage and approximate costs of all requests made on
229 this specific SDBConnection instance.
230
231 .. tip:: This can be out of date, and should only be treated as a
232 rough estimate. Also note that this estimate only applies to the
233 requests made on this specific connection instance. It is by
234 no means an account-wide estimate.
235 """
236 print 'Total Usage: %f compute seconds' % self.box_usage
237 cost = self.box_usage * 0.14
238 print 'Approximate Cost: $%f' % cost
239
240 def get_domain(self, domain_name, validate=True):
241 """
242 Retrieves a :py:class:`boto.sdb.domain.Domain` object whose name
243 matches ``domain_name``.
244
245 :param str domain_name: The name of the domain to retrieve
246 :keyword bool validate: When ``True``, check to see if the domain
247 actually exists. If ``False``, blindly return a
248 :py:class:`Domain <boto.sdb.domain.Domain>` object with the
249 specified name set.
250
251 :raises:
252 :py:class:`boto.exception.SDBResponseError` if ``validate`` is
253 ``True`` and no match could be found.
254
255 :rtype: :py:class:`boto.sdb.domain.Domain`
256 :return: The requested domain
257 """
258 domain = Domain(self, domain_name)
259 if validate:
260 self.select(domain, """select * from `%s` limit 1""" % domain_name)
261 return domain
262
263 def lookup(self, domain_name, validate=True):
264 """
265 Lookup an existing SimpleDB domain. This differs from
266 :py:meth:`get_domain` in that ``None`` is returned if ``validate`` is
267 ``True`` and no match was found (instead of raising an exception).
268
269 :param str domain_name: The name of the domain to retrieve
270
271 :param bool validate: If ``True``, a ``None`` value will be returned
272 if the specified domain can't be found. If ``False``, a
273 :py:class:`Domain <boto.sdb.domain.Domain>` object will be dumbly
274 returned, regardless of whether it actually exists.
275
276 :rtype: :class:`boto.sdb.domain.Domain` object or ``None``
277 :return: The Domain object or ``None`` if the domain does not exist.
278 """
279 try:
280 domain = self.get_domain(domain_name, validate)
281 except:
282 domain = None
283 return domain
284
285 def get_all_domains(self, max_domains=None, next_token=None):
286 """
287 Returns a :py:class:`boto.resultset.ResultSet` containing
288 all :py:class:`boto.sdb.domain.Domain` objects associated with
289 this connection's Access Key ID.
290
291 :keyword int max_domains: Limit the returned
292 :py:class:`ResultSet <boto.resultset.ResultSet>` to the specified
293 number of members.
294 :keyword str next_token: A token string that was returned in an
295 earlier call to this method as the ``next_token`` attribute
296 on the returned :py:class:`ResultSet <boto.resultset.ResultSet>`
297 object. This attribute is set if there are more than Domains than
298 the value specified in the ``max_domains`` keyword. Pass the
299 ``next_token`` value from you earlier query in this keyword to
300 get the next 'page' of domains.
301 """
302 params = {}
303 if max_domains:
304 params['MaxNumberOfDomains'] = max_domains
305 if next_token:
306 params['NextToken'] = next_token
307 return self.get_list('ListDomains', params, [('DomainName', Domain)])
308
309 def create_domain(self, domain_name):
310 """
311 Create a SimpleDB domain.
312
313 :type domain_name: string
314 :param domain_name: The name of the new domain
315
316 :rtype: :class:`boto.sdb.domain.Domain` object
317 :return: The newly created domain
318 """
319 params = {'DomainName':domain_name}
320 d = self.get_object('CreateDomain', params, Domain)
321 d.name = domain_name
322 return d
323
324 def get_domain_and_name(self, domain_or_name):
325 """
326 Given a ``str`` or :class:`boto.sdb.domain.Domain`, return a
327 ``tuple`` with the following members (in order):
328
329 * In instance of :class:`boto.sdb.domain.Domain` for the requested
330 domain
331 * The domain's name as a ``str``
332
333 :type domain_or_name: ``str`` or :class:`boto.sdb.domain.Domain`
334 :param domain_or_name: The domain or domain name to get the domain
335 and name for.
336
337 :raises: :class:`boto.exception.SDBResponseError` when an invalid
338 domain name is specified.
339
340 :rtype: tuple
341 :return: A ``tuple`` with contents outlined as per above.
342 """
343 if (isinstance(domain_or_name, Domain)):
344 return (domain_or_name, domain_or_name.name)
345 else:
346 return (self.get_domain(domain_or_name), domain_or_name)
347
348 def delete_domain(self, domain_or_name):
349 """
350 Delete a SimpleDB domain.
351
352 .. caution:: This will delete the domain and all items within the domain .
353
354 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
355 :param domain_or_name: Either the name of a domain or a Domain object
356
357 :rtype: bool
358 :return: True if successful
359
360 """
361 domain, domain_name = self.get_domain_and_name(domain_or_name)
362 params = {'DomainName':domain_name}
363 return self.get_status('DeleteDomain', params)
364
365 def domain_metadata(self, domain_or_name):
366 """
367 Get the Metadata for a SimpleDB domain.
368
369 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
370 :param domain_or_name: Either the name of a domain or a Domain object
371
372 :rtype: :class:`boto.sdb.domain.DomainMetaData` object
373 :return: The newly created domain metadata object
374 """
375 domain, domain_name = self.get_domain_and_name(domain_or_name)
376 params = {'DomainName':domain_name}
377 d = self.get_object('DomainMetadata', params, DomainMetaData)
378 d.domain = domain
379 return d
380
381 def put_attributes(self, domain_or_name, item_name, attributes,
382 replace=True, expected_value=None):
383 """
384 Store attributes for a given item in a domain.
385
386 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
387 :param domain_or_name: Either the name of a domain or a Domain object
388
389 :type item_name: string
390 :param item_name: The name of the item whose attributes are being
391 stored.
392
393 :type attribute_names: dict or dict-like object
394 :param attribute_names: The name/value pairs to store as attributes
395
396 :type expected_value: list
397 :param expected_value: If supplied, this is a list or tuple consisting
398 of a single attribute name and expected value. The list can be
399 of the form:
400
401 * ['name', 'value']
402
403 In which case the call will first verify that the attribute "name"
404 of this item has a value of "value". If it does, the delete
405 will proceed, otherwise a ConditionalCheckFailed error will be
406 returned. The list can also be of the form:
407
408 * ['name', True|False]
409
410 which will simply check for the existence (True) or
411 non-existence (False) of the attribute.
412
413 :type replace: bool
414 :param replace: Whether the attribute values passed in will replace
415 existing values or will be added as addition values.
416 Defaults to True.
417
418 :rtype: bool
419 :return: True if successful
420 """
421 domain, domain_name = self.get_domain_and_name(domain_or_name)
422 params = {'DomainName' : domain_name,
423 'ItemName' : item_name}
424 self._build_name_value_list(params, attributes, replace)
425 if expected_value:
426 self._build_expected_value(params, expected_value)
427 return self.get_status('PutAttributes', params)
428
429 def batch_put_attributes(self, domain_or_name, items, replace=True):
430 """
431 Store attributes for multiple items in a domain.
432
433 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
434 :param domain_or_name: Either the name of a domain or a Domain object
435
436 :type items: dict or dict-like object
437 :param items: A dictionary-like object. The keys of the dictionary are
438 the item names and the values are themselves dictionaries
439 of attribute names/values, exactly the same as the
440 attribute_names parameter of the scalar put_attributes
441 call.
442
443 :type replace: bool
444 :param replace: Whether the attribute values passed in will replace
445 existing values or will be added as addition values.
446 Defaults to True.
447
448 :rtype: bool
449 :return: True if successful
450 """
451 domain, domain_name = self.get_domain_and_name(domain_or_name)
452 params = {'DomainName' : domain_name}
453 self._build_batch_list(params, items, replace)
454 return self.get_status('BatchPutAttributes', params, verb='POST')
455
456 def get_attributes(self, domain_or_name, item_name, attribute_names=None,
457 consistent_read=False, item=None):
458 """
459 Retrieve attributes for a given item in a domain.
460
461 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
462 :param domain_or_name: Either the name of a domain or a Domain object
463
464 :type item_name: string
465 :param item_name: The name of the item whose attributes are
466 being retrieved.
467
468 :type attribute_names: string or list of strings
469 :param attribute_names: An attribute name or list of attribute names.
470 This parameter is optional. If not supplied, all attributes will
471 be retrieved for the item.
472
473 :type consistent_read: bool
474 :param consistent_read: When set to true, ensures that the most recent
475 data is returned.
476
477 :type item: :class:`boto.sdb.item.Item`
478 :keyword item: Instead of instantiating a new Item object, you may
479 specify one to update.
480
481 :rtype: :class:`boto.sdb.item.Item`
482 :return: An Item with the requested attribute name/values set on it
483 """
484 domain, domain_name = self.get_domain_and_name(domain_or_name)
485 params = {'DomainName' : domain_name,
486 'ItemName' : item_name}
487 if consistent_read:
488 params['ConsistentRead'] = 'true'
489 if attribute_names:
490 if not isinstance(attribute_names, list):
491 attribute_names = [attribute_names]
492 self.build_list_params(params, attribute_names, 'AttributeName')
493 response = self.make_request('GetAttributes', params)
494 body = response.read()
495 if response.status == 200:
496 if item == None:
497 item = self.item_cls(domain, item_name)
498 h = handler.XmlHandler(item, self)
499 xml.sax.parseString(body, h)
500 return item
501 else:
502 raise SDBResponseError(response.status, response.reason, body)
503
504 def delete_attributes(self, domain_or_name, item_name, attr_names=None,
505 expected_value=None):
506 """
507 Delete attributes from a given item in a domain.
508
509 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
510 :param domain_or_name: Either the name of a domain or a Domain object
511
512 :type item_name: string
513 :param item_name: The name of the item whose attributes are being
514 deleted.
515
516 :type attributes: dict, list or :class:`boto.sdb.item.Item`
517 :param attributes: Either a list containing attribute names which
518 will cause all values associated with that attribute
519 name to be deleted or a dict or Item containing the
520 attribute names and keys and list of values to
521 delete as the value. If no value is supplied,
522 all attribute name/values for the item will be
523 deleted.
524
525 :type expected_value: list
526 :param expected_value: If supplied, this is a list or tuple consisting
527 of a single attribute name and expected value. The list can be
528 of the form:
529
530 * ['name', 'value']
531
532 In which case the call will first verify that the attribute "name"
533 of this item has a value of "value". If it does, the delete
534 will proceed, otherwise a ConditionalCheckFailed error will be
535 returned. The list can also be of the form:
536
537 * ['name', True|False]
538
539 which will simply check for the existence (True) or
540 non-existence (False) of the attribute.
541
542 :rtype: bool
543 :return: True if successful
544 """
545 domain, domain_name = self.get_domain_and_name(domain_or_name)
546 params = {'DomainName':domain_name,
547 'ItemName' : item_name}
548 if attr_names:
549 if isinstance(attr_names, list):
550 self._build_name_list(params, attr_names)
551 elif isinstance(attr_names, dict) or isinstance(attr_names, self.ite m_cls):
552 self._build_name_value_list(params, attr_names)
553 if expected_value:
554 self._build_expected_value(params, expected_value)
555 return self.get_status('DeleteAttributes', params)
556
557 def batch_delete_attributes(self, domain_or_name, items):
558 """
559 Delete multiple items in a domain.
560
561 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object.
562 :param domain_or_name: Either the name of a domain or a Domain object
563
564 :type items: dict or dict-like object
565 :param items: A dictionary-like object. The keys of the dictionary are
566 the item names and the values are either:
567
568 * dictionaries of attribute names/values, exactly the
569 same as the attribute_names parameter of the scalar
570 put_attributes call. The attribute name/value pairs
571 will only be deleted if they match the name/value
572 pairs passed in.
573 * None which means that all attributes associated
574 with the item should be deleted.
575
576 :return: True if successful
577 """
578 domain, domain_name = self.get_domain_and_name(domain_or_name)
579 params = {'DomainName' : domain_name}
580 self._build_batch_list(params, items, False)
581 return self.get_status('BatchDeleteAttributes', params, verb='POST')
582
583 def select(self, domain_or_name, query='', next_token=None,
584 consistent_read=False):
585 """
586 Returns a set of Attributes for item names within domain_name that
587 match the query. The query must be expressed in using the SELECT
588 style syntax rather than the original SimpleDB query language.
589 Even though the select request does not require a domain object,
590 a domain object must be passed into this method so the Item objects
591 returned can point to the appropriate domain.
592
593 :type domain_or_name: string or :class:`boto.sdb.domain.Domain` object
594 :param domain_or_name: Either the name of a domain or a Domain object
595
596 :type query: string
597 :param query: The SimpleDB query to be performed.
598
599 :type consistent_read: bool
600 :param consistent_read: When set to true, ensures that the most recent
601 data is returned.
602
603 :rtype: ResultSet
604 :return: An iterator containing the results.
605 """
606 domain, domain_name = self.get_domain_and_name(domain_or_name)
607 params = {'SelectExpression' : query}
608 if consistent_read:
609 params['ConsistentRead'] = 'true'
610 if next_token:
611 params['NextToken'] = next_token
612 try:
613 return self.get_list('Select', params, [('Item', self.item_cls)],
614 parent=domain)
615 except SDBResponseError, e:
616 e.body = "Query: %s\n%s" % (query, e.body)
617 raise e
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698