Repository: ipython/ipython

Issue:
%timeit magic does work on latest pypy anymore
The problem is the use of the undocumented semi-internal API `timer.inner`.
More info [here](https://bitbucket.org/pypy/pypy/issue/1820/ipython-timeit-magic-not-working-anymore)



Discussion:
Looking at the code, the use of inner, and general complexity of `%timeit`has to do with ensuring the code has access to the interactive namespace. Can anyone think of a cleaner way to do this without using undocumented APIs? Perhaps defining a Timer subclass, so we don't rely on private implementation details of the Timer class?

Maybe just pass statment as a function? (instead of a string)

Yes, I think that should work.


Resolving patch:
diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py
--- a/IPython/core/magics/execution.py
+++ b/IPython/core/magics/execution.py
@@ -1,25 +1,19 @@
 # -*- coding: utf-8 -*-
-"""Implementation of execution-related magic functions.
-"""
-from __future__ import print_function
-#-----------------------------------------------------------------------------
-#  Copyright (c) 2012 The IPython Development Team.
-#
-#  Distributed under the terms of the Modified BSD License.
-#
-#  The full license is in the file COPYING.txt, distributed with this software.
-#-----------------------------------------------------------------------------
+"""Implementation of execution-related magic functions."""
 
-#-----------------------------------------------------------------------------
-# Imports
-#-----------------------------------------------------------------------------
+# Copyright (c) IPython Development Team.
+# Distributed under the terms of the Modified BSD License.
+
+from __future__ import print_function
 
-# Stdlib
 import ast
 import bdb
+import gc
+import itertools
 import os
 import sys
 import time
+import timeit
 from pdb import Restart
 
 # cProfile was added in Python2.5
@@ -33,7 +27,6 @@
     except ImportError:
         profile = pstats = None
 
-# Our own packages
 from IPython.core import debugger, oinspect
 from IPython.core import magic_arguments
 from IPython.core import page
@@ -115,6 +108,34 @@ def visit_For(self, node):
         return node
 
 
+class Timer(timeit.Timer):
+    """Timer class that explicitly uses self.inner
+    
+    which is an undocumented implementation detail of CPython,
+    not shared by PyPy.
+    """
+    # Timer.timeit copied from CPython 3.4.2
+    def timeit(self, number=timeit.default_number):
+        """Time 'number' executions of the main statement.
+
+        To be precise, this executes the setup statement once, and
+        then returns the time it takes to execute the main statement
+        a number of times, as a float measured in seconds.  The
+        argument is the number of times through the loop, defaulting
+        to one million.  The main statement, the setup statement and
+        the timer function to be used are passed to the constructor.
+        """
+        it = itertools.repeat(None, number)
+        gcold = gc.isenabled()
+        gc.disable()
+        try:
+            timing = self.inner(it, self.timer)
+        finally:
+            if gcold:
+                gc.enable()
+        return timing
+
+
 @magics_class
 class ExecutionMagics(Magics):
     """Magics related to code execution, debugging, profiling, etc.
@@ -945,8 +966,6 @@ def timeit(self, line='', cell=None):
         does not matter as long as results from timeit.py are not mixed with
         those from %timeit."""
 
-        import timeit
-
         opts, stmt = self.parse_options(line,'n:r:tcp:qo',
                                         posix=False, strict=False)
         if stmt == "" and cell is None:
@@ -963,7 +982,7 @@ def timeit(self, line='', cell=None):
         if hasattr(opts, "c"):
             timefunc = clock
 
-        timer = timeit.Timer(timer=timefunc)
+        timer = Timer(timer=timefunc)
         # this code has tight coupling to the inner workings of timeit.Timer,
         # but is there a better way to achieve that the code stmt has access
         # to the shell namespace?


Test patch: