Esempio n. 1
0
File: stage.py Progetto: jfear/ymp
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.default is None:
            raise YmpRuleError(self.stage,
                               f"Stage Int Parameter must have 'default' set")

        self.regex = f"({self.key}\d+|)"
Esempio n. 2
0
    def filter_input_func(wildcards, input):
        outfiles = []
        files = ensure_list(getattr(input, name))
        if also is None:
            extra_files = [[None] for _ in files]
        else:
            extra_files = [
                ensure_list(getattr(input, extra))
                for extra in ensure_list(also)
            ]
        all_files = [
            fname for fnamell in ([files], extra_files) for fnamel in fnamell
            for fname in fnamel if fname is not None
        ]
        files_exist = [os.path.exists(fname) for fname in all_files]

        if all(files_exist):
            for fname, *extra_fnames in zip(files, *extra_files):
                if isinstance(extra_fnames, str):
                    extra_fnames = [extra_fnames]
                if minsize is not None:
                    if not file_not_empty(fname, minsize=minsize):
                        continue
                    if not all(file_not_empty(fn) for fn in extra_fnames):
                        continue
                outfiles.append(fname)
        elif any(files_exist):
            raise YmpRuleError(None, "Missing files to check for length")
        else:
            outfiles = files
        if join is None:
            return outfiles
        return join.join(outfiles)
Esempio n. 3
0
File: stage.py Progetto: jfear/ymp
    def add_param(self, key, typ, name, value=None, default=None):
        """Add parameter to stage

        Example:
            >>> with Stage("test") as S
            >>>   S.add_param("N", "int", "nval", default=50)
            >>>   rule:
            >>>      shell: "echo {param.nval}"

            This would add a stage "test", optionally callable as "testN123",
            printing "50" or in the case of "testN123" printing "123".

        Args:
          char: The character to use in the Stage name
          typ:  The type of the parameter (int, flag)
          param: Name of parameter in params
          value: value ``{param.xyz}`` should be set to if param given
          default: default value for ``{{param.xyz}}`` if no param given
        """
        if typ == 'flag':
            self.params.append(ParamFlag(self, key, name, value, default))
        elif typ == 'int':
            self.params.append(ParamInt(self, key, name, value, default))
        elif typ == 'choice':
            self.params.append(ParamChoice(self, key, name, value, default))
        else:
            raise YmpRuleError(self, f"Unknown Stage Parameter type '{typ}'")
Esempio n. 4
0
    def check_input_func(wildcards, input):
        files = [
            fname for name in ensure_list(names)
            for fname in ensure_list(getattr(input, name))
        ]
        files_exist = [os.path.exists(fname) for fname in files]

        if all(files_exist):
            nbytes = 0
            nlines = 0
            for fname in files:
                if fname.endswith(".gz"):
                    openfunc = gzip.open
                else:
                    openfunc = open
                with openfunc(fname, "rb") as fd:
                    btes = fd.read(8192)
                    while btes:
                        nlines += btes.count(b"\n")
                        nbytes += len(btes)
                        if nbytes >= minbytes and nlines >= minlines:
                            break
                        btes = fd.read(8192)
            if nbytes < minbytes or nlines < minlines:
                return False
        elif any(files_exist):
            raise YmpRuleError(
                None, f"Missing files to check for length: "
                f"{files}")
        return True
Esempio n. 5
0
File: stage.py Progetto: jfear/ymp
    def __enter__(self):
        if Stage.active is not None:
            raise YmpRuleError(
                self, f"Failed to enter stage '{self.name}', "
                f"already in stage {self.active.name}'.")

        Stage.active = self
        return self
Esempio n. 6
0
        def get_value_(self, key, args, kwargs):
            stage = Stage.active
            if "(" in key:
                args = list(args)
                if key[-1] != ")":
                    raise YmpRuleError(
                        stage, f"Malformed YMP expansion string:'{key}'")
                key, _, args_str = key[:-1].partition("(")
                for arg_str in args_str.split(","):
                    try:
                        arg = int(arg_str)
                    except ValueError:
                        try:
                            arg = float(arg_str)
                        except ValueError:
                            arg = arg_str
                    if isinstance(arg, str) and arg[0] not in ('"', '"'):
                        if "wc" not in kwargs:
                            raise ExpandLateException()
                        arg = getattr(kwargs['wc'], arg)
                    args += [arg]

            # Check Stage variables first. We can do that always:
            if hasattr(stage, key):
                val = getattr(stage, key)
                if hasattr(val, "__call__"):
                    val = val(args, kwargs)
                if val is not None:
                    return val

            # Check StageStack next. This requires the wildcards:
            if "wc" not in kwargs:
                raise ExpandLateException()
            wc = kwargs['wc']
            stack = StageStack.get(stage.wc2path(wc), stage)
            if hasattr(stack, key):
                val = getattr(stack, key)
                if hasattr(val, "__call__"):
                    val = val(args, kwargs)
                if val is not None:
                    return val

            # Lastly, check the project:
            if hasattr(stack.project, key):
                val = getattr(stack.project, key)
                if hasattr(val, "__call__"):
                    val = val(args, kwargs)
                if val is not None:
                    return val
            return super().get_value(key, args, kwargs)
Esempio n. 7
0
    def add_param(self, key, typ, name, value=None, default=None) -> bool:
        """Add parameter to stage

        Example:
            >>> with Stage("test") as S
            >>>   S.add_param("N", "int", "nval", default=50)
            >>>   rule:
            >>>      shell: "echo {param.nval}"

            This would add a stage "test", optionally callable as "testN123",
            printing "50" or in the case of "testN123" printing "123".

        Args:
          char: The character to use in the Stage name
          typ:  The type of the parameter (int, flag)
          param: Name of parameter in params
          value: value ``{param.xyz}`` should be set to if param given
          default: default value for ``{{param.xyz}}`` if no param given
        """
        if self.__regex_:
            raise RuntimeError("?")
        new_param = Param.make(self, typ, key, name, value, default)
        for param in self.__params:
            if param == new_param:
                return False
            if key and param.key == key:
                raise YmpRuleError(
                    self,
                    f"Keys must be uninque. Key '{key}' already used by {param}.\n"
                    f"  while trying to add {new_param}")
            if param.name == name:
                raise YmpRuleError(
                    self,
                    f"Names must be uninque. Name '{name}' already used by {param}.\n"
                    f"  while trying to add {new_param}")
        self.__params.append(new_param)
        return True
Esempio n. 8
0
    def register(self):
        """Add self to registry"""
        cache = self.get_registry()

        names = []
        for attr in 'name', 'altname':
            if hasattr(self, attr):
                names += ensure_list(getattr(self, attr))

        for name in names:
            if (name in cache and self != cache[name]
                    and (self.filename != cache[name].filename
                         or self.lineno != cache[name].lineno)):
                other = cache[name]
                raise YmpRuleError(
                    self,
                    f"Failed to create {self.__class__.__name__} '{names[0]}':"
                    f" already defined in {other.filename}:{other.lineno}")

        for name in names:
            cache[name] = self
Esempio n. 9
0
        def get_value_(self, key, args, kwargs):
            stage = Stage.get_active()
            # Fixme: Guard against stage==None?
            if "(" in key:
                args = list(args)
                if key[-1] != ")":
                    raise YmpRuleError(
                        stage, f"Malformed YMP expansion string:'{key}'")
                key, _, args_str = key[:-1].partition("(")
                for arg_str in args_str.split(","):
                    argname = None
                    if '=' in arg_str:
                        argname, arg_str = arg_str.split("=")
                    try:
                        arg = int(arg_str)
                    except ValueError:
                        try:
                            arg = float(arg_str)
                        except ValueError:
                            arg = arg_str
                    if isinstance(arg, str):
                        if arg in ("True", "False"):
                            arg = bool(arg)
                        if arg[0] not in ('"', '"'):
                            if "wc" not in kwargs:
                                raise ExpandLateException()
                            arg = getattr(kwargs['wc'], arg)
                    if not argname:
                        args += [arg]
                    else:
                        kwargs[argname] = arg

            # Check Stage variables first. We can do that always:
            if hasattr(stage, key):
                val = getattr(stage, key)
                if hasattr(val, "__call__"):
                    val = val(args, kwargs)
                if val is not None:
                    return val

            # Check StageStack next. This requires the wildcards:
            if "wc" not in kwargs:
                raise ExpandLateException()
            wc = kwargs['wc']
            stack = StageStack.instance(stage.wc2path(wc))
            if hasattr(stack, key):
                val = getattr(stack, key)
                if hasattr(val, "__call__"):
                    val = val(args, kwargs)
                if val is not None:
                    return val

            # Check the project:
            if hasattr(stack.project, key):
                val = getattr(stack.project, key)
                if hasattr(val, "__call__"):
                    val = val(args, kwargs)
                if val is not None:
                    return val

            # Expand via super
            return super().get_value(key, args, kwargs)
Esempio n. 10
0
    def __init__(self,
                 env_file: Optional[str] = None,
                 dag: Optional[object] = None,
                 singularity_img=None,
                 name: Optional[str] = None,
                 packages: Optional[Union[list, str]] = None,
                 base: str = "none",
                 channels: Optional[Union[list, str]] = None,
                 rule: Optional[Rule] = None) -> None:
        """Creates an inline defined conda environment

        Args:
          name: Name of conda environment (and basename of file)
          packages: package(s) to be installed into environment. Version
            constraints can be specified in each package string separated from
            the package name by whitespace. E.g. ``"blast =2.6*"``
          channels: channel(s) to be selected for the environment
          base: Select a set of default channels and packages to be added to
            the newly created environment. Sets are defined in conda.defaults
            in ``yml.yml``
        """
        cfg = ymp.get_config()

        pseudo_dag = AttrDict({
            'workflow': {
                'persistence': {
                    'conda_env_path': cfg.absdir.conda_prefix,
                    'conda_env_archive_path': cfg.absdir.conda_archive_prefix
                }
            }
        })

        # must have either name or env_file:
        if (name and env_file) or not (name or env_file):
            raise YmpRuleError(
                self, "Env must have exactly one of `name` and `file`")

        if name:
            self.name = name
        else:
            self.name, _ = op.splitext(op.basename(env_file))

        if env_file:
            self.dynamic = False
            self.filename = env_file
            self.lineno = 1
        else:
            self.dynamic = True

            env_file = op.join(cfg.ensuredir.dynamic_envs, f"{name}.yml")
            defaults = {
                'name':
                self.name,
                'dependencies':
                list(
                    ensure_list(packages) +
                    cfg.conda.defaults[base].dependencies),
                'channels':
                list(
                    ensure_list(channels) + cfg.conda.defaults[base].channels)
            }
            yaml = YAML(typ='rt')
            yaml.default_flow_style = False
            buf = io.StringIO()
            yaml.dump(defaults, buf)
            contents = buf.getvalue()

            disk_contents = ""
            if op.exists(env_file):
                with open(env_file, "r") as inf:
                    disk_contents = inf.read()
            if contents != disk_contents:
                with open(env_file, "w") as out:
                    out.write(contents)

        super().__init__(env_file, pseudo_dag, singularity_img)
        self.register()
Esempio n. 11
0
    def __init__(
            self,
            # Snakemake Params:
            env_file: Optional[str] = None,
            workflow=None,
            env_dir=None,
            container_img=None,
            cleanup=None,
            # YMP Params:
            name: Optional[str] = None,
            packages: Optional[Union[list, str]] = None,
            base: str = "none",
            channels: Optional[Union[list, str]] = None) -> None:
        """Creates an inline defined conda environment

        Args:
          name: Name of conda environment (and basename of file)
          packages: package(s) to be installed into environment. Version
            constraints can be specified in each package string separated from
            the package name by whitespace. E.g. ``"blast =2.6*"``
          channels: channel(s) to be selected for the environment
          base: Select a set of default channels and packages to be added to
            the newly created environment. Sets are defined in conda.defaults
            in ``yml.yml``
        """
        if 'name' in self.__dict__:
            # already initialized
            return
        cfg = ymp.get_config()

        if env_file:
            if name:
                import pdb
                pdb.set_trace()
                raise YmpRuleError(
                    self,
                    "Env must not have both 'name' and 'env_file' parameters'")
            self.dynamic = False
            self.name, _ = op.splitext(op.basename(env_file))
            self.packages = None
            self.base = None
            self.channels = None

            # Override location for exceptions:
            self.filename = env_file
            self.lineno = 1
        elif name:
            self.dynamic = True
            self.name = name
            self.packages = ensure_list(
                packages) + cfg.conda.defaults[base].dependencies
            self.channels = ensure_list(
                channels) + cfg.conda.defaults[base].channels
            env_file = op.join(cfg.ensuredir.dynamic_envs, f"{name}.yml")
            contents = self._get_dynamic_contents()
            self._update_file(env_file, contents)
        else:
            raise YmpRuleError(
                self, "Env must have either 'name' or 'env_file' parameter")

        # Unlike within snakemake, we create these objects before the workflow is fully
        # initialized, which means we need to create a fake one:
        if not workflow:
            workflow = AttrDict({
                'persistence': {
                    'conda_env_path': cfg.ensuredir.conda_prefix,
                    'conda_env_archive_path':
                    cfg.ensuredir.conda_archive_prefix,
                },
                'conda_frontend': cfg.conda.frontend,
                'singularity_args': '',
            })

        super().__init__(env_file, workflow,
                         env_dir if env_dir else cfg.ensuredir.conda_prefix,
                         container_img, cleanup)
        self.register()
Esempio n. 12
0
 def make(cls, stage: BaseStage, typ: str, key: str, name: str, value,
          default) -> "Param":
     if typ not in cls.types:
         raise YmpRuleError(stage, f"Unknown stage Parameter type '{typ}'")
     return cls.types[typ](stage, key, name, value, default)