Statistics
| Branch: | Tag: | Revision:

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()