Mercurial > repos > guerler > springsuite
comparison planemo/lib/python3.7/site-packages/boto/dynamodb/layer2.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) 2011 Mitch Garnaat http://garnaat.org/ | |
| 2 # Copyright (c) 2011 Amazon.com, Inc. or its affiliates. All Rights Reserved | |
| 3 # | |
| 4 # Permission is hereby granted, free of charge, to any person obtaining a | |
| 5 # copy of this software and associated documentation files (the | |
| 6 # "Software"), to deal in the Software without restriction, including | |
| 7 # without limitation the rights to use, copy, modify, merge, publish, dis- | |
| 8 # tribute, sublicense, and/or sell copies of the Software, and to permit | |
| 9 # persons to whom the Software is furnished to do so, subject to the fol- | |
| 10 # lowing conditions: | |
| 11 # | |
| 12 # The above copyright notice and this permission notice shall be included | |
| 13 # in all copies or substantial portions of the Software. | |
| 14 # | |
| 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
| 16 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- | |
| 17 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT | |
| 18 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
| 19 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 20 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS | |
| 21 # IN THE SOFTWARE. | |
| 22 # | |
| 23 from boto.dynamodb.layer1 import Layer1 | |
| 24 from boto.dynamodb.table import Table | |
| 25 from boto.dynamodb.schema import Schema | |
| 26 from boto.dynamodb.item import Item | |
| 27 from boto.dynamodb.batch import BatchList, BatchWriteList | |
| 28 from boto.dynamodb.types import get_dynamodb_type, Dynamizer, \ | |
| 29 LossyFloatDynamizer, NonBooleanDynamizer | |
| 30 | |
| 31 | |
| 32 class TableGenerator(object): | |
| 33 """ | |
| 34 This is an object that wraps up the table_generator function. | |
| 35 The only real reason to have this is that we want to be able | |
| 36 to accumulate and return the ConsumedCapacityUnits element that | |
| 37 is part of each response. | |
| 38 | |
| 39 :ivar last_evaluated_key: A sequence representing the key(s) | |
| 40 of the item last evaluated, or None if no additional | |
| 41 results are available. | |
| 42 | |
| 43 :ivar remaining: The remaining quantity of results requested. | |
| 44 | |
| 45 :ivar table: The table to which the call was made. | |
| 46 """ | |
| 47 | |
| 48 def __init__(self, table, callable, remaining, item_class, kwargs): | |
| 49 self.table = table | |
| 50 self.callable = callable | |
| 51 self.remaining = -1 if remaining is None else remaining | |
| 52 self.item_class = item_class | |
| 53 self.kwargs = kwargs | |
| 54 self._consumed_units = 0.0 | |
| 55 self.last_evaluated_key = None | |
| 56 self._count = 0 | |
| 57 self._scanned_count = 0 | |
| 58 self._response = None | |
| 59 | |
| 60 @property | |
| 61 def count(self): | |
| 62 """ | |
| 63 The total number of items retrieved thus far. This value changes with | |
| 64 iteration and even when issuing a call with count=True, it is necessary | |
| 65 to complete the iteration to assert an accurate count value. | |
| 66 """ | |
| 67 self.response | |
| 68 return self._count | |
| 69 | |
| 70 @property | |
| 71 def scanned_count(self): | |
| 72 """ | |
| 73 As above, but representing the total number of items scanned by | |
| 74 DynamoDB, without regard to any filters. | |
| 75 """ | |
| 76 self.response | |
| 77 return self._scanned_count | |
| 78 | |
| 79 @property | |
| 80 def consumed_units(self): | |
| 81 """ | |
| 82 Returns a float representing the ConsumedCapacityUnits accumulated. | |
| 83 """ | |
| 84 self.response | |
| 85 return self._consumed_units | |
| 86 | |
| 87 @property | |
| 88 def response(self): | |
| 89 """ | |
| 90 The current response to the call from DynamoDB. | |
| 91 """ | |
| 92 return self.next_response() if self._response is None else self._response | |
| 93 | |
| 94 def next_response(self): | |
| 95 """ | |
| 96 Issue a call and return the result. You can invoke this method | |
| 97 while iterating over the TableGenerator in order to skip to the | |
| 98 next "page" of results. | |
| 99 """ | |
| 100 # preserve any existing limit in case the user alters self.remaining | |
| 101 limit = self.kwargs.get('limit') | |
| 102 if (self.remaining > 0 and (limit is None or limit > self.remaining)): | |
| 103 self.kwargs['limit'] = self.remaining | |
| 104 self._response = self.callable(**self.kwargs) | |
| 105 self.kwargs['limit'] = limit | |
| 106 self._consumed_units += self._response.get('ConsumedCapacityUnits', 0.0) | |
| 107 self._count += self._response.get('Count', 0) | |
| 108 self._scanned_count += self._response.get('ScannedCount', 0) | |
| 109 # at the expense of a possibly gratuitous dynamize, ensure that | |
| 110 # early generator termination won't result in bad LEK values | |
| 111 if 'LastEvaluatedKey' in self._response: | |
| 112 lek = self._response['LastEvaluatedKey'] | |
| 113 esk = self.table.layer2.dynamize_last_evaluated_key(lek) | |
| 114 self.kwargs['exclusive_start_key'] = esk | |
| 115 lektuple = (lek['HashKeyElement'],) | |
| 116 if 'RangeKeyElement' in lek: | |
| 117 lektuple += (lek['RangeKeyElement'],) | |
| 118 self.last_evaluated_key = lektuple | |
| 119 else: | |
| 120 self.last_evaluated_key = None | |
| 121 return self._response | |
| 122 | |
| 123 def __iter__(self): | |
| 124 while self.remaining != 0: | |
| 125 response = self.response | |
| 126 for item in response.get('Items', []): | |
| 127 self.remaining -= 1 | |
| 128 yield self.item_class(self.table, attrs=item) | |
| 129 if self.remaining == 0: | |
| 130 break | |
| 131 if response is not self._response: | |
| 132 break | |
| 133 else: | |
| 134 if self.last_evaluated_key is not None: | |
| 135 self.next_response() | |
| 136 continue | |
| 137 break | |
| 138 if response is not self._response: | |
| 139 continue | |
| 140 break | |
| 141 | |
| 142 | |
| 143 class Layer2(object): | |
| 144 | |
| 145 def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, | |
| 146 is_secure=True, port=None, proxy=None, proxy_port=None, | |
| 147 debug=0, security_token=None, region=None, | |
| 148 validate_certs=True, dynamizer=LossyFloatDynamizer, | |
| 149 profile_name=None): | |
| 150 self.layer1 = Layer1(aws_access_key_id, aws_secret_access_key, | |
| 151 is_secure, port, proxy, proxy_port, | |
| 152 debug, security_token, region, | |
| 153 validate_certs=validate_certs, | |
| 154 profile_name=profile_name) | |
| 155 self.dynamizer = dynamizer() | |
| 156 | |
| 157 def use_decimals(self, use_boolean=False): | |
| 158 """ | |
| 159 Use the ``decimal.Decimal`` type for encoding/decoding numeric types. | |
| 160 | |
| 161 By default, ints/floats are used to represent numeric types | |
| 162 ('N', 'NS') received from DynamoDB. Using the ``Decimal`` | |
| 163 type is recommended to prevent loss of precision. | |
| 164 | |
| 165 """ | |
| 166 # Eventually this should be made the default dynamizer. | |
| 167 self.dynamizer = Dynamizer() if use_boolean else NonBooleanDynamizer() | |
| 168 | |
| 169 def dynamize_attribute_updates(self, pending_updates): | |
| 170 """ | |
| 171 Convert a set of pending item updates into the structure | |
| 172 required by Layer1. | |
| 173 """ | |
| 174 d = {} | |
| 175 for attr_name in pending_updates: | |
| 176 action, value = pending_updates[attr_name] | |
| 177 if value is None: | |
| 178 # DELETE without an attribute value | |
| 179 d[attr_name] = {"Action": action} | |
| 180 else: | |
| 181 d[attr_name] = {"Action": action, | |
| 182 "Value": self.dynamizer.encode(value)} | |
| 183 return d | |
| 184 | |
| 185 def dynamize_item(self, item): | |
| 186 d = {} | |
| 187 for attr_name in item: | |
| 188 d[attr_name] = self.dynamizer.encode(item[attr_name]) | |
| 189 return d | |
| 190 | |
| 191 def dynamize_range_key_condition(self, range_key_condition): | |
| 192 """ | |
| 193 Convert a layer2 range_key_condition parameter into the | |
| 194 structure required by Layer1. | |
| 195 """ | |
| 196 return range_key_condition.to_dict() | |
| 197 | |
| 198 def dynamize_scan_filter(self, scan_filter): | |
| 199 """ | |
| 200 Convert a layer2 scan_filter parameter into the | |
| 201 structure required by Layer1. | |
| 202 """ | |
| 203 d = None | |
| 204 if scan_filter: | |
| 205 d = {} | |
| 206 for attr_name in scan_filter: | |
| 207 condition = scan_filter[attr_name] | |
| 208 d[attr_name] = condition.to_dict() | |
| 209 return d | |
| 210 | |
| 211 def dynamize_expected_value(self, expected_value): | |
| 212 """ | |
| 213 Convert an expected_value parameter into the data structure | |
| 214 required for Layer1. | |
| 215 """ | |
| 216 d = None | |
| 217 if expected_value: | |
| 218 d = {} | |
| 219 for attr_name in expected_value: | |
| 220 attr_value = expected_value[attr_name] | |
| 221 if attr_value is True: | |
| 222 attr_value = {'Exists': True} | |
| 223 elif attr_value is False: | |
| 224 attr_value = {'Exists': False} | |
| 225 else: | |
| 226 val = self.dynamizer.encode(expected_value[attr_name]) | |
| 227 attr_value = {'Value': val} | |
| 228 d[attr_name] = attr_value | |
| 229 return d | |
| 230 | |
| 231 def dynamize_last_evaluated_key(self, last_evaluated_key): | |
| 232 """ | |
| 233 Convert a last_evaluated_key parameter into the data structure | |
| 234 required for Layer1. | |
| 235 """ | |
| 236 d = None | |
| 237 if last_evaluated_key: | |
| 238 hash_key = last_evaluated_key['HashKeyElement'] | |
| 239 d = {'HashKeyElement': self.dynamizer.encode(hash_key)} | |
| 240 if 'RangeKeyElement' in last_evaluated_key: | |
| 241 range_key = last_evaluated_key['RangeKeyElement'] | |
| 242 d['RangeKeyElement'] = self.dynamizer.encode(range_key) | |
| 243 return d | |
| 244 | |
| 245 def build_key_from_values(self, schema, hash_key, range_key=None): | |
| 246 """ | |
| 247 Build a Key structure to be used for accessing items | |
| 248 in Amazon DynamoDB. This method takes the supplied hash_key | |
| 249 and optional range_key and validates them against the | |
| 250 schema. If there is a mismatch, a TypeError is raised. | |
| 251 Otherwise, a Python dict version of a Amazon DynamoDB Key | |
| 252 data structure is returned. | |
| 253 | |
| 254 :type hash_key: int|float|str|unicode|Binary | |
| 255 :param hash_key: The hash key of the item you are looking for. | |
| 256 The type of the hash key should match the type defined in | |
| 257 the schema. | |
| 258 | |
| 259 :type range_key: int|float|str|unicode|Binary | |
| 260 :param range_key: The range key of the item your are looking for. | |
| 261 This should be supplied only if the schema requires a | |
| 262 range key. The type of the range key should match the | |
| 263 type defined in the schema. | |
| 264 """ | |
| 265 dynamodb_key = {} | |
| 266 dynamodb_value = self.dynamizer.encode(hash_key) | |
| 267 if list(dynamodb_value.keys())[0] != schema.hash_key_type: | |
| 268 msg = 'Hashkey must be of type: %s' % schema.hash_key_type | |
| 269 raise TypeError(msg) | |
| 270 dynamodb_key['HashKeyElement'] = dynamodb_value | |
| 271 if range_key is not None: | |
| 272 dynamodb_value = self.dynamizer.encode(range_key) | |
| 273 if list(dynamodb_value.keys())[0] != schema.range_key_type: | |
| 274 msg = 'RangeKey must be of type: %s' % schema.range_key_type | |
| 275 raise TypeError(msg) | |
| 276 dynamodb_key['RangeKeyElement'] = dynamodb_value | |
| 277 return dynamodb_key | |
| 278 | |
| 279 def new_batch_list(self): | |
| 280 """ | |
| 281 Return a new, empty :class:`boto.dynamodb.batch.BatchList` | |
| 282 object. | |
| 283 """ | |
| 284 return BatchList(self) | |
| 285 | |
| 286 def new_batch_write_list(self): | |
| 287 """ | |
| 288 Return a new, empty :class:`boto.dynamodb.batch.BatchWriteList` | |
| 289 object. | |
| 290 """ | |
| 291 return BatchWriteList(self) | |
| 292 | |
| 293 def list_tables(self, limit=None): | |
| 294 """ | |
| 295 Return a list of the names of all tables associated with the | |
| 296 current account and region. | |
| 297 | |
| 298 :type limit: int | |
| 299 :param limit: The maximum number of tables to return. | |
| 300 """ | |
| 301 tables = [] | |
| 302 start_table = None | |
| 303 while not limit or len(tables) < limit: | |
| 304 this_round_limit = None | |
| 305 if limit: | |
| 306 this_round_limit = limit - len(tables) | |
| 307 this_round_limit = min(this_round_limit, 100) | |
| 308 result = self.layer1.list_tables(limit=this_round_limit, start_table=start_table) | |
| 309 tables.extend(result.get('TableNames', [])) | |
| 310 start_table = result.get('LastEvaluatedTableName', None) | |
| 311 if not start_table: | |
| 312 break | |
| 313 return tables | |
| 314 | |
| 315 def describe_table(self, name): | |
| 316 """ | |
| 317 Retrieve information about an existing table. | |
| 318 | |
| 319 :type name: str | |
| 320 :param name: The name of the desired table. | |
| 321 | |
| 322 """ | |
| 323 return self.layer1.describe_table(name) | |
| 324 | |
| 325 def table_from_schema(self, name, schema): | |
| 326 """ | |
| 327 Create a Table object from a schema. | |
| 328 | |
| 329 This method will create a Table object without | |
| 330 making any API calls. If you know the name and schema | |
| 331 of the table, you can use this method instead of | |
| 332 ``get_table``. | |
| 333 | |
| 334 Example usage:: | |
| 335 | |
| 336 table = layer2.table_from_schema( | |
| 337 'tablename', | |
| 338 Schema.create(hash_key=('foo', 'N'))) | |
| 339 | |
| 340 :type name: str | |
| 341 :param name: The name of the table. | |
| 342 | |
| 343 :type schema: :class:`boto.dynamodb.schema.Schema` | |
| 344 :param schema: The schema associated with the table. | |
| 345 | |
| 346 :rtype: :class:`boto.dynamodb.table.Table` | |
| 347 :return: A Table object representing the table. | |
| 348 | |
| 349 """ | |
| 350 return Table.create_from_schema(self, name, schema) | |
| 351 | |
| 352 def get_table(self, name): | |
| 353 """ | |
| 354 Retrieve the Table object for an existing table. | |
| 355 | |
| 356 :type name: str | |
| 357 :param name: The name of the desired table. | |
| 358 | |
| 359 :rtype: :class:`boto.dynamodb.table.Table` | |
| 360 :return: A Table object representing the table. | |
| 361 """ | |
| 362 response = self.layer1.describe_table(name) | |
| 363 return Table(self, response) | |
| 364 | |
| 365 lookup = get_table | |
| 366 | |
| 367 def create_table(self, name, schema, read_units, write_units): | |
| 368 """ | |
| 369 Create a new Amazon DynamoDB table. | |
| 370 | |
| 371 :type name: str | |
| 372 :param name: The name of the desired table. | |
| 373 | |
| 374 :type schema: :class:`boto.dynamodb.schema.Schema` | |
| 375 :param schema: The Schema object that defines the schema used | |
| 376 by this table. | |
| 377 | |
| 378 :type read_units: int | |
| 379 :param read_units: The value for ReadCapacityUnits. | |
| 380 | |
| 381 :type write_units: int | |
| 382 :param write_units: The value for WriteCapacityUnits. | |
| 383 | |
| 384 :rtype: :class:`boto.dynamodb.table.Table` | |
| 385 :return: A Table object representing the new Amazon DynamoDB table. | |
| 386 """ | |
| 387 response = self.layer1.create_table(name, schema.dict, | |
| 388 {'ReadCapacityUnits': read_units, | |
| 389 'WriteCapacityUnits': write_units}) | |
| 390 return Table(self, response) | |
| 391 | |
| 392 def update_throughput(self, table, read_units, write_units): | |
| 393 """ | |
| 394 Update the ProvisionedThroughput for the Amazon DynamoDB Table. | |
| 395 | |
| 396 :type table: :class:`boto.dynamodb.table.Table` | |
| 397 :param table: The Table object whose throughput is being updated. | |
| 398 | |
| 399 :type read_units: int | |
| 400 :param read_units: The new value for ReadCapacityUnits. | |
| 401 | |
| 402 :type write_units: int | |
| 403 :param write_units: The new value for WriteCapacityUnits. | |
| 404 """ | |
| 405 response = self.layer1.update_table(table.name, | |
| 406 {'ReadCapacityUnits': read_units, | |
| 407 'WriteCapacityUnits': write_units}) | |
| 408 table.update_from_response(response) | |
| 409 | |
| 410 def delete_table(self, table): | |
| 411 """ | |
| 412 Delete this table and all items in it. After calling this | |
| 413 the Table objects status attribute will be set to 'DELETING'. | |
| 414 | |
| 415 :type table: :class:`boto.dynamodb.table.Table` | |
| 416 :param table: The Table object that is being deleted. | |
| 417 """ | |
| 418 response = self.layer1.delete_table(table.name) | |
| 419 table.update_from_response(response) | |
| 420 | |
| 421 def create_schema(self, hash_key_name, hash_key_proto_value, | |
| 422 range_key_name=None, range_key_proto_value=None): | |
| 423 """ | |
| 424 Create a Schema object used when creating a Table. | |
| 425 | |
| 426 :type hash_key_name: str | |
| 427 :param hash_key_name: The name of the HashKey for the schema. | |
| 428 | |
| 429 :type hash_key_proto_value: int|long|float|str|unicode|Binary | |
| 430 :param hash_key_proto_value: A sample or prototype of the type | |
| 431 of value you want to use for the HashKey. Alternatively, | |
| 432 you can also just pass in the Python type (e.g. int, float, etc.). | |
| 433 | |
| 434 :type range_key_name: str | |
| 435 :param range_key_name: The name of the RangeKey for the schema. | |
| 436 This parameter is optional. | |
| 437 | |
| 438 :type range_key_proto_value: int|long|float|str|unicode|Binary | |
| 439 :param range_key_proto_value: A sample or prototype of the type | |
| 440 of value you want to use for the RangeKey. Alternatively, | |
| 441 you can also pass in the Python type (e.g. int, float, etc.) | |
| 442 This parameter is optional. | |
| 443 """ | |
| 444 hash_key = (hash_key_name, get_dynamodb_type(hash_key_proto_value)) | |
| 445 if range_key_name and range_key_proto_value is not None: | |
| 446 range_key = (range_key_name, | |
| 447 get_dynamodb_type(range_key_proto_value)) | |
| 448 else: | |
| 449 range_key = None | |
| 450 return Schema.create(hash_key, range_key) | |
| 451 | |
| 452 def get_item(self, table, hash_key, range_key=None, | |
| 453 attributes_to_get=None, consistent_read=False, | |
| 454 item_class=Item): | |
| 455 """ | |
| 456 Retrieve an existing item from the table. | |
| 457 | |
| 458 :type table: :class:`boto.dynamodb.table.Table` | |
| 459 :param table: The Table object from which the item is retrieved. | |
| 460 | |
| 461 :type hash_key: int|long|float|str|unicode|Binary | |
| 462 :param hash_key: The HashKey of the requested item. The | |
| 463 type of the value must match the type defined in the | |
| 464 schema for the table. | |
| 465 | |
| 466 :type range_key: int|long|float|str|unicode|Binary | |
| 467 :param range_key: The optional RangeKey of the requested item. | |
| 468 The type of the value must match the type defined in the | |
| 469 schema for the table. | |
| 470 | |
| 471 :type attributes_to_get: list | |
| 472 :param attributes_to_get: A list of attribute names. | |
| 473 If supplied, only the specified attribute names will | |
| 474 be returned. Otherwise, all attributes will be returned. | |
| 475 | |
| 476 :type consistent_read: bool | |
| 477 :param consistent_read: If True, a consistent read | |
| 478 request is issued. Otherwise, an eventually consistent | |
| 479 request is issued. | |
| 480 | |
| 481 :type item_class: Class | |
| 482 :param item_class: Allows you to override the class used | |
| 483 to generate the items. This should be a subclass of | |
| 484 :class:`boto.dynamodb.item.Item` | |
| 485 """ | |
| 486 key = self.build_key_from_values(table.schema, hash_key, range_key) | |
| 487 response = self.layer1.get_item(table.name, key, | |
| 488 attributes_to_get, consistent_read, | |
| 489 object_hook=self.dynamizer.decode) | |
| 490 item = item_class(table, hash_key, range_key, response['Item']) | |
| 491 if 'ConsumedCapacityUnits' in response: | |
| 492 item.consumed_units = response['ConsumedCapacityUnits'] | |
| 493 return item | |
| 494 | |
| 495 def batch_get_item(self, batch_list): | |
| 496 """ | |
| 497 Return a set of attributes for a multiple items in | |
| 498 multiple tables using their primary keys. | |
| 499 | |
| 500 :type batch_list: :class:`boto.dynamodb.batch.BatchList` | |
| 501 :param batch_list: A BatchList object which consists of a | |
| 502 list of :class:`boto.dynamoddb.batch.Batch` objects. | |
| 503 Each Batch object contains the information about one | |
| 504 batch of objects that you wish to retrieve in this | |
| 505 request. | |
| 506 """ | |
| 507 request_items = batch_list.to_dict() | |
| 508 return self.layer1.batch_get_item(request_items, | |
| 509 object_hook=self.dynamizer.decode) | |
| 510 | |
| 511 def batch_write_item(self, batch_list): | |
| 512 """ | |
| 513 Performs multiple Puts and Deletes in one batch. | |
| 514 | |
| 515 :type batch_list: :class:`boto.dynamodb.batch.BatchWriteList` | |
| 516 :param batch_list: A BatchWriteList object which consists of a | |
| 517 list of :class:`boto.dynamoddb.batch.BatchWrite` objects. | |
| 518 Each Batch object contains the information about one | |
| 519 batch of objects that you wish to put or delete. | |
| 520 """ | |
| 521 request_items = batch_list.to_dict() | |
| 522 return self.layer1.batch_write_item(request_items, | |
| 523 object_hook=self.dynamizer.decode) | |
| 524 | |
| 525 def put_item(self, item, expected_value=None, return_values=None): | |
| 526 """ | |
| 527 Store a new item or completely replace an existing item | |
| 528 in Amazon DynamoDB. | |
| 529 | |
| 530 :type item: :class:`boto.dynamodb.item.Item` | |
| 531 :param item: The Item to write to Amazon DynamoDB. | |
| 532 | |
| 533 :type expected_value: dict | |
| 534 :param expected_value: A dictionary of name/value pairs that you expect. | |
| 535 This dictionary should have name/value pairs where the name | |
| 536 is the name of the attribute and the value is either the value | |
| 537 you are expecting or False if you expect the attribute not to | |
| 538 exist. | |
| 539 | |
| 540 :type return_values: str | |
| 541 :param return_values: Controls the return of attribute | |
| 542 name-value pairs before then were changed. Possible | |
| 543 values are: None or 'ALL_OLD'. If 'ALL_OLD' is | |
| 544 specified and the item is overwritten, the content | |
| 545 of the old item is returned. | |
| 546 """ | |
| 547 expected_value = self.dynamize_expected_value(expected_value) | |
| 548 response = self.layer1.put_item(item.table.name, | |
| 549 self.dynamize_item(item), | |
| 550 expected_value, return_values, | |
| 551 object_hook=self.dynamizer.decode) | |
| 552 if 'ConsumedCapacityUnits' in response: | |
| 553 item.consumed_units = response['ConsumedCapacityUnits'] | |
| 554 return response | |
| 555 | |
| 556 def update_item(self, item, expected_value=None, return_values=None): | |
| 557 """ | |
| 558 Commit pending item updates to Amazon DynamoDB. | |
| 559 | |
| 560 :type item: :class:`boto.dynamodb.item.Item` | |
| 561 :param item: The Item to update in Amazon DynamoDB. It is expected | |
| 562 that you would have called the add_attribute, put_attribute | |
| 563 and/or delete_attribute methods on this Item prior to calling | |
| 564 this method. Those queued changes are what will be updated. | |
| 565 | |
| 566 :type expected_value: dict | |
| 567 :param expected_value: A dictionary of name/value pairs that you | |
| 568 expect. This dictionary should have name/value pairs where the | |
| 569 name is the name of the attribute and the value is either the | |
| 570 value you are expecting or False if you expect the attribute | |
| 571 not to exist. | |
| 572 | |
| 573 :type return_values: str | |
| 574 :param return_values: Controls the return of attribute name/value pairs | |
| 575 before they were updated. Possible values are: None, 'ALL_OLD', | |
| 576 'UPDATED_OLD', 'ALL_NEW' or 'UPDATED_NEW'. If 'ALL_OLD' is | |
| 577 specified and the item is overwritten, the content of the old item | |
| 578 is returned. If 'ALL_NEW' is specified, then all the attributes of | |
| 579 the new version of the item are returned. If 'UPDATED_NEW' is | |
| 580 specified, the new versions of only the updated attributes are | |
| 581 returned. | |
| 582 | |
| 583 """ | |
| 584 expected_value = self.dynamize_expected_value(expected_value) | |
| 585 key = self.build_key_from_values(item.table.schema, | |
| 586 item.hash_key, item.range_key) | |
| 587 attr_updates = self.dynamize_attribute_updates(item._updates) | |
| 588 | |
| 589 response = self.layer1.update_item(item.table.name, key, | |
| 590 attr_updates, | |
| 591 expected_value, return_values, | |
| 592 object_hook=self.dynamizer.decode) | |
| 593 item._updates.clear() | |
| 594 if 'ConsumedCapacityUnits' in response: | |
| 595 item.consumed_units = response['ConsumedCapacityUnits'] | |
| 596 return response | |
| 597 | |
| 598 def delete_item(self, item, expected_value=None, return_values=None): | |
| 599 """ | |
| 600 Delete the item from Amazon DynamoDB. | |
| 601 | |
| 602 :type item: :class:`boto.dynamodb.item.Item` | |
| 603 :param item: The Item to delete from Amazon DynamoDB. | |
| 604 | |
| 605 :type expected_value: dict | |
| 606 :param expected_value: A dictionary of name/value pairs that you expect. | |
| 607 This dictionary should have name/value pairs where the name | |
| 608 is the name of the attribute and the value is either the value | |
| 609 you are expecting or False if you expect the attribute not to | |
| 610 exist. | |
| 611 | |
| 612 :type return_values: str | |
| 613 :param return_values: Controls the return of attribute | |
| 614 name-value pairs before then were changed. Possible | |
| 615 values are: None or 'ALL_OLD'. If 'ALL_OLD' is | |
| 616 specified and the item is overwritten, the content | |
| 617 of the old item is returned. | |
| 618 """ | |
| 619 expected_value = self.dynamize_expected_value(expected_value) | |
| 620 key = self.build_key_from_values(item.table.schema, | |
| 621 item.hash_key, item.range_key) | |
| 622 return self.layer1.delete_item(item.table.name, key, | |
| 623 expected=expected_value, | |
| 624 return_values=return_values, | |
| 625 object_hook=self.dynamizer.decode) | |
| 626 | |
| 627 def query(self, table, hash_key, range_key_condition=None, | |
| 628 attributes_to_get=None, request_limit=None, | |
| 629 max_results=None, consistent_read=False, | |
| 630 scan_index_forward=True, exclusive_start_key=None, | |
| 631 item_class=Item, count=False): | |
| 632 """ | |
| 633 Perform a query on the table. | |
| 634 | |
| 635 :type table: :class:`boto.dynamodb.table.Table` | |
| 636 :param table: The Table object that is being queried. | |
| 637 | |
| 638 :type hash_key: int|long|float|str|unicode|Binary | |
| 639 :param hash_key: The HashKey of the requested item. The | |
| 640 type of the value must match the type defined in the | |
| 641 schema for the table. | |
| 642 | |
| 643 :type range_key_condition: :class:`boto.dynamodb.condition.Condition` | |
| 644 :param range_key_condition: A Condition object. | |
| 645 Condition object can be one of the following types: | |
| 646 | |
| 647 EQ|LE|LT|GE|GT|BEGINS_WITH|BETWEEN | |
| 648 | |
| 649 The only condition which expects or will accept two | |
| 650 values is 'BETWEEN', otherwise a single value should | |
| 651 be passed to the Condition constructor. | |
| 652 | |
| 653 :type attributes_to_get: list | |
| 654 :param attributes_to_get: A list of attribute names. | |
| 655 If supplied, only the specified attribute names will | |
| 656 be returned. Otherwise, all attributes will be returned. | |
| 657 | |
| 658 :type request_limit: int | |
| 659 :param request_limit: The maximum number of items to retrieve | |
| 660 from Amazon DynamoDB on each request. You may want to set | |
| 661 a specific request_limit based on the provisioned throughput | |
| 662 of your table. The default behavior is to retrieve as many | |
| 663 results as possible per request. | |
| 664 | |
| 665 :type max_results: int | |
| 666 :param max_results: The maximum number of results that will | |
| 667 be retrieved from Amazon DynamoDB in total. For example, | |
| 668 if you only wanted to see the first 100 results from the | |
| 669 query, regardless of how many were actually available, you | |
| 670 could set max_results to 100 and the generator returned | |
| 671 from the query method will only yeild 100 results max. | |
| 672 | |
| 673 :type consistent_read: bool | |
| 674 :param consistent_read: If True, a consistent read | |
| 675 request is issued. Otherwise, an eventually consistent | |
| 676 request is issued. | |
| 677 | |
| 678 :type scan_index_forward: bool | |
| 679 :param scan_index_forward: Specified forward or backward | |
| 680 traversal of the index. Default is forward (True). | |
| 681 | |
| 682 :type count: bool | |
| 683 :param count: If True, Amazon DynamoDB returns a total | |
| 684 number of items for the Query operation, even if the | |
| 685 operation has no matching items for the assigned filter. | |
| 686 If count is True, the actual items are not returned and | |
| 687 the count is accessible as the ``count`` attribute of | |
| 688 the returned object. | |
| 689 | |
| 690 :type exclusive_start_key: list or tuple | |
| 691 :param exclusive_start_key: Primary key of the item from | |
| 692 which to continue an earlier query. This would be | |
| 693 provided as the LastEvaluatedKey in that query. | |
| 694 | |
| 695 :type item_class: Class | |
| 696 :param item_class: Allows you to override the class used | |
| 697 to generate the items. This should be a subclass of | |
| 698 :class:`boto.dynamodb.item.Item` | |
| 699 | |
| 700 :rtype: :class:`boto.dynamodb.layer2.TableGenerator` | |
| 701 """ | |
| 702 if range_key_condition: | |
| 703 rkc = self.dynamize_range_key_condition(range_key_condition) | |
| 704 else: | |
| 705 rkc = None | |
| 706 if exclusive_start_key: | |
| 707 esk = self.build_key_from_values(table.schema, | |
| 708 *exclusive_start_key) | |
| 709 else: | |
| 710 esk = None | |
| 711 kwargs = {'table_name': table.name, | |
| 712 'hash_key_value': self.dynamizer.encode(hash_key), | |
| 713 'range_key_conditions': rkc, | |
| 714 'attributes_to_get': attributes_to_get, | |
| 715 'limit': request_limit, | |
| 716 'count': count, | |
| 717 'consistent_read': consistent_read, | |
| 718 'scan_index_forward': scan_index_forward, | |
| 719 'exclusive_start_key': esk, | |
| 720 'object_hook': self.dynamizer.decode} | |
| 721 return TableGenerator(table, self.layer1.query, | |
| 722 max_results, item_class, kwargs) | |
| 723 | |
| 724 def scan(self, table, scan_filter=None, | |
| 725 attributes_to_get=None, request_limit=None, max_results=None, | |
| 726 exclusive_start_key=None, item_class=Item, count=False): | |
| 727 """ | |
| 728 Perform a scan of DynamoDB. | |
| 729 | |
| 730 :type table: :class:`boto.dynamodb.table.Table` | |
| 731 :param table: The Table object that is being scanned. | |
| 732 | |
| 733 :type scan_filter: A dict | |
| 734 :param scan_filter: A dictionary where the key is the | |
| 735 attribute name and the value is a | |
| 736 :class:`boto.dynamodb.condition.Condition` object. | |
| 737 Valid Condition objects include: | |
| 738 | |
| 739 * EQ - equal (1) | |
| 740 * NE - not equal (1) | |
| 741 * LE - less than or equal (1) | |
| 742 * LT - less than (1) | |
| 743 * GE - greater than or equal (1) | |
| 744 * GT - greater than (1) | |
| 745 * NOT_NULL - attribute exists (0, use None) | |
| 746 * NULL - attribute does not exist (0, use None) | |
| 747 * CONTAINS - substring or value in list (1) | |
| 748 * NOT_CONTAINS - absence of substring or value in list (1) | |
| 749 * BEGINS_WITH - substring prefix (1) | |
| 750 * IN - exact match in list (N) | |
| 751 * BETWEEN - >= first value, <= second value (2) | |
| 752 | |
| 753 :type attributes_to_get: list | |
| 754 :param attributes_to_get: A list of attribute names. | |
| 755 If supplied, only the specified attribute names will | |
| 756 be returned. Otherwise, all attributes will be returned. | |
| 757 | |
| 758 :type request_limit: int | |
| 759 :param request_limit: The maximum number of items to retrieve | |
| 760 from Amazon DynamoDB on each request. You may want to set | |
| 761 a specific request_limit based on the provisioned throughput | |
| 762 of your table. The default behavior is to retrieve as many | |
| 763 results as possible per request. | |
| 764 | |
| 765 :type max_results: int | |
| 766 :param max_results: The maximum number of results that will | |
| 767 be retrieved from Amazon DynamoDB in total. For example, | |
| 768 if you only wanted to see the first 100 results from the | |
| 769 query, regardless of how many were actually available, you | |
| 770 could set max_results to 100 and the generator returned | |
| 771 from the query method will only yeild 100 results max. | |
| 772 | |
| 773 :type count: bool | |
| 774 :param count: If True, Amazon DynamoDB returns a total | |
| 775 number of items for the Scan operation, even if the | |
| 776 operation has no matching items for the assigned filter. | |
| 777 If count is True, the actual items are not returned and | |
| 778 the count is accessible as the ``count`` attribute of | |
| 779 the returned object. | |
| 780 | |
| 781 :type exclusive_start_key: list or tuple | |
| 782 :param exclusive_start_key: Primary key of the item from | |
| 783 which to continue an earlier query. This would be | |
| 784 provided as the LastEvaluatedKey in that query. | |
| 785 | |
| 786 :type item_class: Class | |
| 787 :param item_class: Allows you to override the class used | |
| 788 to generate the items. This should be a subclass of | |
| 789 :class:`boto.dynamodb.item.Item` | |
| 790 | |
| 791 :rtype: :class:`boto.dynamodb.layer2.TableGenerator` | |
| 792 """ | |
| 793 if exclusive_start_key: | |
| 794 esk = self.build_key_from_values(table.schema, | |
| 795 *exclusive_start_key) | |
| 796 else: | |
| 797 esk = None | |
| 798 kwargs = {'table_name': table.name, | |
| 799 'scan_filter': self.dynamize_scan_filter(scan_filter), | |
| 800 'attributes_to_get': attributes_to_get, | |
| 801 'limit': request_limit, | |
| 802 'count': count, | |
| 803 'exclusive_start_key': esk, | |
| 804 'object_hook': self.dynamizer.decode} | |
| 805 return TableGenerator(table, self.layer1.scan, | |
| 806 max_results, item_class, kwargs) |
