root / snf-common / synnefo / util / keypath.py @ b253c438
History | View | Annotate | Download (6.7 kB)
1 | b253c438 | Georgios D. Tsoukalas | # Copyright 2013 GRNET S.A. All rights reserved.
|
---|---|---|---|
2 | b253c438 | Georgios D. Tsoukalas | #
|
3 | b253c438 | Georgios D. Tsoukalas | # Redistribution and use in source and binary forms, with or
|
4 | b253c438 | Georgios D. Tsoukalas | # without modification, are permitted provided that the following
|
5 | b253c438 | Georgios D. Tsoukalas | # conditions are met:
|
6 | b253c438 | Georgios D. Tsoukalas | #
|
7 | b253c438 | Georgios D. Tsoukalas | # 1. Redistributions of source code must retain the above
|
8 | b253c438 | Georgios D. Tsoukalas | # copyright notice, this list of conditions and the following
|
9 | b253c438 | Georgios D. Tsoukalas | # disclaimer.
|
10 | b253c438 | Georgios D. Tsoukalas | #
|
11 | b253c438 | Georgios D. Tsoukalas | # 2. Redistributions in binary form must reproduce the above
|
12 | b253c438 | Georgios D. Tsoukalas | # copyright notice, this list of conditions and the following
|
13 | b253c438 | Georgios D. Tsoukalas | # disclaimer in the documentation and/or other materials
|
14 | b253c438 | Georgios D. Tsoukalas | # provided with the distribution.
|
15 | b253c438 | Georgios D. Tsoukalas | #
|
16 | b253c438 | Georgios D. Tsoukalas | # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
|
17 | b253c438 | Georgios D. Tsoukalas | # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18 | b253c438 | Georgios D. Tsoukalas | # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
19 | b253c438 | Georgios D. Tsoukalas | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
|
20 | b253c438 | Georgios D. Tsoukalas | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
21 | b253c438 | Georgios D. Tsoukalas | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
22 | b253c438 | Georgios D. Tsoukalas | # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
|
23 | b253c438 | Georgios D. Tsoukalas | # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
24 | b253c438 | Georgios D. Tsoukalas | # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
25 | b253c438 | Georgios D. Tsoukalas | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
26 | b253c438 | Georgios D. Tsoukalas | # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
27 | b253c438 | Georgios D. Tsoukalas | # POSSIBILITY OF SUCH DAMAGE.
|
28 | b253c438 | Georgios D. Tsoukalas | #
|
29 | b253c438 | Georgios D. Tsoukalas | # The views and conclusions contained in the software and
|
30 | b253c438 | Georgios D. Tsoukalas | # documentation are those of the authors and should not be
|
31 | b253c438 | Georgios D. Tsoukalas | # interpreted as representing official policies, either expressed
|
32 | b253c438 | Georgios D. Tsoukalas | # or implied, of GRNET S.A.
|
33 | b253c438 | Georgios D. Tsoukalas | |
34 | b253c438 | Georgios D. Tsoukalas | |
35 | b253c438 | Georgios D. Tsoukalas | def lookup_path(container, path, sep='.', createpath=False): |
36 | b253c438 | Georgios D. Tsoukalas | """
|
37 | b253c438 | Georgios D. Tsoukalas | return (['a','b'],
|
38 | b253c438 | Georgios D. Tsoukalas | [container['a'], container['a']['b']],
|
39 | b253c438 | Georgios D. Tsoukalas | 'c') where path=sep.join(['a','b','c'])
|
40 | b253c438 | Georgios D. Tsoukalas |
|
41 | b253c438 | Georgios D. Tsoukalas | """
|
42 | b253c438 | Georgios D. Tsoukalas | names = path.split(sep) |
43 | b253c438 | Georgios D. Tsoukalas | dirnames = names[:-1]
|
44 | b253c438 | Georgios D. Tsoukalas | basename = names[-1]
|
45 | b253c438 | Georgios D. Tsoukalas | |
46 | b253c438 | Georgios D. Tsoukalas | node = container |
47 | b253c438 | Georgios D. Tsoukalas | name_path = [] |
48 | b253c438 | Georgios D. Tsoukalas | node_path = [node] |
49 | b253c438 | Georgios D. Tsoukalas | for name in dirnames: |
50 | b253c438 | Georgios D. Tsoukalas | name_path.append(name) |
51 | b253c438 | Georgios D. Tsoukalas | if name not in node: |
52 | b253c438 | Georgios D. Tsoukalas | if not createpath: |
53 | b253c438 | Georgios D. Tsoukalas | m = "'{0}': path not found".format(sep.join(name_path))
|
54 | b253c438 | Georgios D. Tsoukalas | raise KeyError(m) |
55 | b253c438 | Georgios D. Tsoukalas | node[name] = {} |
56 | b253c438 | Georgios D. Tsoukalas | try:
|
57 | b253c438 | Georgios D. Tsoukalas | node = node[name] |
58 | b253c438 | Georgios D. Tsoukalas | except TypeError as e: |
59 | b253c438 | Georgios D. Tsoukalas | m = "'{0}': cannot traverse path beyond this node: {1}"
|
60 | b253c438 | Georgios D. Tsoukalas | m = m.format(sep.join(name_path), str(e))
|
61 | b253c438 | Georgios D. Tsoukalas | raise ValueError(m) |
62 | b253c438 | Georgios D. Tsoukalas | node_path.append(node) |
63 | b253c438 | Georgios D. Tsoukalas | |
64 | b253c438 | Georgios D. Tsoukalas | return name_path, node_path, basename
|
65 | b253c438 | Georgios D. Tsoukalas | |
66 | b253c438 | Georgios D. Tsoukalas | |
67 | b253c438 | Georgios D. Tsoukalas | def walk_paths(container): |
68 | b253c438 | Georgios D. Tsoukalas | for name, node in container.iteritems(): |
69 | b253c438 | Georgios D. Tsoukalas | if not hasattr(node, 'items'): |
70 | b253c438 | Georgios D. Tsoukalas | yield [name], [node]
|
71 | b253c438 | Georgios D. Tsoukalas | else:
|
72 | b253c438 | Georgios D. Tsoukalas | for names, nodes in walk_paths(node): |
73 | b253c438 | Georgios D. Tsoukalas | yield [name] + names, [node] + nodes
|
74 | b253c438 | Georgios D. Tsoukalas | |
75 | b253c438 | Georgios D. Tsoukalas | |
76 | b253c438 | Georgios D. Tsoukalas | def list_paths(container, sep='.'): |
77 | b253c438 | Georgios D. Tsoukalas | """
|
78 | b253c438 | Georgios D. Tsoukalas | >>> sorted(list_paths({'a': {'b': {'c': 'd'}}}))
|
79 | b253c438 | Georgios D. Tsoukalas | [('a.b.c', 'd')]
|
80 | b253c438 | Georgios D. Tsoukalas | >>> sorted(list_paths({'a': {'b': {'c': 'd'}, 'e': 3}}))
|
81 | b253c438 | Georgios D. Tsoukalas | [('a.b.c', 'd'), ('a.e', 3)]
|
82 | b253c438 | Georgios D. Tsoukalas | >>> sorted(list_paths({'a': {'b': {'c': 'd'}, 'e': {'f': 3}}}))
|
83 | b253c438 | Georgios D. Tsoukalas | [('a.b.c', 'd'), ('a.e.f', 3)]
|
84 | b253c438 | Georgios D. Tsoukalas | >>> list_paths({})
|
85 | b253c438 | Georgios D. Tsoukalas | []
|
86 | b253c438 | Georgios D. Tsoukalas |
|
87 | b253c438 | Georgios D. Tsoukalas | """
|
88 | b253c438 | Georgios D. Tsoukalas | return [(sep.join(name_path), node_path[-1]) |
89 | b253c438 | Georgios D. Tsoukalas | for name_path, node_path in walk_paths(container)] |
90 | b253c438 | Georgios D. Tsoukalas | |
91 | b253c438 | Georgios D. Tsoukalas | |
92 | b253c438 | Georgios D. Tsoukalas | def del_path(container, path, sep='.', collect=True): |
93 | b253c438 | Georgios D. Tsoukalas | """
|
94 | b253c438 | Georgios D. Tsoukalas | del container['a']['b']['c'] where path=sep.join(['a','b','c'])
|
95 | b253c438 | Georgios D. Tsoukalas |
|
96 | b253c438 | Georgios D. Tsoukalas | >>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c'); d
|
97 | b253c438 | Georgios D. Tsoukalas | {}
|
98 | b253c438 | Georgios D. Tsoukalas | >>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c', collect=False); d
|
99 | b253c438 | Georgios D. Tsoukalas | {'a': {'b': {}}}
|
100 | b253c438 | Georgios D. Tsoukalas | >>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c.d')
|
101 | b253c438 | Georgios D. Tsoukalas | Traceback (most recent call last):
|
102 | b253c438 | Georgios D. Tsoukalas | ValueError: 'a.b.c': cannot traverse path beyond this node:\
|
103 | b253c438 | Georgios D. Tsoukalas | 'str' object does not support item deletion
|
104 | b253c438 | Georgios D. Tsoukalas | """
|
105 | b253c438 | Georgios D. Tsoukalas | |
106 | b253c438 | Georgios D. Tsoukalas | name_path, node_path, basename = \ |
107 | b253c438 | Georgios D. Tsoukalas | lookup_path(container, path, sep=sep, createpath=False)
|
108 | b253c438 | Georgios D. Tsoukalas | |
109 | b253c438 | Georgios D. Tsoukalas | lastnode = node_path.pop() |
110 | b253c438 | Georgios D. Tsoukalas | lastname = basename |
111 | b253c438 | Georgios D. Tsoukalas | try:
|
112 | b253c438 | Georgios D. Tsoukalas | if basename in lastnode: |
113 | b253c438 | Georgios D. Tsoukalas | del lastnode[basename]
|
114 | b253c438 | Georgios D. Tsoukalas | except (TypeError, KeyError) as e: |
115 | b253c438 | Georgios D. Tsoukalas | m = "'{0}': cannot traverse path beyond this node: {1}"
|
116 | b253c438 | Georgios D. Tsoukalas | m = m.format(sep.join(name_path), str(e))
|
117 | b253c438 | Georgios D. Tsoukalas | raise ValueError(m) |
118 | b253c438 | Georgios D. Tsoukalas | |
119 | b253c438 | Georgios D. Tsoukalas | if collect:
|
120 | b253c438 | Georgios D. Tsoukalas | while node_path and not lastnode: |
121 | b253c438 | Georgios D. Tsoukalas | basename = name_path.pop() |
122 | b253c438 | Georgios D. Tsoukalas | lastnode = node_path.pop() |
123 | b253c438 | Georgios D. Tsoukalas | del lastnode[basename]
|
124 | b253c438 | Georgios D. Tsoukalas | |
125 | b253c438 | Georgios D. Tsoukalas | |
126 | b253c438 | Georgios D. Tsoukalas | def get_path(container, path, sep='.'): |
127 | b253c438 | Georgios D. Tsoukalas | """
|
128 | b253c438 | Georgios D. Tsoukalas | return container['a']['b']['c'] where path=sep.join(['a','b','c'])
|
129 | b253c438 | Georgios D. Tsoukalas |
|
130 | b253c438 | Georgios D. Tsoukalas | >>> get_path({'a': {'b': {'c': 'd'}}}, 'a.b.c.d')
|
131 | b253c438 | Georgios D. Tsoukalas | Traceback (most recent call last):
|
132 | b253c438 | Georgios D. Tsoukalas | ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
|
133 | b253c438 | Georgios D. Tsoukalas | string indices must be integers, not str
|
134 | b253c438 | Georgios D. Tsoukalas | >>> get_path({'a': {'b': {'c': 1}}}, 'a.b.c.d')
|
135 | b253c438 | Georgios D. Tsoukalas | Traceback (most recent call last):
|
136 | b253c438 | Georgios D. Tsoukalas | ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
|
137 | b253c438 | Georgios D. Tsoukalas | 'int' object is unsubscriptable
|
138 | b253c438 | Georgios D. Tsoukalas | >>> get_path({'a': {'b': {'c': 1}}}, 'a.b.c')
|
139 | b253c438 | Georgios D. Tsoukalas | 1
|
140 | b253c438 | Georgios D. Tsoukalas | >>> get_path({'a': {'b': {'c': 1}}}, 'a.b')
|
141 | b253c438 | Georgios D. Tsoukalas | {'c': 1}
|
142 | b253c438 | Georgios D. Tsoukalas |
|
143 | b253c438 | Georgios D. Tsoukalas | """
|
144 | b253c438 | Georgios D. Tsoukalas | name_path, node_path, basename = \ |
145 | b253c438 | Georgios D. Tsoukalas | lookup_path(container, path, sep=sep, createpath=False)
|
146 | b253c438 | Georgios D. Tsoukalas | name_path.append(basename) |
147 | b253c438 | Georgios D. Tsoukalas | node = node_path[-1]
|
148 | b253c438 | Georgios D. Tsoukalas | |
149 | b253c438 | Georgios D. Tsoukalas | try:
|
150 | b253c438 | Georgios D. Tsoukalas | return node[basename]
|
151 | b253c438 | Georgios D. Tsoukalas | except TypeError as e: |
152 | b253c438 | Georgios D. Tsoukalas | m = "'{0}': cannot traverse path beyond this node: {1}"
|
153 | b253c438 | Georgios D. Tsoukalas | m = m.format(sep.join(name_path), str(e))
|
154 | b253c438 | Georgios D. Tsoukalas | raise ValueError(m) |
155 | b253c438 | Georgios D. Tsoukalas | except KeyError as e: |
156 | b253c438 | Georgios D. Tsoukalas | m = "'{0}': path not found: {1}"
|
157 | b253c438 | Georgios D. Tsoukalas | m = m.format(sep.join(name_path), str(e))
|
158 | b253c438 | Georgios D. Tsoukalas | raise KeyError(m) |
159 | b253c438 | Georgios D. Tsoukalas | |
160 | b253c438 | Georgios D. Tsoukalas | |
161 | b253c438 | Georgios D. Tsoukalas | def set_path(container, path, value, sep='.', |
162 | b253c438 | Georgios D. Tsoukalas | createpath=False, overwrite=True): |
163 | b253c438 | Georgios D. Tsoukalas | """
|
164 | b253c438 | Georgios D. Tsoukalas | container['a']['b']['c'] = value where path=sep.join(['a','b','c'])
|
165 | b253c438 | Georgios D. Tsoukalas |
|
166 | b253c438 | Georgios D. Tsoukalas | >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c.d', 1)
|
167 | b253c438 | Georgios D. Tsoukalas | Traceback (most recent call last):
|
168 | b253c438 | Georgios D. Tsoukalas | ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
|
169 | b253c438 | Georgios D. Tsoukalas | 'str' object does not support item assignment
|
170 | b253c438 | Georgios D. Tsoukalas | >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.x.d', 1)
|
171 | b253c438 | Georgios D. Tsoukalas | Traceback (most recent call last):
|
172 | b253c438 | Georgios D. Tsoukalas | KeyError: "'a.b.x': path not found"
|
173 | b253c438 | Georgios D. Tsoukalas | >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.x.d', 1, createpath=True)
|
174 | b253c438 | Georgios D. Tsoukalas |
|
175 | b253c438 | Georgios D. Tsoukalas | >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c', 1)
|
176 | b253c438 | Georgios D. Tsoukalas |
|
177 | b253c438 | Georgios D. Tsoukalas | >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c', 1, overwrite=False)
|
178 | b253c438 | Georgios D. Tsoukalas | Traceback (most recent call last):
|
179 | b253c438 | Georgios D. Tsoukalas | ValueError: will not overwrite path 'a.b.c'
|
180 | b253c438 | Georgios D. Tsoukalas |
|
181 | b253c438 | Georgios D. Tsoukalas | """
|
182 | b253c438 | Georgios D. Tsoukalas | name_path, node_path, basename = \ |
183 | b253c438 | Georgios D. Tsoukalas | lookup_path(container, path, sep=sep, createpath=createpath) |
184 | b253c438 | Georgios D. Tsoukalas | name_path.append(basename) |
185 | b253c438 | Georgios D. Tsoukalas | node = node_path[-1]
|
186 | b253c438 | Georgios D. Tsoukalas | |
187 | b253c438 | Georgios D. Tsoukalas | if basename in node and not overwrite: |
188 | b253c438 | Georgios D. Tsoukalas | m = "will not overwrite path '{0}'".format(path)
|
189 | b253c438 | Georgios D. Tsoukalas | raise ValueError(m) |
190 | b253c438 | Georgios D. Tsoukalas | |
191 | b253c438 | Georgios D. Tsoukalas | try:
|
192 | b253c438 | Georgios D. Tsoukalas | node[basename] = value |
193 | b253c438 | Georgios D. Tsoukalas | except TypeError as e: |
194 | b253c438 | Georgios D. Tsoukalas | m = "'{0}': cannot traverse path beyond this node: {1}"
|
195 | b253c438 | Georgios D. Tsoukalas | m = m.format(sep.join(name_path), str(e))
|
196 | b253c438 | Georgios D. Tsoukalas | raise ValueError(m) |
197 | b253c438 | Georgios D. Tsoukalas | |
198 | b253c438 | Georgios D. Tsoukalas | |
199 | b253c438 | Georgios D. Tsoukalas | if __name__ == '__main__': |
200 | b253c438 | Georgios D. Tsoukalas | import doctest |
201 | b253c438 | Georgios D. Tsoukalas | doctest.testmod() |