summaryrefslogtreecommitdiff
path: root/lib/spack/spack/repository.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/spack/repository.py')
-rw-r--r--lib/spack/spack/repository.py103
1 files changed, 80 insertions, 23 deletions
diff --git a/lib/spack/spack/repository.py b/lib/spack/spack/repository.py
index 3367572ef5..31596cee7a 100644
--- a/lib/spack/spack/repository.py
+++ b/lib/spack/spack/repository.py
@@ -54,6 +54,9 @@ repo_config_name = 'repo.yaml' # Top-level filename for repo config.
packages_dir_name = 'packages' # Top-level repo directory containing pkgs.
package_file_name = 'package.py' # Filename for packages in a repository.
+# Guaranteed unused default value for some functions.
+NOT_PROVIDED = object()
+
def _autospec(function):
"""Decorator that automatically converts the argument of a single-arg
@@ -75,7 +78,15 @@ def _make_namespace_module(ns):
def substitute_spack_prefix(path):
"""Replaces instances of $spack with Spack's prefix."""
- return path.replace('$spack', spack.prefix)
+ return re.sub(r'^\$spack', spack.prefix, path)
+
+
+def canonicalize_path(path):
+ """Substitute $spack, expand user home, take abspath."""
+ path = substitute_spack_prefix(path)
+ path = os.path.expanduser(path)
+ path = os.path.abspath(path)
+ return path
class RepoPath(object):
@@ -109,7 +120,10 @@ class RepoPath(object):
repo = Repo(root, self.super_namespace)
self.put_last(repo)
except RepoError as e:
- tty.warn("Failed to initialize repository at '%s'." % root, e.message)
+ tty.warn("Failed to initialize repository at '%s'." % root,
+ e.message,
+ "To remove the bad repository, run this command:",
+ " spack repo rm %s" % root)
def swap(self, other):
@@ -173,6 +187,31 @@ class RepoPath(object):
self.repos.remove(repo)
+ def get_repo(self, namespace, default=NOT_PROVIDED):
+ """Get a repository by namespace.
+ Arguments
+ namespace
+ Look up this namespace in the RepoPath, and return
+ it if found.
+
+ Optional Arguments
+ default
+ If default is provided, return it when the namespace
+ isn't found. If not, raise an UnknownNamespaceError.
+ """
+ fullspace = '%s.%s' % (self.super_namespace, namespace)
+ if fullspace not in self.by_namespace:
+ if default == NOT_PROVIDED:
+ raise UnknownNamespaceError(namespace)
+ return default
+ return self.by_namespace[fullspace]
+
+
+ def first_repo(self):
+ """Get the first repo in precedence order."""
+ return self.repos[0] if self.repos else None
+
+
def all_package_names(self):
"""Return all unique package names in all repositories."""
return self._all_package_names
@@ -229,7 +268,6 @@ class RepoPath(object):
if fullname in sys.modules:
return sys.modules[fullname]
-
# partition fullname into prefix and module name.
namespace, dot, module_name = fullname.rpartition('.')
@@ -242,11 +280,23 @@ class RepoPath(object):
return module
- def repo_for_pkg(self, pkg_name):
+ @_autospec
+ def repo_for_pkg(self, spec):
+ """Given a spec, get the repository for its package."""
+ # If the spec already has a namespace, then return the
+ # corresponding repo if we know about it.
+ if spec.namespace:
+ fullspace = '%s.%s' % (self.super_namespace, spec.namespace)
+ if fullspace not in self.by_namespace:
+ raise UnknownNamespaceError(spec.namespace)
+ return self.by_namespace[fullspace]
+
+ # If there's no namespace, search in the RepoPath.
for repo in self.repos:
- if pkg_name in repo:
+ if spec.name in repo:
return repo
- raise UnknownPackageError(pkg_name)
+ else:
+ raise UnknownPackageError(spec.name)
@_autospec
@@ -255,16 +305,7 @@ class RepoPath(object):
Raises UnknownPackageError if not found.
"""
- # if the spec has a fully qualified namespace, we grab it
- # directly and ignore overlay precedence.
- if spec.namespace:
- fullspace = '%s.%s' % (self.super_namespace, spec.namespace)
- if not fullspace in self.by_namespace:
- raise UnknownPackageError(
- "No configured repository contains package %s." % spec.fullname)
- return self.by_namespace[fullspace].get(spec)
- else:
- return self.repo_for_pkg(spec.name).get(spec)
+ return self.repo_for_pkg(spec).get(spec)
def dirname_for_package_name(self, pkg_name):
@@ -310,7 +351,7 @@ class Repo(object):
"""
# Root directory, containing _repo.yaml and package dirs
# Allow roots to by spack-relative by starting with '$spack'
- self.root = substitute_spack_prefix(root)
+ self.root = canonicalize_path(root)
# super-namespace for all packages in the Repo
self.super_namespace = namespace
@@ -330,7 +371,7 @@ class Repo(object):
# Read configuration and validate namespace
config = self._read_config()
check('namespace' in config, '%s must define a namespace.'
- % join_path(self.root, repo_config_name))
+ % join_path(root, repo_config_name))
self.namespace = config['namespace']
check(re.match(r'[a-zA-Z][a-zA-Z0-9_.]+', self.namespace),
@@ -524,13 +565,22 @@ class Repo(object):
return [p for p in self.all_packages() if p.extends(extendee_spec)]
- def dirname_for_package_name(self, pkg_name):
+ def _check_namespace(self, spec):
+ """Check that the spec's namespace is the same as this repository's."""
+ if spec.namespace and spec.namespace != self.namespace:
+ raise UnknownNamespaceError(spec.namespace)
+
+
+ @_autospec
+ def dirname_for_package_name(self, spec):
"""Get the directory name for a particular package. This is the
directory that contains its package.py file."""
- return join_path(self.packages_path, pkg_name)
+ self._check_namespace(spec)
+ return join_path(self.packages_path, spec.name)
- def filename_for_package_name(self, pkg_name):
+ @_autospec
+ def filename_for_package_name(self, spec):
"""Get the filename for the module we should load for a particular
package. Packages for a Repo live in
``$root/<package_name>/package.py``
@@ -539,8 +589,8 @@ class Repo(object):
package doesn't exist yet, so callers will need to ensure
the package exists before importing.
"""
- validate_module_name(pkg_name)
- pkg_dir = self.dirname_for_package_name(pkg_name)
+ self._check_namespace(spec)
+ pkg_dir = self.dirname_for_package_name(spec.name)
return join_path(pkg_dir, package_file_name)
@@ -679,6 +729,13 @@ class UnknownPackageError(PackageLoadError):
self.name = name
+class UnknownNamespaceError(PackageLoadError):
+ """Raised when we encounter an unknown namespace"""
+ def __init__(self, namespace):
+ super(UnknownNamespaceError, self).__init__(
+ "Unknown namespace: %s" % namespace)
+
+
class FailedConstructorError(PackageLoadError):
"""Raised when a package's class constructor fails."""
def __init__(self, name, exc_type, exc_obj, exc_tb):