]> code.delx.au - transcoding/blobdiff - batchrun.py
batchrun.py --jobs now starts a new jobs immediately after one finishes
[transcoding] / batchrun.py
index b8596ac51c82a2827f7e52a6456f950baf494e9d..fa799dffc359b7364d78bcab63c014709b14b8f7 100755 (executable)
@@ -1,30 +1,78 @@
 #!/usr/bin/env python
 
-import optparse, shlex, subprocess, sys
+import itertools
+import optparse
+import os
+import shlex
+import subprocess
+import sys
+import time
 
-def parseArgs():
-       parser = optparse.OptionParser(usage="%prog batchfile1 [batchfile2] ...")
-       opts, args = parser.parse_args(sys.argv[1:])
-       return args
 
-def run(args):
-       subprocess.Popen(args).wait()
+def run(running_jobs, cmd):
+       p = subprocess.Popen(cmd, stdin=file(os.devnull, 'r'))
+       running_jobs.append(p)
+
+def wait_for_completion(max_jobs, running_jobs):
+       while len(running_jobs) >= max_jobs:
+               time.sleep(1)
+               for job in running_jobs:
+                       if job.poll() is not None:
+                               running_jobs.remove(job)
+                               job.wait()
+
+def parse_file(fd):
+       def count_indent(s):
+               level = 0
+               for ch in s:
+                       if ch == "\t":
+                               level += 1
+                       else:
+                               break
+               return level
 
-def batchProcess(fd):
-       opts = [[], []]
        for line in fd:
-               args = shlex.split(line)
-               if line.startswith("\t\t"):
-                       run(opts[0] + opts[1] + args)
-               elif line.startswith("\t"):
-                       opts[1] = args
-               else:
-                       opts[0] = args
+               if not line.strip():
+                       continue # Ignore blank lines
+               level = count_indent(line)
+               line = line[level:] # Slice off the indentation
+               yield level, line
+       yield 0, None
+
+def batch_process(opts, lines):
+       running_jobs = []
+       cmd = []
+
+       for level, line in lines:
+               old_level = len(cmd) - 1
+               if level <= old_level:
+                       run(running_jobs, itertools.chain(*cmd))
+                       wait_for_completion(opts.jobs, running_jobs)
+
+               # Delete all options that belong to groups that are indented more than
+               # this one
+               cmd = cmd[:level]
+               assert len(cmd) == level
+
+               if line:
+                       cmd.append(shlex.split(line))
+
+       # Wait for remaining jobs to finish
+       wait_for_completion(1, running_jobs)
+
+def parse_args():
+       parser = optparse.OptionParser(usage="%prog batchfile1 [batchfile2] ...")
+       parser.add_option("-j", "--jobs",
+               action="store", dest="jobs", default=1, type="int",
+               help="The number of concurrent jobs to run")
+       opts, args = parser.parse_args(sys.argv[1:])
+       opts.running_jobs = []
+       return opts, args
 
 def main():
-       args = parseArgs()
+       opts, args = parse_args()
        for name in args:
-               batchProcess(open(name))
+               batch_process(opts, parse_file(open(name)))
 
 if __name__ == "__main__":
        main()