Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 15 additions & 11 deletions Lib/test/test_timeit.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,11 +259,12 @@ def run_main(self, seconds_per_increment=1.0, switches=None, timer=None):
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)
Expand Down Expand Up @@ -301,10 +302,11 @@ def test_main_negative_reps(self):
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'])
Expand Down Expand Up @@ -353,10 +355,12 @@ def test_main_with_time_unit(self):
"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:
Expand Down
178 changes: 99 additions & 79 deletions Lib/timeit.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,6 @@

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
Expand Down Expand Up @@ -268,62 +236,114 @@ def main(args=None, *, _wrap_timer=None):
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
colorize = _colorize.can_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 "
"(default 'pass'). Execution time of this "
"setup statement is NOT timed.",
)
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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Use argparse for colour help timeit CLI. Patch by Hugo van Kemenade.
Loading