Commit ef3b9ed0 authored by Antoine Pitrou's avatar Antoine Pitrou

Issue #2527: Add a *globals* argument to timeit functions, in order to...

Issue #2527: Add a *globals* argument to timeit functions, in order to override the globals namespace in which the timed code is executed.

Patch by Ben Roberts.
parent 682c04c7
...@@ -59,10 +59,15 @@ Python Interface ...@@ -59,10 +59,15 @@ Python Interface
The module defines three convenience functions and a public class: The module defines three convenience functions and a public class:
.. function:: timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000) .. function:: timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)
Create a :class:`Timer` instance with the given statement, *setup* code and Create a :class:`Timer` instance with the given statement, *setup* code and
*timer* function and run its :meth:`.timeit` method with *number* executions. *timer* function and run its :meth:`.timeit` method with *number* executions.
The optional *globals* argument specifies a namespace in which to execute the
code.
.. versionchanged:: 3.5
The optional *globals* parameter was added.
.. note:: .. note::
...@@ -71,12 +76,15 @@ The module defines three convenience functions and a public class: ...@@ -71,12 +76,15 @@ The module defines three convenience functions and a public class:
It will instead return the data specified by your return statement. It will instead return the data specified by your return statement.
.. function:: repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=3, number=1000000) .. function:: repeat(stmt='pass', setup='pass', timer=<default timer>, repeat=3, number=1000000, globals=None)
Create a :class:`Timer` instance with the given statement, *setup* code and Create a :class:`Timer` instance with the given statement, *setup* code and
*timer* function and run its :meth:`.repeat` method with the given *repeat* *timer* function and run its :meth:`.repeat` method with the given *repeat*
count and *number* executions. count and *number* executions. The optional *globals* argument specifies a
namespace in which to execute the code.
.. versionchanged:: 3.5
The optional *globals* parameter was added.
.. function:: default_timer() .. function:: default_timer()
...@@ -86,7 +94,7 @@ The module defines three convenience functions and a public class: ...@@ -86,7 +94,7 @@ The module defines three convenience functions and a public class:
:func:`time.perf_counter` is now the default timer. :func:`time.perf_counter` is now the default timer.
.. class:: Timer(stmt='pass', setup='pass', timer=<timer function>) .. class:: Timer(stmt='pass', setup='pass', timer=<timer function>, globals=None)
Class for timing execution speed of small code snippets. Class for timing execution speed of small code snippets.
...@@ -94,7 +102,9 @@ The module defines three convenience functions and a public class: ...@@ -94,7 +102,9 @@ The module defines three convenience functions and a public class:
for setup, and a timer function. Both statements default to ``'pass'``; for setup, and a timer function. Both statements default to ``'pass'``;
the timer function is platform-dependent (see the module doc string). the timer function is platform-dependent (see the module doc string).
*stmt* and *setup* may also contain multiple statements separated by ``;`` *stmt* and *setup* may also contain multiple statements separated by ``;``
or newlines, as long as they don't contain multi-line string literals. or newlines, as long as they don't contain multi-line string literals. The
statement will by default be executed within timeit's namespace; this behavior
can be controlled by passing a namespace to *globals*.
To measure the execution time of the first statement, use the :meth:`.timeit` To measure the execution time of the first statement, use the :meth:`.timeit`
method. The :meth:`.repeat` method is a convenience to call :meth:`.timeit` method. The :meth:`.repeat` method is a convenience to call :meth:`.timeit`
...@@ -105,6 +115,8 @@ The module defines three convenience functions and a public class: ...@@ -105,6 +115,8 @@ The module defines three convenience functions and a public class:
will then be executed by :meth:`.timeit`. Note that the timing overhead is a will then be executed by :meth:`.timeit`. Note that the timing overhead is a
little larger in this case because of the extra function calls. little larger in this case because of the extra function calls.
.. versionchanged:: 3.5
The optional *globals* parameter was added.
.. method:: Timer.timeit(number=1000000) .. method:: Timer.timeit(number=1000000)
...@@ -324,3 +336,17 @@ To give the :mod:`timeit` module access to functions you define, you can pass a ...@@ -324,3 +336,17 @@ To give the :mod:`timeit` module access to functions you define, you can pass a
if __name__ == '__main__': if __name__ == '__main__':
import timeit import timeit
print(timeit.timeit("test()", setup="from __main__ import test")) print(timeit.timeit("test()", setup="from __main__ import test"))
Another option is to pass :func:`globals` to the *globals* parameter, which will cause the code
to be executed within your current global namespace. This can be more convenient
than individually specifying imports::
def f(x):
return x**2
def g(x):
return x**4
def h(x):
return x**8
import timeit
print(timeit.timeit('[func(42) for func in (f,g,h)]', globals=globals()))
...@@ -86,9 +86,10 @@ class TestTimeit(unittest.TestCase): ...@@ -86,9 +86,10 @@ class TestTimeit(unittest.TestCase):
def fake_callable_stmt(self): def fake_callable_stmt(self):
self.fake_timer.inc() self.fake_timer.inc()
def timeit(self, stmt, setup, number=None): def timeit(self, stmt, setup, number=None, globals=None):
self.fake_timer = FakeTimer() self.fake_timer = FakeTimer()
t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer) t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer,
globals=globals)
kwargs = {} kwargs = {}
if number is None: if number is None:
number = DEFAULT_NUMBER number = DEFAULT_NUMBER
...@@ -127,6 +128,17 @@ class TestTimeit(unittest.TestCase): ...@@ -127,6 +128,17 @@ class TestTimeit(unittest.TestCase):
timer=FakeTimer()) timer=FakeTimer())
self.assertEqual(delta_time, 0) self.assertEqual(delta_time, 0)
def test_timeit_globals_args(self):
global _global_timer
_global_timer = FakeTimer()
t = timeit.Timer(stmt='_global_timer.inc()', timer=_global_timer)
self.assertRaises(NameError, t.timeit, number=3)
timeit.timeit(stmt='_global_timer.inc()', timer=_global_timer,
globals=globals(), number=3)
local_timer = FakeTimer()
timeit.timeit(stmt='local_timer.inc()', timer=local_timer,
globals=locals(), number=3)
def repeat(self, stmt, setup, repeat=None, number=None): def repeat(self, stmt, setup, repeat=None, number=None):
self.fake_timer = FakeTimer() self.fake_timer = FakeTimer()
t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer) t = timeit.Timer(stmt=stmt, setup=setup, timer=self.fake_timer)
......
...@@ -60,6 +60,8 @@ default_number = 1000000 ...@@ -60,6 +60,8 @@ default_number = 1000000
default_repeat = 3 default_repeat = 3
default_timer = time.perf_counter default_timer = time.perf_counter
_globals = globals
# Don't change the indentation of the template; the reindent() calls # Don't change the indentation of the template; the reindent() calls
# in Timer.__init__() depend on setup being indented 4 spaces and stmt # in Timer.__init__() depend on setup being indented 4 spaces and stmt
# being indented 8 spaces. # being indented 8 spaces.
...@@ -94,7 +96,9 @@ class Timer: ...@@ -94,7 +96,9 @@ class Timer:
The constructor takes a statement to be timed, an additional The constructor takes a statement to be timed, an additional
statement used for setup, and a timer function. Both statements statement used for setup, and a timer function. Both statements
default to 'pass'; the timer function is platform-dependent (see default to 'pass'; the timer function is platform-dependent (see
module doc string). module doc string). If 'globals' is specified, the code will be
executed within that namespace (as opposed to inside timeit's
namespace).
To measure the execution time of the first statement, use the To measure the execution time of the first statement, use the
timeit() method. The repeat() method is a convenience to call timeit() method. The repeat() method is a convenience to call
...@@ -104,10 +108,12 @@ class Timer: ...@@ -104,10 +108,12 @@ class Timer:
multi-line string literals. multi-line string literals.
""" """
def __init__(self, stmt="pass", setup="pass", timer=default_timer): def __init__(self, stmt="pass", setup="pass", timer=default_timer,
globals=None):
"""Constructor. See class doc string.""" """Constructor. See class doc string."""
self.timer = timer self.timer = timer
ns = {} local_ns = {}
global_ns = _globals() if globals is None else globals
if isinstance(stmt, str): if isinstance(stmt, str):
stmt = reindent(stmt, 8) stmt = reindent(stmt, 8)
if isinstance(setup, str): if isinstance(setup, str):
...@@ -115,19 +121,19 @@ class Timer: ...@@ -115,19 +121,19 @@ class Timer:
src = template.format(stmt=stmt, setup=setup) src = template.format(stmt=stmt, setup=setup)
elif callable(setup): elif callable(setup):
src = template.format(stmt=stmt, setup='_setup()') src = template.format(stmt=stmt, setup='_setup()')
ns['_setup'] = setup local_ns['_setup'] = setup
else: else:
raise ValueError("setup is neither a string nor callable") raise ValueError("setup is neither a string nor callable")
self.src = src # Save for traceback display self.src = src # Save for traceback display
code = compile(src, dummy_src_name, "exec") code = compile(src, dummy_src_name, "exec")
exec(code, globals(), ns) exec(code, global_ns, local_ns)
self.inner = ns["inner"] self.inner = local_ns["inner"]
elif callable(stmt): elif callable(stmt):
self.src = None self.src = None
if isinstance(setup, str): if isinstance(setup, str):
_setup = setup _setup = setup
def setup(): def setup():
exec(_setup, globals(), ns) exec(_setup, global_ns, local_ns)
elif not callable(setup): elif not callable(setup):
raise ValueError("setup is neither a string nor callable") raise ValueError("setup is neither a string nor callable")
self.inner = _template_func(setup, stmt) self.inner = _template_func(setup, stmt)
...@@ -208,14 +214,14 @@ class Timer: ...@@ -208,14 +214,14 @@ class Timer:
return r return r
def timeit(stmt="pass", setup="pass", timer=default_timer, def timeit(stmt="pass", setup="pass", timer=default_timer,
number=default_number): number=default_number, globals=None):
"""Convenience function to create Timer object and call timeit method.""" """Convenience function to create Timer object and call timeit method."""
return Timer(stmt, setup, timer).timeit(number) return Timer(stmt, setup, timer, globals).timeit(number)
def repeat(stmt="pass", setup="pass", timer=default_timer, def repeat(stmt="pass", setup="pass", timer=default_timer,
repeat=default_repeat, number=default_number): repeat=default_repeat, number=default_number, globals=None):
"""Convenience function to create Timer object and call repeat method.""" """Convenience function to create Timer object and call repeat method."""
return Timer(stmt, setup, timer).repeat(repeat, number) return Timer(stmt, setup, timer, globals).repeat(repeat, number)
def main(args=None, *, _wrap_timer=None): def main(args=None, *, _wrap_timer=None):
"""Main program, used when run as a script. """Main program, used when run as a script.
......
...@@ -1130,6 +1130,7 @@ Juan M. Bello Rivas ...@@ -1130,6 +1130,7 @@ Juan M. Bello Rivas
Davide Rizzo Davide Rizzo
Anthony Roach Anthony Roach
Carl Robben Carl Robben
Ben Roberts
Mark Roberts Mark Roberts
Andy Robinson Andy Robinson
Jim Robinson Jim Robinson
......
...@@ -124,6 +124,10 @@ Core and Builtins ...@@ -124,6 +124,10 @@ Core and Builtins
Library Library
------- -------
- Issue #2527: Add a *globals* argument to timeit functions, in order to
override the globals namespace in which the timed code is executed.
Patch by Ben Roberts.
- Issue #22118: Switch urllib.parse to use RFC 3986 semantics for the - Issue #22118: Switch urllib.parse to use RFC 3986 semantics for the
resolution of relative URLs, rather than RFCs 1808 and 2396. resolution of relative URLs, rather than RFCs 1808 and 2396.
Patch by Demian Brecht. Patch by Demian Brecht.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment