← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:py3-fakeswift into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:py3-fakeswift into launchpad:master.

Commit message:
Make the fake Swift fixture work on Python 3

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/386483
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:py3-fakeswift into launchpad:master.
diff --git a/lib/lp/testing/swift/fakeswift.py b/lib/lp/testing/swift/fakeswift.py
index c90dc0d..e9eb5b7 100644
--- a/lib/lp/testing/swift/fakeswift.py
+++ b/lib/lp/testing/swift/fakeswift.py
@@ -110,27 +110,29 @@ class FakeKeystone(resource.Resource):
         """Validate provided credentials and return service catalog."""
         if "application/json" not in request.getHeader('content-type'):
             request.setResponseCode(http.BAD_REQUEST)
-            return ""
-        credentials = json.load(request.content)
+            return b""
+        # XXX cjwatson 2020-06-15: Python 3.5 doesn't allow this to be a
+        # binary file; 3.6 does.
+        credentials = json.loads(request.content.read().decode("UTF-8"))
         if not "auth" in credentials:
             request.setResponseCode(http.FORBIDDEN)
-            return ""
+            return b""
         if ((not "tenantName" in credentials["auth"] or
              not "passwordCredentials" in credentials["auth"])):
             request.setResponseCode(http.FORBIDDEN)
-            return ""
+            return b""
         tenant_name = credentials["auth"]["tenantName"]
         pw_creds = credentials["auth"]["passwordCredentials"]
         username, password = pw_creds.get("username"), pw_creds.get("password")
         if not tenant_name in self.root.tenants:
             request.setResponseCode(http.FORBIDDEN)
-            return ""
+            return b""
         if not username in self.users:
             request.setResponseCode(http.FORBIDDEN)
-            return ""
+            return b""
         if password != DEFAULT_PASSWORD:
             request.setResponseCode(http.FORBIDDEN)
-            return ""
+            return b""
 
         self.ensureValidToken(tenant_name)
 
@@ -142,7 +144,7 @@ class FakeKeystone(resource.Resource):
                 "token": self.getValidToken(tenant_name),
                 "user": self.getUser(username),
                 }
-            })
+            }).encode("UTF-8")
 
 
 def parse_range_header(range):
@@ -175,7 +177,7 @@ class EmptyPage(resource.Resource):
     """Return an empty document."""
     isLeaf = True
 
-    def __init__(self, retcode=http.OK, headers=None, body=""):
+    def __init__(self, retcode=http.OK, headers=None, body=b""):
         resource.Resource.__init__(self)
         self._retcode = retcode
         self._headers = headers
@@ -252,9 +254,9 @@ class SwiftObject(resource.Resource):
             request.setHeader("ETag", self._etag)
         range = request.getHeader("Range")
         size = len(self.contents)
-        if request.method == 'HEAD':
+        if request.method == b'HEAD':
             request.setHeader("Content-Length", str(size))
-            return ""
+            return b""
         if range:
             ranges = parse_range_header(range)
             length = 0
@@ -263,7 +265,7 @@ class SwiftObject(resource.Resource):
                 if begin is None:
                     request.setResponseCode(
                         http.REQUESTED_RANGE_NOT_SATISFIABLE)
-                    return ''
+                    return b''
                 if not end:
                     end = size
                 elif end < size:
@@ -273,7 +275,7 @@ class SwiftObject(resource.Resource):
                         http.REQUESTED_RANGE_NOT_SATISFIABLE)
                     request.setHeader(
                         'content-range', 'bytes */%d' % size)
-                    return ''
+                    return b''
                 else:
                     request.setHeader(
                         'content-range',
@@ -285,7 +287,7 @@ class SwiftObject(resource.Resource):
             else:
                 # multiple ranges should be returned in a multipart response
                 request.setResponseCode(http.REQUESTED_RANGE_NOT_SATISFIABLE)
-                return ''
+                return b''
 
         else:
             request.setHeader("Content-Length", str(size))
@@ -318,7 +320,7 @@ class SwiftObject(resource.Resource):
         request.setHeader("ETag", self.get_etag())
         logger.debug("created object container=%s name=%s size=%d" % (
             self.container, self.name, len(data)))
-        return ""
+        return b""
 
 
 class SwiftContainer(resource.Resource):
@@ -351,18 +353,19 @@ class SwiftContainer(resource.Resource):
         if name and request.postpath:
             name = os.path.join(*((name,)+tuple(request.postpath)))
         assert (name), "Wrong call stack for name='%s'" % (name,)
-        if request.method == "PUT":
+        if request.method == b"PUT":
             child = SwiftObject(self, name)
-        elif request.method in ("GET", "HEAD") :
+        elif request.method in (b"GET", b"HEAD"):
             child = self.container_children.get(name, None)
-        elif request.method == "DELETE":
+        elif request.method == b"DELETE":
             child = self.container_children.get(name, None)
             if child is None: # delete unknown object
                 return EmptyPage(http.NO_CONTENT)
             del self.container_children[name]
             return EmptyPage(http.NO_CONTENT)
         else:
-            logger.error("UNHANDLED request method %s" % request.method)
+            logger.error(
+                "UNHANDLED request method %s" % request.method.decode("UTF-8"))
             return EmptyPage(http.METHOD_NOT_ALLOWED)
         if child is None:
             return EmptyPage(http.NOT_FOUND)
@@ -370,25 +373,25 @@ class SwiftContainer(resource.Resource):
 
     def render_GET(self, request):
         """Return list of keys in response to GET on container."""
-        if request.args.get('format', [])[0] != "json":
+        if request.args.get(b'format', [])[0] != b"json":
             raise NotImplementedError()
 
         results = []
-        marker = request.args.get('marker', [None])[0]
-        end_marker = request.args.get('end_marker', [None])[0]
-        prefix = request.args.get('prefix', [None])[0]
-        format_ = request.args.get('format', [None])[0]
-        delimiter = request.args.get('delimiter', None)
-        path = request.args.get('path', None)
-
-        if format_ != 'json' or delimiter or path:
+        marker = request.args.get(b'marker', [None])[0]
+        end_marker = request.args.get(b'end_marker', [None])[0]
+        prefix = request.args.get(b'prefix', [None])[0]
+        format_ = request.args.get(b'format', [None])[0]
+        delimiter = request.args.get(b'delimiter', None)
+        path = request.args.get(b'path', None)
+
+        if format_ != b'json' or delimiter or path:
             raise NotImplementedError()
 
         # According to the docs, limit will be 10000 if no query
         # parameters are passed. However, as we require at least the
         # 'format' query parameter above, the default is always
         # unlimited.
-        limit = int(request.args.get('limit', [sys.maxint])[0])
+        limit = int(request.args.get(b'limit', [2 ** 31 - 1])[0])
 
         results = []
         for name, child in sorted(self.iter_children()):
@@ -407,14 +410,14 @@ class SwiftContainer(resource.Resource):
                 '%Y-%m-%dT%H:%M:%S.%f')
 
             results.append({
-                'name': name,
+                'name': name.decode('UTF-8'),
                 'bytes': child.get_size(),
                 'hash': child._etag,
                 'content-type': child.content_type,
                 'last_modified': mod_time,
                 })
 
-        return json.dumps(results)
+        return json.dumps(results).encode('UTF-8')
 
 
 class FakeContent(io.IOBase):
@@ -461,7 +464,7 @@ class SizeContainer(SwiftContainer):
     def getChild(self, name, request):
         """Get the next object down the chain."""
         try:
-            fake = FakeContent("0", int(name))
+            fake = FakeContent(b"0", int(name))
             o = SwiftObject(self, name, fake, "text/plain", fake.hexdigest())
             return o
         except ValueError:
@@ -475,7 +478,7 @@ class FakeSwift(resource.Resource):
         resource.Resource.__init__(self)
         self.root = root
         self.containers = {
-            "size": SizeContainer("size", DEFAULT_TENANT_NAME),
+            b"size": SizeContainer(b"size", DEFAULT_TENANT_NAME),
             }
 
     def addContainer(self, name):
@@ -492,24 +495,24 @@ class FakeSwift(resource.Resource):
 
         # if we operate on a key, pass control
         if (((request.postpath and request.postpath[0]) or
-             (not request.postpath and request.method == "GET"))):
+             (not request.postpath and request.method == b"GET"))):
             if container is None:
                 # container does not exist, yet we attempt operation on
                 # an object from that container
                 return EmptyPage(http.NOT_FOUND)
             return container
 
-        if request.method == "HEAD":
+        if request.method == b"HEAD":
             if container is None:
                 return EmptyPage(http.NOT_FOUND)
             return EmptyPage(http.NO_CONTENT)
 
-        if request.method == "PUT":
+        if request.method == b"PUT":
             if container is None:
                 container = self.addContainer(name)
             return EmptyPage()
 
-        if request.method == "DELETE":
+        if request.method == b"DELETE":
             if container is None:  # delete unknown object
                 return EmptyPage(http.NO_CONTENT)
             del self.containers[name]
diff --git a/lib/lp/testing/swift/tests/test_fixture.py b/lib/lp/testing/swift/tests/test_fixture.py
index e3a5a31..234a80f 100644
--- a/lib/lp/testing/swift/tests/test_fixture.py
+++ b/lib/lp/testing/swift/tests/test_fixture.py
@@ -37,8 +37,8 @@ class TestSwiftFixture(TestCase):
 
     def makeSampleObject(self, client, contents, content_type=None):
         """Create a new container and a new sample object within it."""
-        cname = self.factory.getUniqueString()
-        oname = self.factory.getUniqueString()
+        cname = self.factory.getUniqueUnicode()
+        oname = self.factory.getUniqueUnicode()
         client.put_container(cname)
         client.put_object(cname, oname, contents, content_type=content_type)
         return cname, oname
@@ -47,13 +47,13 @@ class TestSwiftFixture(TestCase):
         client = self.swift_fixture.connect()
         size = 30
         headers, body = client.get_object("size", str(size))
-        self.assertEqual("0" * size, body)
+        self.assertEqual(b"0" * size, body)
         self.assertEqual(str(size), headers["content-length"])
         self.assertEqual("text/plain", headers["content-type"])
 
     def test_get_404(self):
         client = self.swift_fixture.connect()
-        cname = self.factory.getUniqueString()
+        cname = self.factory.getUniqueUnicode()
         client.put_container(cname)
         exc = self.assertRaises(
             swiftclient.ClientException,
@@ -72,7 +72,7 @@ class TestSwiftFixture(TestCase):
 
     def test_put(self):
         client = self.swift_fixture.connect()
-        message = "Hello World!"
+        message = b"Hello World!"
         cname, oname = self.makeSampleObject(client, message, "text/something")
         for x in range(1, 10):
             headers, body = client.get_object(cname, oname)
@@ -86,7 +86,7 @@ class TestSwiftFixture(TestCase):
         # Basic container listing.
         start = datetime.utcnow().replace(microsecond=0)
         client = self.swift_fixture.connect()
-        message = "42"
+        message = b"42"
         cname, oname = self.makeSampleObject(client, message, "text/something")
         client.put_object(cname, oname + ".2", message)
 
@@ -105,7 +105,7 @@ class TestSwiftFixture(TestCase):
     def test_get_container_marker(self):
         # Container listing supports the marker parameter.
         client = self.swift_fixture.connect()
-        message = "Hello"
+        message = b"Hello"
         cname, oname = self.makeSampleObject(client, message, "text/something")
         oname2 = oname + ".2"
         oname3 = oname + ".3"
@@ -121,7 +121,7 @@ class TestSwiftFixture(TestCase):
     def test_get_container_end_marker(self):
         # Container listing supports the end_marker parameter.
         client = self.swift_fixture.connect()
-        message = "Hello"
+        message = b"Hello"
         cname, oname = self.makeSampleObject(client, message, "text/something")
         oname2 = oname + ".2"
         oname3 = oname + ".3"
@@ -137,7 +137,7 @@ class TestSwiftFixture(TestCase):
     def test_get_container_limit(self):
         # Container listing supports the limit parameter.
         client = self.swift_fixture.connect()
-        message = "Hello"
+        message = b"Hello"
         cname, oname = self.makeSampleObject(client, message, "text/something")
         oname2 = oname + ".2"
         oname3 = oname + ".3"
@@ -152,7 +152,7 @@ class TestSwiftFixture(TestCase):
 
     def test_get_container_prefix(self):
         client = self.swift_fixture.connect()
-        message = "Hello"
+        message = b"Hello"
         cname, oname = self.makeSampleObject(client, message, "text/something")
         oname2 = "different"
         oname3 = oname + ".3"
@@ -167,7 +167,7 @@ class TestSwiftFixture(TestCase):
 
     def test_get_container_full_listing(self):
         client = self.swift_fixture.connect()
-        message = "42"
+        message = b"42"
         cname, oname = self.makeSampleObject(client, message, "text/something")
 
         _, container = client.get_container(cname, full_listing=True)
@@ -194,7 +194,7 @@ class TestSwiftFixture(TestCase):
         # Things work fine when the Swift server is up.
         self.swift_fixture.startup()
         headers, body = client.get_object("size", str(size))
-        self.assertEqual(body, "0" * size)
+        self.assertEqual(body, b"0" * size)
 
         # But if the Swift server goes away again, we end up with
         # different failures since the connection has already
@@ -214,7 +214,7 @@ class TestSwiftFixture(TestCase):
         # But fresh connections are fine.
         client = self.swift_fixture.connect()
         headers, body = client.get_object("size", str(size))
-        self.assertEqual(body, "0" * size)
+        self.assertEqual(body, b"0" * size)
 
     def test_env(self):
         self.assertEqual(