def providers_for(self, vpkg_spec): if self.provider_index is None: self.provider_index = ProviderIndex(self.all_package_names()) providers = self.provider_index.providers_for(vpkg_spec) if not providers: raise UnknownPackageError("No such virtual package: %s" % vpkg_spec) return providers
class PackageDB(object): def __init__(self, root): """Construct a new package database from a root directory.""" self.root = root self.instances = {} self.provider_index = None @_autospec def get(self, spec): if spec.virtual: raise UnknownPackageError(spec.name) if not spec in self.instances: package_class = self.get_class_for_package_name(spec.name) self.instances[spec.name] = package_class(spec) return self.instances[spec.name] @_autospec def get_installed(self, spec): """Get all the installed specs that satisfy the provided spec constraint.""" return [s for s in self.installed_package_specs() if s.satisfies(spec)] @_autospec def providers_for(self, vpkg_spec): if self.provider_index is None: self.provider_index = ProviderIndex(self.all_package_names()) providers = self.provider_index.providers_for(vpkg_spec) if not providers: raise UnknownPackageError("No such virtual package: %s" % vpkg_spec) return providers def dirname_for_package_name(self, pkg_name): """Get the directory name for a particular package. This is the directory that contains its package.py file.""" return join_path(self.root, pkg_name) def filename_for_package_name(self, pkg_name): """Get the filename for the module we should load for a particular package. Packages for a pacakge DB live in ``$root/<package_name>/package.py`` This will return a proper package.py path even if the 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) return join_path(pkg_dir, _package_file_name) def installed_package_specs(self): """Read installed package names straight from the install directory layout. """ return spack.install_layout.all_specs() @memoized def all_package_names(self): """Generator function for all packages. This looks for ``<pkg_name>/package.py`` files within the root direcotry""" all_package_names = [] for pkg_name in os.listdir(self.root): pkg_dir = join_path(self.root, pkg_name) pkg_file = join_path(pkg_dir, _package_file_name) if os.path.isfile(pkg_file): all_package_names.append(pkg_name) all_package_names.sort() return all_package_names def all_packages(self): for name in self.all_package_names(): yield self.get(name) def exists(self, pkg_name): """Whether a package with the supplied name exists .""" return os.path.exists(self.filename_for_package_name(pkg_name)) @memoized def get_class_for_package_name(self, pkg_name): """Get an instance of the class for a particular package. This method uses Python's ``imp`` package to load python source from a Spack package's ``package.py`` file. A normal python import would only load each package once, but because we do this dynamically, the method needs to be memoized to ensure there is only ONE package class instance, per package, per database. """ file_path = self.filename_for_package_name(pkg_name) if os.path.exists(file_path): if not os.path.isfile(file_path): tty.die("Something's wrong. '%s' is not a file!" % file_path) if not os.access(file_path, os.R_OK): tty.die("Cannot read '%s'!" % file_path) else: raise UnknownPackageError(pkg_name) class_name = mod_to_class(pkg_name) try: module_name = _imported_packages_module + '.' + pkg_name module = imp.load_source(module_name, file_path) except ImportError, e: tty.die("Error while importing %s from %s:\n%s" % ( pkg_name, file_path, e.message)) cls = getattr(module, class_name) if not inspect.isclass(cls): tty.die("%s.%s is not a class" % (pkg_name, class_name)) return cls