| 1 | #!/usr/bin/env python
 | 
| 2 | """
 | 
| 3 | time.py -- Replacement for coreutils 'time'.
 | 
| 4 | 
 | 
| 5 | The interface of this program is modelled after:
 | 
| 6 | 
 | 
| 7 | /usr/bin/time --append --output foo.txt --format '%x %e'
 | 
| 8 | 
 | 
| 9 | Problems with /usr/bin/time:
 | 
| 10 |   - elapsed time only has 2 digits of precision
 | 
| 11 | 
 | 
| 12 | Problems with bash time builtin
 | 
| 13 |   - has no way to get the exit code
 | 
| 14 |   - writes to stderr, so you it's annoying to get both process stderr and
 | 
| 15 |     and
 | 
| 16 | 
 | 
| 17 | This program also writes CSV directly, so you can have commas in fields, etc.
 | 
| 18 | """
 | 
| 19 | 
 | 
| 20 | import csv
 | 
| 21 | import optparse
 | 
| 22 | import sys
 | 
| 23 | import subprocess
 | 
| 24 | import time
 | 
| 25 | 
 | 
| 26 | 
 | 
| 27 | def Options():
 | 
| 28 |   """Returns an option parser instance."""
 | 
| 29 |   p = optparse.OptionParser('time.py [options] ARGV...')
 | 
| 30 |   p.add_option(
 | 
| 31 |       '--tsv', dest='tsv', default=False, action='store_true',
 | 
| 32 |       help='Write output in TSV format')
 | 
| 33 |   p.add_option(
 | 
| 34 |       '-o', '--output', dest='output', default=None,
 | 
| 35 |       help='Name of output file to write to')
 | 
| 36 |   p.add_option(
 | 
| 37 |       '--field', dest='fields', default=[], action='append',
 | 
| 38 |       help='A string to append to each row, after the exit code and status')
 | 
| 39 |   return p
 | 
| 40 | 
 | 
| 41 | 
 | 
| 42 | def main(argv):
 | 
| 43 |   (opts, child_argv) = Options().parse_args(argv[1:])
 | 
| 44 | 
 | 
| 45 |   start_time = time.time()
 | 
| 46 |   exit_code = subprocess.call(child_argv)
 | 
| 47 |   elapsed = time.time() - start_time
 | 
| 48 | 
 | 
| 49 |   fields = tuple(opts.fields)
 | 
| 50 |   with open(opts.output, 'a') as f:
 | 
| 51 |     if opts.tsv:
 | 
| 52 |       # TSV output.
 | 
| 53 |       out = csv.writer(f, delimiter='\t', doublequote=False,
 | 
| 54 |                        quoting=csv.QUOTE_NONE)
 | 
| 55 |     else:
 | 
| 56 |       out = csv.writer(f)
 | 
| 57 |     row = (exit_code, '%.4f' % elapsed) + fields
 | 
| 58 |     out.writerow(row)
 | 
| 59 | 
 | 
| 60 |   # Preserve the command's exit code.  (This means you can't distinguish
 | 
| 61 |   # between a failure of time.py and the command, but that's better than
 | 
| 62 |   # swallowing the error.)
 | 
| 63 |   return exit_code
 | 
| 64 | 
 | 
| 65 | 
 | 
| 66 | if __name__ == '__main__':
 | 
| 67 |   sys.exit(main(sys.argv))
 |