Statistics
| Branch: | Tag: | Revision:

root / astakosclient / astakosclient / keypath.py @ 8975f6f6

History | View | Annotate | Download (7.1 kB)

1 00d2a0ee Georgios D. Tsoukalas
# Copyright 2012, 2013 GRNET S.A. All rights reserved.
2 00d2a0ee Georgios D. Tsoukalas
#
3 00d2a0ee Georgios D. Tsoukalas
# Redistribution and use in source and binary forms, with or
4 00d2a0ee Georgios D. Tsoukalas
# without modification, are permitted provided that the following
5 00d2a0ee Georgios D. Tsoukalas
# conditions are met:
6 00d2a0ee Georgios D. Tsoukalas
#
7 00d2a0ee Georgios D. Tsoukalas
#   1. Redistributions of source code must retain the above
8 00d2a0ee Georgios D. Tsoukalas
#      copyright notice, this list of conditions and the following
9 00d2a0ee Georgios D. Tsoukalas
#      disclaimer.
10 00d2a0ee Georgios D. Tsoukalas
#
11 00d2a0ee Georgios D. Tsoukalas
#   2. Redistributions in binary form must reproduce the above
12 00d2a0ee Georgios D. Tsoukalas
#      copyright notice, this list of conditions and the following
13 00d2a0ee Georgios D. Tsoukalas
#      disclaimer in the documentation and/or other materials
14 00d2a0ee Georgios D. Tsoukalas
#      provided with the distribution.
15 00d2a0ee Georgios D. Tsoukalas
#
16 00d2a0ee Georgios D. Tsoukalas
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 00d2a0ee Georgios D. Tsoukalas
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 00d2a0ee Georgios D. Tsoukalas
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 00d2a0ee Georgios D. Tsoukalas
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 00d2a0ee Georgios D. Tsoukalas
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 00d2a0ee Georgios D. Tsoukalas
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 00d2a0ee Georgios D. Tsoukalas
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 00d2a0ee Georgios D. Tsoukalas
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 00d2a0ee Georgios D. Tsoukalas
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 00d2a0ee Georgios D. Tsoukalas
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 00d2a0ee Georgios D. Tsoukalas
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 00d2a0ee Georgios D. Tsoukalas
# POSSIBILITY OF SUCH DAMAGE.
28 00d2a0ee Georgios D. Tsoukalas
#
29 00d2a0ee Georgios D. Tsoukalas
# The views and conclusions contained in the software and
30 00d2a0ee Georgios D. Tsoukalas
# documentation are those of the authors and should not be
31 00d2a0ee Georgios D. Tsoukalas
# interpreted as representing official policies, either expressed
32 00d2a0ee Georgios D. Tsoukalas
# or implied, of GRNET S.A.
33 8975f6f6 Christos Stavrakakis
import copy
34 00d2a0ee Georgios D. Tsoukalas
35 00d2a0ee Georgios D. Tsoukalas
36 00d2a0ee Georgios D. Tsoukalas
def dict_merge(a, b):
37 00d2a0ee Georgios D. Tsoukalas
    """
38 00d2a0ee Georgios D. Tsoukalas
    http://www.xormedia.com/recursively-merge-dictionaries-in-python/
39 00d2a0ee Georgios D. Tsoukalas
    """
40 00d2a0ee Georgios D. Tsoukalas
    if not isinstance(b, dict):
41 00d2a0ee Georgios D. Tsoukalas
        return b
42 00d2a0ee Georgios D. Tsoukalas
    result = copy.deepcopy(a)
43 00d2a0ee Georgios D. Tsoukalas
    for k, v in b.iteritems():
44 00d2a0ee Georgios D. Tsoukalas
        if k in result and isinstance(result[k], dict):
45 00d2a0ee Georgios D. Tsoukalas
                result[k] = dict_merge(result[k], v)
46 00d2a0ee Georgios D. Tsoukalas
        else:
47 00d2a0ee Georgios D. Tsoukalas
            result[k] = copy.deepcopy(v)
48 00d2a0ee Georgios D. Tsoukalas
    return result
49 00d2a0ee Georgios D. Tsoukalas
50 00d2a0ee Georgios D. Tsoukalas
51 00d2a0ee Georgios D. Tsoukalas
def lookup_path(container, path, sep='.', createpath=False):
52 00d2a0ee Georgios D. Tsoukalas
    """
53 00d2a0ee Georgios D. Tsoukalas
    return (['a','b'],
54 00d2a0ee Georgios D. Tsoukalas
            [container['a'], container['a']['b']],
55 00d2a0ee Georgios D. Tsoukalas
            'c')  where path=sep.join(['a','b','c'])
56 00d2a0ee Georgios D. Tsoukalas

57 00d2a0ee Georgios D. Tsoukalas
    """
58 00d2a0ee Georgios D. Tsoukalas
    names = path.split(sep)
59 00d2a0ee Georgios D. Tsoukalas
    dirnames = names[:-1]
60 00d2a0ee Georgios D. Tsoukalas
    basename = names[-1]
61 00d2a0ee Georgios D. Tsoukalas
62 00d2a0ee Georgios D. Tsoukalas
    node = container
63 00d2a0ee Georgios D. Tsoukalas
    name_path = []
64 00d2a0ee Georgios D. Tsoukalas
    node_path = [node]
65 00d2a0ee Georgios D. Tsoukalas
    for name in dirnames:
66 00d2a0ee Georgios D. Tsoukalas
        name_path.append(name)
67 00d2a0ee Georgios D. Tsoukalas
        if name not in node:
68 00d2a0ee Georgios D. Tsoukalas
            if not createpath:
69 00d2a0ee Georgios D. Tsoukalas
                m = "'{0}': path not found".format(sep.join(name_path))
70 00d2a0ee Georgios D. Tsoukalas
                raise KeyError(m)
71 00d2a0ee Georgios D. Tsoukalas
            node[name] = {}
72 00d2a0ee Georgios D. Tsoukalas
        try:
73 00d2a0ee Georgios D. Tsoukalas
            node = node[name]
74 00d2a0ee Georgios D. Tsoukalas
        except TypeError as e:
75 00d2a0ee Georgios D. Tsoukalas
            m = "'{0}': cannot traverse path beyond this node: {1}"
76 00d2a0ee Georgios D. Tsoukalas
            m = m.format(sep.join(name_path), str(e))
77 00d2a0ee Georgios D. Tsoukalas
            raise ValueError(m)
78 00d2a0ee Georgios D. Tsoukalas
        node_path.append(node)
79 00d2a0ee Georgios D. Tsoukalas
80 00d2a0ee Georgios D. Tsoukalas
    return name_path, node_path, basename
81 00d2a0ee Georgios D. Tsoukalas
82 00d2a0ee Georgios D. Tsoukalas
83 00d2a0ee Georgios D. Tsoukalas
def walk_paths(container):
84 00d2a0ee Georgios D. Tsoukalas
    for name, node in container.iteritems():
85 00d2a0ee Georgios D. Tsoukalas
        if not hasattr(node, 'items'):
86 00d2a0ee Georgios D. Tsoukalas
            yield [name], [node]
87 00d2a0ee Georgios D. Tsoukalas
        else:
88 00d2a0ee Georgios D. Tsoukalas
            for names, nodes in walk_paths(node):
89 00d2a0ee Georgios D. Tsoukalas
                yield [name] + names, [node] + nodes
90 00d2a0ee Georgios D. Tsoukalas
91 00d2a0ee Georgios D. Tsoukalas
92 00d2a0ee Georgios D. Tsoukalas
def list_paths(container, sep='.'):
93 00d2a0ee Georgios D. Tsoukalas
    """
94 00d2a0ee Georgios D. Tsoukalas
    >>> sorted(list_paths({'a': {'b': {'c': 'd'}}}))
95 00d2a0ee Georgios D. Tsoukalas
    [('a.b.c', 'd')]
96 00d2a0ee Georgios D. Tsoukalas
    >>> sorted(list_paths({'a': {'b': {'c': 'd'}, 'e': 3}}))
97 00d2a0ee Georgios D. Tsoukalas
    [('a.b.c', 'd'), ('a.e', 3)]
98 00d2a0ee Georgios D. Tsoukalas
    >>> sorted(list_paths({'a': {'b': {'c': 'd'}, 'e': {'f': 3}}}))
99 00d2a0ee Georgios D. Tsoukalas
    [('a.b.c', 'd'), ('a.e.f', 3)]
100 00d2a0ee Georgios D. Tsoukalas
    >>> list_paths({})
101 00d2a0ee Georgios D. Tsoukalas
    []
102 00d2a0ee Georgios D. Tsoukalas

103 00d2a0ee Georgios D. Tsoukalas
    """
104 00d2a0ee Georgios D. Tsoukalas
    return [(sep.join(name_path), node_path[-1])
105 00d2a0ee Georgios D. Tsoukalas
            for name_path, node_path in walk_paths(container)]
106 00d2a0ee Georgios D. Tsoukalas
107 00d2a0ee Georgios D. Tsoukalas
108 00d2a0ee Georgios D. Tsoukalas
def del_path(container, path, sep='.', collect=True):
109 00d2a0ee Georgios D. Tsoukalas
    """
110 00d2a0ee Georgios D. Tsoukalas
    del container['a']['b']['c'] where path=sep.join(['a','b','c'])
111 00d2a0ee Georgios D. Tsoukalas

112 00d2a0ee Georgios D. Tsoukalas
    >>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c'); d
113 00d2a0ee Georgios D. Tsoukalas
    {}
114 00d2a0ee Georgios D. Tsoukalas
    >>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c', collect=False); d
115 00d2a0ee Georgios D. Tsoukalas
    {'a': {'b': {}}}
116 00d2a0ee Georgios D. Tsoukalas
    >>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c.d')
117 00d2a0ee Georgios D. Tsoukalas
    Traceback (most recent call last):
118 00d2a0ee Georgios D. Tsoukalas
    ValueError: 'a.b.c': cannot traverse path beyond this node:\
119 00d2a0ee Georgios D. Tsoukalas
 'str' object does not support item deletion
120 00d2a0ee Georgios D. Tsoukalas
    """
121 00d2a0ee Georgios D. Tsoukalas
122 00d2a0ee Georgios D. Tsoukalas
    name_path, node_path, basename = \
123 8975f6f6 Christos Stavrakakis
        lookup_path(container, path, sep=sep, createpath=False)
124 00d2a0ee Georgios D. Tsoukalas
125 00d2a0ee Georgios D. Tsoukalas
    lastnode = node_path.pop()
126 00d2a0ee Georgios D. Tsoukalas
    try:
127 00d2a0ee Georgios D. Tsoukalas
        if basename in lastnode:
128 00d2a0ee Georgios D. Tsoukalas
            del lastnode[basename]
129 00d2a0ee Georgios D. Tsoukalas
    except (TypeError, KeyError) as e:
130 00d2a0ee Georgios D. Tsoukalas
        m = "'{0}': cannot traverse path beyond this node: {1}"
131 00d2a0ee Georgios D. Tsoukalas
        m = m.format(sep.join(name_path), str(e))
132 00d2a0ee Georgios D. Tsoukalas
        raise ValueError(m)
133 00d2a0ee Georgios D. Tsoukalas
134 00d2a0ee Georgios D. Tsoukalas
    if collect:
135 00d2a0ee Georgios D. Tsoukalas
        while node_path and not lastnode:
136 00d2a0ee Georgios D. Tsoukalas
            basename = name_path.pop()
137 00d2a0ee Georgios D. Tsoukalas
            lastnode = node_path.pop()
138 00d2a0ee Georgios D. Tsoukalas
            del lastnode[basename]
139 00d2a0ee Georgios D. Tsoukalas
140 00d2a0ee Georgios D. Tsoukalas
141 00d2a0ee Georgios D. Tsoukalas
def get_path(container, path, sep='.'):
142 00d2a0ee Georgios D. Tsoukalas
    """
143 00d2a0ee Georgios D. Tsoukalas
    return container['a']['b']['c'] where path=sep.join(['a','b','c'])
144 00d2a0ee Georgios D. Tsoukalas

145 00d2a0ee Georgios D. Tsoukalas
    >>> get_path({'a': {'b': {'c': 'd'}}}, 'a.b.c.d')
146 00d2a0ee Georgios D. Tsoukalas
    Traceback (most recent call last):
147 00d2a0ee Georgios D. Tsoukalas
    ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
148 00d2a0ee Georgios D. Tsoukalas
 string indices must be integers, not str
149 00d2a0ee Georgios D. Tsoukalas
    >>> get_path({'a': {'b': {'c': 1}}}, 'a.b.c.d')
150 00d2a0ee Georgios D. Tsoukalas
    Traceback (most recent call last):
151 00d2a0ee Georgios D. Tsoukalas
    ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
152 00d2a0ee Georgios D. Tsoukalas
 'int' object is unsubscriptable
153 00d2a0ee Georgios D. Tsoukalas
    >>> get_path({'a': {'b': {'c': 1}}}, 'a.b.c')
154 00d2a0ee Georgios D. Tsoukalas
    1
155 00d2a0ee Georgios D. Tsoukalas
    >>> get_path({'a': {'b': {'c': 1}}}, 'a.b')
156 00d2a0ee Georgios D. Tsoukalas
    {'c': 1}
157 00d2a0ee Georgios D. Tsoukalas

158 00d2a0ee Georgios D. Tsoukalas
    """
159 00d2a0ee Georgios D. Tsoukalas
    name_path, node_path, basename = \
160 8975f6f6 Christos Stavrakakis
        lookup_path(container, path, sep=sep, createpath=False)
161 00d2a0ee Georgios D. Tsoukalas
    name_path.append(basename)
162 00d2a0ee Georgios D. Tsoukalas
    node = node_path[-1]
163 00d2a0ee Georgios D. Tsoukalas
164 00d2a0ee Georgios D. Tsoukalas
    try:
165 00d2a0ee Georgios D. Tsoukalas
        return node[basename]
166 00d2a0ee Georgios D. Tsoukalas
    except TypeError as e:
167 00d2a0ee Georgios D. Tsoukalas
        m = "'{0}': cannot traverse path beyond this node: {1}"
168 00d2a0ee Georgios D. Tsoukalas
        m = m.format(sep.join(name_path), str(e))
169 00d2a0ee Georgios D. Tsoukalas
        raise ValueError(m)
170 00d2a0ee Georgios D. Tsoukalas
    except KeyError as e:
171 00d2a0ee Georgios D. Tsoukalas
        m = "'{0}': path not found: {1}"
172 00d2a0ee Georgios D. Tsoukalas
        m = m.format(sep.join(name_path), str(e))
173 00d2a0ee Georgios D. Tsoukalas
        raise KeyError(m)
174 00d2a0ee Georgios D. Tsoukalas
175 00d2a0ee Georgios D. Tsoukalas
176 00d2a0ee Georgios D. Tsoukalas
def set_path(container, path, value, sep='.',
177 00d2a0ee Georgios D. Tsoukalas
             createpath=False, overwrite=True):
178 00d2a0ee Georgios D. Tsoukalas
    """
179 00d2a0ee Georgios D. Tsoukalas
    container['a']['b']['c'] = value where path=sep.join(['a','b','c'])
180 00d2a0ee Georgios D. Tsoukalas

181 00d2a0ee Georgios D. Tsoukalas
    >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c.d', 1)
182 00d2a0ee Georgios D. Tsoukalas
    Traceback (most recent call last):
183 00d2a0ee Georgios D. Tsoukalas
    ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
184 00d2a0ee Georgios D. Tsoukalas
 'str' object does not support item assignment
185 00d2a0ee Georgios D. Tsoukalas
    >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.x.d', 1)
186 00d2a0ee Georgios D. Tsoukalas
    Traceback (most recent call last):
187 00d2a0ee Georgios D. Tsoukalas
    KeyError: "'a.b.x': path not found"
188 00d2a0ee Georgios D. Tsoukalas
    >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.x.d', 1, createpath=True)
189 8975f6f6 Christos Stavrakakis

190 00d2a0ee Georgios D. Tsoukalas
    >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c', 1)
191 8975f6f6 Christos Stavrakakis

192 00d2a0ee Georgios D. Tsoukalas
    >>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c', 1, overwrite=False)
193 00d2a0ee Georgios D. Tsoukalas
    Traceback (most recent call last):
194 00d2a0ee Georgios D. Tsoukalas
    ValueError: will not overwrite path 'a.b.c'
195 00d2a0ee Georgios D. Tsoukalas

196 00d2a0ee Georgios D. Tsoukalas
    """
197 00d2a0ee Georgios D. Tsoukalas
    name_path, node_path, basename = \
198 8975f6f6 Christos Stavrakakis
        lookup_path(container, path, sep=sep, createpath=createpath)
199 00d2a0ee Georgios D. Tsoukalas
    name_path.append(basename)
200 00d2a0ee Georgios D. Tsoukalas
    node = node_path[-1]
201 00d2a0ee Georgios D. Tsoukalas
202 00d2a0ee Georgios D. Tsoukalas
    if basename in node and not overwrite:
203 00d2a0ee Georgios D. Tsoukalas
        m = "will not overwrite path '{0}'".format(path)
204 00d2a0ee Georgios D. Tsoukalas
        raise ValueError(m)
205 00d2a0ee Georgios D. Tsoukalas
206 00d2a0ee Georgios D. Tsoukalas
    try:
207 00d2a0ee Georgios D. Tsoukalas
        node[basename] = value
208 00d2a0ee Georgios D. Tsoukalas
    except TypeError as e:
209 00d2a0ee Georgios D. Tsoukalas
        m = "'{0}': cannot traverse path beyond this node: {1}"
210 00d2a0ee Georgios D. Tsoukalas
        m = m.format(sep.join(name_path), str(e))
211 00d2a0ee Georgios D. Tsoukalas
        raise ValueError(m)
212 00d2a0ee Georgios D. Tsoukalas
213 00d2a0ee Georgios D. Tsoukalas
214 00d2a0ee Georgios D. Tsoukalas
if __name__ == '__main__':
215 00d2a0ee Georgios D. Tsoukalas
    import doctest
216 00d2a0ee Georgios D. Tsoukalas
    doctest.testmod()