summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Lindsley <daniel@toastdriven.com>2013-05-20 16:49:28 -0700
committerDaniel Lindsley <daniel@toastdriven.com>2013-05-20 16:49:28 -0700
commit9bffa78496b63f54ddb22460af570f6f1e69c9f9 (patch)
tree62be007798a805b2eb2d08794b03bb5e70f3b401
parentfc8d7dd54eba7e51c3b425d46bc0643f12be6fa2 (diff)
parent2cdd72908e941c0e6ea34f72c415c85d86f0da2a (diff)
downloadboto-2.9.4.tar.gz
Merge branch 'release-2.9.4'2.9.4
* release-2.9.4: Bumping version to 2.9.4 Finalized release notes for v2.9.4. Added tests for the cloudformation millisecond bug. Fixing cloudformation time stamps when API returns milliseconds A few PEP8 fixes and added some missing files to the EC2 ref docs index. Fixed regex that failed on S3 buckets with uppercase letters. Add release note item for bug fix Adjust part size as needed in create_archive_from_file Started the release notes for 2.9.4. Added pypi downloads badge. change os.path.normpath to posixpath.normpath because on windows os.path.normpath converts forward-slashes to backward-slashes Made the release & date line up again. Add Released: prefix for date in README Fixed up a test failure in the DynamoDB suite. Started the 2.9.4-dev cycle.
-rw-r--r--README.rst8
-rw-r--r--boto/__init__.py4
-rw-r--r--boto/auth.py4
-rw-r--r--boto/cloudformation/stack.py12
-rw-r--r--boto/ec2/instancestatus.py21
-rw-r--r--boto/ec2/tag.py11
-rw-r--r--boto/ec2/vmtype.py9
-rw-r--r--boto/ec2/volumestatus.py21
-rw-r--r--boto/glacier/vault.py3
-rw-r--r--docs/source/ref/ec2.rst57
-rw-r--r--docs/source/releasenotes/v2.9.4.rst30
-rw-r--r--tests/unit/cloudformation/test_stack.py13
-rw-r--r--tests/unit/dynamodb2/test_table.py12
-rw-r--r--tests/unit/glacier/test_vault.py14
14 files changed, 177 insertions, 42 deletions
diff --git a/README.rst b/README.rst
index 0b26f50b..c5904b48 100644
--- a/README.rst
+++ b/README.rst
@@ -1,11 +1,15 @@
####
boto
####
-boto 2.9.3
-30-Apr-2013
+boto 2.9.4
+
+Released: 20-May-2013
.. image:: https://travis-ci.org/boto/boto.png?branch=develop
:target: https://travis-ci.org/boto/boto
+
+.. image:: https://pypip.in/d/boto/badge.png
+ :target: https://crate.io/packages/boto/
************
Introduction
diff --git a/boto/__init__.py b/boto/__init__.py
index 1ea07916..45ae1d04 100644
--- a/boto/__init__.py
+++ b/boto/__init__.py
@@ -36,14 +36,14 @@ import logging.config
import urlparse
from boto.exception import InvalidUriError
-__version__ = '2.9.3'
+__version__ = '2.9.4'
Version = __version__ # for backware compatibility
UserAgent = 'Boto/%s (%s)' % (__version__, sys.platform)
config = Config()
# Regex to disallow buckets violating charset or not [3..255] chars total.
-BUCKET_NAME_RE = re.compile(r'^[a-z0-9][a-z0-9\._-]{1,253}[a-z0-9]$')
+BUCKET_NAME_RE = re.compile(r'^[a-zA-Z0-9][a-zA-Z0-9\._-]{1,253}[a-zA-Z0-9]$')
# Regex to disallow buckets with individual DNS labels longer than 63.
TOO_LONG_DNS_NAME_COMP = re.compile(r'[-_a-z0-9]{64}')
GENERATION_RE = re.compile(r'(?P<versionless_uri_str>.+)'
diff --git a/boto/auth.py b/boto/auth.py
index 3d694569..cd7ac68f 100644
--- a/boto/auth.py
+++ b/boto/auth.py
@@ -36,10 +36,10 @@ import copy
import datetime
from email.utils import formatdate
import hmac
-import os
import sys
import time
import urllib
+import posixpath
from boto.auth_handler import AuthHandler
from boto.exception import BotoClientError
@@ -385,7 +385,7 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
def canonical_uri(self, http_request):
# Normalize the path.
- normalized = os.path.normpath(http_request.auth_path)
+ normalized = posixpath.normpath(http_request.auth_path)
# Then urlencode whatever's left.
encoded = urllib.quote(normalized)
return encoded
diff --git a/boto/cloudformation/stack.py b/boto/cloudformation/stack.py
index a27df49b..289e18f4 100644
--- a/boto/cloudformation/stack.py
+++ b/boto/cloudformation/stack.py
@@ -41,7 +41,10 @@ class Stack(object):
def endElement(self, name, value, connection):
if name == 'CreationTime':
- self.creation_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
+ try:
+ self.creation_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
+ except ValueError:
+ self.creation_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')
elif name == "Description":
self.description = value
elif name == "DisableRollback":
@@ -212,7 +215,7 @@ class Tag(dict):
self._current_value = value
else:
setattr(self, name, value)
-
+
if self._current_key and self._current_value:
self[self._current_key] = self._current_value
self._current_key = None
@@ -351,7 +354,10 @@ class StackEvent(object):
elif name == "StackName":
self.stack_name = value
elif name == "Timestamp":
- self.timestamp = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
+ try:
+ self.timestamp = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
+ except ValueError:
+ self.timestamp = datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')
else:
setattr(self, name, value)
diff --git a/boto/ec2/instancestatus.py b/boto/ec2/instancestatus.py
index 3a9b5434..166732eb 100644
--- a/boto/ec2/instancestatus.py
+++ b/boto/ec2/instancestatus.py
@@ -16,11 +16,12 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
+
class Details(dict):
"""
A dict object that contains name/value pairs which provide
@@ -38,7 +39,8 @@ class Details(dict):
self[self._name] = value
else:
setattr(self, name, value)
-
+
+
class Event(object):
"""
A status event for an instance.
@@ -57,7 +59,7 @@ class Event(object):
self.description = description
self.not_before = not_before
self.not_after = not_after
-
+
def __repr__(self):
return 'Event:%s' % self.code
@@ -76,6 +78,7 @@ class Event(object):
else:
setattr(self, name, value)
+
class Status(object):
"""
A generic Status object used for system status and instance status.
@@ -90,7 +93,7 @@ class Status(object):
if not details:
details = Details()
self.details = details
-
+
def __repr__(self):
return 'Status:%s' % self.status
@@ -105,8 +108,9 @@ class Status(object):
else:
setattr(self, name, value)
+
class EventSet(list):
-
+
def startElement(self, name, attrs, connection):
if name == 'item':
event = Event()
@@ -118,6 +122,7 @@ class EventSet(list):
def endElement(self, name, value, connection):
setattr(self, name, value)
+
class InstanceStatus(object):
"""
Represents an EC2 Instance status as reported by
@@ -137,7 +142,7 @@ class InstanceStatus(object):
:ivar instance_status: A Status object that reports impaired
functionality that arises from problems internal to the instance.
"""
-
+
def __init__(self, id=None, zone=None, events=None,
state_code=None, state_name=None):
self.id = id
@@ -174,6 +179,7 @@ class InstanceStatus(object):
else:
setattr(self, name, value)
+
class InstanceStatusSet(list):
"""
A list object that contains the results of a call to
@@ -191,7 +197,7 @@ class InstanceStatusSet(list):
list.__init__(self)
self.connection = connection
self.next_token = None
-
+
def startElement(self, name, attrs, connection):
if name == 'item':
status = InstanceStatus()
@@ -204,4 +210,3 @@ class InstanceStatusSet(list):
if name == 'NextToken':
self.next_token = value
setattr(self, name, value)
-
diff --git a/boto/ec2/tag.py b/boto/ec2/tag.py
index 8032e6ff..deb2c788 100644
--- a/boto/ec2/tag.py
+++ b/boto/ec2/tag.py
@@ -15,11 +15,12 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
+
class TagSet(dict):
"""
A TagSet is used to collect the tags associated with a particular
@@ -27,7 +28,7 @@ class TagSet(dict):
can, this dict object will be used to collect those values. See
:class:`boto.ec2.ec2object.TaggedEC2Object` for more details.
"""
-
+
def __init__(self, connection=None):
self.connection = connection
self._current_key = None
@@ -55,7 +56,7 @@ class Tag(object):
also the ID of the resource to which the tag is attached
as well as the type of the resource.
"""
-
+
def __init__(self, connection=None, res_id=None, res_type=None,
name=None, value=None):
self.connection = connection
@@ -81,7 +82,3 @@ class Tag(object):
self.value = value
else:
setattr(self, name, value)
-
-
-
-
diff --git a/boto/ec2/vmtype.py b/boto/ec2/vmtype.py
index 423ddbfb..fdb4f369 100644
--- a/boto/ec2/vmtype.py
+++ b/boto/ec2/vmtype.py
@@ -22,6 +22,7 @@
from boto.ec2.ec2object import EC2Object
+
class VmType(EC2Object):
"""
Represents an EC2 VM Type
@@ -32,7 +33,8 @@ class VmType(EC2Object):
:ivar disk: The amount of disk space in gigabytes for this vm type
"""
- def __init__(self, connection=None, name=None, cores=None, memory=None, disk=None):
+ def __init__(self, connection=None, name=None, cores=None,
+ memory=None, disk=None):
EC2Object.__init__(self, connection)
self.connection = connection
self.name = name
@@ -41,7 +43,8 @@ class VmType(EC2Object):
self.disk = disk
def __repr__(self):
- return 'VmType:%s-%s,%s,%s' % (self.name, self.cores, self.memory, self.disk)
+ return 'VmType:%s-%s,%s,%s' % (self.name, self.cores,
+ self.memory, self.disk)
def endElement(self, name, value, connection):
if name == 'euca:name':
@@ -54,5 +57,3 @@ class VmType(EC2Object):
self.memory = value
else:
setattr(self, name, value)
-
-
diff --git a/boto/ec2/volumestatus.py b/boto/ec2/volumestatus.py
index 7bbc173c..78de2bb0 100644
--- a/boto/ec2/volumestatus.py
+++ b/boto/ec2/volumestatus.py
@@ -16,13 +16,14 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
-# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
from boto.ec2.instancestatus import Status, Details
+
class Event(object):
"""
A status event for an instance.
@@ -43,7 +44,7 @@ class Event(object):
self.description = description
self.not_before = not_before
self.not_after = not_after
-
+
def __repr__(self):
return 'Event:%s' % self.type
@@ -64,8 +65,9 @@ class Event(object):
else:
setattr(self, name, value)
+
class EventSet(list):
-
+
def startElement(self, name, attrs, connection):
if name == 'item':
event = Event()
@@ -77,6 +79,7 @@ class EventSet(list):
def endElement(self, name, value, connection):
setattr(self, name, value)
+
class Action(object):
"""
An action for an instance.
@@ -92,7 +95,7 @@ class Action(object):
self.id = id
self.type = type
self.description = description
-
+
def __repr__(self):
return 'Action:%s' % self.code
@@ -111,8 +114,9 @@ class Action(object):
else:
setattr(self, name, value)
+
class ActionSet(list):
-
+
def startElement(self, name, attrs, connection):
if name == 'item':
action = Action()
@@ -124,6 +128,7 @@ class ActionSet(list):
def endElement(self, name, value, connection):
setattr(self, name, value)
+
class VolumeStatus(object):
"""
Represents an EC2 Volume status as reported by
@@ -136,7 +141,7 @@ class VolumeStatus(object):
:ivar events: A list of events relevant to the instance.
:ivar actions: A list of events relevant to the instance.
"""
-
+
def __init__(self, id=None, zone=None):
self.id = id
self.zone = zone
@@ -167,6 +172,7 @@ class VolumeStatus(object):
else:
setattr(self, name, value)
+
class VolumeStatusSet(list):
"""
A list object that contains the results of a call to
@@ -184,7 +190,7 @@ class VolumeStatusSet(list):
list.__init__(self)
self.connection = connection
self.next_token = None
-
+
def startElement(self, name, attrs, connection):
if name == 'item':
status = VolumeStatus()
@@ -197,4 +203,3 @@ class VolumeStatusSet(list):
if name == 'NextToken':
self.next_token = value
setattr(self, name, value)
-
diff --git a/boto/glacier/vault.py b/boto/glacier/vault.py
index 6e47bf76..ac019ac9 100644
--- a/boto/glacier/vault.py
+++ b/boto/glacier/vault.py
@@ -161,8 +161,7 @@ class Vault(object):
if not file_obj:
file_size = os.path.getsize(filename)
try:
- min_part_size = minimum_part_size(file_size,
- self.DefaultPartSize)
+ part_size = minimum_part_size(file_size, part_size)
except ValueError:
raise UploadArchiveError("File size of %s bytes exceeds "
"40,000 GB archive limit of Glacier.")
diff --git a/docs/source/ref/ec2.rst b/docs/source/ref/ec2.rst
index 96f511e2..db98293c 100644
--- a/docs/source/ref/ec2.rst
+++ b/docs/source/ref/ec2.rst
@@ -23,6 +23,13 @@ boto.ec2.autoscale
See the :doc:`Auto Scaling Reference <autoscale>`.
+boto.ec2.blockdevicemapping
+---------------------------
+
+.. automodule:: boto.ec2.blockdevicemapping
+ :members:
+ :undoc-members:
+
boto.ec2.buyreservation
-----------------------
@@ -54,6 +61,13 @@ boto.ec2.elb
See the :doc:`ELB Reference <elb>`.
+boto.ec2.group
+----------------
+
+.. automodule:: boto.ec2.group
+ :members:
+ :undoc-members:
+
boto.ec2.image
--------------
@@ -89,6 +103,27 @@ boto.ec2.keypair
:members:
:undoc-members:
+boto.ec2.launchspecification
+----------------
+
+.. automodule:: boto.ec2.launchspecification
+ :members:
+ :undoc-members:
+
+boto.ec2.networkinterface
+----------------
+
+.. automodule:: boto.ec2.networkinterface
+ :members:
+ :undoc-members:
+
+boto.ec2.placementgroup
+----------------
+
+.. automodule:: boto.ec2.placementgroup
+ :members:
+ :undoc-members:
+
boto.ec2.regioninfo
-------------------
@@ -124,6 +159,20 @@ boto.ec2.spotinstancerequest
:members:
:undoc-members:
+boto.ec2.tag
+----------------------------
+
+.. automodule:: boto.ec2.tag
+ :members:
+ :undoc-members:
+
+boto.ec2.vmtype
+----------------------------
+
+.. automodule:: boto.ec2.vmtype
+ :members:
+ :undoc-members:
+
boto.ec2.volume
---------------
@@ -131,10 +180,16 @@ boto.ec2.volume
:members:
:undoc-members:
+boto.ec2.volumestatus
+---------------------
+
+.. automodule:: boto.ec2.volumestatus
+ :members:
+ :undoc-members:
+
boto.ec2.zone
-------------
.. automodule:: boto.ec2.zone
:members:
:undoc-members:
-
diff --git a/docs/source/releasenotes/v2.9.4.rst b/docs/source/releasenotes/v2.9.4.rst
new file mode 100644
index 00000000..1956dbff
--- /dev/null
+++ b/docs/source/releasenotes/v2.9.4.rst
@@ -0,0 +1,30 @@
+boto v2.9.4
+===========
+
+:date: 2013/05/20
+
+This release adds updated Elastic Transcoder support & fixes several bugs
+from recent releases & API updates.
+
+
+Features
+--------
+
+* Updated Elastic Transcoder support - It now supports HLS, WebM, MPEG2-TS & a
+ host of `other features`_. (SHA: 89196a)
+
+ .. _`other features`: http://aws.typepad.com/aws/2013/05/new-features-for-the-amazon-elastic-transcoder.html
+
+
+Bugfixes
+--------
+
+* Fixed a bug in the canonicalization of URLs on Windows. (SHA: 09ef8c)
+* Fixed glacier part size bug (issue: 1478, SHA: 9e04171)
+* Fixed a bug in the bucket regex for S3 involving capital letters.
+ (SHA: 950031)
+* Fixed a bug where timestamps from Cloudformation would fail to be parsed.
+ (SHA: b40542)
+* Several documentation improvements/fixes:
+
+ * Added autodocs for many of the EC2 apis. (SHA: 79f939)
diff --git a/tests/unit/cloudformation/test_stack.py b/tests/unit/cloudformation/test_stack.py
index 4f0db6aa..54d2dc90 100644
--- a/tests/unit/cloudformation/test_stack.py
+++ b/tests/unit/cloudformation/test_stack.py
@@ -1,4 +1,5 @@
#!/usr/bin/env python
+import datetime
import xml.sax
import unittest
import boto.handler
@@ -59,5 +60,17 @@ class TestStackParse(unittest.TestCase):
tags = rs[0].tags
self.assertEqual(tags, {u'key0': u'value0', u'key1': u'value1'})
+ def test_creation_time_with_millis(self):
+ millis_xml = SAMPLE_XML.replace(
+ "<CreationTime>2013-01-10T05:04:56Z</CreationTime>",
+ "<CreationTime>2013-01-10T05:04:56.102342Z</CreationTime>"
+ )
+
+ rs = boto.resultset.ResultSet([('member', boto.cloudformation.stack.Stack)])
+ h = boto.handler.XmlHandler(rs, None)
+ xml.sax.parseString(millis_xml, h)
+ creation_time = rs[0].creation_time
+ self.assertEqual(creation_time, datetime.datetime(2013, 1, 10, 5, 4, 56, 102342))
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/unit/dynamodb2/test_table.py b/tests/unit/dynamodb2/test_table.py
index 053ca099..591f69c4 100644
--- a/tests/unit/dynamodb2/test_table.py
+++ b/tests/unit/dynamodb2/test_table.py
@@ -1621,7 +1621,9 @@ class TableTestCase(unittest.TestCase):
'ComparisonOperator': 'LE',
}
},
- limit=2
+ limit=2,
+ segment=None,
+ total_segments=None
)
# Now alter the expected.
@@ -1640,7 +1642,9 @@ class TableTestCase(unittest.TestCase):
friend_count__lte=2,
exclusive_start_key={
'username': 'adam',
- }
+ },
+ segment=None,
+ total_segments=None
)
usernames = [res['username'] for res in results['results']]
self.assertEqual(usernames, ['alice', 'bob', 'jane'])
@@ -1659,7 +1663,9 @@ class TableTestCase(unittest.TestCase):
'username': {
'S': 'adam',
},
- }
+ },
+ segment=None,
+ total_segments=None
)
def test_query(self):
diff --git a/tests/unit/glacier/test_vault.py b/tests/unit/glacier/test_vault.py
index f61d5874..a4ef008b 100644
--- a/tests/unit/glacier/test_vault.py
+++ b/tests/unit/glacier/test_vault.py
@@ -82,6 +82,20 @@ class TestVault(unittest.TestCase):
self.vault.create_archive_writer.assert_called_with(
description=mock.ANY, part_size=self.vault.DefaultPartSize)
+ def test_part_size_needs_to_be_adjusted(self):
+ # If we have a large file (400 GB)
+ self.getsize.return_value = 400 * 1024 * 1024 * 1024
+ self.vault.create_archive_writer = mock.Mock()
+ # When we try to upload the file.
+ with mock.patch('boto.glacier.vault.open', self.mock_open,
+ create=True):
+ self.vault.create_archive_from_file('myfile')
+ # We should automatically bump up the part size used to
+ # 64 MB.
+ expected_part_size = 64 * 1024 * 1024
+ self.vault.create_archive_writer.assert_called_with(
+ description=mock.ANY, part_size=expected_part_size)
+
class TestConcurrentUploads(unittest.TestCase):