Esempio n. 1
0
def get_spec_with_expansion(filepath, override_vars=None):
    """
    Return a MerlinSpec with overrides and expansion, without
    creating a MerlinStudy.
    """
    expanded_spec_text = expand_spec_no_study(filepath, override_vars)
    return MerlinSpec.load_spec_from_string(expanded_spec_text)
Esempio n. 2
0
    def setUp(self):
        self.tmpdir = tempfile.mkdtemp()
        self.merlin_spec_filepath = os.path.join(self.tmpdir, "basic_ensemble.yaml")

        with open(self.merlin_spec_filepath, "w+") as _file:
            _file.write(MERLIN_SPEC)

        self.spec = MerlinSpec.load_specification(self.merlin_spec_filepath)
Esempio n. 3
0
    def setUp(self):
        self.tmpdir = tempfile.mkdtemp()
        self.merlin_spec_filepath = os.path.join(self.tmpdir, "no_merlin.yaml")

        with open(self.merlin_spec_filepath, "w+") as _file:
            _file.write(NO_MERLIN)

        self.spec = MerlinSpec.load_specification(self.merlin_spec_filepath)
Esempio n. 4
0
    def get_expanded_spec(self):
        """
        Get a new yaml spec file with defaults, cli overrides, and variable expansions.
        Useful for provenance.
        """
        # get specification including defaults and cli-overridden user variables
        full_spec_text = dump_with_overrides(self.original_spec, self.override_vars)
        new_spec = MerlinSpec.load_spec_from_string(full_spec_text)

        # expand user variables
        new_spec_text = expand_by_line(
            new_spec.dump(), MerlinStudy.get_user_vars(new_spec)
        )
        # expand reserved words
        new_spec_text = expand_by_line(new_spec_text, self.special_vars)

        return MerlinSpec.load_spec_from_string(new_spec_text)
Esempio n. 5
0
def expand_spec_no_study(filepath, override_vars=None):
    """
    Get the expanded text of a spec without creating
    a MerlinStudy. Expansion is limited to user variables
    (the ones defined inside the yaml spec or at the command
    line).
    """
    error_override_vars(override_vars, filepath)
    spec = MerlinSpec.load_specification(filepath)
    full_spec = dump_with_overrides(spec, override_vars)
    spec = MerlinSpec.load_spec_from_string(full_spec)

    uvars = []
    if "variables" in spec.environment:
        uvars.append(spec.environment["variables"])
    if "labels" in spec.environment:
        uvars.append(spec.environment["labels"])
    evaluated_uvars = determine_user_variables(*uvars)

    return expand_by_line(full_spec.split("\n"), evaluated_uvars)
Esempio n. 6
0
    def expanded_spec(self):
        """
        Determines, writes to yaml, and loads into memory an expanded
        specification.
        """
        # Write expanded yaml spec
        self.expanded_filepath = os.path.join(
            self.info, self.spec.name.replace(" ", "_") + ".yaml"
        )

        # If we are restarting, we don't need to re-expand, just need to read
        # in the previously expanded spec
        if self.restart_dir is None:
            self.write_expanded_spec(self.expanded_filepath)

        return MerlinSpec.load_specification(self.expanded_filepath)
Esempio n. 7
0
    def __init__(
        self,
        filepath,
        override_vars=None,
        restart_dir=None,
        samples_file=None,
        dry_run=False,
        no_errors=False,
        pgen_file=None,
        pargs=None,
    ):
        self.original_spec = MerlinSpec.load_specification(filepath)
        self.override_vars = override_vars
        error_override_vars(self.override_vars, self.original_spec.path)

        self.samples_file = samples_file
        self.label_clash_error()
        self.dry_run = dry_run
        self.no_errors = no_errors

        # If we load from a file, record that in the object for provenance
        # downstream
        if self.samples_file is not None:
            self.original_spec.merlin["samples"]["file"] = self.samples_file
            self.original_spec.merlin["samples"]["generate"]["cmd"] = ""

        self.restart_dir = restart_dir

        self.special_vars = {
            "SPECROOT": self.original_spec.specroot,
            "MERLIN_TIMESTAMP": self.timestamp,
            "MERLIN_INFO": self.info,
            "MERLIN_WORKSPACE": self.workspace,
            "OUTPUT_PATH": self.output_path,
            "MERLIN_SUCCESS": str(int(ReturnCode.OK)),
            "MERLIN_RESTART": str(int(ReturnCode.RESTART)),
            "MERLIN_SOFT_FAIL": str(int(ReturnCode.SOFT_FAIL)),
            "MERLIN_HARD_FAIL": str(int(ReturnCode.HARD_FAIL)),
            "MERLIN_RETRY": str(int(ReturnCode.RETRY)),
        }

        self.pgen_file = pgen_file
        self.pargs = pargs

        self.dag = None
        self.load_dag()
Esempio n. 8
0
def stop_workers(args):
    """
    CLI command for stopping all workers.

    :param `args`: parsed CLI arguments
    """
    print(banner_small)
    worker_names = []
    if args.spec:
        spec_path = verify_filepath(args.spec)
        spec = MerlinSpec.load_specification(spec_path)
        worker_names = spec.get_worker_names()
        for worker_name in worker_names:
            if "$" in worker_name:
                LOG.warning(
                    f"Worker '{worker_name}' is unexpanded. Target provenance spec instead?"
                )
    router.stop_workers(args.task_server, worker_names, args.queues, args.workers)
Esempio n. 9
0
    def write_expanded_spec(self, dest):
        """
        Write a new yaml spec file with defaults and variable expansions.
        Useful for provenance.

        :param `dest`: destination for fully expanded yaml file
        """
        # specification text including defaults and overridden user variables
        full_spec = dump_with_overrides(self.spec, self.override_vars)

        with open(dest, "w") as dumped_file:
            dumped_file.write(full_spec)

        # update spec so that user_vars update will be accurate
        self.spec = MerlinSpec.load_specification(dest)

        # expand user variables
        self.write_expand_by_line(dest, self.user_vars)
        # expand reserved words
        self.write_expand_by_line(dest, self.special_vars)
Esempio n. 10
0
    def get_expanded_spec(self):
        """
        Get a new yaml spec file with defaults, cli overrides, and variable expansions.
        Useful for provenance.
        """
        # get specification including defaults and cli-overridden user variables
        new_env = replace_override_vars(self.original_spec.environment,
                                        self.override_vars)
        new_spec = deepcopy(self.original_spec)
        new_spec.environment = new_env

        # expand user variables
        new_spec_text = expand_by_line(new_spec.dump(),
                                       MerlinStudy.get_user_vars(new_spec))

        # expand reserved words
        new_spec_text = expand_by_line(new_spec_text, self.special_vars)

        result = MerlinSpec.load_spec_from_string(new_spec_text)
        return expand_env_vars(result)
Esempio n. 11
0
    def get_adapter_config(self, override_type=None):
        spec = MerlinSpec.load_specification(self.spec.path)
        adapter_config = dict(spec.batch)

        if "type" not in adapter_config.keys():
            adapter_config["type"] = "local"

        # The type may be overriden, preserve the batch type
        adapter_config["batch_type"] = adapter_config["type"]

        if override_type is not None:
            adapter_config["type"] = override_type

        # if a dry run was ordered by the yaml spec OR the cli flag, do a dry run.
        adapter_config["dry_run"] = self.dry_run or adapter_config["dry_run"]

        # Add the version if using flux to switch the command in the step
        if adapter_config["batch_type"] == "flux":
            adapter_config["flux_command"] = self.flux_command

        LOG.debug(f"Adapter config = {adapter_config}")
        return adapter_config
Esempio n. 12
0
    def expanded_spec(self):
        """
        Determines, writes to yaml, and loads into memory an expanded
        specification.
        """
        # If we are restarting, we don't need to re-expand, just need to read
        # in the previously expanded spec
        if self.restart_dir is not None:
            return self.get_expanded_spec()

        result = self.get_expanded_spec()
        expanded_name = result.description["name"].replace(
            " ", "_") + ".expanded.yaml"

        # Set expanded filepath
        expanded_filepath = os.path.join(self.info, expanded_name)

        # expand provenance spec filename
        if contains_token(self.original_spec.name) or contains_shell_ref(
                self.original_spec.name):
            name = f"{result.description['name'].replace(' ', '_')}_{self.timestamp}"
            name = expand_line(name, {}, env_vars=True)
            if "/" in name:
                raise ValueError(
                    f"Expanded value '{name}' for field 'name' in section 'description' is not a valid filename."
                )
            expanded_workspace = os.path.join(self.output_path, name)

            if result.merlin["samples"]:
                sample_file = result.merlin["samples"]["file"]
                if sample_file.startswith(self.workspace):
                    new_samples_file = sample_file.replace(
                        self.workspace, expanded_workspace)
                    result.merlin["samples"]["generate"][
                        "cmd"] = result.merlin["samples"]["generate"][
                            "cmd"].replace(self.workspace, expanded_workspace)
                    result.merlin["samples"]["file"] = new_samples_file

            shutil.move(self.workspace, expanded_workspace)
            self.workspace = expanded_workspace
            self.info = os.path.join(self.workspace, "merlin_info")
            self.special_vars["MERLIN_INFO"] = self.info

            expanded_filepath = os.path.join(self.info, expanded_name)
            new_spec_text = expand_by_line(result.dump(),
                                           MerlinStudy.get_user_vars(result))
            result = MerlinSpec.load_spec_from_string(new_spec_text)
            result = expand_env_vars(result)

        # pgen
        if self.pgen_file:
            env = result.get_study_environment()
            result.globals = self.load_pgen(self.pgen_file, self.pargs, env)

        # copy the --samplesfile (if any) into merlin_info
        if self.samples_file:
            shutil.copyfile(
                self.samples_file,
                os.path.join(self.info, os.path.basename(self.samples_file)),
            )

        # write expanded spec for provenance
        with open(expanded_filepath, "w") as f:
            f.write(result.dump())

        # write original spec for provenance
        result = MerlinSpec.load_spec_from_string(result.dump())
        result.path = expanded_filepath
        name = result.description["name"].replace(" ", "_")
        self.write_original_spec(name)

        # write partially-expanded spec for provenance
        partial_spec = deepcopy(self.original_spec)
        if "variables" in result.environment:
            partial_spec.environment["variables"] = result.environment[
                "variables"]
        if "labels" in result.environment:
            partial_spec.environment["labels"] = result.environment["labels"]
        partial_spec_path = os.path.join(self.info, name + ".partial.yaml")
        with open(partial_spec_path, "w") as f:
            f.write(partial_spec.dump())

        LOG.info(f"Study workspace is '{self.workspace}'.")
        return result