]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Changed DictModel to dict with attribute access
authorYves-Gwenael Bourhis <yves-gwenael.bourhis@cloudwatt.com>
Thu, 2 Jan 2014 14:19:17 +0000 (15:19 +0100)
committerYves-Gwenael Bourhis <yves-gwenael.bourhis@cloudwatt.com>
Wed, 4 Jun 2014 09:44:34 +0000 (11:44 +0200)
DictModel is a dict where keys are accessible via attribute access.
The old version was an object with attributes created from dict keys and many
attributes where accessible only via getattr because they did not have a valid
python attribute naming (i.e.: 'provider:network_type' is not a valid
python variable/attribute name)::

    >>> d.provider:network_type
      File "<stdin>", line 1
        d.provider:network_type
              ^
    SyntaxError: invalid syntax

This time we just subclass dict with attribute access to keys.

So dict keys are accessible via attribute access but remain accessible via key
access::

    >>> d = DictModel({'foo': 'bar', 'provider:network_type': 'something'})
    >>> d.foo == d['foo']
    ... True
    >>> getattr(d, 'provider:network_type') == d.get('provider:network_type')
    ... True
    >>> getattr(d, 'provider:network_type') == d['provider:network_type']
    ... True

One of the big advantages when debugging is that now in pdb, pp(d) (where d is
a DictModel instance) shows a nice dictionary structure, while with the old
version whe had to perform a "dir(d)" and introspect each attribute...

Change-Id: I05fad7cd9763f97f61680d45e1b6592a80049541
Closes-Bug: 1251653

neutron/agent/linux/dhcp.py
neutron/tests/unit/test_dhcp_agent.py

index 1dc8ad90028b479d4aad1b1b8662e7607b50a7f9..a0c2357793b7af5768ee2a82f681f822c158b24b 100644 (file)
@@ -77,17 +77,46 @@ WIN2k3_STATIC_DNS = 249
 NS_PREFIX = 'qdhcp-'
 
 
-class DictModel(object):
+class DictModel(dict):
     """Convert dict into an object that provides attribute access to values."""
-    def __init__(self, d):
-        for key, value in d.iteritems():
-            if isinstance(value, list):
-                value = [DictModel(item) if isinstance(item, dict) else item
-                         for item in value]
-            elif isinstance(value, dict):
-                value = DictModel(value)
-
-            setattr(self, key, value)
+
+    def __init__(self, *args, **kwargs):
+        """Convert dict values to DictModel values."""
+        super(DictModel, self).__init__(*args, **kwargs)
+
+        def needs_upgrade(item):
+            """Check if `item` is a dict and needs to be changed to DictModel.
+            """
+            return isinstance(item, dict) and not isinstance(item, DictModel)
+
+        def upgrade(item):
+            """Upgrade item if it needs to be upgraded."""
+            if needs_upgrade(item):
+                return DictModel(item)
+            else:
+                return item
+
+        for key, value in self.iteritems():
+            if isinstance(value, (list, tuple)):
+                # Keep the same type but convert dicts to DictModels
+                self[key] = type(value)(
+                    (upgrade(item) for item in value)
+                )
+            elif needs_upgrade(value):
+                # Change dict instance values to DictModel instance values
+                self[key] = DictModel(value)
+
+    def __getattr__(self, name):
+        try:
+            return self[name]
+        except KeyError as e:
+            raise AttributeError(e)
+
+    def __setattr__(self, name, value):
+        self[name] = value
+
+    def __delattr__(self, name):
+        del self[name]
 
 
 class NetModel(DictModel):
index ebe2be4c04a83cb0b17007518ed565b871665e48..4c797acae008e943aa304bc75c33fba5267b7a7f 100644 (file)
@@ -776,7 +776,7 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
                                                  fake_network)
 
     def test_port_update_end(self):
-        payload = dict(port=vars(fake_port2))
+        payload = dict(port=fake_port2)
         self.cache.get_network_by_id.return_value = fake_network
         self.cache.get_port_by_id.return_value = fake_port2
         self.dhcp.port_update_end(None, payload)
@@ -787,7 +787,7 @@ class TestDhcpAgentEventHandler(base.BaseTestCase):
                                                  fake_network)
 
     def test_port_update_change_ip_on_port(self):
-        payload = dict(port=vars(fake_port1))
+        payload = dict(port=fake_port1)
         self.cache.get_network_by_id.return_value = fake_network
         updated_fake_port1 = copy.deepcopy(fake_port1)
         updated_fake_port1.fixed_ips[0].ip_address = '172.9.9.99'