import os class DependencyResolutionError(Exception): pass class DependencyItem: def __init__(self): self.__target = None self.__dep_list = None def name(self): return None def target(self): return self.__target def dependencies(self): return None def dependency_list(self): return self.__dep_list def publish(self): return None def reset_resolution(self): self.__dep_list = None def resolve(self, **kwargs): if self.__dep_list is not None: return self.__dep_list ctx = None dep_list = None if 'ctx' in kwargs: ctx = kwargs['ctx'] else: ctx = {} if 'dep_list' in kwargs: dep_list = kwargs['dep_list'] else: dep_list = [] if self.name() in ctx: return ctx[self.name()] = self deps = self.dependencies() if deps is None: return for d in deps.values(): d_value = d.resolve_self() if d_value is not None: d_value.resolve(ctx=ctx, dep_list=dep_list) dep_list.append(self) return dep_list class Dependency: def __init__(self, parent): self.__parent = parent def name(self, parent): return None def parent_name(self): return self.__parent def resolve_self(self): return None def is_resolved(self): return False class SeedDependency(Dependency): def __init__(self, **kwargs): self.__target_seed = None if 'name' in kwargs: self.__name = kwargs['name'] if 'parent' in kwargs: super().__init__(kwargs['parent']) def name(self): return self.__name def target(self): return self.__target_seed def resolve_self(self): from seed import Seed if self.__target_seed is not None: return self.__target_seed target = Seed.get(self.__name) if target is None: raise DependencyResolutionError( '{} depends on seed "{}", but the required seed cannot be found'.format(self.parent_name(), self.__name)) self.__target_seed = target return self.__target_seed def is_resolved(self): return self.__target_seed is not None class ProgramDependency(Dependency): def __init__(self, **kwargs): if 'name' in kwargs: self.__name = kwargs['name'] if 'parent' in kwargs: super().__init__(kwargs['parent']) def __is_exe(self, fpath): return os.path.isfile(fpath) and os.access(fpath, os.X_OK) def name(self): return self.__name def resolve_self(self): if self.__is_exe(self.__name): return None path_string = os.environ['PATH'] path_list = path_string.split(':') for d in path_list: exe_path = os.path.join(d, self.__name) if self.__is_exe(exe_path): return None raise DependencyResolutionError(f'{self.parent_name()} depends on command `{self.__name}`, but the command cannot be found. Make sure that the named command exists, the containing directory is in $PATH if necessary, and the executable file has the correct permissions.')