Browse Source

Add MultiDict class implementation

Change-Id: I129ed55783a57224e8ab7079e292d6ce9754e355
tags/kilo-eol
Harkirat Singh 4 years ago
parent
commit
4a3a8712a3

+ 73
- 0
networking_brocade/vyatta/common/utils.py View File

@@ -14,7 +14,9 @@
14 14
 #    under the License.
15 15
 
16 16
 import collections
17
+
17 18
 from eventlet import greenthread
19
+import six
18 20
 
19 21
 
20 22
 RouteRule = collections.namedtuple('RouteRule', 'dest_cidr, next_hop')
@@ -34,3 +36,74 @@ def retry(fn, args=None, kwargs=None, exceptions=None, limit=1, delay=0):
34 36
             greenthread.sleep(delay)
35 37
         limit -= 1
36 38
     raise
39
+
40
+
41
+class MultiDict(collections.MutableMapping):
42
+
43
+    def __init__(self, mapping=None):
44
+        self._items = {}
45
+
46
+        if isinstance(mapping, MultiDict):
47
+            for key, value in mapping.lists():
48
+                self._items[key] = value[:]
49
+        elif isinstance(mapping, dict):
50
+            for key, value in six.iteritems(mapping):
51
+                self._items[key] = [value]
52
+        elif mapping is not None:
53
+            for key, value in mapping:
54
+                self._items.setdefault(key, []).append(value)
55
+
56
+    def __getitem__(self, key):
57
+        return self._items[key][0]
58
+
59
+    def __setitem__(self, key, value):
60
+        self._items[key] = [value]
61
+
62
+    def __delitem__(self, key):
63
+        del self._items[key]
64
+
65
+    def __len__(self):
66
+        return len(self._items)
67
+
68
+    def __iter__(self):
69
+        return six.iterkeys(self._items)
70
+
71
+    def __repr__(self):
72
+        items = []
73
+        for key, lst in six.iteritems(self._items):
74
+            for item in lst:
75
+                items.append((key, item))
76
+        return 'MultiDict {0!r}'.format(items)
77
+
78
+    def add(self, key, value):
79
+        self._items.setdefault(key, []).append(value)
80
+
81
+    def getlist(self, key, default=None):
82
+        try:
83
+            return self._items[key]
84
+        except KeyError:
85
+            return default or []
86
+
87
+    def setlist(self, key, value):
88
+        self._items[key] = list(value)
89
+
90
+    def setlistdefault(self, key, default_list=None):
91
+        if key in self._items:
92
+            default_list = self._items[key]
93
+        else:
94
+            if default_list is None:
95
+                default_list = []
96
+            else:
97
+                default_list = list(default_list)
98
+            self._items[key] = default_list
99
+
100
+        return default_list
101
+
102
+    def copy(self):
103
+        return self.__class__(self)
104
+
105
+    def lists(self):
106
+        return six.iteritems(self._items)
107
+
108
+    def listvalues(self):
109
+        return six.itervalues(self._items)

+ 3
- 2
networking_brocade/vyatta/common/vrouter_config.py View File

@@ -13,6 +13,7 @@
13 13
 #    License for the specific language governing permissions and limitations
14 14
 #    under the License.
15 15
 
16
+from networking_brocade.vyatta.common import utils
16 17
 
17 18
 TOKEN_GROUP = 'group'
18 19
 TOKEN_PARAM = 'param'
@@ -45,13 +46,13 @@ def config_iter(config):
45 46
 
46 47
 
47 48
 def parse_group(lines):
48
-    result = {}
49
+    result = utils.MultiDict()
49 50
 
50 51
     for line in lines:
51 52
         token, key, value = parse_line(line)
52 53
 
53 54
         if token == TOKEN_PARAM:
54
-            result[key] = value
55
+            result.setlistdefault(key).append(value)
55 56
         elif token == TOKEN_GROUP:
56 57
             result[key] = parse_group(lines)
57 58
         else:

+ 98
- 0
networking_brocade/vyatta/tests/test_utils.py View File

@@ -0,0 +1,98 @@
1
+# Copyright 2015 Brocade Communications System, Inc.
2
+# All Rights Reserved.
3
+#
4
+#    Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+#    not use this file except in compliance with the License. You may obtain
6
+#    a copy of the License at
7
+#
8
+#         http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+#    Unless required by applicable law or agreed to in writing, software
11
+#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+#    License for the specific language governing permissions and limitations
14
+#    under the License.
15
+
16
+import collections
17
+
18
+import testtools
19
+
20
+from networking_brocade.vyatta.common import utils
21
+
22
+
23
+class TestMultiDict(testtools.TestCase):
24
+
25
+    def setUp(self):
26
+        super(TestMultiDict, self).setUp()
27
+        # Create from list of tuples
28
+        mapping = [('a', 1), ('b', 2), ('a', 2), ('d', 3),
29
+                   ('a', 1), ('a', 3), ('d', 4), ('c', 3)]
30
+        self.md = utils.MultiDict(mapping)
31
+
32
+    def test_init(self):
33
+        md = utils.MultiDict()
34
+        self.assertIsInstance(md, collections.MutableMapping)
35
+
36
+        # Create from dict
37
+        mapping = {'a': 1, 'b': 2, 'c': 3}
38
+        md = utils.MultiDict(mapping)
39
+        self.assertEqual(md['a'], 1)
40
+        self.assertEqual(md.getlist('a'), [1])
41
+
42
+    def test_getitem(self):
43
+        # __getitem__
44
+        self.assertEqual(self.md['a'], 1)
45
+        self.assertEqual(self.md['c'], 3)
46
+        with testtools.ExpectedException(KeyError):
47
+            self.md['e']
48
+
49
+        # get
50
+        self.assertEqual(self.md.get('a'), 1)
51
+        self.assertEqual(self.md.get('e'), None)
52
+
53
+        # getlist
54
+        self.assertEqual(self.md.getlist('a'), [1, 2, 1, 3])
55
+        self.assertEqual(self.md.getlist('d'), [3, 4])
56
+        self.assertEqual(self.md.getlist('x'), [])
57
+
58
+    def test_setitem(self):
59
+        # __setitem__
60
+        self.md['a'] = 42
61
+        self.assertEqual(self.md['a'], 42)
62
+        self.assertEqual(self.md.getlist('a'), [42])
63
+
64
+        # setlist
65
+        self.md.setlist('a', [1, 2, 3])
66
+        self.assertEqual(self.md['a'], 1)
67
+        self.assertEqual(self.md.getlist('a'), [1, 2, 3])
68
+
69
+        # check that setlist does not affects initial list
70
+        lst = [1, 2, 3]
71
+        self.md.setlist('a', lst)
72
+        self.md.add('a', 42)
73
+        self.assertEqual(lst, [1, 2, 3])
74
+
75
+        # setdefault
76
+        # TODO(asaprykin): Check setdefault without arg
77
+        self.assertEqual(self.md.setdefault('u', 32), 32)
78
+        self.assertEqual(self.md.getlist('u'), [32])
79
+
80
+    def test_delitem(self):
81
+        # __delitem__
82
+        del self.md['a']
83
+        self.assertNotIn('a', self.md)
84
+        with testtools.ExpectedException(KeyError):
85
+            del self.md['x']
86
+
87
+    def test_setlistdefault(self):
88
+
89
+        self.assertEqual(self.md.setlistdefault('a'), [1, 2, 1, 3])
90
+        self.assertEqual(self.md.setlistdefault('d'), [3, 4])
91
+
92
+        self.assertEqual(self.md.setlistdefault('u1'), [])
93
+        self.assertEqual(self.md.setlistdefault('u3', [42]), [42])
94
+
95
+        with testtools.ExpectedException(TypeError):
96
+            self.md.setlistdefault('u2', False)
97
+        with testtools.ExpectedException(TypeError):
98
+            self.md.setlistdefault('u2', 32)

Loading…
Cancel
Save