Index: recipe_engine/loader.py |
diff --git a/recipe_engine/loader.py b/recipe_engine/loader.py |
index eafbeb5d4f29b4187c134b979b36a3bfb0e3ff6b..a88d6c7432e5371b5024f1d33810eec08a6afdb4 100644 |
--- a/recipe_engine/loader.py |
+++ b/recipe_engine/loader.py |
@@ -41,8 +41,8 @@ class RecipeScript(object): |
with _preserve_path(): |
execfile(script_path, script_vars) |
- script_vars['LOADED_DEPS'] = universe.deps_from_mixed( |
- script_vars.get('DEPS', []), os.path.basename(script_path)) |
+ script_vars['LOADED_DEPS'] = universe.deps_from_spec( |
+ script_vars.get('DEPS', [])) |
return cls(script_vars) |
@@ -62,8 +62,10 @@ class Dependency(object): |
class PathDependency(Dependency): |
- def __init__(self, path, local_name, universe, base_path=None): |
- self._path = _normalize_path(base_path, path) |
+ def __init__(self, path, local_name, universe): |
+ assert os.path.isabs(path), ( |
+ 'Path dependencies must be absolute, but %s is not' % path) |
+ self._path = path |
self._local_name = local_name |
# We forbid modules from living outside our main paths to keep clients |
@@ -95,19 +97,32 @@ class NamedDependency(PathDependency): |
raise NoSuchRecipe('Recipe module named %s does not exist' % name) |
+class PackageDependency(PathDependency): |
+ # TODO(luqui): Forbid depending on a module from a (locally) undeclared |
+ # dependency. |
+ def __init__(self, package, module, local_name, universe): |
+ mod_path = ( |
+ universe.package_deps.get_package(package).module_path(module)) |
+ super(PackageDependency, self).__init__( |
+ mod_path, local_name, universe=universe) |
+ |
+ |
class RecipeUniverse(object): |
- def __init__(self, module_dirs, recipe_dirs): |
+ def __init__(self, package_deps): |
self._loaded = {} |
- self._module_dirs = module_dirs[:] |
- self._recipe_dirs = recipe_dirs[:] |
+ self._package_deps = package_deps |
@property |
def module_dirs(self): |
- return self._module_dirs |
+ return self._package_deps.all_module_dirs |
@property |
def recipe_dirs(self): |
- return self._recipe_dirs |
+ return self._package_deps.all_recipe_dirs |
+ |
+ @property |
+ def package_deps(self): |
+ return self._package_deps |
def load(self, dep): |
"""Load a Dependency.""" |
@@ -123,26 +138,31 @@ class RecipeUniverse(object): |
self._loaded[name] = mod |
return mod |
- def deps_from_names(self, deps): |
- """Load dependencies given a list simple module names (old style).""" |
- return { dep: self.load(NamedDependency(dep, universe=self)) |
- for dep in deps } |
- |
- def deps_from_paths(self, deps, base_path): |
- """Load dependencies given a dictionary of local names to module paths |
- (new style).""" |
- return { name: self.load(PathDependency(path, name, |
- universe=self, base_path=base_path)) |
- for name, path in deps.iteritems() } |
- |
- def deps_from_mixed(self, deps, base_path): |
- """Load dependencies given either a new style or old style deps spec.""" |
- if isinstance(deps, (list, tuple)): |
- return self.deps_from_names(deps) |
- elif isinstance(deps, dict): |
- return self.deps_from_paths(deps, base_path) |
+ def _dep_from_name(self, name): |
+ if '/' in name: |
+ [package,module] = name.split('/') |
+ dep = PackageDependency(package, module, module, universe=self) |
else: |
- raise ValueError('%s is not a valid or known deps structure' % deps) |
+ # Old style: bare module name, search paths to find it. |
+ module = name |
+ dep = NamedDependency(name, universe=self) |
+ |
+ return module, dep |
+ |
+ def deps_from_spec(self, spec): |
+ # Automatic local names. |
+ if isinstance(spec, (list, tuple)): |
+ deps = {} |
+ for item in spec: |
+ name, dep = self._dep_from_name(item) |
+ deps[name] = self.load(dep) |
+ # Explicit local names. |
+ elif isinstance(spec, dict): |
+ deps = {} |
+ for name, item in spec.iteritems(): |
+ _, dep = self._dep_from_name(item) |
+ deps[name] = self.load(dep) |
+ return deps |
def load_recipe(self, recipe): |
"""Given name of a recipe, loads and returns it as RecipeScript instance. |
@@ -163,10 +183,11 @@ class RecipeUniverse(object): |
module_name, example = recipe.split(':') |
assert example.endswith('example') |
for module_dir in self.module_dirs: |
- for subitem in os.listdir(module_dir): |
- if module_name == subitem: |
- return RecipeScript.from_script_path( |
- os.path.join(module_dir, subitem, 'example.py'), self) |
+ if os.path.isdir(module_dir): |
+ for subitem in os.listdir(module_dir): |
+ if module_name == subitem: |
+ return RecipeScript.from_script_path( |
+ os.path.join(module_dir, subitem, 'example.py'), self) |
raise NoSuchRecipe(recipe, |
'Recipe example %s:%s does not exist' % |
(module_name, example)) |
@@ -214,13 +235,6 @@ def _preserve_path(): |
sys.path = old_path |
-def _normalize_path(base_path, path): |
- if base_path is None or os.path.isabs(path): |
- return os.path.realpath(path) |
- else: |
- return os.path.realpath(os.path.join(base_path, path)) |
- |
- |
def _find_and_load_module(fullname, modname, path): |
imp.acquire_lock() |
try: |
@@ -244,8 +258,7 @@ def _load_recipe_module_module(path, universe): |
mod = _find_and_load_module(fullname, modname, path) |
# This actually loads the dependencies. |
- mod.LOADED_DEPS = universe.deps_from_mixed( |
- getattr(mod, 'DEPS', []), os.path.basename(path)) |
+ mod.LOADED_DEPS = universe.deps_from_spec(getattr(mod, 'DEPS', [])) |
# Prevent any modules that mess with sys.path from leaking. |
with _preserve_path(): |