Support Python 3 using six

This commit is contained in:
David Baumgold 2014-08-26 13:25:50 -04:00
commit eedb4c4b73
67 changed files with 455 additions and 255 deletions

View file

@ -5,6 +5,8 @@ import datetime
import hashlib
import copy
import itertools
import codecs
import six
from moto.core import BaseBackend
from moto.core.utils import iso_8601_datetime, rfc_1123_datetime
@ -59,7 +61,11 @@ class FakeKey(object):
def etag(self):
if self._etag is None:
value_md5 = hashlib.md5()
value_md5.update(bytes(self.value))
if isinstance(self.value, six.text_type):
value = self.value.encode("utf-8")
else:
value = self.value
value_md5.update(value)
self._etag = value_md5.hexdigest()
return '"{0}"'.format(self._etag)
@ -112,9 +118,11 @@ class FakeMultipart(object):
def __init__(self, key_name):
self.key_name = key_name
self.parts = {}
self.id = base64.b64encode(os.urandom(UPLOAD_ID_BYTES)).replace('=', '').replace('+', '')
rand_b64 = base64.b64encode(os.urandom(UPLOAD_ID_BYTES))
self.id = rand_b64.decode('utf-8').replace('=', '').replace('+', '')
def complete(self):
decode_hex = codecs.getdecoder("hex_codec")
total = bytearray()
md5s = bytearray()
last_part_name = len(self.list_parts())
@ -122,7 +130,8 @@ class FakeMultipart(object):
for part in self.list_parts():
if part.name != last_part_name and len(part.value) < UPLOAD_PART_MIN_SIZE:
return None, None
md5s.extend(part.etag.replace('"', '').decode('hex'))
part_etag = part.etag.replace('"', '')
md5s.extend(decode_hex(part_etag)[0])
total.extend(part.value)
etag = hashlib.md5()
@ -296,7 +305,7 @@ class S3Backend(BaseBackend):
key_results = set()
folder_results = set()
if prefix:
for key_name, key in bucket.keys.iteritems():
for key_name, key in bucket.keys.items():
if key_name.startswith(prefix):
key_without_prefix = key_name.replace(prefix, "", 1)
if delimiter and delimiter in key_without_prefix:
@ -306,7 +315,7 @@ class S3Backend(BaseBackend):
else:
key_results.add(key)
else:
for key_name, key in bucket.keys.iteritems():
for key_name, key in bucket.keys.items():
if delimiter and delimiter in key_name:
# If delimiter, we need to split out folder_results
folder_results.add(key_name.split(delimiter)[0])

View file

@ -1,5 +1,6 @@
from __future__ import unicode_literals
from urlparse import parse_qs, urlparse
import six
from six.moves.urllib.parse import parse_qs, urlparse
import re
from jinja2 import Template
@ -32,7 +33,7 @@ class ResponseObject(object):
except MissingBucket:
return 404, headers, ""
if isinstance(response, basestring):
if isinstance(response, six.string_types):
return 200, headers, response
else:
status_code, headers, response_content = response
@ -74,7 +75,7 @@ class ResponseObject(object):
for unsup in ('delimiter', 'prefix', 'max-uploads'):
if unsup in querystring:
raise NotImplementedError("Listing multipart uploads with {} has not been implemented yet.".format(unsup))
multiparts = list(self.backend.get_all_multiparts(bucket_name).itervalues())
multiparts = list(self.backend.get_all_multiparts(bucket_name).values())
template = Template(S3_ALL_MULTIPARTS)
return 200, headers, template.render(
bucket_name=bucket_name,
@ -129,7 +130,7 @@ class ResponseObject(object):
def _bucket_response_put(self, request, bucket_name, querystring, headers):
if 'versioning' in querystring:
ver = re.search('<Status>([A-Za-z]+)</Status>', request.body)
ver = re.search('<Status>([A-Za-z]+)</Status>', request.body.decode('utf-8'))
if ver:
self.backend.set_bucket_versioning(bucket_name, ver.group(1))
template = Template(S3_BUCKET_VERSIONING)
@ -172,7 +173,7 @@ class ResponseObject(object):
else:
#HTTPretty, build new form object
form = {}
for kv in request.body.split('&'):
for kv in request.body.decode('utf-8').split('&'):
k, v = kv.split('=')
form[k] = v
@ -198,7 +199,7 @@ class ResponseObject(object):
def _bucket_response_delete_keys(self, request, bucket_name, headers):
template = Template(S3_DELETE_KEYS_RESPONSE)
keys = minidom.parseString(request.body).getElementsByTagName('Key')
keys = minidom.parseString(request.body.decode('utf-8')).getElementsByTagName('Key')
deleted_names = []
error_names = []
@ -218,7 +219,7 @@ class ResponseObject(object):
except MissingBucket:
return 404, headers, ""
if isinstance(response, basestring):
if isinstance(response, six.string_types):
return 200, headers, response
else:
status_code, headers, response_content = response
@ -229,7 +230,7 @@ class ResponseObject(object):
if replace is True:
key.clear_metadata()
for header in request.headers:
if isinstance(header, basestring):
if isinstance(header, six.string_types):
result = meta_regex.match(header)
if result:
meta_key = result.group(0).lower()
@ -359,7 +360,7 @@ class ResponseObject(object):
return 204, headers, template.render(bucket=removed_key)
def _key_response_post(self, body, parsed_url, bucket_name, query, key_name, headers):
if body == '' and parsed_url.query == 'uploads':
if body == b'' and parsed_url.query == 'uploads':
multipart = self.backend.initiate_multipart(bucket_name, key_name)
template = Template(S3_MULTIPART_INITIATE_RESPONSE)
response = template.render(

View file

@ -1,14 +1,13 @@
from __future__ import unicode_literals
import re
import sys
import urllib2
import urlparse
from six.moves.urllib.parse import urlparse, unquote
bucket_name_regex = re.compile("(.+).s3.amazonaws.com")
def bucket_name_from_url(url):
domain = urlparse.urlparse(url).netloc
domain = urlparse(url).netloc
if domain.startswith('www.'):
domain = domain[4:]
@ -26,7 +25,7 @@ def bucket_name_from_url(url):
def clean_key_name(key_name):
return urllib2.unquote(key_name)
return unquote(key_name)
class _VersionedKeyStore(dict):