Fix and move NamespaceTrie to spack.util.naming
- fix up routines in namespace trie. - trie can now hold intermediate elements. - trie now has a test case.
This commit is contained in:
parent
72c9604bcb
commit
38fdd063d9
4 changed files with 154 additions and 42 deletions
|
@ -58,45 +58,6 @@ def converter(self, spec_like, *args, **kwargs):
|
||||||
return converter
|
return converter
|
||||||
|
|
||||||
|
|
||||||
class NamespaceTrie(object):
|
|
||||||
def __init__(self):
|
|
||||||
self._elements = {}
|
|
||||||
|
|
||||||
|
|
||||||
def __setitem__(self, namespace, repo):
|
|
||||||
parts = namespace.split('.')
|
|
||||||
cur = self._elements
|
|
||||||
for p in parts[:-1]:
|
|
||||||
if p not in cur:
|
|
||||||
cur[p] = {}
|
|
||||||
cur = cur[p]
|
|
||||||
|
|
||||||
cur[parts[-1]] = repo
|
|
||||||
|
|
||||||
|
|
||||||
def __getitem__(self, namespace):
|
|
||||||
parts = namespace.split('.')
|
|
||||||
cur = self._elements
|
|
||||||
for p in parts:
|
|
||||||
if p not in cur:
|
|
||||||
raise KeyError("Can't find namespace %s in trie" % namespace)
|
|
||||||
cur = cur[p]
|
|
||||||
return cur
|
|
||||||
|
|
||||||
|
|
||||||
def __contains__(self, namespace):
|
|
||||||
parts = namespace.split('.')
|
|
||||||
cur = self._elements
|
|
||||||
for p in parts:
|
|
||||||
if not isinstance(cur, dict):
|
|
||||||
return False
|
|
||||||
if p not in cur:
|
|
||||||
return False
|
|
||||||
cur = cur[p]
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PackageFinder(object):
|
class PackageFinder(object):
|
||||||
"""A PackageFinder is a wrapper around a list of PackageDBs.
|
"""A PackageFinder is a wrapper around a list of PackageDBs.
|
||||||
|
|
||||||
|
@ -172,7 +133,7 @@ def all_packages(self):
|
||||||
|
|
||||||
|
|
||||||
def providers_for(self, vpkg_name):
|
def providers_for(self, vpkg_name):
|
||||||
# TODO: THIS IS WRONG; shoudl use more than first repo
|
# TODO: THIS IS WRONG; should use more than first repo
|
||||||
return self.repos[0].providers_for(vpkg_name)
|
return self.repos[0].providers_for(vpkg_name)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,8 @@
|
||||||
'configure_guess',
|
'configure_guess',
|
||||||
'unit_install',
|
'unit_install',
|
||||||
'lock',
|
'lock',
|
||||||
'database']
|
'database',
|
||||||
|
'namespace_trie']
|
||||||
|
|
||||||
|
|
||||||
def list_tests():
|
def list_tests():
|
||||||
|
|
83
lib/spack/spack/test/namespace_trie.py
Normal file
83
lib/spack/spack/test/namespace_trie.py
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
##############################################################################
|
||||||
|
# Copyright (c) 2013, Lawrence Livermore National Security, LLC.
|
||||||
|
# Produced at the Lawrence Livermore National Laboratory.
|
||||||
|
#
|
||||||
|
# This file is part of Spack.
|
||||||
|
# Written by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
|
||||||
|
# LLNL-CODE-647188
|
||||||
|
#
|
||||||
|
# For details, see https://scalability-llnl.github.io/spack
|
||||||
|
# Please also see the LICENSE file for our notice and the LGPL.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License (as published by
|
||||||
|
# the Free Software Foundation) version 2.1 dated February 1999.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
|
||||||
|
# conditions of the GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License
|
||||||
|
# along with this program; if not, write to the Free Software Foundation,
|
||||||
|
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
##############################################################################
|
||||||
|
import unittest
|
||||||
|
from spack.util.naming import NamespaceTrie
|
||||||
|
|
||||||
|
|
||||||
|
class NamespaceTrieTest(unittest.TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.trie = NamespaceTrie()
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_single(self):
|
||||||
|
self.trie['foo'] = 'bar'
|
||||||
|
self.assertEqual(self.trie['foo'], 'bar')
|
||||||
|
self.assertTrue('foo' in self.trie)
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_multiple(self):
|
||||||
|
self.trie['foo.bar'] = 'baz'
|
||||||
|
self.assertEqual(self.trie['foo.bar'], 'baz')
|
||||||
|
|
||||||
|
self.assertFalse('foo' in self.trie)
|
||||||
|
self.assertFalse('foo.bar.baz' in self.trie)
|
||||||
|
self.assertTrue('foo.bar' in self.trie)
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_three(self):
|
||||||
|
# add a three-level namespace
|
||||||
|
self.trie['foo.bar.baz'] = 'quux'
|
||||||
|
self.assertEqual(self.trie['foo.bar.baz'], 'quux')
|
||||||
|
|
||||||
|
self.assertFalse('foo' in self.trie)
|
||||||
|
self.assertFalse('foo.bar' in self.trie)
|
||||||
|
self.assertTrue('foo.bar.baz' in self.trie)
|
||||||
|
|
||||||
|
# Try to add a second element in a higher space
|
||||||
|
self.trie['foo.bar'] = 'blah'
|
||||||
|
|
||||||
|
self.assertFalse('foo' in self.trie)
|
||||||
|
|
||||||
|
self.assertTrue('foo.bar' in self.trie)
|
||||||
|
self.assertEqual(self.trie['foo.bar'], 'blah')
|
||||||
|
|
||||||
|
self.assertTrue('foo.bar.baz' in self.trie)
|
||||||
|
self.assertEqual(self.trie['foo.bar.baz'], 'quux')
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_none_single(self):
|
||||||
|
self.trie['foo'] = None
|
||||||
|
self.assertEqual(self.trie['foo'], None)
|
||||||
|
self.assertTrue('foo' in self.trie)
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_none_multiple(self):
|
||||||
|
self.trie['foo.bar'] = None
|
||||||
|
self.assertEqual(self.trie['foo.bar'], None)
|
||||||
|
|
||||||
|
self.assertFalse('foo' in self.trie)
|
||||||
|
self.assertFalse('foo.bar.baz' in self.trie)
|
||||||
|
self.assertTrue('foo.bar' in self.trie)
|
|
@ -3,11 +3,12 @@
|
||||||
import string
|
import string
|
||||||
import itertools
|
import itertools
|
||||||
import re
|
import re
|
||||||
|
from StringIO import StringIO
|
||||||
|
|
||||||
import spack
|
import spack
|
||||||
|
|
||||||
__all__ = ['mod_to_class', 'spack_module_to_python_module', 'valid_module_name',
|
__all__ = ['mod_to_class', 'spack_module_to_python_module', 'valid_module_name',
|
||||||
'validate_module_name', 'possible_spack_module_names']
|
'validate_module_name', 'possible_spack_module_names', 'NamespaceTrie']
|
||||||
|
|
||||||
# Valid module names can contain '-' but can't start with it.
|
# Valid module names can contain '-' but can't start with it.
|
||||||
_valid_module_re = r'^\w[\w-]*$'
|
_valid_module_re = r'^\w[\w-]*$'
|
||||||
|
@ -90,3 +91,69 @@ def __init__(self, name):
|
||||||
super(InvalidModuleNameError, self).__init__(
|
super(InvalidModuleNameError, self).__init__(
|
||||||
"Invalid module name: " + name)
|
"Invalid module name: " + name)
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
|
|
||||||
|
class NamespaceTrie(object):
|
||||||
|
class Element(object):
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, separator='.'):
|
||||||
|
self._subspaces = {}
|
||||||
|
self._value = None
|
||||||
|
self._sep = separator
|
||||||
|
|
||||||
|
|
||||||
|
def __setitem__(self, namespace, value):
|
||||||
|
first, sep, rest = namespace.partition(self._sep)
|
||||||
|
|
||||||
|
if not first:
|
||||||
|
self._value = NamespaceTrie.Element(value)
|
||||||
|
return
|
||||||
|
|
||||||
|
if first not in self._subspaces:
|
||||||
|
self._subspaces[first] = NamespaceTrie()
|
||||||
|
|
||||||
|
self._subspaces[first][rest] = value
|
||||||
|
|
||||||
|
|
||||||
|
def _get_helper(self, namespace, full_name):
|
||||||
|
first, sep, rest = namespace.partition(self._sep)
|
||||||
|
if not first:
|
||||||
|
if not self._value:
|
||||||
|
raise KeyError("Can't find namespace '%s' in trie" % full_name)
|
||||||
|
return self._value.value
|
||||||
|
elif first not in self._subspaces:
|
||||||
|
raise KeyError("Can't find namespace '%s' in trie" % full_name)
|
||||||
|
else:
|
||||||
|
return self._subspaces[first]._get_helper(rest, full_name)
|
||||||
|
|
||||||
|
|
||||||
|
def __getitem__(self, namespace):
|
||||||
|
return self._get_helper(namespace, namespace)
|
||||||
|
|
||||||
|
|
||||||
|
def __contains__(self, namespace):
|
||||||
|
first, sep, rest = namespace.partition(self._sep)
|
||||||
|
if not first:
|
||||||
|
return self._value is not None
|
||||||
|
elif first not in self._subspaces:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return rest in self._subspaces[first]
|
||||||
|
|
||||||
|
|
||||||
|
def _str_helper(self, stream, level=0):
|
||||||
|
indent = (level * ' ')
|
||||||
|
for name in sorted(self._subspaces):
|
||||||
|
stream.write(indent + name + '\n')
|
||||||
|
if self._value:
|
||||||
|
stream.write(indent + ' ' + repr(self._value.value))
|
||||||
|
stream.write(self._subspaces[name]._str_helper(stream, level+1))
|
||||||
|
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
stream = StringIO()
|
||||||
|
self._str_helper(stream)
|
||||||
|
return stream.getvalue()
|
||||||
|
|
Loading…
Reference in a new issue