Commits

Johannes Köster committed d307af9

Special handling of rules with no output but run declaration. This
ensures a PHONY like behaviour of such rules.

Comments (0)

Files changed (5)

                 # TODO find a way to handle added/removed input files here?
                 if not job.output:
                     if job.input:
-                        reason.updated_input_run.update(
-                            [f for f in job.input if not f.exists])
+                        if job.rule.norun:
+                            reason.updated_input_run.update(
+                                [f for f in job.input if not f.exists])
+                        else:
+                            reason.nooutput = True
                     else:
                         reason.noio = True
                 else:

snakemake/jobs.py

         self.incomplete_output = set()
         self.forced = False
         self.noio = False
+        self.nooutput = False
         self.derived = True
 
     def __str__(self):
         else:
             if self.noio:
                 s.append("Rules with neither input nor "
-                    "output files are always executed")
+                    "output files are always executed.")
+            elif self.nooutput:
+                s.append("Rules with a run or shell declaration but no output "
+                    "are always executed.")
             else:
                 if self.missing_output:
                     s.append("Missing output files: {}".format(
 
     def __bool__(self):
         return bool(self.updated_input or self.missing_output or self.forced
-            or self.updated_input_run or self.noio)
+            or self.updated_input_run or self.noio or self.nooutput)

snakemake/parser.py

 
     def end(self):
         if not self.run:
+            yield "@workflow.norun()"
+            yield "\n"
             for t in self.subautomaton("run", rulename=self.rulename).start():
                 yield t
             # the end is detected.

snakemake/rules.py

             self.snakefile = snakefile
             self.run_func = None
             self.shellcmd = None
+            self.norun = False
         elif len(args) == 1:
             other = args[0]
             self.name = other.name
             self.snakefile = other.snakefile
             self.run_func = other.run_func
             self.shellcmd = other.shellcmd
+            self.norun = other.norun
 
     def dynamic_branch(self, wildcards, input=True):
         def get_io(rule):

snakemake/workflow.py

                 rule.log = ruleinfo.log
             if ruleinfo.message:
                 rule.message = ruleinfo.message
+            rule.norun = ruleinfo.norun
             rule.docstring = ruleinfo.docstring
             rule.run_func = ruleinfo.func
             rule.shellcmd = ruleinfo.shellcmd
             return ruleinfo
         return decorate
 
+    def norun(self):
+        def decorate(ruleinfo):
+            ruleinfo.norun = True
+            return ruleinfo
+        return decorate
+
     def run(self, func):
         return RuleInfo(func)
 
     def __init__(self, func):
         self.func = func
         self.shellcmd = None
+        self.norun = False
         self.input = None
         self.output = None
         self.params = None