[med-svn] [snakemake] 01/03: Imported Upstream version 3.5.5

Kevin Murray daube-guest at moszumanska.debian.org
Fri Jan 29 15:23:14 UTC 2016


This is an automated email from the git hooks/post-receive script.

daube-guest pushed a commit to branch master
in repository snakemake.

commit 14aba72c911da3298a0180e5510e6503dcd8e879
Author: Kevin Murray <spam at kdmurray.id.au>
Date:   Fri Jan 29 17:17:35 2016 +0200

    Imported Upstream version 3.5.5
---
 docker/Dockerfile                                  |   7 --
 snakemake/__init__.py                              |  26 ++++-
 snakemake/dag.py                                   |  75 ++++++++++++-
 snakemake/executors.py                             |  11 +-
 snakemake/jobs.py                                  |  10 +-
 snakemake/parser.py                                |  14 ++-
 snakemake/remote/FTP.py                            |   2 +-
 snakemake/remote/GS.py                             |   1 +
 snakemake/remote/HTTP.py                           |   2 +-
 snakemake/remote/SFTP.py                           |   2 +-
 snakemake/remote/dropbox.py                        |   2 +-
 snakemake/script.py                                | 122 +++++++++++----------
 snakemake/shell.py                                 |   2 +-
 snakemake/version.py                               |   2 +-
 snakemake/workflow.py                              |  28 ++++-
 snakemake/wrapper.py                               |  22 ++++
 tests/test_omitfrom/Snakefile                      |  54 +++++++++
 .../test_omitfrom/expected-results/independent.txt |   0
 tests/test_omitfrom/expected-results/levelone.txt  |   0
 .../expected-results/leveltwo_first.txt            |   0
 .../expected-results/leveltwo_second.txt           |   0
 tests/test_omitfrom/expected-results/test1.second  |   0
 tests/test_omitfrom/expected-results/test2.second  |   0
 tests/test_until/Snakefile                         |  54 +++++++++
 tests/test_until/expected-results/levelone.txt     |   0
 .../test_until/expected-results/leveltwo_first.txt |   0
 .../expected-results/leveltwo_second.txt           |   0
 tests/test_until/expected-results/test1.second     |   0
 tests/test_until/expected-results/test2.second     |   0
 tests/tests.py                                     |  11 ++
 30 files changed, 357 insertions(+), 90 deletions(-)

diff --git a/docker/Dockerfile b/docker/Dockerfile
deleted file mode 100644
index 9792c7a..0000000
--- a/docker/Dockerfile
+++ /dev/null
@@ -1,7 +0,0 @@
-# a docker image based on Ubuntu with snakemake installed
-FROM ubuntu:14.04
-MAINTAINER Johannes Köster <johannes.koester at tu-dortmund.de>
-RUN apt-get -qq update
-RUN apt-get install -qqy python3-setuptools python3-docutils python3-flask
-RUN easy_install3 snakemake
-ENTRYPOINT ["snakemake"]
diff --git a/snakemake/__init__.py b/snakemake/__init__.py
index 566e443..33ba87c 100644
--- a/snakemake/__init__.py
+++ b/snakemake/__init__.py
@@ -42,6 +42,8 @@ def snakemake(snakefile,
               forcetargets=False,
               forceall=False,
               forcerun=[],
+              until=[],
+              omit_from=[],
               prioritytargets=[],
               stats=None,
               printreason=False,
@@ -272,6 +274,7 @@ def snakemake(snakefile,
                         overwrite_config=overwrite_config,
                         overwrite_workdir=workdir,
                         overwrite_configfile=configfile,
+                        overwrite_clusterconfig=cluster_config,
                         config_args=config_args,
                         debug=debug)
 
@@ -313,7 +316,6 @@ def snakemake(snakefile,
                                        quiet=quiet,
                                        keepgoing=keepgoing,
                                        cluster=cluster,
-                                       cluster_config=cluster_config,
                                        cluster_sync=cluster_sync,
                                        drmaa=drmaa,
                                        jobname=jobname,
@@ -351,6 +353,8 @@ def snakemake(snakefile,
                     forceall=forceall,
                     forcerun=forcerun,
                     prioritytargets=prioritytargets,
+                    until=until,
+                    omit_from=omit_from,
                     quiet=quiet,
                     keepgoing=keepgoing,
                     printshellcmds=printshellcmds,
@@ -358,7 +362,6 @@ def snakemake(snakefile,
                     printrulegraph=printrulegraph,
                     printdag=printdag,
                     cluster=cluster,
-                    cluster_config=cluster_config,
                     cluster_sync=cluster_sync,
                     jobname=jobname,
                     drmaa=drmaa,
@@ -512,7 +515,7 @@ def get_argument_parser():
               "resources: gpu=1. If now two rules require 1 of the resource "
               "'gpu' they won't be run in parallel by the scheduler."))
     parser.add_argument(
-        "--config",
+        "--config", "-C",
         nargs="*",
         metavar="KEY=VALUE",
         help=
@@ -625,6 +628,21 @@ def get_argument_parser():
         help=("Tell the scheduler to assign creation of given targets "
               "(and all their dependencies) highest priority. (EXPERIMENTAL)"))
     parser.add_argument(
+        "--until", "-U",
+        nargs="+",
+        metavar="TARGET",
+        help=("Runs the pipeline until it reaches the specified rules or "
+              "files. Only runs jobs that are dependencies of the specified "
+              "rule or files, does not run sibling DAGs. "))
+    parser.add_argument(
+        "--omit-from", "-O",
+        nargs="+",
+        metavar="TARGET",
+        help=("Prevent the execution or creation of the given rules or files "
+              "as well as any rules or files that are downstream of these targets "
+              "in the DAG. Also runs jobs in sibling DAGs that are independent of the "
+              "rules or files specified here."))
+    parser.add_argument(
         "--allow-ambiguity", "-a",
         action="store_true",
         help=("Don't check for ambiguous rules and simply use the first if "
@@ -945,6 +963,8 @@ def main():
                             forceall=args.forceall,
                             forcerun=args.forcerun,
                             prioritytargets=args.prioritize,
+                            until=args.until,
+                            omit_from=args.omit_from,
                             stats=args.stats,
                             nocolor=args.nocolor,
                             quiet=args.quiet,
diff --git a/snakemake/dag.py b/snakemake/dag.py
index a767bf1..eb2e8b5 100644
--- a/snakemake/dag.py
+++ b/snakemake/dag.py
@@ -37,6 +37,10 @@ class DAG:
                  forcefiles=None,
                  priorityfiles=None,
                  priorityrules=None,
+                 untilfiles=None,
+                 untilrules=None,
+                 omitfiles=None,
+                 omitrules=None,
                  ignore_ambiguity=False,
                  force_incomplete=False,
                  ignore_incomplete=False,
@@ -68,6 +72,10 @@ class DAG:
 
         self.forcerules = set()
         self.forcefiles = set()
+        self.untilrules = set()
+        self.untilfiles = set()
+        self.omitrules = set()
+        self.omitfiles = set()
         self.updated_subworkflow_files = set()
         if forceall:
             self.forcerules.update(self.rules)
@@ -75,6 +83,15 @@ class DAG:
             self.forcerules.update(forcerules)
         if forcefiles:
             self.forcefiles.update(forcefiles)
+        if untilrules: # keep only the rule names
+            self.untilrules.update(set(rule.name for rule in untilrules))
+        if untilfiles:
+            self.untilfiles.update(untilfiles)
+        if omitrules:
+            self.omitrules.update(set(rule.name for rule in omitrules))
+        if omitfiles:
+            self.omitfiles.update(omitfiles)
+
         self.omitforce = set()
 
         self.force_incomplete = force_incomplete
@@ -95,6 +112,9 @@ class DAG:
             self.targetjobs.add(job)
 
         self.update_needrun()
+        self.set_until_jobs()
+        self.delete_omitfrom_jobs()
+
 
     def update_output_index(self):
         self.output_index = OutputIndex(self.rules)
@@ -522,6 +542,45 @@ class DAG:
 
         self._len = len(_needrun)
 
+
+    def in_until(self, job):
+        return (job.rule.name in self.untilrules or
+                not self.untilfiles.isdisjoint(job.output))
+
+
+    def in_omitfrom(self, job):
+        return (job.rule.name in self.omitrules or
+                not self.omitfiles.isdisjoint(job.output))
+
+    def until_jobs(self):
+        'Returns a generator of jobs specified by untiljobs'
+        return (job for job in self.jobs if self.in_until(job))
+
+    def omitfrom_jobs(self):
+        'Returns a generator of jobs specified by omitfromjobs'
+        return (job for job in self.jobs if self.in_omitfrom(job))
+
+    def downstream_of_omitfrom(self):
+        "Returns the downstream of --omit-from rules or files."
+        return filter(lambda job: not self.in_omitfrom(job),
+                      self.bfs(self.depending, *self.omitfrom_jobs()))
+
+    def delete_omitfrom_jobs(self):
+        "Removes jobs downstream of jobs specified by --omit-from."
+        if not self.omitrules and not self.omitfiles:
+            return
+        downstream_jobs = list(self.downstream_of_omitfrom()) # need to cast as list before deleting jobs
+        for job in downstream_jobs:
+            self.delete_job(job,
+                            recursive=False,
+                            add_dependencies=True)
+
+    def set_until_jobs(self):
+        "Removes jobs downstream of jobs specified by --omit-from."
+        if not self.untilrules and not self.untilfiles:
+            return
+        self.targetjobs = set(self.until_jobs())
+
     def update_priority(self):
         """ Update job priorities. """
         prioritized = (lambda job: job.rule in self.priorityrules or
@@ -624,7 +683,14 @@ class DAG:
                         self.replace_job(job_, newjob_)
         return newjob
 
-    def delete_job(self, job, recursive=True):
+    def delete_job(self, job,
+                   recursive=True,
+                   add_dependencies=False):
+        if job in self.targetjobs:
+            self.targetjobs.remove(job)
+        if add_dependencies:
+            for _job in self.dependencies[job]:
+                self.targetjobs.add(_job)
         for job_ in self.depending[job]:
             del self.dependencies[job_][job]
         del self.depending[job]
@@ -646,6 +712,9 @@ class DAG:
             self._ready_jobs.remove(job)
 
     def replace_job(self, job, newjob):
+        if job in self.targetjobs:
+            self.targetjobs.remove(job)
+            self.targetjobs.add(newjob)
         depending = list(self.depending[job].items())
         if self.finished(job):
             self._finished.add(newjob)
@@ -657,9 +726,6 @@ class DAG:
             if not job_.dynamic_input:
                 self.dependencies[job_][newjob].update(files)
                 self.depending[newjob][job_].update(files)
-        if job in self.targetjobs:
-            self.targetjobs.remove(job)
-            self.targetjobs.add(newjob)
 
     def specialize_rule(self, rule, newrule):
         assert newrule is not None
@@ -735,6 +801,7 @@ class DAG:
         if post:
             yield job
 
+
     def is_isomorph(self, job1, job2):
         if job1.rule != job2.rule:
             return False
diff --git a/snakemake/executors.py b/snakemake/executors.py
index 7bb145e..42931ae 100644
--- a/snakemake/executors.py
+++ b/snakemake/executors.py
@@ -299,7 +299,7 @@ class ClusterExecutor(RealExecutor):
             'cd {workflow.workdir_init} && '
             '{workflow.snakemakepath} --snakefile {workflow.snakefile} '
             '--force -j{cores} --keep-target-files --keep-shadow '
-            '--wait-for-files {local_input} --latency-wait {latency_wait} '
+            '--wait-for-files {wait_for_files} --latency-wait {latency_wait} '
             '--benchmark-repeats {benchmark_repeats} '
             '{overwrite_workdir} {overwrite_config} --nocolor '
             '--notemp --quiet --no-hooks --nolock {target}')
@@ -368,7 +368,9 @@ class ClusterExecutor(RealExecutor):
                 " ".join(self.workflow.config_args))
 
         target = job.output if job.output else job.rule.name
-        local_input = " ".join(job.local_input)
+        wait_for_files = list(job.local_input) + [self.tmpdir]
+        if job.shadow_dir:
+            wait_for_files.append(job.shadow_dir)
         format = partial(str.format,
                          job=job,
                          overwrite_workdir=overwrite_workdir,
@@ -378,7 +380,8 @@ class ClusterExecutor(RealExecutor):
                          properties=job.json(),
                          latency_wait=self.latency_wait,
                          benchmark_repeats=self.benchmark_repeats,
-                         target=target, local_input=local_input, **kwargs)
+                         target=target, wait_for_files=" ".join(wait_for_files),
+                         **kwargs)
         try:
             exec_job = format(self.exec_job)
             with open(jobscript, "w") as f:
@@ -578,7 +581,7 @@ class SynchronousClusterExecutor(ClusterExecutor):
                         os.remove(active_job.jobscript)
                         self.print_job_error(active_job.job)
                         print_exception(ClusterJobException(active_job.job, self.dag.jobid(active_job.job),
-                                                            jobscript),
+                                                            active_job.jobscript),
                                         self.workflow.linemaps)
                         active_job.error_callback(active_job.job)
             time.sleep(1)
diff --git a/snakemake/jobs.py b/snakemake/jobs.py
index 7c548aa..e8f0433 100644
--- a/snakemake/jobs.py
+++ b/snakemake/jobs.py
@@ -146,9 +146,10 @@ class Job:
     def expanded_shadowed_output(self):
         """ Get the paths of output files, resolving shadow directory. """
         if not self.shadow_dir:
-            return self.expanded_output
-        for f in self.expanded_output:
-            yield os.path.join(self.shadow_dir, f)
+            yield from self.expanded_output
+        else:
+            for f in self.expanded_output:
+                yield os.path.join(self.shadow_dir, f)
 
     @property
     def dynamic_wildcards(self):
@@ -463,8 +464,9 @@ class Job:
     def __eq__(self, other):
         if other is None:
             return False
-        return self.rule == other.rule and (
+        return (self.rule == other.rule and (
             self.dynamic_output or self.wildcards_dict == other.wildcards_dict)
+                and (self.dynamic_input or self.input == other.input))
 
     def __lt__(self, other):
         return self.rule.__lt__(other.rule)
diff --git a/snakemake/parser.py b/snakemake/parser.py
index e1eccf9..7fda2be 100644
--- a/snakemake/parser.py
+++ b/snakemake/parser.py
@@ -520,6 +520,15 @@ class Script(Run):
         yield token.string, token
 
 
+class Wrapper(Script):
+    def start(self):
+        for t in super(Script, self).start():
+            yield t
+        yield "\n"
+        yield INDENT * (self.effective_indent + 1)
+        yield 'wrapper('
+
+
 class Rule(GlobalKeywordState):
     subautomata = dict(input=Input,
                        output=Output,
@@ -534,7 +543,8 @@ class Rule(GlobalKeywordState):
                        shadow=Shadow,
                        run=Run,
                        shell=Shell,
-                       script=Script)
+                       script=Script,
+                       wrapper=Wrapper)
 
     def __init__(self, snakefile, base_indent=0, dedent=0, root=True):
         super().__init__(snakefile,
@@ -582,7 +592,7 @@ class Rule(GlobalKeywordState):
     def block_content(self, token):
         if is_name(token):
             try:
-                if token.string == "run" or token.string == "shell" or token.string == "script":
+                if token.string == "run" or token.string == "shell" or token.string == "script" or token.string == "wrapper":
                     if self.run:
                         raise self.error(
                             "Multiple run or shell keywords in rule {}.".format(
diff --git a/snakemake/remote/FTP.py b/snakemake/remote/FTP.py
index 8ce6153..da268d9 100644
--- a/snakemake/remote/FTP.py
+++ b/snakemake/remote/FTP.py
@@ -9,7 +9,7 @@ from contextlib import contextmanager
 
 # module-specific
 from snakemake.remote import AbstractRemoteProvider, DomainObject
-from snakemake.exceptions import FTPFileException
+from snakemake.exceptions import FTPFileException, WorkflowError
 import snakemake.io 
 
 try:
diff --git a/snakemake/remote/GS.py b/snakemake/remote/GS.py
index d7febbe..fb9c3e5 100644
--- a/snakemake/remote/GS.py
+++ b/snakemake/remote/GS.py
@@ -5,6 +5,7 @@ __license__ = "MIT"
 
 # module-specific
 from snakemake.remote.S3 import RemoteObject, RemoteProvider as S3RemoteProvider
+from snakemake.exceptions import WorkflowError
 
 try:
     # third-party modules
diff --git a/snakemake/remote/HTTP.py b/snakemake/remote/HTTP.py
index b529763..11959f6 100644
--- a/snakemake/remote/HTTP.py
+++ b/snakemake/remote/HTTP.py
@@ -10,7 +10,7 @@ from contextlib import contextmanager
 
 # module-specific
 from snakemake.remote import AbstractRemoteProvider, DomainObject
-from snakemake.exceptions import HTTPFileException
+from snakemake.exceptions import HTTPFileException, WorkflowError
 import snakemake.io
 
 try:
diff --git a/snakemake/remote/SFTP.py b/snakemake/remote/SFTP.py
index d16b354..bdfff0e 100644
--- a/snakemake/remote/SFTP.py
+++ b/snakemake/remote/SFTP.py
@@ -8,7 +8,7 @@ from contextlib import contextmanager
 
 # module-specific
 from snakemake.remote import AbstractRemoteProvider, DomainObject
-from snakemake.exceptions import SFTPFileException
+from snakemake.exceptions import SFTPFileException, WorkflowError
 import snakemake.io 
 
 try:
diff --git a/snakemake/remote/dropbox.py b/snakemake/remote/dropbox.py
index 258b6a5..bf3a0de 100644
--- a/snakemake/remote/dropbox.py
+++ b/snakemake/remote/dropbox.py
@@ -8,7 +8,7 @@ from contextlib import contextmanager
 
 # module-specific
 from snakemake.remote import AbstractRemoteProvider, AbstractRemoteObject
-from snakemake.exceptions import DropboxFileException
+from snakemake.exceptions import DropboxFileException, WorkflowError
 import snakemake.io 
 
 try:
diff --git a/snakemake/script.py b/snakemake/script.py
index faedbe1..f059b66 100644
--- a/snakemake/script.py
+++ b/snakemake/script.py
@@ -6,6 +6,8 @@ __license__ = "MIT"
 import inspect
 import os
 import traceback
+from urllib.request import urlopen
+from urllib.error import URLError
 
 from snakemake.utils import format
 from snakemake.logging import logger
@@ -79,61 +81,67 @@ def script(basedir, path, input, output, params, wildcards, threads, resources,
     Load a script from the given basedir + path and execute it.
     Supports Python 3 and R.
     """
-    path = format(os.path.join(basedir, path), stepout=1)
+    if not path.startswith("http"):
+        path = path.lstrip("file://")
+        path = "file://" + os.path.abspath(os.path.join(basedir, path))
+    path = format(path, stepout=1)
 
-    if path.endswith(".py"):
-        with open(path) as source:
-            try:
-                exec(compile(source.read(), path, "exec"), {
-                    "snakemake": Snakemake(input, output, params, wildcards,
-                                           threads, resources, log, config)
-                })
-            except (Exception, BaseException) as ex:
-                raise WorkflowError("".join(traceback.format_exception(type(ex), ex, ex.__traceback__)))
-    elif path.endswith(".R"):
-        try:
-            import rpy2.robjects as robjects
-        except ImportError:
-            raise ValueError(
-                "Python 3 package rpy2 needs to be installed to use the R function.")
-        with open(path) as source:
-            preamble = """
-            Snakemake <- setClass(
-                "Snakemake",
-                slots = c(
-                    input = "list",
-                    output = "list",
-                    params = "list",
-                    wildcards = "list",
-                    threads = "numeric",
-                    log = "list",
-                    resources = "list",
-                    config = "list"
-                )
-            )
-            snakemake <- Snakemake(
-                input = {},
-                output = {},
-                params = {},
-                wildcards = {},
-                threads = {},
-                log = {},
-                resources = {},
-                config = {}
-            )
-            """.format(REncoder.encode_namedlist(input),
-                       REncoder.encode_namedlist(output),
-                       REncoder.encode_namedlist(params),
-                       REncoder.encode_namedlist(wildcards), threads,
-                       REncoder.encode_namedlist(log),
-                       REncoder.encode_namedlist({
-                           name: value
-                           for name, value in resources.items()
-                           if name != "_cores" and name != "_nodes"
-                       }), REncoder.encode_dict(config))
-            logger.debug(preamble)
-            source = preamble + source.read()
-            robjects.r(source)
-    else:
-        raise ValueError(
-            "Unsupported script: Expecting either Python (.py) or R (.R) script.")
+    try:
+        with urlopen(path) as source:
+            if path.endswith(".py"):
+                try:
+                    exec(compile(source.read().decode(), path, "exec"), {
+                        "snakemake": Snakemake(input, output, params, wildcards,
+                                               threads, resources, log, config)
+                    })
+                except (Exception, BaseException) as ex:
+                    raise WorkflowError("".join(traceback.format_exception(type(ex), ex, ex.__traceback__)))
+            elif path.endswith(".R"):
+                try:
+                    import rpy2.robjects as robjects
+                except ImportError:
+                    raise ValueError(
+                        "Python 3 package rpy2 needs to be installed to use the R function.")
+                with urlopen(path) as source:
+                    preamble = """
+                    Snakemake <- setClass(
+                        "Snakemake",
+                        slots = c(
+                            input = "list",
+                            output = "list",
+                            params = "list",
+                            wildcards = "list",
+                            threads = "numeric",
+                            log = "list",
+                            resources = "list",
+                            config = "list"
+                        )
+                    )
+                    snakemake <- Snakemake(
+                        input = {},
+                        output = {},
+                        params = {},
+                        wildcards = {},
+                        threads = {},
+                        log = {},
+                        resources = {},
+                        config = {}
+                    )
+                    """.format(REncoder.encode_namedlist(input),
+                               REncoder.encode_namedlist(output),
+                               REncoder.encode_namedlist(params),
+                               REncoder.encode_namedlist(wildcards), threads,
+                               REncoder.encode_namedlist(log),
+                               REncoder.encode_namedlist({
+                                   name: value
+                                   for name, value in resources.items()
+                                   if name != "_cores" and name != "_nodes"
+                               }), REncoder.encode_dict(config))
+                    logger.debug(preamble)
+                    source = preamble + source.read().decode()
+                    robjects.r(source)
+            else:
+                raise ValueError(
+                    "Unsupported script: Expecting either Python (.py) or R (.R) script.")
+    except URLError as e:
+        raise WorkflowError(e)
diff --git a/snakemake/shell.py b/snakemake/shell.py
index bf59350..2c2a2c2 100644
--- a/snakemake/shell.py
+++ b/snakemake/shell.py
@@ -28,7 +28,7 @@ class shell:
     @classmethod
     def executable(cls, cmd):
         if os.path.split(cmd)[-1] == "bash":
-            cls._process_prefix = "set -e -o pipefail; "
+            cls._process_prefix = "set -euo pipefail; "
         cls._process_args["executable"] = cmd
 
     @classmethod
diff --git a/snakemake/version.py b/snakemake/version.py
index 7a82992..4c5818f 100644
--- a/snakemake/version.py
+++ b/snakemake/version.py
@@ -1 +1 @@
-__version__ = "3.5.4"
+__version__ = "3.5.5"
diff --git a/snakemake/workflow.py b/snakemake/workflow.py
index a222fc0..42490a3 100644
--- a/snakemake/workflow.py
+++ b/snakemake/workflow.py
@@ -27,6 +27,7 @@ from snakemake.io import protected, temp, temporary, expand, dynamic, glob_wildc
 from snakemake.persistence import Persistence
 from snakemake.utils import update_config
 from snakemake.script import script
+from snakemake.wrapper import wrapper
 
 class Workflow:
     def __init__(self,
@@ -37,6 +38,7 @@ class Workflow:
                  overwrite_config=dict(),
                  overwrite_workdir=None,
                  overwrite_configfile=None,
+                 overwrite_clusterconfig=dict(),
                  config_args=None,
                  debug=False):
         """
@@ -64,6 +66,7 @@ class Workflow:
         self.overwrite_shellcmd = overwrite_shellcmd
         self.overwrite_config = overwrite_config
         self.overwrite_configfile = overwrite_configfile
+        self.overwrite_clusterconfig = overwrite_clusterconfig
         self.config_args = config_args
         self._onsuccess = lambda log: None
         self._onerror = lambda log: None
@@ -74,6 +77,10 @@ class Workflow:
         config = dict()
         config.update(self.overwrite_config)
 
+        global cluster_config
+        cluster_config = dict()
+        cluster_config.update(self.overwrite_clusterconfig)
+
         global rules
         rules = Rules()
 
@@ -165,6 +172,8 @@ class Workflow:
                 forcetargets=False,
                 forceall=False,
                 forcerun=None,
+                until=[],
+                omit_from=[],
                 prioritytargets=None,
                 quiet=False,
                 keepgoing=False,
@@ -172,7 +181,6 @@ class Workflow:
                 printreason=False,
                 printdag=False,
                 cluster=None,
-                cluster_config=None,
                 cluster_sync=None,
                 jobname=None,
                 immediate_submit=False,
@@ -225,19 +233,29 @@ class Workflow:
         if not targets:
             targets = [self.first_rule
                        ] if self.first_rule is not None else list()
+                       
         if prioritytargets is None:
             prioritytargets = list()
         if forcerun is None:
             forcerun = list()
+        if until is None:
+            until = list()
+        if omit_from is None:
+            omit_from = list()
 
         priorityrules = set(rules(prioritytargets))
         priorityfiles = set(files(prioritytargets))
         forcerules = set(rules(forcerun))
         forcefiles = set(files(forcerun))
+        untilrules = set(rules(until))
+        untilfiles = set(files(until))
+        omitrules = set(rules(omit_from))
+        omitfiles = set(files(omit_from))
         targetrules = set(chain(rules(targets),
                                 filterfalse(Rule.has_wildcards, priorityrules),
-                                filterfalse(Rule.has_wildcards, forcerules)))
-        targetfiles = set(chain(files(targets), priorityfiles, forcefiles))
+                                filterfalse(Rule.has_wildcards, forcerules),
+                                filterfalse(Rule.has_wildcards, untilrules)))
+        targetfiles = set(chain(files(targets), priorityfiles, forcefiles, untilfiles))
         if forcetargets:
             forcefiles.update(targetfiles)
             forcerules.update(targetrules)
@@ -264,6 +282,10 @@ class Workflow:
             forcerules=forcerules,
             priorityfiles=priorityfiles,
             priorityrules=priorityrules,
+            untilfiles=untilfiles,
+            untilrules=untilrules,
+            omitfiles=omitfiles,
+            omitrules=omitrules,
             ignore_ambiguity=ignore_ambiguity,
             force_incomplete=force_incomplete,
             ignore_incomplete=ignore_incomplete or printdag or printrulegraph,
diff --git a/snakemake/wrapper.py b/snakemake/wrapper.py
new file mode 100644
index 0000000..e6cc2ec
--- /dev/null
+++ b/snakemake/wrapper.py
@@ -0,0 +1,22 @@
+__author__ = "Johannes Köster"
+__copyright__ = "Copyright 2016, Johannes Köster"
+__email__ = "koester at jimmy.harvard.edu"
+__license__ = "MIT"
+
+
+import os
+
+from snakemake.script import script
+
+
+def wrapper(path, input, output, params, wildcards, threads, resources, log, config):
+    """
+    Load a wrapper from https://bitbucket.org/snakemake/snakemake-wrappers under
+    the given path + wrapper.py and execute it.
+    """
+    # TODO handle requirements.txt
+    if not (path.startswith("http") or path.startswith("file:")):
+        path = os.path.join("https://bitbucket.org/snakemake/snakemake-wrappers/raw", path)
+    if not (path.endswith("wrapper.py") or path.endswith("wrapper.R")):
+        path = os.path.join(path, "wrapper.py")
+    script("", path, input, output, params, wildcards, threads, resources, log, config)
diff --git a/tests/test_omitfrom/Snakefile b/tests/test_omitfrom/Snakefile
new file mode 100644
index 0000000..fcabec1
--- /dev/null
+++ b/tests/test_omitfrom/Snakefile
@@ -0,0 +1,54 @@
+
+rule all: 
+    input:
+        "levelthree.txt",
+        "independent.txt",
+        expand("test{num}.final", num=[1, 2])
+
+rule levelone:
+    output: "levelone.txt"
+    shell: "touch {output}"
+
+rule leveltwo_first:
+    input: rules.levelone.output
+    output: "leveltwo_first.txt"   
+    shell: "cp -f {input} {output}"
+
+rule leveltwo_second:
+    input: rules.levelone.output
+    output: "leveltwo_second.txt"
+    shell: "cp -f {input} {output}"
+
+rule levelthree: # should not be created
+    input: 
+        rules.leveltwo_first.output, 
+        rules.leveltwo_second.output
+    output: "levelthree.txt"
+    shell: "cat {input} > {output}"
+
+rule independent: # should be created in --omit-from but not --until
+    output: "independent.txt"
+    shell: "touch {output}"
+
+###### Wildcard Rules #######
+
+rule zeroth_wildcard:
+    output: "test{num}.txt"
+    shell: "touch {output}"
+
+rule first_wildcard:
+    input: 'test{num}.txt'
+    output: 'test{num}.first'
+    shell: 'cp -f {input} {output}'
+
+rule second_wildcard:
+    input: 'test{num}.first'
+    output: 'test{num}.second'
+    shell: 'cp -f {input} {output}'
+
+rule final_wildcard:
+    input: 'test{num}.second'
+    output: 'test{num}.final'
+    shell: 'cp -f {input} {output}'
+
+
diff --git a/tests/test_omitfrom/expected-results/independent.txt b/tests/test_omitfrom/expected-results/independent.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_omitfrom/expected-results/levelone.txt b/tests/test_omitfrom/expected-results/levelone.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_omitfrom/expected-results/leveltwo_first.txt b/tests/test_omitfrom/expected-results/leveltwo_first.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_omitfrom/expected-results/leveltwo_second.txt b/tests/test_omitfrom/expected-results/leveltwo_second.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_omitfrom/expected-results/test1.second b/tests/test_omitfrom/expected-results/test1.second
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_omitfrom/expected-results/test2.second b/tests/test_omitfrom/expected-results/test2.second
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_until/Snakefile b/tests/test_until/Snakefile
new file mode 100644
index 0000000..fcabec1
--- /dev/null
+++ b/tests/test_until/Snakefile
@@ -0,0 +1,54 @@
+
+rule all: 
+    input:
+        "levelthree.txt",
+        "independent.txt",
+        expand("test{num}.final", num=[1, 2])
+
+rule levelone:
+    output: "levelone.txt"
+    shell: "touch {output}"
+
+rule leveltwo_first:
+    input: rules.levelone.output
+    output: "leveltwo_first.txt"   
+    shell: "cp -f {input} {output}"
+
+rule leveltwo_second:
+    input: rules.levelone.output
+    output: "leveltwo_second.txt"
+    shell: "cp -f {input} {output}"
+
+rule levelthree: # should not be created
+    input: 
+        rules.leveltwo_first.output, 
+        rules.leveltwo_second.output
+    output: "levelthree.txt"
+    shell: "cat {input} > {output}"
+
+rule independent: # should be created in --omit-from but not --until
+    output: "independent.txt"
+    shell: "touch {output}"
+
+###### Wildcard Rules #######
+
+rule zeroth_wildcard:
+    output: "test{num}.txt"
+    shell: "touch {output}"
+
+rule first_wildcard:
+    input: 'test{num}.txt'
+    output: 'test{num}.first'
+    shell: 'cp -f {input} {output}'
+
+rule second_wildcard:
+    input: 'test{num}.first'
+    output: 'test{num}.second'
+    shell: 'cp -f {input} {output}'
+
+rule final_wildcard:
+    input: 'test{num}.second'
+    output: 'test{num}.final'
+    shell: 'cp -f {input} {output}'
+
+
diff --git a/tests/test_until/expected-results/levelone.txt b/tests/test_until/expected-results/levelone.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_until/expected-results/leveltwo_first.txt b/tests/test_until/expected-results/leveltwo_first.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_until/expected-results/leveltwo_second.txt b/tests/test_until/expected-results/leveltwo_second.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_until/expected-results/test1.second b/tests/test_until/expected-results/test1.second
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_until/expected-results/test2.second b/tests/test_until/expected-results/test2.second
new file mode 100644
index 0000000..e69de29
diff --git a/tests/tests.py b/tests/tests.py
index 011ddc4..e428e23 100644
--- a/tests/tests.py
+++ b/tests/tests.py
@@ -300,6 +300,17 @@ def test_script():
 def test_shadow():
     run(dpath("test_shadow"))
 
+def test_until():
+    run(dpath("test_until"),
+        until=["leveltwo_first", # rule name
+               "leveltwo_second.txt", # file name
+               "second_wildcard"]) # wildcard rule
+
+def test_omitfrom():
+    run(dpath("test_omitfrom"), 
+        omit_from=["leveltwo_first", # rule name
+                   "leveltwo_second.txt", # file name
+                   "second_wildcard"]) # wildcard rule
 
 def test_nonstr_params():
     run(dpath("test_nonstr_params"))

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/snakemake.git



More information about the debian-med-commit mailing list