Permalink
Browse files

Merge branch 'release-2.23.0'

  • Loading branch information...
toastdriven committed Jan 10, 2014
2 parents 487ea4e + 9f4e4e7 commit 5b3d2053d443fc29271b2045b09adb515949394e
Showing with 1,769 additions and 622 deletions.
  1. +2 −2 README.rst
  2. +1 −1 boto/__init__.py
  3. +6 −6 boto/cloudfront/distribution.py
  4. +1 −1 boto/cloudfront/identity.py
  5. +1 −1 boto/cloudfront/invalidation.py
  6. +1 −1 boto/cloudsearch/layer1.py
  7. +7 −3 boto/connection.py
  8. +1 −1 boto/core/dictresponse.py
  9. +3 −3 boto/dynamodb/item.py
  10. +3 −0 boto/dynamodb/types.py
  11. +55 −4 boto/dynamodb2/table.py
  12. +9 −5 boto/ec2/cloudwatch/__init__.py
  13. +78 −16 boto/ec2/connection.py
  14. +1 −1 boto/ec2/elb/loadbalancer.py
  15. +1 −1 boto/ecs/__init__.py
  16. +3 −3 boto/ecs/item.py
  17. +2 −0 boto/glacier/concurrent.py
  18. +27 −2 boto/glacier/job.py
  19. +861 −221 boto/glacier/layer1.py
  20. +40 −1 boto/glacier/vault.py
  21. +9 −9 boto/gs/key.py
  22. +5 −7 boto/gs/resumable_upload_handler.py
  23. +1 −1 boto/handler.py
  24. +1 −1 boto/jsonresponse.py
  25. +1 −1 boto/manage/cmdshell.py
  26. +1 −1 boto/manage/server.py
  27. +1 −1 boto/manage/task.py
  28. +8 −8 boto/manage/volume.py
  29. +1 −1 boto/mashups/order.py
  30. +1 −1 boto/mturk/layoutparam.py
  31. +1 −1 boto/mturk/qualification.py
  32. +19 −14 boto/mws/connection.py
  33. +21 −5 boto/provider.py
  34. +1 −1 boto/pyami/config.py
  35. +1 −1 boto/pyami/installers/ubuntu/ebs.py
  36. +5 −5 boto/rds/__init__.py
  37. +1 −1 boto/rds/dbsubnetgroup.py
  38. +1 −1 boto/rds/parametergroup.py
  39. +7 −7 boto/route53/record.py
  40. +2 −2 boto/s3/connection.py
  41. +11 −11 boto/s3/key.py
  42. +1 −1 boto/s3/resumable_download_handler.py
  43. +1 −1 boto/sdb/connection.py
  44. +1 −1 boto/sdb/db/key.py
  45. +9 −9 boto/sdb/db/manager/sdbmanager.py
  46. +3 −3 boto/sdb/db/manager/xmlmanager.py
  47. +1 −1 boto/sdb/db/model.py
  48. +8 −8 boto/sdb/db/property.py
  49. +1 −1 boto/sdb/db/query.py
  50. +9 −9 boto/sdb/db/sequence.py
  51. +1 −1 boto/sdb/item.py
  52. +3 −3 boto/sdb/queryresultset.py
  53. +1 −1 boto/services/message.py
  54. +1 −1 boto/sqs/message.py
  55. +207 −215 boto/support/layer1.py
  56. +1 −0 docs/source/index.rst
  57. +8 −0 docs/source/ref/autoscale.rst
  58. +49 −0 docs/source/releasenotes/v2.23.0.rst
  59. +4 −1 scripts/git-release-notes.py
  60. +10 −10 tests/integration/__init__.py
  61. +10 −0 tests/integration/dynamodb2/test_highlevel.py
  62. +12 −0 tests/unit/dynamodb/test_types.py
  63. +77 −1 tests/unit/dynamodb2/test_table.py
  64. +45 −0 tests/unit/ec2/test_connection.py
  65. +22 −0 tests/unit/glacier/test_job.py
  66. +17 −0 tests/unit/glacier/test_layer2.py
  67. +27 −2 tests/unit/mws/test_connection.py
  68. +38 −0 tests/unit/provider/test_provider.py
View
@@ -1,9 +1,9 @@
####
boto
####
-boto 2.22.1
+boto 2.23.0
-Released: 6-January-2014
+Released: 10-January-2014
.. image:: https://travis-ci.org/boto/boto.png?branch=develop
:target: https://travis-ci.org/boto/boto
View
@@ -37,7 +37,7 @@
import urlparse
from boto.exception import InvalidUriError
-__version__ = '2.22.1'
+__version__ = '2.23.0'
Version = __version__ # for backware compatibility
# http://bugs.python.org/issue7980
@@ -350,11 +350,11 @@ def update(self, enabled=None, cnames=None, comment=None):
self.config.cnames, self.config.comment,
self.config.trusted_signers,
self.config.default_root_object)
- if enabled != None:
+ if enabled is not None:
new_config.enabled = enabled
- if cnames != None:
+ if cnames is not None:
new_config.cnames = cnames
- if comment != None:
+ if comment is not None:
new_config.comment = comment
self.etag = self.connection.set_distribution_config(self.id, self.etag, new_config)
self.config = new_config
@@ -730,11 +730,11 @@ def update(self, enabled=None, cnames=None, comment=None):
self.config.cnames,
self.config.comment,
self.config.trusted_signers)
- if enabled != None:
+ if enabled is not None:
new_config.enabled = enabled
- if cnames != None:
+ if cnames is not None:
new_config.cnames = cnames
- if comment != None:
+ if comment is not None:
new_config.comment = comment
self.etag = self.connection.set_streaming_distribution_config(self.id,
self.etag,
@@ -52,7 +52,7 @@ def update(self, comment=None):
new_config = OriginAccessIdentityConfig(self.connection,
self.config.caller_reference,
self.config.comment)
- if comment != None:
+ if comment is not None:
new_config.comment = comment
self.etag = self.connection.set_origin_identity_config(self.id, self.etag, new_config)
self.config = new_config
@@ -75,7 +75,7 @@ def escape(self, p):
def to_xml(self):
"""Get this batch as XML"""
- assert self.connection != None
+ assert self.connection is not None
s = '<?xml version="1.0" encoding="UTF-8"?>\n'
s += '<InvalidationBatch xmlns="http://cloudfront.amazonaws.com/doc/%s/">\n' % self.connection.Version
for p in self.paths:
@@ -88,7 +88,7 @@ def get_response(self, doc_path, action, params, path='/',
for p in doc_path:
inner = inner.get(p)
if not inner:
- return None if list_marker == None else []
+ return None if list_marker is None else []
if isinstance(inner, list):
return inner
else:
View
@@ -434,6 +434,10 @@ def __init__(self, host, aws_access_key_id=None,
:keyword str aws_secret_access_key: Your AWS Secret Access Key
(provided by Amazon). If none is specified, the value in your
``AWS_SECRET_ACCESS_KEY`` environmental variable is used.
+ :keyword str security_token: The security token associated with
+ temporary credentials issued by STS. Optional unless using
+ temporary credentials. If none is specified, the environment
+ variable ``AWS_SECURITY_TOKEN`` is used if defined.
:type is_secure: boolean
:param is_secure: Whether the connection is over SSL
@@ -680,7 +684,7 @@ def handle_proxy(self, proxy, proxy_port, proxy_user, proxy_pass):
self.proxy_port = self.port
self.no_proxy = os.environ.get('no_proxy', '') or os.environ.get('NO_PROXY', '')
- self.use_proxy = (self.proxy != None)
+ self.use_proxy = (self.proxy is not None)
def get_http_connection(self, host, port, is_secure):
conn = self._pool.get_http_connection(host, port, is_secure)
@@ -982,11 +986,11 @@ def build_base_http_request(self, method, path, auth_path,
path = self.get_path(path)
if auth_path is not None:
auth_path = self.get_path(auth_path)
- if params == None:
+ if params is None:
params = {}
else:
params = params.copy()
- if headers == None:
+ if headers is None:
headers = {}
else:
headers = headers.copy()
@@ -47,7 +47,7 @@ def __init__(self, root_node, connection):
def startElement(self, name, attrs):
self.current_text = ''
t = self.nodes[-1][1].startElement(name, attrs, self.connection)
- if t != None:
+ if t is not None:
if isinstance(t, tuple):
self.nodes.append(t)
else:
View
@@ -41,13 +41,13 @@ def __init__(self, table, hash_key=None, range_key=None, attrs=None):
self._updates = None
self._hash_key_name = self.table.schema.hash_key_name
self._range_key_name = self.table.schema.range_key_name
- if attrs == None:
+ if attrs is None:
attrs = {}
- if hash_key == None:
+ if hash_key is None:
hash_key = attrs.get(self._hash_key_name, None)
self[self._hash_key_name] = hash_key
if self._range_key_name:
- if range_key == None:
+ if range_key is None:
range_key = attrs.get(self._range_key_name, None)
self[self._range_key_name] = range_key
self._updates = {}
View
@@ -136,6 +136,9 @@ def dynamize_value(val):
class Binary(object):
def __init__(self, value):
+ if not isinstance(value, basestring):
+ raise TypeError('Value must be a string of binary data!')
+
self.value = value
def encode(self):
View
@@ -8,6 +8,7 @@
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.exception import JSONResponseError
class Table(object):
@@ -436,7 +437,7 @@ def _encode_keys(self, keys):
return raw_key
- def get_item(self, consistent=False, **kwargs):
+ def get_item(self, consistent=False, attributes=None, **kwargs):
"""
Fetches an item (record) from a table in DynamoDB.
@@ -448,6 +449,10 @@ def get_item(self, consistent=False, **kwargs):
a consistent (but more expensive) read from DynamoDB.
(Default: ``False``)
+ Optionally accepts an ``attributes`` parameter, which should be a
+ list of fieldname to fetch. (Default: ``None``, which means all fields
+ should be fetched)
+
Returns an ``Item`` instance containing all the data for that record.
Example::
@@ -480,12 +485,52 @@ def get_item(self, consistent=False, **kwargs):
item_data = self.connection.get_item(
self.table_name,
raw_key,
+ attributes_to_get=attributes,
consistent_read=consistent
)
item = Item(self)
item.load(item_data)
return item
+ def has_item(self, **kwargs):
+ """
+ Return whether an item (record) exists within a table in DynamoDB.
+
+ To specify the key of the item you'd like to get, you can specify the
+ key attributes as kwargs.
+
+ Optionally accepts a ``consistent`` parameter, which should be a
+ boolean. If you provide ``True``, it will perform
+ a consistent (but more expensive) read from DynamoDB.
+ (Default: ``False``)
+
+ Optionally accepts an ``attributes`` parameter, which should be a
+ list of fieldnames to fetch. (Default: ``None``, which means all fields
+ should be fetched)
+
+ Returns ``True`` if an ``Item`` is present, ``False`` if not.
+
+ Example::
+
+ # Simple, just hash-key schema.
+ >>> users.has_item(username='johndoe')
+ True
+
+ # Complex schema, item not present.
+ >>> users.has_item(
+ ... username='johndoe',
+ ... date_joined='2014-01-07'
+ ... )
+ False
+
+ """
+ try:
+ self.get_item(**kwargs)
+ except JSONResponseError:
+ return False
+
+ return True
+
def lookup(self, *args, **kwargs):
"""
Look up an entry in DynamoDB. This is mostly backwards compatible
@@ -524,7 +569,6 @@ def new_item(self, *args):
data[self.schema[x].name] = arg
return Item(self, data=data)
-
def put_item(self, data, overwrite=False):
"""
Saves an entire item to DynamoDB.
@@ -969,7 +1013,7 @@ def _query(self, limit=None, index=None, reverse=False, consistent=False,
}
def scan(self, limit=None, segment=None, total_segments=None,
- max_page_size=None, **filter_kwargs):
+ max_page_size=None, attributes=None, **filter_kwargs):
"""
Scans across all items within a DynamoDB table.
@@ -1000,6 +1044,11 @@ def scan(self, limit=None, segment=None, total_segments=None,
the scan from drowning out other queries. (Default: ``None`` -
fetch as many as DynamoDB will return)
+ Optionally accepts an ``attributes`` parameter, which should be a
+ tuple. If you provide any attributes only these will be fetched
+ from DynamoDB. This uses the ``AttributesToGet`` and set's
+ ``Select`` to ``SPECIFIC_ATTRIBUTES`` API.
+
Returns a ``ResultSet``, which transparently handles the pagination of
results you get back.
@@ -1034,12 +1083,13 @@ def scan(self, limit=None, segment=None, total_segments=None,
'limit': limit,
'segment': segment,
'total_segments': total_segments,
+ 'attributes': attributes,
})
results.to_call(self._scan, **kwargs)
return results
def _scan(self, limit=None, exclusive_start_key=None, segment=None,
- total_segments=None, **filter_kwargs):
+ total_segments=None, attributes=None, **filter_kwargs):
"""
The internal method that performs the actual scan. Used extensively
by ``ResultSet`` to perform each (paginated) request.
@@ -1048,6 +1098,7 @@ def _scan(self, limit=None, exclusive_start_key=None, segment=None,
'limit': limit,
'segment': segment,
'total_segments': total_segments,
+ 'attributes_to_get': attributes,
}
if exclusive_start_key:
@@ -178,11 +178,11 @@ def aslist(a):
metric_data['StatisticValues.Minimum'] = s['minimum']
metric_data['StatisticValues.SampleCount'] = s['samplecount']
metric_data['StatisticValues.Sum'] = s['sum']
- if value != None:
+ if value is not None:
msg = 'You supplied a value and statistics for a ' + \
'metric.Posting statistics and not value.'
boto.log.warn(msg)
- elif value != None:
+ elif value is not None:
metric_data['Value'] = v
else:
raise Exception('Must specify a value or statistics to put.')
@@ -273,9 +273,13 @@ def list_metrics(self, next_token=None, dimensions=None,
pairs that will be used to filter the results. The key in
the dictionary is the name of a Dimension. The value in
the dictionary is either a scalar value of that Dimension
- name that you want to filter on, a list of values to
- filter on or None if you want all metrics with that
- Dimension name.
+ name that you want to filter on or None if you want all
+ metrics with that Dimension name. To be included in the
+ result a metric must contain all specified dimensions,
+ although the metric may contain additional dimensions beyond
+ the requested metrics. The Dimension names, and values must
+ be strings between 1 and 250 characters long. A maximum of
+ 10 dimensions are allowed.
:type metric_name: str
:param metric_name: The name of the Metric to filter against. If None,
Oops, something went wrong.

0 comments on commit 5b3d205

Please sign in to comment.