From d9bef35e3c0c4c1dfa5760fd76c720afa1956170 Mon Sep 17 00:00:00 2001
From: Georg Brandl <georg@python.org>
Date: Tue, 13 Mar 2007 19:32:21 +0000
Subject: Patch #1533909: the timeit module now accepts callables in addition
 to strings for the code to time and the setup code. Also added two
 convenience functions for instantiating a Timer and calling its methods.

---
 Doc/lib/libtimeit.tex | 25 ++++++++++++++++++++
 Lib/timeit.py         | 64 ++++++++++++++++++++++++++++++++++++++++++---------
 Misc/ACKS             |  1 +
 Misc/NEWS             |  4 ++++
 4 files changed, 83 insertions(+), 11 deletions(-)

diff --git a/Doc/lib/libtimeit.tex b/Doc/lib/libtimeit.tex
index 1c4e05b..f4f3cc7 100644
--- a/Doc/lib/libtimeit.tex
+++ b/Doc/lib/libtimeit.tex
@@ -31,6 +31,13 @@ To measure the execution time of the first statement, use the
 \method{timeit()} method.  The \method{repeat()} method is a
 convenience to call \method{timeit()} multiple times and return a list
 of results.
+
+\versionchanged[The \var{stmt} and \var{setup} parameters can now also
+                take objects that are callable without arguments. This
+		will embed calls to them in a timer function that will
+		then be executed by \method{timeit()}.  Note that the timing
+		overhead is a little larger in this case because of the
+		extra function calls]{2.6}
 \end{classdesc}
 
 \begin{methoddesc}{print_exc}{\optional{file=\constant{None}}}
@@ -97,6 +104,24 @@ measured.  If so, GC can be re-enabled as the first statement in the
 \end{methoddesc}
 
 
+Starting with version 2.6, the module also defines two convenience functions:
+
+\begin{funcdesc}{repeat}{stmt\optional{, setup\optional{, timer\optional{,
+                         repeat\code{=3} \optional{, number\code{=1000000}}}}}}
+Create a \class{Timer} instance with the given statement, setup code and timer
+function and run its \method{repeat} method with the given repeat count and
+\var{number} executions.
+\versionadded{2.6}
+\end{funcdesc}
+
+\begin{funcdesc}{timeit}{stmt\optional{, setup\optional{, timer\optional{,
+                         number\code{=1000000}}}}}
+Create a \class{Timer} instance with the given statement, setup code and timer
+function and run its \method{timeit} method with \var{number} executions.
+\versionadded{2.6}
+\end{funcdesc}
+
+
 \subsection{Command Line Interface}
 
 When called as a program from the command line, the following form is used:
diff --git a/Lib/timeit.py b/Lib/timeit.py
index 8c0f7a5..09942f3 100644
--- a/Lib/timeit.py
+++ b/Lib/timeit.py
@@ -90,6 +90,17 @@ def reindent(src, indent):
     """Helper to reindent a multi-line statement."""
     return src.replace("\n", "\n" + " "*indent)
 
+def _template_func(setup, func):
+    """Create a timer function. Used if the "statement" is a callable."""
+    def inner(_it, _timer):
+        setup()
+        _t0 = _timer()
+        for _i in _it:
+            func()
+        _t1 = _timer()
+        return _t1 - _t0
+    return inner
+
 class Timer:
     """Class for timing execution speed of small code snippets.
 
@@ -109,14 +120,32 @@ class Timer:
     def __init__(self, stmt="pass", setup="pass", timer=default_timer):
         """Constructor.  See class doc string."""
         self.timer = timer
-        stmt = reindent(stmt, 8)
-        setup = reindent(setup, 4)
-        src = template % {'stmt': stmt, 'setup': setup}
-        self.src = src # Save for traceback display
-        code = compile(src, dummy_src_name, "exec")
         ns = {}
-        exec code in globals(), ns
-        self.inner = ns["inner"]
+        if isinstance(stmt, basestring):
+            stmt = reindent(stmt, 8)
+            if isinstance(setup, basestring):
+                setup = reindent(setup, 4)
+                src = template % {'stmt': stmt, 'setup': setup}
+            elif callable(setup):
+                src = template % {'stmt': stmt, 'setup': '_setup()'}
+                ns['_setup'] = setup
+            else:
+                raise ValueError("setup is neither a string nor callable")
+            self.src = src # Save for traceback display
+            code = compile(src, dummy_src_name, "exec")
+            exec code in globals(), ns
+            self.inner = ns["inner"]
+        elif callable(stmt):
+            self.src = None
+            if isinstance(setup, basestring):
+                _setup = setup
+                def setup():
+                    exec _setup in globals(), ns
+            elif not callable(setup):
+                raise ValueError("setup is neither a string nor callable")
+            self.inner = _template_func(setup, stmt)
+        else:
+            raise ValueError("stmt is neither a string nor callable")
 
     def print_exc(self, file=None):
         """Helper to print a traceback from the timed code.
@@ -136,10 +165,13 @@ class Timer:
         sent; it defaults to sys.stderr.
         """
         import linecache, traceback
-        linecache.cache[dummy_src_name] = (len(self.src),
-                                           None,
-                                           self.src.split("\n"),
-                                           dummy_src_name)
+        if self.src is not None:
+            linecache.cache[dummy_src_name] = (len(self.src),
+                                               None,
+                                               self.src.split("\n"),
+                                               dummy_src_name)
+        # else the source is already stored somewhere else
+
         traceback.print_exc(file=file)
 
     def timeit(self, number=default_number):
@@ -189,6 +221,16 @@ class Timer:
             r.append(t)
         return r
 
+def timeit(stmt="pass", setup="pass", timer=default_timer,
+           number=default_number):
+  """Convenience function to create Timer object and call timeit method."""
+  return Timer(stmt, setup, timer).timeit(number)
+
+def repeat(stmt="pass", setup="pass", timer=default_timer,
+           repeat=default_repeat, number=default_number):
+  """Convenience function to create Timer object and call repeat method."""
+  return Timer(stmt, setup, timer).repeat(repeat, number)
+
 def main(args=None):
     """Main program, used when run as a script.
 
diff --git a/Misc/ACKS b/Misc/ACKS
index 898af48..7b6002c 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -156,6 +156,7 @@ Ben Darnell
 Jonathan Dasteel
 John DeGood
 Vincent Delft
+Erik Demaine
 Roger Dev
 Toby Dickenson
 Yves Dionne
diff --git a/Misc/NEWS b/Misc/NEWS
index d03c4b5..ee4b538 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -168,6 +168,10 @@ Core and builtins
 Library
 -------
 
+- Patch #1533909: the timeit module now accepts callables in addition to
+  strings for the code to time and the setup code. Also added two
+  convenience functions for instantiating a Timer and calling its methods.
+
 - Patch #1537850: tempfile.NamedTemporaryFile now has a "delete" parameter
   which can be set to False to prevent the default delete-on-close
   behavior.
-- 
cgit v0.12

