diff --git a/src/cijoe/cli/cli.py b/src/cijoe/cli/cli.py index 09ecd0c4..ef174d6a 100644 --- a/src/cijoe/cli/cli.py +++ b/src/cijoe/cli/cli.py @@ -187,7 +187,6 @@ def cli_example(args): err = 0 resources = get_resources() - resource = resources["configs"].get(f"{args.example}.default-config", None) if resource is None: log.error( @@ -276,7 +275,7 @@ def cli_workflow(args): log.error(f"aborting; output({args.output}) directory already exists") return errno.EPERM - config = Config(args.config) + config = Config([args.config]) errors = config.load() if errors: log_errors(errors) @@ -417,9 +416,10 @@ def parse_args(): workflow_group.add_argument( "--config", "-c", + nargs="+", type=Path, - default=Path(os.environ.get("CIJOE_DEFAULT_CONFIG", DEFAULT_CONFIG_FILENAME)), - help="Path to the Configuration file.", + default=[Path(os.environ.get("CIJOE_DEFAULT_CONFIG", DEFAULT_CONFIG_FILENAME))], + help="Paths to the Configuration file.", ) workflow_group.add_argument( "--workflow", @@ -552,10 +552,17 @@ def main(args=None): for filearg in ["config", "workflow"]: argv = getattr(args, filearg) - path = search_for_file(argv) - if path is None: - log.error(f"{filearg}({argv}) does not exist; exiting") - return errno.EINVAL + if isinstance(argv, list): + for p in argv: + path = search_for_file(p) + if path is None: + log.error(f"{filearg}({p}) does not exist; exiting") + return errno.EINVAL + else: + path = search_for_file(argv) + if path is None: + log.error(f"{filearg}({argv}) does not exist; exiting") + return errno.EINVAL setattr(args, filearg, path) diff --git a/src/cijoe/core/resources.py b/src/cijoe/core/resources.py index 3a20b461..896a1036 100644 --- a/src/cijoe/core/resources.py +++ b/src/cijoe/core/resources.py @@ -156,7 +156,30 @@ def content_from_file(self): self.content = resource_file.read() -class Config(Resource): +class ResourceMultiFiles(object): + """Base representation of a Resource that spans over multiple files""" + + def __init__(self, paths: List[Path], pkg=None): + self.paths = [path.resolve() for path in paths] + self.pkg = pkg + + self.content = None + + prefix = ".".join(pkg.name.split(".")[1:-1]) + "." if pkg else "" + + self.ident = f"{prefix}{'_'.join([path.stem for path in self.paths])}" + + @property + def path(self) -> Path | None: + if len(self.paths): + return self.paths[0] + return None + + def __repr__(self): + return "_".join([str(path) for path in self.paths]) + + +class Config(ResourceMultiFiles): """ Encapsulation of a CIJOE config-file, e.g. 'default-config.toml' @@ -165,8 +188,8 @@ class Config(Resource): SUFFIX = ".toml" - def __init__(self, path: Path, pkg=None): - super().__init__(path, pkg) + def __init__(self, paths: List[Path], pkg=None): + super().__init__(paths, pkg) self.options: Dict[str, Any] = {} @@ -174,7 +197,9 @@ def load(self): """Populates self.options on success. Returns a list of errors otherwise""" # config_dict = dict_from_yamlfile(self.path) - config_dict = dict_from_tomlfile(self.path) + config_dict = {} + for path in self.paths: + config_dict.update(dict_from_tomlfile(path)) errors = dict_substitute(config_dict, default_context()) if errors: @@ -184,14 +209,14 @@ def load(self): return [] @staticmethod - def from_path(path, pkg=None): + def from_path(path: Path, pkg=None): """Instantiate a Config from path, returning None on error""" path = Path(path).resolve() if not path.exists(): return None - config = Config(path, pkg) + config = Config([path], pkg) errors = config.load() if errors: return None @@ -461,7 +486,7 @@ def __process_candidate(self, candidate: Path, category: str, pkg): if not resource.content_has_script_func(): category = "auxiliary" elif category == "configs": - resource = Config(candidate, pkg) + resource = Config([candidate], pkg) elif category == "workflows": resource = Workflow(candidate, pkg) else: