(root)/
Python-3.11.7/
Lib/
test/
test_timeit.py
       1  import timeit
       2  import unittest
       3  import sys
       4  import io
       5  from textwrap import dedent
       6  
       7  from test.support import captured_stdout
       8  from test.support import captured_stderr
       9  
      10  # timeit's default number of iterations.
      11  DEFAULT_NUMBER = 1000000
      12  
      13  # timeit's default number of repetitions.
      14  DEFAULT_REPEAT = 5
      15  
      16  # XXX: some tests are commented out that would improve the coverage but take a
      17  # long time to run because they test the default number of loops, which is
      18  # large.  The tests could be enabled if there was a way to override the default
      19  # number of loops during testing, but this would require changing the signature
      20  # of some functions that use the default as a default argument.
      21  
      22  class ESC[4;38;5;81mFakeTimer:
      23      BASE_TIME = 42.0
      24      def __init__(self, seconds_per_increment=1.0):
      25          self.count = 0
      26          self.setup_calls = 0
      27          self.seconds_per_increment=seconds_per_increment
      28          timeit._fake_timer = self
      29  
      30      def __call__(self):
      31          return self.BASE_TIME + self.count * self.seconds_per_increment
      32  
      33      def inc(self):
      34          self.count += 1
      35  
      36      def setup(self):
      37          self.setup_calls += 1
      38  
      39      def wrap_timer(self, timer):
      40          """Records 'timer' and returns self as callable timer."""
      41          self.saved_timer = timer
      42          return self
      43  
      44  class ESC[4;38;5;81mTestTimeit(ESC[4;38;5;149munittestESC[4;38;5;149m.ESC[4;38;5;149mTestCase):
      45  
      46      def tearDown(self):
      47          try:
      48              del timeit._fake_timer
      49          except AttributeError:
      50              pass
      51  
      52      def test_reindent_empty(self):
      53          self.assertEqual(timeit.reindent("", 0), "")
      54          self.assertEqual(timeit.reindent("", 4), "")
      55  
      56      def test_reindent_single(self):
      57          self.assertEqual(timeit.reindent("pass", 0), "pass")
      58          self.assertEqual(timeit.reindent("pass", 4), "pass")
      59  
      60      def test_reindent_multi_empty(self):
      61          self.assertEqual(timeit.reindent("\n\n", 0), "\n\n")
      62          self.assertEqual(timeit.reindent("\n\n", 4), "\n    \n    ")
      63  
      64      def test_reindent_multi(self):
      65          self.assertEqual(timeit.reindent(
      66              "print()\npass\nbreak", 0),
      67              "print()\npass\nbreak")
      68          self.assertEqual(timeit.reindent(
      69              "print()\npass\nbreak", 4),
      70              "print()\n    pass\n    break")
      71  
      72      def test_timer_invalid_stmt(self):
      73          self.assertRaises(ValueError, timeit.Timer, stmt=None)
      74          self.assertRaises(SyntaxError, timeit.Timer, stmt='return')
      75          self.assertRaises(SyntaxError, timeit.Timer, stmt='yield')
      76          self.assertRaises(SyntaxError, timeit.Timer, stmt='yield from ()')
      77          self.assertRaises(SyntaxError, timeit.Timer, stmt='break')
      78          self.assertRaises(SyntaxError, timeit.Timer, stmt='continue')
      79          self.assertRaises(SyntaxError, timeit.Timer, stmt='from timeit import *')
      80          self.assertRaises(SyntaxError, timeit.Timer, stmt='  pass')
      81          self.assertRaises(SyntaxError, timeit.Timer,
      82                            setup='while False:\n  pass', stmt='  break')
      83  
      84      def test_timer_invalid_setup(self):
      85          self.assertRaises(ValueError, timeit.Timer, setup=None)
      86          self.assertRaises(SyntaxError, timeit.Timer, setup='return')
      87          self.assertRaises(SyntaxError, timeit.Timer, setup='yield')
      88          self.assertRaises(SyntaxError, timeit.Timer, setup='yield from ()')
      89          self.assertRaises(SyntaxError, timeit.Timer, setup='break')
      90          self.assertRaises(SyntaxError, timeit.Timer, setup='continue')
      91          self.assertRaises(SyntaxError, timeit.Timer, setup='from timeit import *')
      92          self.assertRaises(SyntaxError, timeit.Timer, setup='  pass')
      93  
      94      def test_timer_empty_stmt(self):
      95          timeit.Timer(stmt='')
      96          timeit.Timer(stmt=' \n\t\f')
      97          timeit.Timer(stmt='# comment')
      98  
      99      fake_setup = "import timeit\ntimeit._fake_timer.setup()"
     100      fake_stmt = "import timeit\ntimeit._fake_timer.inc()"
     101  
     102      def fake_callable_setup(self):
     103          self.fake_timer.setup()
     104  
     105      def fake_callable_stmt(self):
     106          self.fake_timer.inc()
     107  
     108      def timeit(self, stmt, setup, number=None, globals=None):
     109          self.fake_timer = FakeTimer()
     110          t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer,
     111                  globals=globals)
     112          kwargs = {}
     113          if number is None:
     114              number = DEFAULT_NUMBER
     115          else:
     116              kwargs['number'] = number
     117          delta_time = t.timeit(**kwargs)
     118          self.assertEqual(self.fake_timer.setup_calls, 1)
     119          self.assertEqual(self.fake_timer.count, number)
     120          self.assertEqual(delta_time, number)
     121  
     122      # Takes too long to run in debug build.
     123      #def test_timeit_default_iters(self):
     124      #    self.timeit(self.fake_stmt, self.fake_setup)
     125  
     126      def test_timeit_zero_iters(self):
     127          self.timeit(self.fake_stmt, self.fake_setup, number=0)
     128  
     129      def test_timeit_few_iters(self):
     130          self.timeit(self.fake_stmt, self.fake_setup, number=3)
     131  
     132      def test_timeit_callable_stmt(self):
     133          self.timeit(self.fake_callable_stmt, self.fake_setup, number=3)
     134  
     135      def test_timeit_callable_setup(self):
     136          self.timeit(self.fake_stmt, self.fake_callable_setup, number=3)
     137  
     138      def test_timeit_callable_stmt_and_setup(self):
     139          self.timeit(self.fake_callable_stmt,
     140                  self.fake_callable_setup, number=3)
     141  
     142      # Takes too long to run in debug build.
     143      #def test_timeit_function(self):
     144      #    delta_time = timeit.timeit(self.fake_stmt, self.fake_setup,
     145      #            timer=FakeTimer())
     146      #    self.assertEqual(delta_time, DEFAULT_NUMBER)
     147  
     148      def test_timeit_function_zero_iters(self):
     149          delta_time = timeit.timeit(self.fake_stmt, self.fake_setup, number=0,
     150                  timer=FakeTimer())
     151          self.assertEqual(delta_time, 0)
     152  
     153      def test_timeit_globals_args(self):
     154          global _global_timer
     155          _global_timer = FakeTimer()
     156          t = timeit.Timer(stmt='_global_timer.inc()', timer=_global_timer)
     157          self.assertRaises(NameError, t.timeit, number=3)
     158          timeit.timeit(stmt='_global_timer.inc()', timer=_global_timer,
     159                        globals=globals(), number=3)
     160          local_timer = FakeTimer()
     161          timeit.timeit(stmt='local_timer.inc()', timer=local_timer,
     162                        globals=locals(), number=3)
     163  
     164      def repeat(self, stmt, setup, repeat=None, number=None):
     165          self.fake_timer = FakeTimer()
     166          t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer)
     167          kwargs = {}
     168          if repeat is None:
     169              repeat = DEFAULT_REPEAT
     170          else:
     171              kwargs['repeat'] = repeat
     172          if number is None:
     173              number = DEFAULT_NUMBER
     174          else:
     175              kwargs['number'] = number
     176          delta_times = t.repeat(**kwargs)
     177          self.assertEqual(self.fake_timer.setup_calls, repeat)
     178          self.assertEqual(self.fake_timer.count, repeat * number)
     179          self.assertEqual(delta_times, repeat * [float(number)])
     180  
     181      # Takes too long to run in debug build.
     182      #def test_repeat_default(self):
     183      #    self.repeat(self.fake_stmt, self.fake_setup)
     184  
     185      def test_repeat_zero_reps(self):
     186          self.repeat(self.fake_stmt, self.fake_setup, repeat=0)
     187  
     188      def test_repeat_zero_iters(self):
     189          self.repeat(self.fake_stmt, self.fake_setup, number=0)
     190  
     191      def test_repeat_few_reps_and_iters(self):
     192          self.repeat(self.fake_stmt, self.fake_setup, repeat=3, number=5)
     193  
     194      def test_repeat_callable_stmt(self):
     195          self.repeat(self.fake_callable_stmt, self.fake_setup,
     196                  repeat=3, number=5)
     197  
     198      def test_repeat_callable_setup(self):
     199          self.repeat(self.fake_stmt, self.fake_callable_setup,
     200                  repeat=3, number=5)
     201  
     202      def test_repeat_callable_stmt_and_setup(self):
     203          self.repeat(self.fake_callable_stmt, self.fake_callable_setup,
     204                  repeat=3, number=5)
     205  
     206      # Takes too long to run in debug build.
     207      #def test_repeat_function(self):
     208      #    delta_times = timeit.repeat(self.fake_stmt, self.fake_setup,
     209      #            timer=FakeTimer())
     210      #    self.assertEqual(delta_times, DEFAULT_REPEAT * [float(DEFAULT_NUMBER)])
     211  
     212      def test_repeat_function_zero_reps(self):
     213          delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, repeat=0,
     214                  timer=FakeTimer())
     215          self.assertEqual(delta_times, [])
     216  
     217      def test_repeat_function_zero_iters(self):
     218          delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, number=0,
     219                  timer=FakeTimer())
     220          self.assertEqual(delta_times, DEFAULT_REPEAT * [0.0])
     221  
     222      def assert_exc_string(self, exc_string, expected_exc_name):
     223          exc_lines = exc_string.splitlines()
     224          self.assertGreater(len(exc_lines), 2)
     225          self.assertTrue(exc_lines[0].startswith('Traceback'))
     226          self.assertTrue(exc_lines[-1].startswith(expected_exc_name))
     227  
     228      def test_print_exc(self):
     229          s = io.StringIO()
     230          t = timeit.Timer("1/0")
     231          try:
     232              t.timeit()
     233          except:
     234              t.print_exc(s)
     235          self.assert_exc_string(s.getvalue(), 'ZeroDivisionError')
     236  
     237      MAIN_DEFAULT_OUTPUT = "1 loop, best of 5: 1 sec per loop\n"
     238  
     239      def run_main(self, seconds_per_increment=1.0, switches=None, timer=None):
     240          if timer is None:
     241              timer = FakeTimer(seconds_per_increment=seconds_per_increment)
     242          if switches is None:
     243              args = []
     244          else:
     245              args = switches[:]
     246          args.append(self.fake_stmt)
     247          # timeit.main() modifies sys.path, so save and restore it.
     248          orig_sys_path = sys.path[:]
     249          with captured_stdout() as s:
     250              timeit.main(args=args, _wrap_timer=timer.wrap_timer)
     251          sys.path[:] = orig_sys_path[:]
     252          return s.getvalue()
     253  
     254      def test_main_bad_switch(self):
     255          s = self.run_main(switches=['--bad-switch'])
     256          self.assertEqual(s, dedent("""\
     257              option --bad-switch not recognized
     258              use -h/--help for command line help
     259              """))
     260  
     261      def test_main_seconds(self):
     262          s = self.run_main(seconds_per_increment=5.5)
     263          self.assertEqual(s, "1 loop, best of 5: 5.5 sec per loop\n")
     264  
     265      def test_main_milliseconds(self):
     266          s = self.run_main(seconds_per_increment=0.0055)
     267          self.assertEqual(s, "50 loops, best of 5: 5.5 msec per loop\n")
     268  
     269      def test_main_microseconds(self):
     270          s = self.run_main(seconds_per_increment=0.0000025, switches=['-n100'])
     271          self.assertEqual(s, "100 loops, best of 5: 2.5 usec per loop\n")
     272  
     273      def test_main_fixed_iters(self):
     274          s = self.run_main(seconds_per_increment=2.0, switches=['-n35'])
     275          self.assertEqual(s, "35 loops, best of 5: 2 sec per loop\n")
     276  
     277      def test_main_setup(self):
     278          s = self.run_main(seconds_per_increment=2.0,
     279                  switches=['-n35', '-s', 'print("CustomSetup")'])
     280          self.assertEqual(s, "CustomSetup\n" * DEFAULT_REPEAT +
     281                  "35 loops, best of 5: 2 sec per loop\n")
     282  
     283      def test_main_multiple_setups(self):
     284          s = self.run_main(seconds_per_increment=2.0,
     285                  switches=['-n35', '-s', 'a = "CustomSetup"', '-s', 'print(a)'])
     286          self.assertEqual(s, "CustomSetup\n" * DEFAULT_REPEAT +
     287                  "35 loops, best of 5: 2 sec per loop\n")
     288  
     289      def test_main_fixed_reps(self):
     290          s = self.run_main(seconds_per_increment=60.0, switches=['-r9'])
     291          self.assertEqual(s, "1 loop, best of 9: 60 sec per loop\n")
     292  
     293      def test_main_negative_reps(self):
     294          s = self.run_main(seconds_per_increment=60.0, switches=['-r-5'])
     295          self.assertEqual(s, "1 loop, best of 1: 60 sec per loop\n")
     296  
     297      @unittest.skipIf(sys.flags.optimize >= 2, "need __doc__")
     298      def test_main_help(self):
     299          s = self.run_main(switches=['-h'])
     300          # Note: It's not clear that the trailing space was intended as part of
     301          # the help text, but since it's there, check for it.
     302          self.assertEqual(s, timeit.__doc__ + ' ')
     303  
     304      def test_main_verbose(self):
     305          s = self.run_main(switches=['-v'])
     306          self.assertEqual(s, dedent("""\
     307                  1 loop -> 1 secs
     308  
     309                  raw times: 1 sec, 1 sec, 1 sec, 1 sec, 1 sec
     310  
     311                  1 loop, best of 5: 1 sec per loop
     312              """))
     313  
     314      def test_main_very_verbose(self):
     315          s = self.run_main(seconds_per_increment=0.000_030, switches=['-vv'])
     316          self.assertEqual(s, dedent("""\
     317                  1 loop -> 3e-05 secs
     318                  2 loops -> 6e-05 secs
     319                  5 loops -> 0.00015 secs
     320                  10 loops -> 0.0003 secs
     321                  20 loops -> 0.0006 secs
     322                  50 loops -> 0.0015 secs
     323                  100 loops -> 0.003 secs
     324                  200 loops -> 0.006 secs
     325                  500 loops -> 0.015 secs
     326                  1000 loops -> 0.03 secs
     327                  2000 loops -> 0.06 secs
     328                  5000 loops -> 0.15 secs
     329                  10000 loops -> 0.3 secs
     330  
     331                  raw times: 300 msec, 300 msec, 300 msec, 300 msec, 300 msec
     332  
     333                  10000 loops, best of 5: 30 usec per loop
     334              """))
     335  
     336      def test_main_with_time_unit(self):
     337          unit_sec = self.run_main(seconds_per_increment=0.003,
     338                  switches=['-u', 'sec'])
     339          self.assertEqual(unit_sec,
     340                  "100 loops, best of 5: 0.003 sec per loop\n")
     341          unit_msec = self.run_main(seconds_per_increment=0.003,
     342                  switches=['-u', 'msec'])
     343          self.assertEqual(unit_msec,
     344                  "100 loops, best of 5: 3 msec per loop\n")
     345          unit_usec = self.run_main(seconds_per_increment=0.003,
     346                  switches=['-u', 'usec'])
     347          self.assertEqual(unit_usec,
     348                  "100 loops, best of 5: 3e+03 usec per loop\n")
     349          # Test invalid unit input
     350          with captured_stderr() as error_stringio:
     351              invalid = self.run_main(seconds_per_increment=0.003,
     352                      switches=['-u', 'parsec'])
     353          self.assertEqual(error_stringio.getvalue(),
     354                      "Unrecognized unit. Please select nsec, usec, msec, or sec.\n")
     355  
     356      def test_main_exception(self):
     357          with captured_stderr() as error_stringio:
     358              s = self.run_main(switches=['1/0'])
     359          self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError')
     360  
     361      def test_main_exception_fixed_reps(self):
     362          with captured_stderr() as error_stringio:
     363              s = self.run_main(switches=['-n1', '1/0'])
     364          self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError')
     365  
     366      def autorange(self, seconds_per_increment=1/1024, callback=None):
     367          timer = FakeTimer(seconds_per_increment=seconds_per_increment)
     368          t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer)
     369          return t.autorange(callback)
     370  
     371      def test_autorange(self):
     372          num_loops, time_taken = self.autorange()
     373          self.assertEqual(num_loops, 500)
     374          self.assertEqual(time_taken, 500/1024)
     375  
     376      def test_autorange_second(self):
     377          num_loops, time_taken = self.autorange(seconds_per_increment=1.0)
     378          self.assertEqual(num_loops, 1)
     379          self.assertEqual(time_taken, 1.0)
     380  
     381      def test_autorange_with_callback(self):
     382          def callback(a, b):
     383              print("{} {:.3f}".format(a, b))
     384          with captured_stdout() as s:
     385              num_loops, time_taken = self.autorange(callback=callback)
     386          self.assertEqual(num_loops, 500)
     387          self.assertEqual(time_taken, 500/1024)
     388          expected = ('1 0.001\n'
     389                      '2 0.002\n'
     390                      '5 0.005\n'
     391                      '10 0.010\n'
     392                      '20 0.020\n'
     393                      '50 0.049\n'
     394                      '100 0.098\n'
     395                      '200 0.195\n'
     396                      '500 0.488\n')
     397          self.assertEqual(s.getvalue(), expected)
     398  
     399  
     400  if __name__ == '__main__':
     401      unittest.main()