Browse Source

Merge "Make QuotaImageTagsProxy deep-copyable"

Zuul 3 months ago
parent
commit
fb2c2e7de1
2 changed files with 26 additions and 15 deletions
  1. 9
    8
      glance/quota/__init__.py
  2. 17
    7
      glance/tests/unit/test_quota.py

+ 9
- 8
glance/quota/__init__.py View File

@@ -165,14 +165,15 @@ class QuotaImageTagsProxy(object):
165 165
         return self.tags.__len__(*args, **kwargs)
166 166
 
167 167
     def __getattr__(self, name):
168
-        # Use TypeError here, not AttributeError, as the latter is how we
169
-        # know a tag is not present. TypeError says "this object is not
170
-        # what it claims to be".
171
-        try:
172
-            tags = self.__getattribute__('tags')
173
-        except AttributeError:
174
-            raise TypeError('QuotaImageTagsProxy has no tags.')
175
-        return getattr(tags, name)
168
+        # Protect against deepcopy, which calls getattr. __getattr__
169
+        # is only called when an attribute is not "normal", so when
170
+        # self.tags is called, this is not.
171
+        if name == 'tags':
172
+            try:
173
+                return self.__getattribute__('tags')
174
+            except AttributeError:
175
+                return None
176
+        return getattr(self.tags, name)
176 177
 
177 178
 
178 179
 class ImageMemberFactoryProxy(glance.domain.proxy.ImageMembershipFactory):

+ 17
- 7
glance/tests/unit/test_quota.py View File

@@ -12,6 +12,7 @@
12 12
 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 13
 #    License for the specific language governing permissions and limitations
14 14
 #    under the License.
15
+import copy
15 16
 import uuid
16 17
 
17 18
 import mock
@@ -596,15 +597,24 @@ class TestQuotaImageTagsProxy(test_utils.BaseTestCase):
596 597
             items.remove(item)
597 598
         self.assertEqual(0, len(items))
598 599
 
599
-    def test_tags_attr_exception(self):
600
+    def test_tags_attr_no_loop(self):
600 601
         proxy = glance.quota.QuotaImageTagsProxy(None)
601
-        self.assertRaises(AttributeError, lambda: proxy.foo)
602
-
603
-        # Remove tags to cause the object to be broken. If tags
604
-        # is not there and we weren't raising TypeError, we'd
605
-        # get an infinite loop when calling 'proxy.foo'.
602
+        self.assertEqual(set([]), proxy.tags)
603
+
604
+    def test_tags_deepcopy(self):
605
+        proxy = glance.quota.QuotaImageTagsProxy(set(['a', 'b']))
606
+        proxy_copy = copy.deepcopy(proxy)
607
+        self.assertEqual(set(['a', 'b']), proxy_copy.tags)
608
+        self.assertIn('a', proxy_copy)
609
+        # remove is a found via __getattr__
610
+        proxy_copy.remove('a')
611
+        self.assertNotIn('a', proxy_copy)
612
+
613
+    def test_tags_delete(self):
614
+        proxy = glance.quota.QuotaImageTagsProxy(set(['a', 'b']))
615
+        self.assertEqual(set(['a', 'b']), proxy.tags)
606 616
         del proxy.tags
607
-        self.assertRaises(TypeError, lambda: proxy.foo)
617
+        self.assertIsNone(proxy.tags)
608 618
 
609 619
 
610 620
 class TestImageMemberQuotas(test_utils.BaseTestCase):

Loading…
Cancel
Save