From 89f87c61e7e5c4fa69379e4039ad4b8221142a7c Mon Sep 17 00:00:00 2001 From: Yves-Gwenael Bourhis Date: Thu, 2 Jan 2014 15:19:17 +0100 Subject: [PATCH] Changed DictModel to dict with attribute access 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 "", 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 | 49 +++++++++++++++++++++------ neutron/tests/unit/test_dhcp_agent.py | 4 +-- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index 1dc8ad900..a0c235779 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -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): diff --git a/neutron/tests/unit/test_dhcp_agent.py b/neutron/tests/unit/test_dhcp_agent.py index ebe2be4c0..4c797acae 100644 --- a/neutron/tests/unit/test_dhcp_agent.py +++ b/neutron/tests/unit/test_dhcp_agent.py @@ -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' -- 2.45.2