From 6733d6065583d098276d66f31a431b0dfc25810f Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Sun, 14 Sep 2014 11:43:53 +0800 Subject: common modules: tidy up to meet PEP8 better --- boto/auth.py | 20 ++--- boto/auth_handler.py | 10 ++- boto/connection.py | 78 ++++++++--------- boto/contrib/__init__.py | 3 +- boto/contrib/ymlmessage.py | 1 + boto/exception.py | 33 ++++++-- boto/handler.py | 3 +- boto/https_connection.py | 189 +++++++++++++++++++++--------------------- boto/jsonresponse.py | 5 +- boto/plugin.py | 25 +++--- boto/provider.py | 6 +- boto/regioninfo.py | 2 +- boto/requestlog.py | 14 ++-- boto/resultset.py | 12 +-- boto/storage_uri.py | 145 ++++++++++++++++---------------- boto/utils.py | 7 +- scripts/git-release-notes.py | 2 +- scripts/rebuild_endpoints.py | 1 + tests/__init__.py | 2 +- tests/integration/__init__.py | 2 +- tests/test.py | 3 +- tests/unit/__init__.py | 2 + tests/unit/test_connection.py | 29 ++++--- tests/unit/test_exception.py | 17 ++-- tests/unit/test_regioninfo.py | 2 - 25 files changed, 328 insertions(+), 285 deletions(-) diff --git a/boto/auth.py b/boto/auth.py index df8dcccd..bc8d3b7c 100644 --- a/boto/auth.py +++ b/boto/auth.py @@ -37,8 +37,6 @@ import datetime from email.utils import formatdate import hmac import os -import sys -import time import posixpath from boto.compat import urllib, encodebytes @@ -378,7 +376,7 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys): path = http_request.auth_path # Normalize the path # in windows normpath('/') will be '\\' so we chane it back to '/' - normalized = posixpath.normpath(path).replace('\\','/') + normalized = posixpath.normpath(path).replace('\\', '/') # Then urlencode whatever's left. encoded = urllib.parse.quote(normalized) if len(path) > 1 and path.endswith('/'): @@ -474,7 +472,7 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys): def signature(self, http_request, string_to_sign): key = self._provider.secret_key k_date = self._sign(('AWS4' + key).encode('utf-8'), - http_request.timestamp) + http_request.timestamp) k_region = self._sign(k_date, http_request.region_name) k_service = self._sign(k_region, http_request.service_name) k_signing = self._sign(k_service, 'aws4_request') @@ -570,7 +568,7 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler): # Hooray for the only difference! The main SigV4 signer only does # ``Host`` + ``x-amz-*``. But S3 wants pretty much everything # signed, except for authorization itself. - if not lname in ['authorization']: + if lname not in ['authorization']: headers_to_sign[name] = value return headers_to_sign @@ -667,7 +665,7 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler): return super(S3HmacAuthV4Handler, self).payload(http_request) def add_auth(self, req, **kwargs): - if not 'x-amz-content-sha256' in req.headers: + if 'x-amz-content-sha256' not in req.headers: if '_sha256' in req.headers: req.headers['x-amz-content-sha256'] = req.headers.pop('_sha256') else: @@ -751,7 +749,6 @@ class QueryAuthHandler(AuthHandler): def add_auth(self, http_request, **kwargs): headers = http_request.headers - params = http_request.params qs = self._build_query_string( http_request.params ) @@ -899,7 +896,7 @@ class POSTPathQSV2AuthHandler(QuerySignatureV2AuthHandler, AuthHandler): # already be there, we need to get rid of that and rebuild it req.path = req.path.split('?')[0] req.path = (req.path + '?' + qs + - '&Signature=' + urllib.parse.quote_plus(signature)) + '&Signature=' + urllib.parse.quote_plus(signature)) def get_auth_handler(host, config, provider, requested_capability=None): @@ -925,7 +922,6 @@ def get_auth_handler(host, config, provider, requested_capability=None): """ ready_handlers = [] auth_handlers = boto.plugin.get_plugin(AuthHandler, requested_capability) - total_handlers = len(auth_handlers) for handler in auth_handlers: try: ready_handlers.append(handler(host, config, provider)) @@ -936,9 +932,9 @@ def get_auth_handler(host, config, provider, requested_capability=None): checked_handlers = auth_handlers names = [handler.__name__ for handler in checked_handlers] raise boto.exception.NoAuthHandlerFound( - 'No handler was ready to authenticate. %d handlers were checked.' - ' %s ' - 'Check your credentials' % (len(names), str(names))) + 'No handler was ready to authenticate. %d handlers were checked.' + ' %s ' + 'Check your credentials' % (len(names), str(names))) # We select the last ready auth handler that was loaded, to allow users to # customize how auth works in environments where there are shared boto diff --git a/boto/auth_handler.py b/boto/auth_handler.py index e6d131af..a8583f8a 100644 --- a/boto/auth_handler.py +++ b/boto/auth_handler.py @@ -14,7 +14,7 @@ # 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. @@ -25,8 +25,10 @@ Defines an interface which all Auth handlers need to implement. from boto.plugin import Plugin + class NotReadyToAuthenticate(Exception): - pass + pass + class AuthHandler(Plugin): @@ -37,10 +39,10 @@ class AuthHandler(Plugin): :type host: string :param host: The host to which the request is being sent. - :type config: boto.pyami.Config + :type config: boto.pyami.Config :param config: Boto configuration. - :type provider: boto.provider.Provider + :type provider: boto.provider.Provider :param provider: Provider details. Raises: diff --git a/boto/connection.py b/boto/connection.py index 8640ec48..09452358 100644 --- a/boto/connection.py +++ b/boto/connection.py @@ -90,7 +90,7 @@ ON_APP_ENGINE = all(key in os.environ for key in ( PORTS_BY_SECURITY = {True: 443, False: 80} -DEFAULT_CA_CERTS_FILE = os.path.join(os.path.dirname(os.path.abspath(boto.cacerts.__file__ )), "cacerts.txt") +DEFAULT_CA_CERTS_FILE = os.path.join(os.path.dirname(os.path.abspath(boto.cacerts.__file__)), "cacerts.txt") class HostConnectionPool(object): @@ -485,13 +485,13 @@ class AWSAuthConnection(object): validate_certs) if self.https_validate_certificates and not HAVE_HTTPS_CONNECTION: raise BotoClientError( - "SSL server certificate validation is enabled in boto " - "configuration, but Python dependencies required to " - "support this feature are not available. Certificate " - "validation is only supported when running under Python " - "2.6 or later.") + "SSL server certificate validation is enabled in boto " + "configuration, but Python dependencies required to " + "support this feature are not available. Certificate " + "validation is only supported when running under Python " + "2.6 or later.") certs_file = config.get_value( - 'Boto', 'ca_certificates_file', DEFAULT_CA_CERTS_FILE) + 'Boto', 'ca_certificates_file', DEFAULT_CA_CERTS_FILE) if certs_file == 'system': certs_file = None self.ca_certificates_file = certs_file @@ -508,7 +508,7 @@ class AWSAuthConnection(object): self.http_unretryable_exceptions = [] if HAVE_HTTPS_CONNECTION: self.http_unretryable_exceptions.append( - https_connection.InvalidCertificateException) + https_connection.InvalidCertificateException) # define values in socket exceptions we don't want to catch self.socket_exception_values = (errno.EINTR,) @@ -565,7 +565,7 @@ class AWSAuthConnection(object): self._connection = (self.host, self.port, self.is_secure) self._last_rs = None self._auth_handler = auth.get_auth_handler( - host, config, self.provider, self._required_auth_capability()) + host, config, self.provider, self._required_auth_capability()) if getattr(self, 'AuthServiceName', None) is not None: self.auth_service_name = self.AuthServiceName self.request_hook = None @@ -667,9 +667,9 @@ class AWSAuthConnection(object): self.proxy_pass = proxy_pass if 'http_proxy' in os.environ and not self.proxy: pattern = re.compile( - '(?:http://)?' \ - '(?:(?P[\w\-\.]+):(?P.*)@)?' \ - '(?P[\w\-\.]+)' \ + '(?:http://)?' + '(?:(?P[\w\-\.]+):(?P.*)@)?' + '(?P[\w\-\.]+)' '(?::(?P\d+))?' ) match = pattern.match(os.environ['http_proxy']) @@ -689,8 +689,8 @@ class AWSAuthConnection(object): self.proxy_pass = config.get_value('Boto', 'proxy_pass', None) if not self.proxy_port and self.proxy: - print("http_proxy environment variable does not specify " \ - "a port, using default") + print("http_proxy environment variable does not specify " + "a port, using default") self.proxy_port = self.port self.no_proxy = os.environ.get('no_proxy', '') or os.environ.get('NO_PROXY', '') @@ -740,30 +740,30 @@ class AWSAuthConnection(object): if is_secure: boto.log.debug( - 'establishing HTTPS connection: host=%s, kwargs=%s', - host, http_connection_kwargs) + 'establishing HTTPS connection: host=%s, kwargs=%s', + host, http_connection_kwargs) if self.use_proxy and not self.skip_proxy(host): connection = self.proxy_ssl(host, is_secure and 443 or 80) elif self.https_connection_factory: connection = self.https_connection_factory(host) elif self.https_validate_certificates and HAVE_HTTPS_CONNECTION: connection = https_connection.CertValidatingHTTPSConnection( - host, ca_certs=self.ca_certificates_file, - **http_connection_kwargs) + host, ca_certs=self.ca_certificates_file, + **http_connection_kwargs) else: - connection = http_client.HTTPSConnection(host, - **http_connection_kwargs) + connection = http_client.HTTPSConnection( + host, **http_connection_kwargs) else: boto.log.debug('establishing HTTP connection: kwargs=%s' % - http_connection_kwargs) + http_connection_kwargs) if self.https_connection_factory: # even though the factory says https, this is too handy # to not be able to allow overriding for http also. - connection = self.https_connection_factory(host, - **http_connection_kwargs) + connection = self.https_connection_factory( + host, **http_connection_kwargs) else: - connection = http_client.HTTPConnection(host, - **http_connection_kwargs) + connection = http_client.HTTPConnection( + host, **http_connection_kwargs) if self.debug > 1: connection.set_debuglevel(self.debug) # self.connection must be maintained for backwards-compatibility @@ -822,7 +822,7 @@ class AWSAuthConnection(object): if self.https_validate_certificates and HAVE_HTTPS_CONNECTION: msg = "wrapping ssl socket for proxied connection; " if self.ca_certificates_file: - msg += "CA certificate file=%s" %self.ca_certificates_file + msg += "CA certificate file=%s" % self.ca_certificates_file else: msg += "using system provided SSL certs" boto.log.debug(msg) @@ -836,7 +836,7 @@ class AWSAuthConnection(object): hostname = self.host.split(':', 0)[0] if not https_connection.ValidateCertificateHostname(cert, hostname): raise https_connection.InvalidCertificateException( - hostname, cert, 'hostname mismatch') + hostname, cert, 'hostname mismatch') else: # Fallback for old Python without ssl.wrap_socket if hasattr(http_client, 'ssl'): @@ -1008,8 +1008,8 @@ class AWSAuthConnection(object): 'encountered unretryable %s exception, re-raising' % e.__class__.__name__) raise - boto.log.debug('encountered %s exception, reconnecting' % \ - e.__class__.__name__) + boto.log.debug('encountered %s exception, reconnecting' % + e.__class__.__name__) connection = self.new_http_connection(request.host, request.port, self.is_secure) time.sleep(next_sleep) @@ -1041,8 +1041,7 @@ class AWSAuthConnection(object): headers = {} else: headers = headers.copy() - if (self.host_header and - not boto.utils.find_matching_headers('host', headers)): + if self.host_header and not boto.utils.find_matching_headers('host', headers): headers['host'] = self.host_header host = host or self.host if self.use_proxy: @@ -1085,14 +1084,15 @@ class AWSQueryConnection(AWSAuthConnection): proxy_user=None, proxy_pass=None, host=None, debug=0, https_connection_factory=None, path='/', security_token=None, validate_certs=True, profile_name=None): - super(AWSQueryConnection, self).__init__(host, aws_access_key_id, - aws_secret_access_key, - is_secure, port, proxy, - proxy_port, proxy_user, proxy_pass, - debug, https_connection_factory, path, - security_token=security_token, - validate_certs=validate_certs, - profile_name=profile_name) + super(AWSQueryConnection, self).__init__( + host, aws_access_key_id, + aws_secret_access_key, + is_secure, port, proxy, + proxy_port, proxy_user, proxy_pass, + debug, https_connection_factory, path, + security_token=security_token, + validate_certs=validate_certs, + profile_name=profile_name) def _required_auth_capability(self): return [] diff --git a/boto/contrib/__init__.py b/boto/contrib/__init__.py index 303dbb66..a7e571e2 100644 --- a/boto/contrib/__init__.py +++ b/boto/contrib/__init__.py @@ -14,9 +14,8 @@ # 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. # - diff --git a/boto/contrib/ymlmessage.py b/boto/contrib/ymlmessage.py index 6f3dd20c..ae6aea48 100644 --- a/boto/contrib/ymlmessage.py +++ b/boto/contrib/ymlmessage.py @@ -28,6 +28,7 @@ This module requires the yaml module. from boto.sqs.message import Message import yaml + class YAMLMessage(Message): """ The YAMLMessage class provides a YAML compatible message. Encoding and diff --git a/boto/exception.py b/boto/exception.py index 9baa0999..36c226fa 100644 --- a/boto/exception.py +++ b/boto/exception.py @@ -30,9 +30,10 @@ import xml.sax import boto from boto import handler -from boto.compat import json, six, StandardError +from boto.compat import json, StandardError from boto.resultset import ResultSet + class BotoClientError(StandardError): """ General Boto Client error (error accessing AWS) @@ -112,7 +113,7 @@ class BotoServerError(StandardError): try: h = handler.XmlHandlerWrapper(self, self) h.parseString(self.body) - except (TypeError, xml.sax.SAXParseException) as pe: + except (TypeError, xml.sax.SAXParseException): # What if it's JSON? Let's try that. try: parsed = json.loads(self.body) @@ -209,6 +210,7 @@ class StorageCreateError(BotoServerError): else: return super(StorageCreateError, self).endElement(name, value, connection) + class S3CreateError(StorageCreateError): """ Error creating a bucket or key on S3. @@ -294,15 +296,15 @@ class StorageResponseError(BotoServerError): super(StorageResponseError, self).__init__(status, reason, body) def startElement(self, name, attrs, connection): - return super(StorageResponseError, self).startElement(name, attrs, - connection) + return super(StorageResponseError, self).startElement( + name, attrs, connection) def endElement(self, name, value, connection): if name == 'Resource': self.resource = value else: - return super(StorageResponseError, self).endElement(name, value, - connection) + return super(StorageResponseError, self).endElement( + name, value, connection) def _cleanupParsedProperties(self): super(StorageResponseError, self)._cleanupParsedProperties() @@ -332,8 +334,8 @@ class EC2ResponseError(BotoServerError): self.errors = None self._errorResultSet = [] super(EC2ResponseError, self).__init__(status, reason, body) - self.errors = [ (e.error_code, e.error_message) \ - for e in self._errorResultSet ] + self.errors = [ + (e.error_code, e.error_message) for e in self._errorResultSet] if len(self.errors): self.error_code, self.error_message = self.errors[0] @@ -348,7 +350,7 @@ class EC2ResponseError(BotoServerError): if name == 'RequestID': self.request_id = value else: - return None # don't call subclass here + return None # don't call subclass here def _cleanupParsedProperties(self): super(EC2ResponseError, self)._cleanupParsedProperties() @@ -420,30 +422,35 @@ class SDBResponseError(BotoServerError): """ pass + class AWSConnectionError(BotoClientError): """ General error connecting to Amazon Web Services. """ pass + class StorageDataError(BotoClientError): """ Error receiving data from a storage service. """ pass + class S3DataError(StorageDataError): """ Error receiving data from S3. """ pass + class GSDataError(StorageDataError): """ Error receiving data from GS. """ pass + class InvalidUriError(Exception): """Exception raised when URI is invalid.""" @@ -451,6 +458,7 @@ class InvalidUriError(Exception): super(InvalidUriError, self).__init__(message) self.message = message + class InvalidAclError(Exception): """Exception raised when ACL XML is invalid.""" @@ -458,6 +466,7 @@ class InvalidAclError(Exception): super(InvalidAclError, self).__init__(message) self.message = message + class InvalidCorsError(Exception): """Exception raised when CORS XML is invalid.""" @@ -465,10 +474,12 @@ class InvalidCorsError(Exception): super(InvalidCorsError, self).__init__(message) self.message = message + class NoAuthHandlerFound(Exception): """Is raised when no auth handlers were found ready to authenticate.""" pass + class InvalidLifecycleConfigError(Exception): """Exception raised when GCS lifecycle configuration XML is invalid.""" @@ -476,6 +487,7 @@ class InvalidLifecycleConfigError(Exception): super(InvalidLifecycleConfigError, self).__init__(message) self.message = message + # Enum class for resumable upload failure disposition. class ResumableTransferDisposition(object): # START_OVER means an attempt to resume an existing transfer failed, @@ -500,6 +512,7 @@ class ResumableTransferDisposition(object): # upload ID. ABORT = 'ABORT' + class ResumableUploadException(Exception): """ Exception raised for various resumable upload problems. @@ -516,6 +529,7 @@ class ResumableUploadException(Exception): return 'ResumableUploadException("%s", %s)' % ( self.message, self.disposition) + class ResumableDownloadException(Exception): """ Exception raised for various resumable download problems. @@ -532,6 +546,7 @@ class ResumableDownloadException(Exception): return 'ResumableDownloadException("%s", %s)' % ( self.message, self.disposition) + class TooManyRecordsException(Exception): """ Exception raised when a search of Route53 records returns more diff --git a/boto/handler.py b/boto/handler.py index b079ada6..3b5f0732 100644 --- a/boto/handler.py +++ b/boto/handler.py @@ -14,7 +14,7 @@ # 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. @@ -23,6 +23,7 @@ import xml.sax from boto.compat import StringIO + class XmlHandler(xml.sax.ContentHandler): def __init__(self, root_node, connection): diff --git a/boto/https_connection.py b/boto/https_connection.py index 9222fbde..ddc31a15 100644 --- a/boto/https_connection.py +++ b/boto/https_connection.py @@ -27,111 +27,112 @@ import boto from boto.compat import six, http_client -class InvalidCertificateException(http_client.HTTPException): - """Raised when a certificate is provided with an invalid hostname.""" - def __init__(self, host, cert, reason): - """Constructor. +class InvalidCertificateException(http_client.HTTPException): + """Raised when a certificate is provided with an invalid hostname.""" - Args: - host: The hostname the connection was made to. - cert: The SSL certificate (as a dictionary) the host returned. - """ - http_client.HTTPException.__init__(self) - self.host = host - self.cert = cert - self.reason = reason + def __init__(self, host, cert, reason): + """Constructor. - def __str__(self): - return ('Host %s returned an invalid certificate (%s): %s' % - (self.host, self.reason, self.cert)) + Args: + host: The hostname the connection was made to. + cert: The SSL certificate (as a dictionary) the host returned. + """ + http_client.HTTPException.__init__(self) + self.host = host + self.cert = cert + self.reason = reason -def GetValidHostsForCert(cert): - """Returns a list of valid host globs for an SSL certificate. - - Args: - cert: A dictionary representing an SSL certificate. - Returns: - list: A list of valid host globs. - """ - if 'subjectAltName' in cert: - return [x[1] for x in cert['subjectAltName'] if x[0].lower() == 'dns'] - else: - return [x[0][1] for x in cert['subject'] - if x[0][0].lower() == 'commonname'] + def __str__(self): + return ('Host %s returned an invalid certificate (%s): %s' % + (self.host, self.reason, self.cert)) -def ValidateCertificateHostname(cert, hostname): - """Validates that a given hostname is valid for an SSL certificate. - - Args: - cert: A dictionary representing an SSL certificate. - hostname: The hostname to test. - Returns: - bool: Whether or not the hostname is valid for this certificate. - """ - hosts = GetValidHostsForCert(cert) - boto.log.debug( - "validating server certificate: hostname=%s, certificate hosts=%s", - hostname, hosts) - for host in hosts: - host_re = host.replace('.', '\.').replace('*', '[^.]*') - if re.search('^%s$' % (host_re,), hostname, re.I): - return True - return False +def GetValidHostsForCert(cert): + """Returns a list of valid host globs for an SSL certificate. -class CertValidatingHTTPSConnection(http_client.HTTPConnection): - """An HTTPConnection that connects over SSL and validates certificates.""" + Args: + cert: A dictionary representing an SSL certificate. + Returns: + list: A list of valid host globs. + """ + if 'subjectAltName' in cert: + return [x[1] for x in cert['subjectAltName'] if x[0].lower() == 'dns'] + else: + return [x[0][1] for x in cert['subject'] + if x[0][0].lower() == 'commonname'] - default_port = http_client.HTTPS_PORT - def __init__(self, host, port=default_port, key_file=None, cert_file=None, - ca_certs=None, strict=None, **kwargs): - """Constructor. +def ValidateCertificateHostname(cert, hostname): + """Validates that a given hostname is valid for an SSL certificate. Args: - host: The hostname. Can be in 'host:port' form. - port: The port. Defaults to 443. - key_file: A file containing the client's private key - cert_file: A file containing the client's certificates - ca_certs: A file contianing a set of concatenated certificate authority - certs for validating the server against. - strict: When true, causes BadStatusLine to be raised if the status line - can't be parsed as a valid HTTP/1.0 or 1.1 status line. + cert: A dictionary representing an SSL certificate. + hostname: The hostname to test. + Returns: + bool: Whether or not the hostname is valid for this certificate. """ - if six.PY2: - # Python 3.2 and newer have deprecated and removed the strict - # parameter. Since the params are supported as keyword arguments - # we conditionally add it here. - kwargs['strict'] = strict - - http_client.HTTPConnection.__init__(self, host=host, port=port, **kwargs) - self.key_file = key_file - self.cert_file = cert_file - self.ca_certs = ca_certs - - def connect(self): - "Connect to a host on a given (SSL) port." - if hasattr(self, "timeout"): - sock = socket.create_connection((self.host, self.port), self.timeout) - else: - sock = socket.create_connection((self.host, self.port)) - msg = "wrapping ssl socket; " - if self.ca_certs: - msg += "CA certificate file=%s" %self.ca_certs - else: - msg += "using system provided SSL certs" - boto.log.debug(msg) - self.sock = ssl.wrap_socket(sock, keyfile=self.key_file, - certfile=self.cert_file, - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=self.ca_certs) - cert = self.sock.getpeercert() - hostname = self.host.split(':', 0)[0] - if not ValidateCertificateHostname(cert, hostname): - raise InvalidCertificateException(hostname, - cert, - 'remote hostname "%s" does not match '\ - 'certificate' % hostname) + hosts = GetValidHostsForCert(cert) + boto.log.debug( + "validating server certificate: hostname=%s, certificate hosts=%s", + hostname, hosts) + for host in hosts: + host_re = host.replace('.', '\.').replace('*', '[^.]*') + if re.search('^%s$' % (host_re,), hostname, re.I): + return True + return False +class CertValidatingHTTPSConnection(http_client.HTTPConnection): + """An HTTPConnection that connects over SSL and validates certificates.""" + + default_port = http_client.HTTPS_PORT + + def __init__(self, host, port=default_port, key_file=None, cert_file=None, + ca_certs=None, strict=None, **kwargs): + """Constructor. + + Args: + host: The hostname. Can be in 'host:port' form. + port: The port. Defaults to 443. + key_file: A file containing the client's private key + cert_file: A file containing the client's certificates + ca_certs: A file contianing a set of concatenated certificate authority + certs for validating the server against. + strict: When true, causes BadStatusLine to be raised if the status line + can't be parsed as a valid HTTP/1.0 or 1.1 status line. + """ + if six.PY2: + # Python 3.2 and newer have deprecated and removed the strict + # parameter. Since the params are supported as keyword arguments + # we conditionally add it here. + kwargs['strict'] = strict + + http_client.HTTPConnection.__init__(self, host=host, port=port, **kwargs) + self.key_file = key_file + self.cert_file = cert_file + self.ca_certs = ca_certs + + def connect(self): + "Connect to a host on a given (SSL) port." + if hasattr(self, "timeout"): + sock = socket.create_connection((self.host, self.port), self.timeout) + else: + sock = socket.create_connection((self.host, self.port)) + msg = "wrapping ssl socket; " + if self.ca_certs: + msg += "CA certificate file=%s" % self.ca_certs + else: + msg += "using system provided SSL certs" + boto.log.debug(msg) + self.sock = ssl.wrap_socket(sock, keyfile=self.key_file, + certfile=self.cert_file, + cert_reqs=ssl.CERT_REQUIRED, + ca_certs=self.ca_certs) + cert = self.sock.getpeercert() + hostname = self.host.split(':', 0)[0] + if not ValidateCertificateHostname(cert, hostname): + raise InvalidCertificateException(hostname, + cert, + 'remote hostname "%s" does not match ' + 'certificate' % hostname) diff --git a/boto/jsonresponse.py b/boto/jsonresponse.py index ac3f1b4a..f872b429 100644 --- a/boto/jsonresponse.py +++ b/boto/jsonresponse.py @@ -23,6 +23,7 @@ import xml.sax from boto import utils + class XmlHandler(xml.sax.ContentHandler): def __init__(self, root_node, connection): @@ -52,7 +53,8 @@ class XmlHandler(xml.sax.ContentHandler): if not isinstance(s, bytes): s = s.encode('utf-8') xml.sax.parseString(s, self) - + + class Element(dict): def __init__(self, connection=None, element_name=None, @@ -116,6 +118,7 @@ class Element(dict): elif isinstance(self.parent, ListElement): self.parent.append(value) + class ListElement(list): def __init__(self, connection=None, element_name=None, diff --git a/boto/plugin.py b/boto/plugin.py index f8b592cc..2c2931c9 100644 --- a/boto/plugin.py +++ b/boto/plugin.py @@ -14,7 +14,7 @@ # 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. @@ -26,19 +26,20 @@ Implements plugin related api. To define a new plugin just subclass Plugin, like this. class AuthPlugin(Plugin): - pass + pass Then start creating subclasses of your new plugin. class MyFancyAuth(AuthPlugin): - capability = ['sign', 'vmac'] + capability = ['sign', 'vmac'] The actual interface is duck typed. - """ import glob -import imp, os.path +import imp +import os.path + class Plugin(object): """Base class for all plugins.""" @@ -50,10 +51,11 @@ class Plugin(object): """Returns true if the requested capability is supported by this plugin """ for c in requested_capability: - if not c in cls.capability: + if c not in cls.capability: return False return True + def get_plugin(cls, requested_capability=None): if not requested_capability: requested_capability = [] @@ -63,18 +65,20 @@ def get_plugin(cls, requested_capability=None): result.append(handler) return result + def _import_module(filename): (path, name) = os.path.split(filename) (name, ext) = os.path.splitext(name) (file, filename, data) = imp.find_module(name, [path]) try: - return imp.load_module(name, file, filename, data) + return imp.load_module(name, file, filename, data) finally: - if file: - file.close() + if file: + file.close() + +_plugin_loaded = False -_plugin_loaded = False def load_plugins(config): global _plugin_loaded @@ -87,4 +91,3 @@ def load_plugins(config): directory = config.get('Plugin', 'plugin_directory') for file in glob.glob(os.path.join(directory, '*.py')): _import_module(file) - diff --git a/boto/provider.py b/boto/provider.py index 8e04bff3..0da2f78a 100644 --- a/boto/provider.py +++ b/boto/provider.py @@ -69,7 +69,8 @@ STORAGE_PERMISSIONS_ERROR = 'StoragePermissionsError' STORAGE_RESPONSE_ERROR = 'StorageResponseError' -class ProfileNotFoundError(ValueError): pass +class ProfileNotFoundError(ValueError): + pass class Provider(object): @@ -252,7 +253,7 @@ class Provider(object): # datetime docs. seconds_left = ( (delta.microseconds + (delta.seconds + delta.days * 24 * 3600) - * 10**6) / 10**6) + * 10 ** 6) / 10 ** 6) if seconds_left < (5 * 60): boto.log.debug("Credentials need to be refreshed.") return True @@ -444,6 +445,7 @@ class Provider(object): def supports_chunked_transfer(self): return self.ChunkedTransferSupport[self.name] + # Static utility method for getting default Provider. def get_default(): return Provider('aws') diff --git a/boto/regioninfo.py b/boto/regioninfo.py index 5862f16d..6aeda122 100644 --- a/boto/regioninfo.py +++ b/boto/regioninfo.py @@ -124,7 +124,7 @@ def get_regions(service_name, region_cls=None, connection_cls=None): """ endpoints = load_regions() - if not service_name in endpoints: + if service_name not in endpoints: raise BotoClientError( "Service '%s' not found in endpoints." % service_name ) diff --git a/boto/requestlog.py b/boto/requestlog.py index 5f1c2551..d8009fe7 100644 --- a/boto/requestlog.py +++ b/boto/requestlog.py @@ -1,9 +1,11 @@ - +import sys from datetime import datetime from threading import Thread import Queue from boto.utils import RequestHook +from boto.compat import long_type + class RequestLogger(RequestHook): """ @@ -14,18 +16,16 @@ class RequestLogger(RequestHook): self.request_log_file = open(filename, 'w') self.request_log_queue = Queue.Queue(100) Thread(target=self._request_log_worker).start() - def handle_request_data(self, request, response, error=False): len = 0 if error else response.getheader('Content-Length') now = datetime.now() time = now.strftime('%Y-%m-%d %H:%M:%S') td = (now - request.start_time) - duration = (td.microseconds + long(td.seconds + td.days*24*3600) * 1e6) / 1e6 - + duration = (td.microseconds + long_type(td.seconds + td.days * 24 * 3600) * 1e6) / 1e6 + # write output including timestamp, status code, response time, response size, request action self.request_log_queue.put("'%s', '%s', '%s', '%s', '%s'\n" % (time, response.status, duration, len, request.params['Action'])) - def _request_log_worker(self): while True: @@ -35,5 +35,5 @@ class RequestLogger(RequestHook): self.request_log_file.flush() self.request_log_queue.task_done() except: - import traceback; traceback.print_exc(file=sys.stdout) - + import traceback + traceback.print_exc(file=sys.stdout) diff --git a/boto/resultset.py b/boto/resultset.py index 83052582..189a47a3 100644 --- a/boto/resultset.py +++ b/boto/resultset.py @@ -21,14 +21,15 @@ from boto.s3.user import User + class ResultSet(list): """ The ResultSet is used to pass results back from the Amazon services to the client. It is light wrapper around Python's :py:class:`list` class, - with some additional methods for parsing XML results from AWS. - Because I don't really want any dependencies on external libraries, - I'm using the standard SAX parser that comes with Python. The good news is - that it's quite fast and efficient but it makes some things rather + with some additional methods for parsing XML results from AWS. + Because I don't really want any dependencies on external libraries, + I'm using the standard SAX parser that comes with Python. The good news is + that it's quite fast and efficient but it makes some things rather difficult. You can pass in, as the marker_elem parameter, a list of tuples. @@ -54,7 +55,7 @@ class ResultSet(list): self.next_key_marker = None self.next_upload_id_marker = None self.next_version_id_marker = None - self.next_generation_marker= None + self.next_generation_marker = None self.version_id_marker = None self.is_truncated = False self.next_token = None @@ -132,6 +133,7 @@ class ResultSet(list): else: setattr(self, name, value) + class BooleanResult(object): def __init__(self, marker_elem=None): diff --git a/boto/storage_uri.py b/boto/storage_uri.py index 40fee473..34b7b060 100755 --- a/boto/storage_uri.py +++ b/boto/storage_uri.py @@ -87,8 +87,8 @@ class StorageUri(object): for arg in args: if args[arg]: sys.stderr.write( - 'Warning: %s ignores argument: %s=%s\n' % - (function_name, arg, str(args[arg]))) + 'Warning: %s ignores argument: %s=%s\n' % + (function_name, arg, str(args[arg]))) def connect(self, access_key_id=None, secret_access_key=None, **kwargs): """ @@ -103,7 +103,7 @@ class StorageUri(object): connection_args = dict(self.connection_args or ()) if (hasattr(self, 'suppress_consec_slashes') and - 'suppress_consec_slashes' not in connection_args): + 'suppress_consec_slashes' not in connection_args): connection_args['suppress_consec_slashes'] = ( self.suppress_consec_slashes) connection_args.update(kwargs) @@ -167,7 +167,7 @@ class StorageUri(object): if all_versions: return (v for v in bucket.list_versions( prefix=prefix, delimiter=delimiter, headers=headers) - if not isinstance(v, DeleteMarker)) + if not isinstance(v, DeleteMarker)) else: return bucket.list(prefix=prefix, delimiter=delimiter, headers=headers) @@ -247,7 +247,7 @@ class BucketStorageUri(StorageUri): """ delim = '/' - capabilities = set([]) # A set of additional capabilities. + capabilities = set([]) # A set of additional capabilities. def __init__(self, scheme, bucket_name=None, object_name=None, debug=0, connection_args=None, suppress_consec_slashes=True, @@ -299,37 +299,37 @@ class BucketStorageUri(StorageUri): self._build_uri_strings() def _build_uri_strings(self): - if self.bucket_name and self.object_name: - self.versionless_uri = '%s://%s/%s' % (self.scheme, self.bucket_name, - self.object_name) - if self.generation: - self.version_specific_uri = '%s#%s' % (self.versionless_uri, - self.generation) - elif self.version_id: - self.version_specific_uri = '%s#%s' % ( - self.versionless_uri, self.version_id) - if self.is_version_specific: - self.uri = self.version_specific_uri - else: - self.uri = self.versionless_uri - elif self.bucket_name: - self.uri = ('%s://%s/' % (self.scheme, self.bucket_name)) - else: - self.uri = ('%s://' % self.scheme) + if self.bucket_name and self.object_name: + self.versionless_uri = '%s://%s/%s' % (self.scheme, self.bucket_name, + self.object_name) + if self.generation: + self.version_specific_uri = '%s#%s' % (self.versionless_uri, + self.generation) + elif self.version_id: + self.version_specific_uri = '%s#%s' % ( + self.versionless_uri, self.version_id) + if self.is_version_specific: + self.uri = self.version_specific_uri + else: + self.uri = self.versionless_uri + elif self.bucket_name: + self.uri = ('%s://%s/' % (self.scheme, self.bucket_name)) + else: + self.uri = ('%s://' % self.scheme) def _update_from_key(self, key): - self._update_from_values( - getattr(key, 'version_id', None), - getattr(key, 'generation', None), - getattr(key, 'is_latest', None), - getattr(key, 'md5', None)) + self._update_from_values( + getattr(key, 'version_id', None), + getattr(key, 'generation', None), + getattr(key, 'is_latest', None), + getattr(key, 'md5', None)) def _update_from_values(self, version_id, generation, is_latest, md5): - self.version_id = version_id - self.generation = generation - self.is_latest = is_latest - self._build_uri_strings() - self.md5 = md5 + self.version_id = version_id + self.generation = generation + self.is_latest = is_latest + self._build_uri_strings() + self.md5 = md5 def get_key(self, validate=False, headers=None, version_id=None): self._check_object_uri('get_key') @@ -388,14 +388,14 @@ class BucketStorageUri(StorageUri): is_latest = key.is_latest return BucketStorageUri( - key.provider.get_provider_name(), - bucket_name=key.bucket.name, - object_name=key.name, - debug=self.debug, - suppress_consec_slashes=self.suppress_consec_slashes, - version_id=version_id, - generation=generation, - is_latest=is_latest) + key.provider.get_provider_name(), + bucket_name=key.bucket.name, + object_name=key.name, + debug=self.debug, + suppress_consec_slashes=self.suppress_consec_slashes, + version_id=version_id, + generation=generation, + is_latest=is_latest) def get_acl(self, validate=False, headers=None, version_id=None): """returns a bucket's acl""" @@ -464,8 +464,8 @@ class BucketStorageUri(StorageUri): 'URIs.' % self.scheme) if self.object_name: if recursive: - raise ValueError('add_group_email_grant() on key-ful URI cannot ' - 'specify recursive=True') + raise ValueError('add_group_email_grant() on key-ful URI cannot ' + 'specify recursive=True') key = self.get_key(validate, headers) self.check_response(key, 'key', self.uri) key.add_group_email_grant(permission, email_address, headers) @@ -556,10 +556,10 @@ class BucketStorageUri(StorageUri): # Pass storage_class param only if this is a GCS bucket. (In S3 the # storage class is specified on the key object.) if self.scheme == 'gs': - return conn.create_bucket(self.bucket_name, headers, location, policy, - storage_class) + return conn.create_bucket(self.bucket_name, headers, location, policy, + storage_class) else: - return conn.create_bucket(self.bucket_name, headers, location, policy) + return conn.create_bucket(self.bucket_name, headers, location, policy) def delete_bucket(self, headers=None): self._check_bucket_uri('delete_bucket') @@ -583,27 +583,27 @@ class BucketStorageUri(StorageUri): key_name = key_name or self.object_name or '' bucket = self.get_bucket(validate, headers) if self.generation: - bucket.set_acl( - acl_or_str, key_name, headers, generation=self.generation, - if_generation=if_generation, if_metageneration=if_metageneration) + bucket.set_acl( + acl_or_str, key_name, headers, generation=self.generation, + if_generation=if_generation, if_metageneration=if_metageneration) else: - version_id = version_id or self.version_id - bucket.set_acl(acl_or_str, key_name, headers, version_id) + version_id = version_id or self.version_id + bucket.set_acl(acl_or_str, key_name, headers, version_id) def set_xml_acl(self, xmlstring, key_name='', validate=False, headers=None, - version_id=None, if_generation=None, if_metageneration=None): + version_id=None, if_generation=None, if_metageneration=None): """Sets or updates a bucket's ACL with an XML string.""" self._check_bucket_uri('set_xml_acl') key_name = key_name or self.object_name or '' bucket = self.get_bucket(validate, headers) if self.generation: - bucket.set_xml_acl( - xmlstring, key_name, headers, generation=self.generation, - if_generation=if_generation, if_metageneration=if_metageneration) + bucket.set_xml_acl( + xmlstring, key_name, headers, generation=self.generation, + if_generation=if_generation, if_metageneration=if_metageneration) else: - version_id = version_id or self.version_id - bucket.set_xml_acl(xmlstring, key_name, headers, - version_id=version_id) + version_id = version_id or self.version_id + bucket.set_xml_acl(xmlstring, key_name, headers, + version_id=version_id) def set_def_xml_acl(self, xmlstring, validate=False, headers=None): """Sets or updates a bucket's default object ACL with an XML string.""" @@ -699,14 +699,16 @@ class BucketStorageUri(StorageUri): self._check_object_uri('copy_key') dst_bucket = self.get_bucket(validate=False, headers=headers) if src_generation: - return dst_bucket.copy_key(new_key_name=self.object_name, + return dst_bucket.copy_key( + new_key_name=self.object_name, src_bucket_name=src_bucket_name, src_key_name=src_key_name, metadata=metadata, storage_class=storage_class, preserve_acl=preserve_acl, encrypt_key=encrypt_key, headers=headers, query_args=query_args, src_generation=src_generation) else: - return dst_bucket.copy_key(new_key_name=self.object_name, + return dst_bucket.copy_key( + new_key_name=self.object_name, src_bucket_name=src_bucket_name, src_key_name=src_key_name, metadata=metadata, src_version_id=src_version_id, storage_class=storage_class, preserve_acl=preserve_acl, @@ -766,7 +768,7 @@ class BucketStorageUri(StorageUri): component_keys.append(suri.new_key()) component_keys[-1].generation = suri.generation self.generation = self.new_key().compose( - component_keys, content_type=content_type, headers=headers) + component_keys, content_type=content_type, headers=headers) self._build_uri_strings() return self @@ -786,12 +788,13 @@ class BucketStorageUri(StorageUri): bucket.configure_lifecycle(lifecycle_config, headers) def exists(self, headers=None): - """Returns True if the object exists or False if it doesn't""" - if not self.object_name: - raise InvalidUriError('exists on object-less URI (%s)' % self.uri) - bucket = self.get_bucket() - key = bucket.get_key(self.object_name, headers=headers) - return bool(key) + """Returns True if the object exists or False if it doesn't""" + if not self.object_name: + raise InvalidUriError('exists on object-less URI (%s)' % self.uri) + bucket = self.get_bucket() + key = bucket.get_key(self.object_name, headers=headers) + return bool(key) + class FileStorageUri(StorageUri): """ @@ -881,8 +884,8 @@ class FileStorageUri(StorageUri): self.get_key().close() def exists(self, _headers_not_used=None): - """Returns True if the file exists or False if it doesn't""" - # The _headers_not_used parameter is ignored. It is only there to ensure - # that this method's signature is identical to the exists method on the - # BucketStorageUri class. - return os.path.exists(self.object_name) + """Returns True if the file exists or False if it doesn't""" + # The _headers_not_used parameter is ignored. It is only there to ensure + # that this method's signature is identical to the exists method on the + # BucketStorageUri class. + return os.path.exists(self.object_name) diff --git a/boto/utils.py b/boto/utils.py index dca332fe..0e7e3a79 100644 --- a/boto/utils.py +++ b/boto/utils.py @@ -392,7 +392,7 @@ def get_instance_metadata(version='latest', url='http://169.254.169.254', try: metadata_url = _build_instance_metadata_url(url, version, data) return _get_instance_metadata(metadata_url, num_retries=num_retries, timeout=timeout) - except urllib.error.URLError as e: + except urllib.error.URLError: return None @@ -414,7 +414,7 @@ def get_instance_identity(version='latest', url='http://169.254.169.254', if field: iid[field] = val return iid - except urllib.error.URLError as e: + except urllib.error.URLError: return None @@ -436,6 +436,7 @@ ISO8601_MS = '%Y-%m-%dT%H:%M:%S.%fZ' RFC1123 = '%a, %d %b %Y %H:%M:%S %Z' LOCALE_LOCK = threading.Lock() + @contextmanager def setlocale(name): """ @@ -449,6 +450,7 @@ def setlocale(name): finally: locale.setlocale(locale.LC_ALL, saved) + def get_ts(ts=None): if not ts: ts = time.gmtime() @@ -1038,6 +1040,7 @@ def merge_headers_by_name(name, headers): return ','.join(str(headers[h]) for h in matching_headers if headers[h] is not None) + class RequestHook(object): """ This can be extended and supplied to the connection object diff --git a/scripts/git-release-notes.py b/scripts/git-release-notes.py index 6655b61c..5b6faaaf 100755 --- a/scripts/git-release-notes.py +++ b/scripts/git-release-notes.py @@ -30,7 +30,7 @@ for hunk in revisions.split('~~~')[:-1]: parents = lines[1].split(' ', 1)[1].split(' ') message = ' '.join(lines[2:]) - #print(commit, parents) + # print(commit, parents) if RELEASE.search(message): print('Found release commit, stopping:') diff --git a/scripts/rebuild_endpoints.py b/scripts/rebuild_endpoints.py index f5f12809..37ac37d8 100644 --- a/scripts/rebuild_endpoints.py +++ b/scripts/rebuild_endpoints.py @@ -19,6 +19,7 @@ def fetch_endpoints(): return resp.text + def parse_xml(raw_xml): return pq(raw_xml, parser='xml') diff --git a/tests/__init__.py b/tests/__init__.py index b3fc3a0c..771ca94b 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -14,7 +14,7 @@ # 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. diff --git a/tests/integration/__init__.py b/tests/integration/__init__.py index ed359279..f03a609b 100644 --- a/tests/integration/__init__.py +++ b/tests/integration/__init__.py @@ -24,6 +24,7 @@ Base class to make checking the certs easier. """ + # We subclass from ``object`` instead of ``TestCase`` here so that this doesn't # add noise to the test suite (otherwise these no-ops would run on every # import). @@ -60,4 +61,3 @@ class ServiceCertVerificationTest(object): always succeed (like fetch a list, even if it's empty). """ pass - diff --git a/tests/test.py b/tests/test.py index 7e91af35..692ed4dd 100755 --- a/tests/test.py +++ b/tests/test.py @@ -71,6 +71,7 @@ PY3_WHITELIST = ( 'tests/unit/test_regioninfo.py', ) + def main(whitelist=[]): description = ("Runs boto unit and/or integration tests. " "Arguments will be passed on to nosetests. " @@ -83,7 +84,7 @@ def main(whitelist=[]): known_args, remaining_args = parser.parse_known_args() attribute_args = [] for service_attribute in known_args.service_tests: - attribute_args.extend(['-a', '!notdefault,' +service_attribute]) + attribute_args.extend(['-a', '!notdefault,' + service_attribute]) if not attribute_args: # If the user did not specify any filtering criteria, we at least # will filter out any test tagged 'notdefault'. diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py index 56d386f8..9751decd 100644 --- a/tests/unit/__init__.py +++ b/tests/unit/__init__.py @@ -1,6 +1,7 @@ from boto.compat import http_client from tests.compat import mock, unittest + class AWSMockServiceTestCase(unittest.TestCase): """Base class for mocking aws services.""" # This param is used by the unittest module to display a full @@ -46,6 +47,7 @@ class AWSMockServiceTestCase(unittest.TestCase): response.getheaders.return_value = header response.msg = dict(header) + def overwrite_header(arg, default=None): header_dict = dict(header) if arg in header_dict: diff --git a/tests/unit/test_connection.py b/tests/unit/test_connection.py index 97514361..432e44e9 100644 --- a/tests/unit/test_connection.py +++ b/tests/unit/test_connection.py @@ -72,6 +72,7 @@ class MockAWSService(AWSQueryConnection): """ APIVersion = '2012-01-01' + def _required_auth_capability(self): return ['sign-v2'] @@ -95,6 +96,7 @@ class MockAWSService(AWSQueryConnection): validate_certs=validate_certs, profile_name=profile_name) + class TestAWSAuthConnection(unittest.TestCase): def test_get_path(self): conn = AWSAuthConnection( @@ -182,8 +184,9 @@ class TestAWSAuthConnection(unittest.TestCase): 'testhost', aws_access_key_id='access_key', aws_secret_access_key='secret') - request = conn.build_base_http_request(method='POST', path='/', - auth_path=None, params=None, headers=None, data='', host=None) + request = conn.build_base_http_request( + method='POST', path='/', auth_path=None, params=None, headers=None, + data='', host=None) conn.set_host_header(request) self.assertEqual(request.headers['Host'], 'testhost') @@ -193,15 +196,17 @@ class TestAWSAuthConnection(unittest.TestCase): aws_access_key_id='access_key', aws_secret_access_key='secret', port=8773) - request = conn.build_base_http_request(method='POST', path='/', - auth_path=None, params=None, headers=None, data='', host=None) + request = conn.build_base_http_request( + method='POST', path='/', auth_path=None, params=None, headers=None, + data='', host=None) conn.set_host_header(request) self.assertEqual(request.headers['Host'], 'testhost:8773') + class V4AuthConnection(AWSAuthConnection): def __init__(self, host, aws_access_key_id, aws_secret_access_key, port=443): - AWSAuthConnection.__init__(self, host, aws_access_key_id, - aws_secret_access_key, port=port) + AWSAuthConnection.__init__( + self, host, aws_access_key_id, aws_secret_access_key, port=port) def _required_auth_capability(self): return ['hmac-v4'] @@ -209,15 +214,17 @@ class V4AuthConnection(AWSAuthConnection): class TestAWSQueryConnection(unittest.TestCase): def setUp(self): - self.region = RegionInfo(name='cc-zone-1', - endpoint='mockservice.cc-zone-1.amazonaws.com', - connection_cls=MockAWSService) + self.region = RegionInfo( + name='cc-zone-1', + endpoint='mockservice.cc-zone-1.amazonaws.com', + connection_cls=MockAWSService) HTTPretty.enable() def tearDown(self): HTTPretty.disable() + class TestAWSQueryConnectionSimple(TestAWSQueryConnection): def test_query_connection_basis(self): HTTPretty.register_uri(HTTPretty.POST, @@ -263,7 +270,7 @@ class TestAWSQueryConnectionSimple(TestAWSQueryConnection): aws_secret_access_key='secret', proxy="NON_EXISTENT_HOSTNAME", proxy_port="3128", - is_secure = False) + is_secure=False) resp = conn.make_request('myCmd', {'par1': 'foo', 'par2': 'baz'}, @@ -453,7 +460,7 @@ class TestAWSQueryStatus(TestAWSQueryConnection): content_type='text/xml') conn = self.region.connect(aws_access_key_id='access_key', - aws_secret_access_key='secret') + aws_secret_access_key='secret') with self.assertRaises(BotoServerError): resp = conn.get_status('getStatus', {'par1': 'foo', 'par2': 'baz'}, diff --git a/tests/unit/test_exception.py b/tests/unit/test_exception.py index a14f0dca..d9a2bdd3 100644 --- a/tests/unit/test_exception.py +++ b/tests/unit/test_exception.py @@ -4,6 +4,7 @@ from boto.exception import BotoServerError, S3CreateError, JSONResponseError from httpretty import HTTPretty, httprettified + class TestBotoServerError(unittest.TestCase): def test_botoservererror_basics(self): @@ -45,7 +46,8 @@ class TestBotoServerError(unittest.TestCase): e73bb2bb-63e3-9cdc-f220-6332de66dbbe """ bse = BotoServerError('403', 'Forbidden', body=xml) - self.assertEqual(bse.error_message, + self.assertEqual( + bse.error_message, 'Session does not have permission to perform (sdb:CreateDomain) on ' 'resource (arn:aws:sdb:us-east-1:xxxxxxx:domain/test_domain). ' 'Contact account owner.') @@ -84,18 +86,19 @@ class TestBotoServerError(unittest.TestCase): self.assertEqual(s3ce.error_code, 'BucketAlreadyOwnedByYou') self.assertEqual(s3ce.status, '409') self.assertEqual(s3ce.reason, 'Conflict') - self.assertEqual(s3ce.error_message, - 'Your previous request to create the named bucket succeeded ' - 'and you already own it.') + self.assertEqual( + s3ce.error_message, + 'Your previous request to create the named bucket succeeded ' + 'and you already own it.') self.assertEqual(s3ce.error_message, s3ce.message) self.assertEqual(s3ce.request_id, 'FF8B86A32CC3FE4F') def test_message_json_response_error(self): # This test comes from https://forums.aws.amazon.com/thread.jspa?messageID=374936 body = { - '__type': 'com.amazon.coral.validate#ValidationException', - 'message': 'The attempted filter operation is not supported ' - 'for the provided filter argument count'} + '__type': 'com.amazon.coral.validate#ValidationException', + 'message': 'The attempted filter operation is not supported ' + 'for the provided filter argument count'} jre = JSONResponseError('400', 'Bad Request', body=body) diff --git a/tests/unit/test_regioninfo.py b/tests/unit/test_regioninfo.py index 0f492788..c46614d4 100644 --- a/tests/unit/test_regioninfo.py +++ b/tests/unit/test_regioninfo.py @@ -23,8 +23,6 @@ import os from tests.unit import unittest import boto -from boto.compat import json -from boto.exception import BotoServerError from boto.regioninfo import RegionInfo, load_endpoint_json, merge_endpoints from boto.regioninfo import load_regions, get_regions -- cgit v1.2.1