From b7719486d6bb339ea066b5b4a980c87d7198016a Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Fri, 23 Jan 2015 01:46:13 -0600 Subject: [PATCH] Fix RuntimeError on Python 3 while listing objects The Object class has two properties which confuse attribute setting when receiving body values which directly correspond to the *name* of the attribute on the class, but not to the expected *prop*. Receiving "last_modified" in the body causes both "last_modified" and it's expected prop name, "last-modified", to be set, thus changing the size of the attrs dictionary inside of Resource.__init__. This change also slightly modifies test_head, which was operating in a slightly odd way in the first place, but turned into a failing test because it wasn't passing through the proper channels to set its attributes because we were side-stepping it.. Change-Id: I987c85fce8d8f6a97ca92e0482c8ff997d848752 Closes-bug: 1414089 --- openstack/object_store/v1/obj.py | 4 ++-- openstack/tests/object_store/v1/test_obj.py | 25 ++++++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/openstack/object_store/v1/obj.py b/openstack/object_store/v1/obj.py index 2d1417e76..ca720cff1 100644 --- a/openstack/object_store/v1/obj.py +++ b/openstack/object_store/v1/obj.py @@ -84,12 +84,12 @@ class Object(resource.Resource): #: the object, in bytes. content_length = resource.prop("content-length") #: The MIME type of the object. - content_type = resource.prop("content-type") + content_type = resource.prop("content_type", alias="content-type") #: The type of ranges that the object accepts. accept_ranges = resource.prop("accept-ranges") #: The date and time that the object was created or the last #: time that the metadata was changed. - last_modified = resource.prop("last-modified") + last_modified = resource.prop("last_modified", alias="last-modified") #: For objects smaller than 5 GB, this value is the MD5 checksum #: of the object content. The value is not quoted. #: For manifest objects, this value is the MD5 checksum of the diff --git a/openstack/tests/object_store/v1/test_obj.py b/openstack/tests/object_store/v1/test_obj.py index 89e81fcb4..ee0ff4648 100644 --- a/openstack/tests/object_store/v1/test_obj.py +++ b/openstack/tests/object_store/v1/test_obj.py @@ -20,12 +20,23 @@ from openstack.object_store.v1 import obj CONTAINER_NAME = "mycontainer" OBJECT_NAME = "myobject" +# Object can receive both last-modified in headers and last_modified in +# the body. However, originally, only last-modified was handled as an +# expected prop but it was named last_modified. Under Python 3, creating +# an Object with the body value last_modified causes the _attrs dictionary +# size to change while iterating over its values as we have an attribute +# called `last_modified` and we attempt to grow an additional attribute +# called `last-modified`, which is the "name" of `last_modified`. +# The same is true of content_type and content-type, or any prop +# attribute which would follow the same pattern. +# This example should represent the body values returned by a GET, so the keys +# must be underscores. OBJ_EXAMPLE = { "hash": "243f87b91224d85722564a80fd3cb1f1", - "last-modified": "2014-07-13T18:41:03.319240", + "last_modified": "2014-07-13T18:41:03.319240", "bytes": 252466, "name": OBJECT_NAME, - "content-type": "application/octet-stream" + "content_type": "application/octet-stream" } HEAD_EXAMPLE = { @@ -72,15 +83,7 @@ class TestObject(testtools.TestCase): self.assertEqual(CONTAINER_NAME, sot.container) def test_head(self): - sot = obj.Object.existing(**OBJ_EXAMPLE) - - # Update object with HEAD data - sot._attrs.update(HEAD_EXAMPLE) - - # Attributes from creation - self.assertEqual(OBJ_EXAMPLE['name'], sot.name) - self.assertEqual(OBJ_EXAMPLE['hash'], sot.hash) - self.assertEqual(OBJ_EXAMPLE['bytes'], sot.bytes) + sot = obj.Object.existing(**HEAD_EXAMPLE) # Attributes from header self.assertEqual(HEAD_EXAMPLE['container'], sot.container)