multidict.py revision 5467:6d9df90d70d7
1# Copyright (c) 2005 The Regents of The University of Michigan
2# All rights reserved.
3#
4# Redistribution and use in source and binary forms, with or without
5# modification, are permitted provided that the following conditions are
6# met: redistributions of source code must retain the above copyright
7# notice, this list of conditions and the following disclaimer;
8# redistributions in binary form must reproduce the above copyright
9# notice, this list of conditions and the following disclaimer in the
10# documentation and/or other materials provided with the distribution;
11# neither the name of the copyright holders nor the names of its
12# contributors may be used to endorse or promote products derived from
13# this software without specific prior written permission.
14#
15# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26#
27# Authors: Nathan Binkert
28
29__all__ = [ 'multidict' ]
30
31class multidict(object):
32    def __init__(self, parent = {}, **kwargs):
33        self.local = dict(**kwargs)
34        self.parent = parent
35        self.deleted = {}
36
37    def __str__(self):
38        return str(dict(self.items()))
39
40    def __repr__(self):
41        return `dict(self.items())`
42
43    def __contains__(self, key):
44        return self.local.has_key(key) or self.parent.has_key(key)
45
46    def __delitem__(self, key):
47        try:
48            del self.local[key]
49        except KeyError, e:
50            if key in self.parent:
51                self.deleted[key] = True
52            else:
53                raise KeyError, e
54
55    def __setitem__(self, key, value):
56        self.deleted.pop(key, False)
57        self.local[key] = value
58
59    def __getitem__(self, key):
60        try:
61            return self.local[key]
62        except KeyError, e:
63            if not self.deleted.get(key, False) and key in self.parent:
64                return self.parent[key]
65            else:
66                raise KeyError, e
67
68    def __len__(self):
69        return len(self.local) + len(self.parent)
70
71    def next(self):
72        for key,value in self.local.items():
73            yield key,value
74
75        if self.parent:
76            for key,value in self.parent.next():
77                if key not in self.local and key not in self.deleted:
78                    yield key,value
79
80    def has_key(self, key):
81        return key in self
82
83    def iteritems(self):
84        for item in self.next():
85            yield item
86
87    def items(self):
88        return [ item for item in self.next() ]
89
90    def iterkeys(self):
91        for key,value in self.next():
92            yield key
93
94    def keys(self):
95        return [ key for key,value in self.next() ]
96
97    def itervalues(self):
98        for key,value in self.next():
99            yield value
100
101    def values(self):
102        return [ value for key,value in self.next() ]
103
104    def get(self, key, default=None):
105        try:
106            return self[key]
107        except KeyError, e:
108            return default
109
110    def setdefault(self, key, default):
111        try:
112            return self[key]
113        except KeyError:
114            self.deleted.pop(key, False)
115            self.local[key] = default
116            return default
117
118    def _dump(self):
119        print 'multidict dump'
120        node = self
121        while isinstance(node, multidict):
122            print '    ', node.local
123            node = node.parent
124
125    def _dumpkey(self, key):
126        values = []
127        node = self
128        while isinstance(node, multidict):
129            if key in node.local:
130                values.append(node.local[key])
131            node = node.parent
132        print key, values
133
134if __name__ == '__main__':
135    test1 = multidict()
136    test2 = multidict(test1)
137    test3 = multidict(test2)
138    test4 = multidict(test3)
139
140    test1['a'] = 'test1_a'
141    test1['b'] = 'test1_b'
142    test1['c'] = 'test1_c'
143    test1['d'] = 'test1_d'
144    test1['e'] = 'test1_e'
145
146    test2['a'] = 'test2_a'
147    del test2['b']
148    test2['c'] = 'test2_c'
149    del test1['a']
150
151    test2.setdefault('f', multidict)
152
153    print 'test1>', test1.items()
154    print 'test2>', test2.items()
155    #print test1['a']
156    print test1['b']
157    print test1['c']
158    print test1['d']
159    print test1['e']
160
161    print test2['a']
162    #print test2['b']
163    print test2['c']
164    print test2['d']
165    print test2['e']
166
167    for key in test2.iterkeys():
168        print key
169
170    test2.get('g', 'foo')
171    #test2.get('b')
172    test2.get('b', 'bar')
173    test2.setdefault('b', 'blah')
174    print test1
175    print test2
176    print `test2`
177
178    print len(test2)
179
180    test3['a'] = [ 0, 1, 2, 3 ]
181
182    print test4
183