return s.getvalue()
def test_main_bad_switch(self):
- s = self.run_main(switches=['--bad-switch'])
- self.assertEqual(s, dedent("""\
- option --bad-switch not recognized
- use -h/--help for command line help
- """))
+ with captured_stderr() as error_stringio:
+ s = self.run_main(switches=["--bad-switch"])
+ self.assertEqual(s, "")
+ self.assertIn(
+ "unrecognized arguments: --bad-switch", error_stringio.getvalue()
+ )
def test_main_seconds(self):
s = self.run_main(seconds_per_increment=5.5)
s = self.run_main(seconds_per_increment=60.0, switches=['-r-5'])
self.assertEqual(s, "1 loop, best of 1: 60 sec per loop\n")
- @unittest.skipIf(sys.flags.optimize >= 2, "need __doc__")
def test_main_help(self):
s = self.run_main(switches=['-h'])
- self.assertEqual(s, timeit.__doc__)
+ self.assertIn("Tool for measuring execution time", s)
+ self.assertIn("-n", s)
+ self.assertIn("--number", s)
def test_main_verbose(self):
s = self.run_main(switches=['-v'])
"100 loops, best of 5: 3e+03 usec per loop\n")
# Test invalid unit input
with captured_stderr() as error_stringio:
- invalid = self.run_main(seconds_per_increment=0.003,
- switches=['-u', 'parsec'])
- self.assertEqual(error_stringio.getvalue(),
- "Unrecognized unit. Please select nsec, usec, msec, or sec.\n")
+ invalid = self.run_main(
+ seconds_per_increment=0.003, switches=["-u", "parsec"]
+ )
+ self.assertIn(
+ "choose from nsec, usec, msec, sec", error_stringio.getvalue()
+ )
def test_main_exception(self):
with captured_stderr() as error_stringio:
Library usage: see the Timer class.
-Command line usage:
- python timeit.py [-n N] [-r N] [-s S] [-p] [-h] [-t T] [--] [statement]
-
-Options:
- -n/--number N: how many times to execute 'statement' (default: see below)
- -r/--repeat N: how many times to repeat the timer (default 5)
- -s/--setup S: statement to be executed once initially (default 'pass').
- Execution time of this setup statement is NOT timed.
- -p/--process: use time.process_time() (default is time.perf_counter())
- -v/--verbose: print raw timing results; repeat for more digits precision
- -u/--unit: set the output time unit (nsec, usec, msec, or sec)
- -t/--target-time T: if --number is 0 the code will run until it
- takes *at least* this many seconds
- (default: 0.2)
- -h/--help: print this usage message and exit
- --: separate options from statement, use when statement starts with -
- statement: statement to be timed (default 'pass')
-
-A multi-line statement may be given by specifying each line as a
-separate argument; indented lines are possible by enclosing an
-argument in quotes and using leading spaces. Multiple -s options are
-treated similarly.
-
-If -n is not given, a suitable number of loops is calculated by trying
-increasing numbers from the sequence 1, 2, 5, 10, 20, 50, ... until the
-total time is at least --target-time seconds.
-
-Note: there is a certain baseline overhead associated with executing a
-pass statement. It differs between versions. The code here doesn't try
-to hide it, but you should be aware of it. The baseline overhead can be
-measured by invoking the program without arguments.
-
Classes:
Timer
is not None, it must be a callable that accepts a timer function
and returns another timer function (used for unit testing).
"""
- import getopt
+ import argparse
if args is None:
args = sys.argv[1:]
import _colorize
theme = _colorize.get_theme(force_color=colorize).timeit
reset = theme.reset
+ epilog = """\
+A multi-line statement may be given by specifying each line as a
+separate argument; indented lines are possible by enclosing an
+argument in quotes and using leading spaces. Multiple `-s` options are
+treated similarly.
+
+If `-n` is not given, a suitable number of loops is calculated by trying
+increasing numbers from the sequence 1, 2, 5, 10, 20, 50, ... until the
+total time is at least `--target-time` seconds.
+
+Note: there is a certain baseline overhead associated with executing a
+pass statement. It differs between versions. The code here doesn't try
+to hide it, but you should be aware of it. The baseline overhead can be
+measured by invoking the program without arguments."""
+
+ parser = argparse.ArgumentParser(
+ prog="python -m timeit",
+ description="""\
+Tool for measuring execution time of small code snippets.
+
+This module avoids a number of common traps for measuring execution
+times. See also Tim Peters' introduction to the Algorithms chapter in
+the Python Cookbook, published by O'Reilly.
+
+Library usage: see the `Timer` class.""",
+ epilog=epilog,
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ )
+ parser.add_argument(
+ "-n",
+ "--number",
+ type=int,
+ default=0,
+ help="how many times to execute 'statement' (default: see below)",
+ )
+ parser.add_argument(
+ "-r",
+ "--repeat",
+ type=int,
+ default=default_repeat,
+ help="how many times to repeat the timer (default %(default)s)",
+ )
+ parser.add_argument(
+ "-s",
+ "--setup",
+ action="append",
+ default=[],
+ help="statement to be executed once initially. "
+ "Execution time of this setup statement is NOT timed. "
+ "(default 'pass')",
+ )
+ parser.add_argument(
+ "-p",
+ "--process",
+ action="store_true",
+ help="use time.process_time() (default is time.perf_counter())",
+ )
+ parser.add_argument(
+ "-t",
+ "--target-time",
+ type=float,
+ default=default_target_time,
+ help="if --number is 0 the code will run until it takes "
+ "at least this many seconds (default %(default)s)",
+ )
+ parser.add_argument(
+ "-v",
+ "--verbose",
+ action="count",
+ default=0,
+ help="print raw timing results; repeat for more digits precision",
+ )
+ parser.add_argument(
+ "-u",
+ "--unit",
+ default=None,
+ choices=["nsec", "usec", "msec", "sec"],
+ help="set the output time unit",
+ )
+ parser.add_argument(
+ "statement",
+ nargs="*",
+ default=["pass"],
+ help="statement to be timed (default 'pass')",
+ )
try:
- opts, args = getopt.getopt(args, "n:u:s:r:pt:vh",
- ["number=", "setup=", "repeat=",
- "process", "target-time=",
- "verbose", "unit=", "help"])
- except getopt.error as err:
- print(err)
- print("use -h/--help for command line help")
- return 2
-
- timer = default_timer
- stmt = "\n".join(args) or "pass"
- number = 0 # auto-determine
- target_time = default_target_time
- setup = []
- repeat = default_repeat
- verbose = 0
- time_unit = None
+ ns = parser.parse_args(args)
+ except SystemExit as e:
+ return e.code
+
+ timer = time.process_time if ns.process else default_timer
+ stmt = "\n".join(ns.statement) or "pass"
+ number = ns.number
+ target_time = ns.target_time
+ setup = "\n".join(ns.setup) or "pass"
+ repeat = max(ns.repeat, 1)
+ verbose = ns.verbose
+ time_unit = ns.unit
units = {"nsec": 1e-9, "usec": 1e-6, "msec": 1e-3, "sec": 1.0}
- precision = 3
- for o, a in opts:
- if o in ("-n", "--number"):
- number = int(a)
- if o in ("-s", "--setup"):
- setup.append(a)
- if o in ("-u", "--unit"):
- if a in units:
- time_unit = a
- else:
- print("Unrecognized unit. Please select nsec, usec, msec, or sec.",
- file=sys.stderr)
- return 2
- if o in ("-r", "--repeat"):
- repeat = int(a)
- if repeat <= 0:
- repeat = 1
- if o in ("-p", "--process"):
- timer = time.process_time
- if o in ("-t", "--target-time"):
- target_time = float(a)
- if o in ("-v", "--verbose"):
- if verbose:
- precision += 1
- verbose += 1
- if o in ("-h", "--help"):
- print(__doc__, end="")
- return 0
- setup = "\n".join(setup) or "pass"
+ precision = 3 + max(verbose - 1, 0)
# Include the current directory, so that local imports work (sys.path
# contains the directory of this script, rather than the current