Commit a8fa58b5 authored by Giorgos Korfiatis's avatar Giorgos Korfiatis

common: Do not encode key paths as string

Handle explicitly key paths as lists of keys. This allows the key
to be any python object, as is the case in python dicts.

Provide utility unpack() to convert from a string representation to
a list of keys. It assumes that a key is either a string or an integer
(if it looks like one).
parent e39a8ba2
......@@ -34,4 +34,5 @@
from astakos.im.settings import astakos_services
from synnefo.util.keypath import get_path
resources = get_path(astakos_services, 'astakos_account.resources').values()
resources = get_path(
astakos_services, ['astakos_account', 'resources']).values()
......@@ -50,11 +50,11 @@ BASE_HOST, BASE_PATH = parse_base_url(BASE_URL)
astakos_services = deepcopy(vanilla_astakos_services)
fill_endpoints(astakos_services, BASE_URL)
ACCOUNTS_PREFIX = get_path(astakos_services, 'astakos_account.prefix')
VIEWS_PREFIX = get_path(astakos_services, 'astakos_ui.prefix')
KEYSTONE_PREFIX = get_path(astakos_services, 'astakos_identity.prefix')
WEBLOGIN_PREFIX = get_path(astakos_services, 'astakos_weblogin.prefix')
ADMIN_PREFIX = get_path(astakos_services, 'astakos_admin.prefix')
ACCOUNTS_PREFIX = get_path(astakos_services, ['astakos_account', 'prefix'])
VIEWS_PREFIX = get_path(astakos_services, ['astakos_ui', 'prefix'])
KEYSTONE_PREFIX = get_path(astakos_services, ['astakos_identity', 'prefix'])
WEBLOGIN_PREFIX = get_path(astakos_services, ['astakos_weblogin', 'prefix'])
ADMIN_PREFIX = get_path(astakos_services, ['astakos_admin', 'prefix'])
# Set the expiration time of newly created auth tokens
# to be this many hours after their creation time.
......
......@@ -39,16 +39,16 @@ from urlparse import urlparse
def fill_endpoints(services, base_url):
for name, service in services.iteritems():
prefix = get_path(service, 'prefix')
endpoints = get_path(service, 'endpoints')
prefix = get_path(service, ['prefix'])
endpoints = get_path(service, ['endpoints'])
for endpoint in endpoints:
version = get_path(endpoint, 'versionId')
publicURL = get_path(endpoint, 'publicURL')
version = get_path(endpoint, ['versionId'])
publicURL = get_path(endpoint, ['publicURL'])
if publicURL is not None:
continue
publicURL = join_urls(base_url, prefix, version).rstrip('/')
set_path(endpoint, 'publicURL', publicURL)
set_path(endpoint, ['publicURL'], publicURL)
def filter_public(services):
......
......@@ -36,52 +36,49 @@ import re
integer_re = re.compile('-?[0-9]+')
def join_path(sep, path):
iterable = ((str(n) if isinstance(n, (int, long)) else n) for n in path)
return sep.join(iterable)
def unpack(pathstr, sep='.'):
"""
>>> unpack('a.-2.x')
['a', -2, 'x']
"""
names = pathstr.split(sep)
parse = lambda x: int(x) if integer_re.match(x) else x
return [parse(x) for x in names]
def lookup_path(container, path, sep='.', createpath=False):
def lookup_path(container, path, createpath=False):
"""
return (['a','b'],
[container['a'], container['a']['b']],
'c') where path=sep.join(['a','b','c'])
'c') where path=['a','b','c']
"""
names = path.split(sep)
dirnames = names[:-1]
basename = names[-1]
if integer_re.match(basename):
basename = int(basename)
dirnames = path[:-1]
basename = path[-1]
node = container
name_path = []
node_path = [node]
for name in dirnames:
name_path.append(name)
if integer_re.match(name):
name = int(name)
try:
node = node[name]
except KeyError as e:
if not createpath:
m = "'{0}': path not found".format(join_path(sep, name_path))
m = "{0}: path not found".format(name_path)
raise KeyError(m)
node[name] = {}
node = node[name]
except IndexError as e:
if not createpath:
m = "'{0}': path not found: {1}".format(
join_path(sep, name_path), e)
m = "{0}: path not found: {1}".format(name_path, e)
raise KeyError(m)
size = name if name > 0 else -name
node += (dict() for _ in xrange(len(node), size))
node = node[name]
except TypeError as e:
m = "'{0}': cannot traverse path beyond this node: {1}"
m = m.format(join_path(sep, name_path), str(e))
m = "{0}: cannot traverse path beyond this node: {1}"
m = m.format(name_path, str(e))
raise ValueError(m)
node_path.append(node)
......@@ -97,49 +94,49 @@ def walk_paths(container):
yield [name] + names, [node] + nodes
def list_paths(container, sep='.'):
def list_paths(container):
"""
>>> sorted(list_paths({'a': {'b': {'c': 'd'}}}))
[('a.b.c', 'd')]
[(['a', 'b', 'c'], 'd')]
>>> sorted(list_paths({'a': {'b': {'c': 'd'}, 'e': 3}}))
[('a.b.c', 'd'), ('a.e', 3)]
[(['a', 'b', 'c'], 'd'), (['a', 'e'], 3)]
>>> sorted(list_paths({'a': {'b': {'c': 'd'}, 'e': {'f': 3}}}))
[('a.b.c', 'd'), ('a.e.f', 3)]
[(['a', 'b', 'c'], 'd'), (['a', 'e', 'f'], 3)]
>>> sorted(list_paths({'a': [{'b': 3}, 2]}))
[('a', [{'b': 3}, 2])]
[(['a'], [{'b': 3}, 2])]
>>> list_paths({})
[]
"""
return [(join_path(sep, name_path), node_path[-1])
return [(name_path, node_path[-1])
for name_path, node_path in walk_paths(container)]
def del_path(container, path, sep='.', collect=True):
def del_path(container, path, collect=True):
"""
del container['a']['b']['c'] where path=sep.join(['a','b','c'])
del container['a']['b']['c'] where path=['a','b','c']
>>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c'); d
>>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, ['a', 'b', 'c']); d
{}
>>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c', collect=False); d
>>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, ['a', 'b', 'c'],\
collect=False); d
{'a': {'b': {}}}
>>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, 'a.b.c.d')
>>> d = {'a': {'b': {'c': 'd'}}}; del_path(d, ['a', 'b', 'c', 'd'])
Traceback (most recent call last):
ValueError: 'a.b.c': cannot traverse path beyond this node:\
ValueError: ['a', 'b', 'c']: cannot traverse path beyond this node:\
'str' object does not support item deletion
"""
name_path, node_path, basename = \
lookup_path(container, path, sep=sep, createpath=False)
lookup_path(container, path, createpath=False)
lastnode = node_path.pop()
lastname = basename
try:
if basename in lastnode:
del lastnode[basename]
except (TypeError, KeyError) as e:
m = "'{0}': cannot traverse path beyond this node: {1}"
m = m.format(join_path(sep, name_path), str(e))
m = "{0}: cannot traverse path beyond this node: {1}"
m = m.format(name_path, str(e))
raise ValueError(m)
if collect:
......@@ -149,100 +146,111 @@ def del_path(container, path, sep='.', collect=True):
del lastnode[basename]
def get_path(container, path, sep='.'):
def get_path(container, path):
"""
return container['a']['b']['c'] where path=sep.join(['a','b','c'])
return container['a']['b']['c'] where path=['a','b','c']
>>> get_path({'a': {'b': {'c': 'd'}}}, 'a.b.c.d')
>>> get_path({'a': {'b': {'c': 'd'}}}, ['a', 'b', 'c', 'd'])
Traceback (most recent call last):
ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
ValueError: ['a', 'b', 'c', 'd']: cannot traverse path beyond this node:\
string indices must be integers, not str
>>> get_path({'a': {'b': {'c': 1}}}, 'a.b.c.d')
>>> get_path({'a': {'b': {'c': 1}}}, ['a', 'b', 'c', 'd'])
Traceback (most recent call last):
ValueError: 'a.b.c.d': cannot traverse path beyond this node:\
'int' object is unsubscriptable
>>> get_path({'a': {'b': {'c': 1}}}, 'a.b.c')
ValueError: ['a', 'b', 'c', 'd']: cannot traverse path beyond this node:\
'int' object has no attribute '__getitem__'
>>> get_path({'a': {'b': {'c': 1}}}, ['a', 'b', 'c'])
1
>>> get_path({'a': {'b': {'c': 1}}}, 'a.b')
>>> get_path({'a': {'b': {'c': 1}}}, ['a', 'b'])
{'c': 1}
>>> get_path({'a': [{'z': 1}]}, 'a.0')
>>> get_path({'a': [{'z': 1}]}, ['a', 'b'])
Traceback (most recent call last):
ValueError: ['a', 'b']: cannot traverse path beyond this node:\
list indices must be integers, not str
>>> get_path({'a': [{'z': 1}]}, ['a', 0])
{'z': 1}
>>> get_path({'a': [{'z': 1}]}, 'a.0.z')
>>> get_path({'a': [{'z': 1}]}, ['a', 1])
Traceback (most recent call last):
KeyError: "['a', 1]: path not found: list index out of range"
>>> get_path({'a': [{'z': 1}]}, ['a', 0, 'z'])
1
>>> get_path({'a': [{'z': 1}]}, 'a.-1.z')
>>> get_path({'a': [{'z': 1}]}, ['a', -1, 'z'])
1
"""
name_path, node_path, basename = \
lookup_path(container, path, sep=sep, createpath=False)
lookup_path(container, path, createpath=False)
name_path.append(basename)
node = node_path[-1]
try:
return node[basename]
except TypeError as e:
m = "'{0}': cannot traverse path beyond this node: {1}"
m = m.format(join_path(sep, name_path), str(e))
m = "{0}: cannot traverse path beyond this node: {1}"
m = m.format(name_path, str(e))
raise ValueError(m)
except KeyError as e:
m = "'{0}': path not found: {1}"
m = m.format(join_path(sep, name_path), str(e))
m = "{0}: path not found: {1}"
m = m.format(name_path, str(e))
raise KeyError(m)
except IndexError as e:
m = "{0}: path not found: {1}"
m = m.format(name_path, str(e))
raise KeyError(m)
def set_path(container, path, value, sep='.',
createpath=False, overwrite=True):
def set_path(container, path, value, createpath=False, overwrite=True):
"""
container['a']['b']['c'] = value where path=sep.join(['a','b','c'])
container['a']['b']['c'] = value where path=['a','b','c']
>>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c.d', 1)
>>> set_path({'a': {'b': {'c': 'd'}}}, ['a', 'b', 'c', 'd'], 1)
Traceback (most recent call last):
ValueError: 'a.b.c.d': cannot index non-object node with string
>>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.x.d', 1)
ValueError: ['a', 'b', 'c', 'd']: cannot index, node is neither dict nor\
list
>>> set_path({'a': {'b': {'c': 'd'}}}, ['a', 'b', 'x', 'd'], 1)
Traceback (most recent call last):
KeyError: "'a.b.x': path not found"
>>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.x.d', 1, createpath=True)
KeyError: "['a', 'b', 'x']: path not found"
>>> set_path({'a': {'b': {'c': 'd'}}}, ['a', 'b', 'x', 'd'], 1,\
createpath=True)
>>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c', 1)
>>> set_path({'a': {'b': {'c': 'd'}}}, ['a', 'b', 'c'], 1)
>>> set_path({'a': {'b': {'c': 'd'}}}, 'a.b.c', 1, overwrite=False)
>>> set_path({'a': {'b': {'c': 'd'}}}, ['a', 'b', 'c'], 1, overwrite=False)
Traceback (most recent call last):
ValueError: will not overwrite path 'a.b.c'
>>> d = {'a': [{'z': 1}]}; set_path(d, 'a.-2.1', 2, createpath=False)
ValueError: will not overwrite path ['a', 'b', 'c']
>>> d = {'a': [{'z': 1}]}; set_path(d, ['a', -2, 1], 2, createpath=False)
Traceback (most recent call last):
KeyError: "'a.-2': path not found: list index out of range"
>>> d = {'a': [{'z': 1}]}; set_path(d, 'a.-2.1', 2, createpath=True)
Traceback (most recent call last):
ValueError: 'a.-2.1': will not index object node with integer
>>> d = {'a': [{'z': 1}]}; set_path(d, 'a.-2.z', 2, createpath=True); \
d['a'][-2]['z']
KeyError: "['a', -2]: path not found: list index out of range"
>>> d = {'a': [{'z': 1}]}; set_path(d, ['a', -2, 1], 2, createpath=True); \
d['a'][-2][1]
2
"""
name_path, node_path, basename = \
lookup_path(container, path, sep=sep, createpath=createpath)
lookup_path(container, path, createpath=createpath)
name_path.append(basename)
node = node_path[-1]
if basename in node and not overwrite:
m = "will not overwrite path '{0}'".format(path)
m = "will not overwrite path {0}".format(path)
raise ValueError(m)
is_object_node = hasattr(node, 'keys')
is_string_name = isinstance(basename, basestring)
if not is_string_name and is_object_node:
m = "'{0}': will not index object node with integer"
m = m.format(join_path(sep, name_path))
is_list_node = isinstance(node, list)
if not is_object_node and not is_list_node:
m = "{0}: cannot index, node is neither dict nor list"
m = m.format(name_path)
raise ValueError(m)
if is_string_name and not is_object_node:
m = "'{0}': cannot index non-object node with string"
m = m.format(join_path(sep, name_path))
is_integer = isinstance(basename, (int, long))
if is_list_node and not is_integer:
m = "{0}: cannot index list node without an integer"
m = m.format(name_path)
raise ValueError(m)
try:
node[basename] = value
except TypeError as e:
m = "'{0}': cannot traverse path beyond this node: {1}"
m = m.format(join_path(sep, name_path), str(e))
m = "{0}: cannot traverse path beyond this node: {1}"
m = m.format(name_path, str(e))
raise ValueError(m)
......
......@@ -35,7 +35,7 @@ import logging
from django.conf import settings
from synnefo.lib import join_urls, parse_base_url
from synnefo.util.keypath import get_path, set_path
from synnefo.util.keypath import get_path, set_path, unpack
from synnefo.api.services import cyclades_services as vanilla_cyclades_services
from synnefo.lib.services import fill_endpoints
from astakosclient import AstakosClient
......@@ -57,16 +57,16 @@ CUSTOMIZE_SERVICES = getattr(settings, 'CYCLADES_CUSTOMIZE_SERVICES', ())
cyclades_services = deepcopy(vanilla_cyclades_services)
fill_endpoints(cyclades_services, BASE_URL)
for path, value in CUSTOMIZE_SERVICES:
set_path(cyclades_services, path, value, createpath=True)
COMPUTE_PREFIX = get_path(cyclades_services, 'cyclades_compute.prefix')
NETWORK_PREFIX = get_path(cyclades_services, 'cyclades_network.prefix')
VMAPI_PREFIX = get_path(cyclades_services, 'cyclades_vmapi.prefix')
PLANKTON_PREFIX = get_path(cyclades_services, 'cyclades_plankton.prefix')
HELPDESK_PREFIX = get_path(cyclades_services, 'cyclades_helpdesk.prefix')
UI_PREFIX = get_path(cyclades_services, 'cyclades_ui.prefix')
USERDATA_PREFIX = get_path(cyclades_services, 'cyclades_userdata.prefix')
ADMIN_PREFIX = get_path(cyclades_services, 'cyclades_admin.prefix')
set_path(cyclades_services, unpack(path), value, createpath=True)
COMPUTE_PREFIX = get_path(cyclades_services, ['cyclades_compute', 'prefix'])
NETWORK_PREFIX = get_path(cyclades_services, ['cyclades_network', 'prefix'])
VMAPI_PREFIX = get_path(cyclades_services, ['cyclades_vmapi', 'prefix'])
PLANKTON_PREFIX = get_path(cyclades_services, ['cyclades_plankton', 'prefix'])
HELPDESK_PREFIX = get_path(cyclades_services, ['cyclades_helpdesk', 'prefix'])
UI_PREFIX = get_path(cyclades_services, ['cyclades_ui', 'prefix'])
USERDATA_PREFIX = get_path(cyclades_services, ['cyclades_userdata', 'prefix'])
ADMIN_PREFIX = get_path(cyclades_services, ['cyclades_admin', 'prefix'])
COMPUTE_ROOT_URL = join_urls(BASE_URL, COMPUTE_PREFIX)
......
......@@ -35,5 +35,5 @@ from synnefo.util.keypath import get_path
from synnefo.api.services import cyclades_services
resources = \
get_path(cyclades_services, 'cyclades_compute.resources').values() +\
get_path(cyclades_services, 'cyclades_network.resources').values()
get_path(cyclades_services, ['cyclades_compute', 'resources']).values() +\
get_path(cyclades_services, ['cyclades_network', 'resources']).values()
......@@ -34,4 +34,5 @@
from synnefo.util.keypath import get_path
from pithos.api.settings import pithos_services
resources = get_path(pithos_services, 'pithos_object-store.resources').values()
resources = get_path(pithos_services,
['pithos_object-store', 'resources']).values()
......@@ -59,9 +59,9 @@ BASE_HOST, BASE_PATH = parse_base_url(BASE_URL)
pithos_services = deepcopy(vanilla_pithos_services)
fill_endpoints(pithos_services, BASE_URL)
PITHOS_PREFIX = get_path(pithos_services, 'pithos_object-store.prefix')
PUBLIC_PREFIX = get_path(pithos_services, 'pithos_public.prefix')
UI_PREFIX = get_path(pithos_services, 'pithos_ui.prefix')
PITHOS_PREFIX = get_path(pithos_services, ['pithos_object-store', 'prefix'])
PUBLIC_PREFIX = get_path(pithos_services, ['pithos_public', 'prefix'])
UI_PREFIX = get_path(pithos_services, ['pithos_ui', 'prefix'])
VIEW_PREFIX = join_urls(UI_PREFIX, 'view')
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment