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

Unified Diff: third_party/boto/boto/dynamodb2/table.py

Issue 698893003: Update checked in version of gsutil to version 4.6 (Closed) Base URL: http://dart.googlecode.com/svn/third_party/gsutil/
Patch Set: Created 6 years, 1 month 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « third_party/boto/boto/dynamodb2/results.py ('k') | third_party/boto/boto/ec2/__init__.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/boto/boto/dynamodb2/table.py
===================================================================
--- third_party/boto/boto/dynamodb2/table.py (revision 33376)
+++ third_party/boto/boto/dynamodb2/table.py (working copy)
@@ -7,7 +7,8 @@
from boto.dynamodb2.items import Item
from boto.dynamodb2.layer1 import DynamoDBConnection
from boto.dynamodb2.results import ResultSet, BatchGetResultSet
-from boto.dynamodb2.types import Dynamizer, FILTER_OPERATORS, QUERY_OPERATORS
+from boto.dynamodb2.types import (Dynamizer, FILTER_OPERATORS, QUERY_OPERATORS,
+ STRING)
from boto.exception import JSONResponseError
@@ -170,7 +171,7 @@
... ],
... throughput={
... 'read':10,
- ... 'write":10,
+ ... 'write':10,
... }),
... ])
@@ -232,18 +233,29 @@
)
return table
- def _introspect_schema(self, raw_schema):
+ def _introspect_schema(self, raw_schema, raw_attributes=None):
"""
Given a raw schema structure back from a DynamoDB response, parse
out & build the high-level Python objects that represent them.
"""
schema = []
+ sane_attributes = {}
+ if raw_attributes:
+ for field in raw_attributes:
+ sane_attributes[field['AttributeName']] = field['AttributeType']
+
for field in raw_schema:
+ data_type = sane_attributes.get(field['AttributeName'], STRING)
+
if field['KeyType'] == 'HASH':
- schema.append(HashKey(field['AttributeName']))
+ schema.append(
+ HashKey(field['AttributeName'], data_type=data_type)
+ )
elif field['KeyType'] == 'RANGE':
- schema.append(RangeKey(field['AttributeName']))
+ schema.append(
+ RangeKey(field['AttributeName'], data_type=data_type)
+ )
else:
raise exceptions.UnknownSchemaFieldError(
"%s was seen, but is unknown. Please report this at "
@@ -280,7 +292,7 @@
)
name = field['IndexName']
- kwargs['parts'] = self._introspect_schema(field['KeySchema'])
+ kwargs['parts'] = self._introspect_schema(field['KeySchema'], None)
indexes.append(index_klass(name, **kwargs))
return indexes
@@ -319,7 +331,8 @@
if not self.schema:
# Since we have the data, build the schema.
raw_schema = result['Table'].get('KeySchema', [])
- self.schema = self._introspect_schema(raw_schema)
+ raw_attributes = result['Table'].get('AttributeDefinitions', [])
+ self.schema = self._introspect_schema(raw_schema, raw_attributes)
if not self.indexes:
# Build the index information as well.
@@ -488,6 +501,8 @@
attributes_to_get=attributes,
consistent_read=consistent
)
+ if 'Item' not in item_data:
+ raise exceptions.ItemNotFound("Item %s couldn't be found." % kwargs)
item = Item(self)
item.load(item_data)
return item
@@ -526,7 +541,7 @@
"""
try:
self.get_item(**kwargs)
- except JSONResponseError:
+ except (JSONResponseError, exceptions.ItemNotFound):
return False
return True
@@ -633,17 +648,36 @@
self.connection.update_item(self.table_name, raw_key, item_data, **kwargs)
return True
- def delete_item(self, **kwargs):
+ def delete_item(self, expected=None, conditional_operator=None, **kwargs):
"""
- Deletes an item in DynamoDB.
+ Deletes a single item. You can perform a conditional delete operation
+ that deletes the item if it exists, or if it has an expected attribute
+ value.
+ Conditional deletes are useful for only deleting items if specific
+ conditions are met. If those conditions are met, DynamoDB performs
+ the delete. Otherwise, the item is not deleted.
+
+ To specify the expected attribute values of the item, you can pass a
+ dictionary of conditions to ``expected``. Each condition should follow
+ the pattern ``<attributename>__<comparison_operator>=<value_to_expect>``.
+
**IMPORTANT** - Be careful when using this method, there is no undo.
To specify the key of the item you'd like to get, you can specify the
key attributes as kwargs.
- Returns ``True`` on success.
+ Optionally accepts an ``expected`` parameter which is a dictionary of
+ expected attribute value conditions.
+ Optionally accepts a ``conditional_operator`` which applies to the
+ expected attribute value conditions:
+
+ + `AND` - If all of the conditions evaluate to true (default)
+ + `OR` - True if at least one condition evaluates to true
+
+ Returns ``True`` on success, ``False`` on failed conditional delete.
+
Example::
# A simple hash key.
@@ -661,9 +695,21 @@
... })
True
+ # Conditional delete
+ >>> users.delete_item(username='johndoe',
+ ... expected={'balance__eq': 0})
+ True
"""
+ expected = self._build_filters(expected, using=FILTER_OPERATORS)
raw_key = self._encode_keys(kwargs)
- self.connection.delete_item(self.table_name, raw_key)
+
+ try:
+ self.connection.delete_item(self.table_name, raw_key,
+ expected=expected,
+ conditional_operator=conditional_operator)
+ except exceptions.ConditionalCheckFailedException:
+ return False
+
return True
def get_key_fields(self):
@@ -742,6 +788,9 @@
An internal method for taking query/scan-style ``**kwargs`` & turning
them into the raw structure DynamoDB expects for filtering.
"""
+ if filter_kwargs is None:
+ return
+
filters = {}
for field_and_op, value in filter_kwargs.items():
@@ -801,17 +850,34 @@
def query(self, limit=None, index=None, reverse=False, consistent=False,
attributes=None, max_page_size=None, **filter_kwargs):
"""
+ **WARNING:** This method is provided **strictly** for
+ backward-compatibility. It returns results in an incorrect order.
+
+ If you are writing new code, please use ``Table.query_2``.
+ """
+ reverse = not reverse
+ return self.query_2(limit=limit, index=index, reverse=reverse,
+ consistent=consistent, attributes=attributes,
+ max_page_size=max_page_size, **filter_kwargs)
+
+ def query_2(self, limit=None, index=None, reverse=False,
+ consistent=False, attributes=None, max_page_size=None,
+ query_filter=None, conditional_operator=None,
+ **filter_kwargs):
+ """
Queries for a set of matching items in a DynamoDB table.
Queries can be performed against a hash key, a hash+range key or
- against any data stored in your local secondary indexes.
+ against any data stored in your local secondary indexes. Query filters
+ can be used to filter on arbitrary fields.
**Note** - You can not query against arbitrary fields within the data
- stored in DynamoDB.
+ stored in DynamoDB unless you specify ``query_filter`` values.
To specify the filters of the items you'd like to get, you can specify
the filters as kwargs. Each filter kwarg should follow the pattern
- ``<fieldname>__<filter_operation>=<value_to_look_for>``.
+ ``<fieldname>__<filter_operation>=<value_to_look_for>``. Query filters
+ are specified in the same way.
Optionally accepts a ``limit`` parameter, which should be an integer
count of the total number of items to return. (Default: ``None`` -
@@ -822,7 +888,7 @@
(Default: ``None``)
Optionally accepts a ``reverse`` parameter, which will present the
- results in reverse order. (Default: ``None`` - normal order)
+ results in reverse order. (Default: ``False`` - normal order)
Optionally accepts a ``consistent`` parameter, which should be a
boolean. If you provide ``True``, it will force a consistent read of
@@ -840,6 +906,15 @@
the scan from drowning out other queries. (Default: ``None`` -
fetch as many as DynamoDB will return)
+ Optionally accepts a ``query_filter`` which is a dictionary of filter
+ conditions against any arbitrary field in the returned data.
+
+ Optionally accepts a ``conditional_operator`` which applies to the
+ query filter conditions:
+
+ + `AND` - True if all filter conditions evaluate to true (default)
+ + `OR` - True if at least one filter condition evaluates to true
+
Returns a ``ResultSet``, which transparently handles the pagination of
results you get back.
@@ -878,12 +953,29 @@
'John'
'Fred'
+ # Filter by non-indexed field(s)
+ >>> results = users.query(
+ ... last_name__eq='Doe',
+ ... reverse=True,
+ ... query_filter={
+ ... 'first_name__beginswith': 'A'
+ ... }
+ ... )
+ >>> for res in results:
+ ... print res['first_name'] + ' ' + res['last_name']
+ 'Alice Doe'
+
"""
if self.schema:
- if len(self.schema) == 1 and len(filter_kwargs) <= 1:
- raise exceptions.QueryError(
- "You must specify more than one key to filter on."
- )
+ if len(self.schema) == 1:
+ if len(filter_kwargs) <= 1:
+ if not self.global_indexes or not len(self.global_indexes):
+ # If the schema only has one field, there's <= 1 filter
+ # param & no Global Secondary Indexes, this is user
+ # error. Bail early.
+ raise exceptions.QueryError(
+ "You must specify more than one key to filter on."
+ )
if attributes is not None:
select = 'SPECIFIC_ATTRIBUTES'
@@ -901,20 +993,26 @@
'consistent': consistent,
'select': select,
'attributes_to_get': attributes,
+ 'query_filter': query_filter,
+ 'conditional_operator': conditional_operator,
})
results.to_call(self._query, **kwargs)
return results
- def query_count(self, index=None, consistent=False, **filter_kwargs):
+ def query_count(self, index=None, consistent=False, conditional_operator=None,
+ query_filter=None, scan_index_forward=True, limit=None,
+ **filter_kwargs):
"""
Queries the exact count of matching items in a DynamoDB table.
Queries can be performed against a hash key, a hash+range key or
- against any data stored in your local secondary indexes.
+ against any data stored in your local secondary indexes. Query filters
+ can be used to filter on arbitrary fields.
To specify the filters of the items you'd like to get, you can specify
the filters as kwargs. Each filter kwarg should follow the pattern
- ``<fieldname>__<filter_operation>=<value_to_look_for>``.
+ ``<fieldname>__<filter_operation>=<value_to_look_for>``. Query filters
+ are specified in the same way.
Optionally accepts an ``index`` parameter, which should be a string of
name of the local secondary index you want to query against.
@@ -925,9 +1023,34 @@
the data (more expensive). (Default: ``False`` - use eventually
consistent reads)
+ Optionally accepts a ``query_filter`` which is a dictionary of filter
+ conditions against any arbitrary field in the returned data.
+
+ Optionally accepts a ``conditional_operator`` which applies to the
+ query filter conditions:
+
+ + `AND` - True if all filter conditions evaluate to true (default)
+ + `OR` - True if at least one filter condition evaluates to true
+
Returns an integer which represents the exact amount of matched
items.
+ :type scan_index_forward: boolean
+ :param scan_index_forward: Specifies ascending (true) or descending
+ (false) traversal of the index. DynamoDB returns results reflecting
+ the requested order determined by the range key. If the data type
+ is Number, the results are returned in numeric order. For String,
+ the results are returned in order of ASCII character code values.
+ For Binary, DynamoDB treats each byte of the binary data as
+ unsigned when it compares binary values.
+
+ If ScanIndexForward is not specified, the results are returned in
+ ascending order.
+
+ :type limit: integer
+ :param limit: The maximum number of items to evaluate (not necessarily
+ the number of matching items).
+
Example::
# Look for last names equal to "Doe".
@@ -949,18 +1072,27 @@
using=QUERY_OPERATORS
)
+ built_query_filter = self._build_filters(
+ query_filter,
+ using=FILTER_OPERATORS
+ )
+
raw_results = self.connection.query(
self.table_name,
index_name=index,
consistent_read=consistent,
select='COUNT',
key_conditions=key_conditions,
+ query_filter=built_query_filter,
+ conditional_operator=conditional_operator,
+ limit=limit,
+ scan_index_forward=scan_index_forward,
)
return int(raw_results.get('Count', 0))
def _query(self, limit=None, index=None, reverse=False, consistent=False,
exclusive_start_key=None, select=None, attributes_to_get=None,
- **filter_kwargs):
+ query_filter=None, conditional_operator=None, **filter_kwargs):
"""
The internal method that performs the actual queries. Used extensively
by ``ResultSet`` to perform each (paginated) request.
@@ -968,12 +1100,15 @@
kwargs = {
'limit': limit,
'index_name': index,
- 'scan_index_forward': reverse,
'consistent_read': consistent,
'select': select,
- 'attributes_to_get': attributes_to_get
+ 'attributes_to_get': attributes_to_get,
+ 'conditional_operator': conditional_operator,
}
+ if reverse:
+ kwargs['scan_index_forward'] = False
+
if exclusive_start_key:
kwargs['exclusive_start_key'] = {}
@@ -987,6 +1122,11 @@
using=QUERY_OPERATORS
)
+ kwargs['query_filter'] = self._build_filters(
+ query_filter,
+ using=FILTER_OPERATORS
+ )
+
raw_results = self.connection.query(
self.table_name,
**kwargs
@@ -1013,13 +1153,14 @@
}
def scan(self, limit=None, segment=None, total_segments=None,
- max_page_size=None, attributes=None, **filter_kwargs):
+ max_page_size=None, attributes=None, conditional_operator=None,
+ **filter_kwargs):
"""
Scans across all items within a DynamoDB table.
Scans can be performed against a hash key or a hash+range key. You can
additionally filter the results after the table has been read but
- before the response is returned.
+ before the response is returned by using query filters.
To specify the filters of the items you'd like to get, you can specify
the filters as kwargs. Each filter kwarg should follow the pattern
@@ -1084,12 +1225,14 @@
'segment': segment,
'total_segments': total_segments,
'attributes': attributes,
+ 'conditional_operator': conditional_operator,
})
results.to_call(self._scan, **kwargs)
return results
def _scan(self, limit=None, exclusive_start_key=None, segment=None,
- total_segments=None, attributes=None, **filter_kwargs):
+ total_segments=None, attributes=None, conditional_operator=None,
+ **filter_kwargs):
"""
The internal method that performs the actual scan. Used extensively
by ``ResultSet`` to perform each (paginated) request.
@@ -1099,6 +1242,7 @@
'segment': segment,
'total_segments': total_segments,
'attributes_to_get': attributes,
+ 'conditional_operator': conditional_operator,
}
if exclusive_start_key:
@@ -1176,7 +1320,7 @@
# We pass the keys to the constructor instead, so it can maintain it's
# own internal state as to what keys have been processed.
results = BatchGetResultSet(keys=keys, max_batch_get=self.max_batch_get)
- results.to_call(self._batch_get, consistent=False)
+ results.to_call(self._batch_get, consistent=consistent)
return results
def _batch_get(self, keys, consistent=False):
« no previous file with comments | « third_party/boto/boto/dynamodb2/results.py ('k') | third_party/boto/boto/ec2/__init__.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698