Repository: ipython/ipython (Python)

Issue:
%%time magic prevents assignment
The `%%time` cell magic is preventing me from binding names. I found this in notebook code on Travis (py 3.6) and reproduced it locally (py 3.7) in notebook and in `ipython` at the terminal. It doesn't seem to apply to all cell magics:

```
Python 3.7.2 | packaged by conda-forge | (default, Mar 19 2019, 20:46:22)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: %%time
   ...: foo = "bar"
   ...:
   ...:
CPU times: user 3 µs, sys: 0 ns, total: 3 µs
Wall time: 6.91 µs

In [2]: foo
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-2-f1d2d2f924e9> in <module>
----> 1 foo

NameError: name 'foo' is not defined

In [3]: %%prun
   ...: baz = "qux"
   ...:
   ...:
         3 function calls in 0.000 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 {built-in method builtins.exec}
        1    0.000    0.000    0.000    0.000 <string>:2(<module>)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

In [4]: baz
Out[4]: 'qux'
```

Confirmed that 7.3.0 worked fine locally.


Discussion:
Looking through the PRs merged as part of 7.4.0, my best guess is this is an unexpected consequence of #11542 -- at least, I can see how this (and perhaps #11665, if `pos` existed as a list before the cell ran) would be caused by changing how scope is handled in cell magics. And I don't see anything else that looks like a likely cause.

But I don't know anything about the internals of magics: @Carreau, could this be the problem?

The workaround is just to stay on 7.3.0.
You are right that this is due to #11542 , I'll see if I can come up with a fix. 
`%%time` is used a lot, this affects many users & notebooks.
@Carreau or anyone - is it possible to do a patch release for this soon?
(for now I'll go back to v7.3 and tell others to do the same)
"%time" and "%%time" behave differently
![изображение](https://user-images.githubusercontent.com/966341/55426450-7f89d580-558d-11e9-92a6-0a8f82ab3a58.png)

![изображение](https://user-images.githubusercontent.com/966341/55426634-f45d0f80-558d-11e9-8800-d616ff271b5f.png)

![изображение](https://user-images.githubusercontent.com/966341/55426715-1b1b4600-558e-11e9-9875-95b75e0738e0.png)

but run_call_magic &&  run_line_magic  are identical
![изображение](https://user-images.githubusercontent.com/966341/55427243-3d619380-558f-11e9-889c-7e84eb4f5537.png)

It is not clear how it ever worked before
![изображение](https://user-images.githubusercontent.com/966341/55427748-5b7bc380-5590-11e9-9247-ff752bce0016.png)

By the way, in "%%timeit" the situation is exactly the same, but at least it behaves predictably

![изображение](https://user-images.githubusercontent.com/966341/55429330-e4482e80-5593-11e9-9ab8-782d29642138.png)

@wanick : I believe the `%%timeit` behavior was the same before 7.4.0 (and made sense to me: `timeit` does multiple runs -- which one should be the result?) Of course, there's no such ambiguity with `%%time`.
Well I'll fix it once ai have time. The thing is my $DAYJOB is not full
time open source anymore so IPython has effectively less than 20% of a
maintainer. And I prefer to use this time to review people code and be
friendly in a hope to get more people to help.

On Tue, Apr 2, 2019, 14:45 David W.H. Swenson <notifications@github.com>
wrote:

> @wanick <https://github.com/wanick> : I believe the %%timeit behavior was
> the same before 7.4.0 (and made sense to me: timeit does multiple runs --
> which one should be the result?) Of course, there's no such ambiguity with
> %%time.
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/ipython/ipython/issues/11659#issuecomment-479217282>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAUez_RUQ7rT-3imrMP1uKBMFKMCYFo7ks5vc89bgaJpZM4cD5yv>
> .
>

@dwhswenson , True... I meant the same decorators and difference behavior 

@Carreau,  I solved this problem like this.

![изображение](https://user-images.githubusercontent.com/966341/55442056-ba9fff00-55b5-11e9-83a2-72812c5bc3d6.png)

If this is a solution, I can make a pull request

Thank you guys. It all makes sense to me, I'm just wondering whether I need to update/edit all my Jupyter notebook now, or you're planning to restore the expected/canonical behaviour.

Thanks!
We're planning to fix it as soon as we have time; it will likely make it to the 7.5 release that I'll try to make before end of month. 
I should stop using "We", I'm practically the only developer reviewing and managing IPython itself.
This should have been fixed in a patch release but I only got time today and we're close to a month since last release so this is going to be in the next minor (7.5) if the fix is correct.

Thus I would appreciate review and testing of https://github.com/ipython/ipython/pull/11698, and once merged I'll start the process to do a 7.5 for before PyCon tutorials.
Thanks @minrk, BTW the above was not a critic toward you, and you are supposed to be on leave, so go enjoy your sleepless nights :-) 
I'm experiencing the same issue.  I just finished a fresh install of Anaconda on windows and discovered this problem. However it only occurs with my base environment:
         conda version : 4.7.5
         conda-build version : 3.18.5
         python version : 3.7.3.final.0

I created (for a different reason) an environment with python 3.6 and python 3.7  (conda create -n testspacy37 python=3.x) and %%time works there 
Hi,

Should `%%time` and `%%timeit` behave differently with respect to variable assignments?

Basically, I can't access variables defined within a `%%timeit` block, but within a `%%time` block it works fine:

```python3
Python 3.6.8 (default, Jan 14 2019, 11:02:34) 
Type 'copyright', 'credits' or 'license' for more information
IPython 7.7.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: import numpy as np
In [2]: size = 20
In [3]: D = np.random.rand(size, size)
In [4]: I = np.random.rand(size, size, size, size)
In [5]: %%timeit
   ...: Gloop = np.zeros((size, size))
   ...: for p in range(size):
   ...:     for q in range(size):
   ...:         for r in range(size):
   ...:             for s in range(size):
   ...:                 Gloop[p, q] += (2 * I[p, q, r, s] - I[p, r, q, s]) * D[r, s]
   ...:
229 ms ± 2.44 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [6]: Gloop
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-6-b2508f9fc33d> in <module>
----> 1 Gloop

NameError: name 'Gloop' is not defined

In [7]: %%time
   ...: Gloop = np.zeros((size, size))
   ...: for p in range(size):
   ...:     for q in range(size):
   ...:         for r in range(size):
   ...:             for s in range(size):
   ...:                 Gloop[p, q] += (2 * I[p, q, r, s] - I[p, r, q, s]) * D[r, s]
   ...:
CPU times: user 264 ms, sys: 0 ns, total: 264 ms
Wall time: 263 ms

In [8]: Gloop
Out[8]:
array([[107.84826652,  87.35710355,  97.97752913,  86.50282191,
           ...
        101.92858435, 108.97229643, 103.99891131, 107.81191102]])
```

Does this have to do with the present issue?
By the way, I'm using Ubuntu 18.04.3 LTS:

```bash
% uname -a
Linux mothership 4.15.0-60-generic #67-Ubuntu SMP Thu Aug 22 16:55:30 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
% python3 --version
Python 3.6.8
% pip3 --version
pip 9.0.1 from /usr/lib/python3/dist-packages (python 3.6)
% ipython3 --version
7.7.0
```
Making both work the same is likely tricky, `%%timeit` is a wrapper around the Python [timeit](https://docs.python.org/3/library/timeit.html) module; andone of the issues is that it runs things many times.  So a global statement like `n += 1` will have weird effects. 

We actually do not run things in global scope, but create an anonymous function and run _this function_. So in your example, Gloop is not really globlal.

I'm pretty sure starting your timeit with `global Gloop` should work around the issue. It's just quite hard.

If you findtime to send a pull-request to fix it I'll be happy to try to review it; I unfortunately won't have the time myself.


@wanick you explained %time %%time really well thanks for that.
i have a query why my wall time = 0 
I tried your example as well but same wall time 0 
can you please guide what I am doing wrong.
![Annotation 2020-04-12 210440](https://user-images.githubusercontent.com/58139318/79073674-51db7e80-7d01-11ea-95a4-66bd46b2a0a7.png)

same error on M1 Mac

Python 3.8.12
conda 4.11.0
ipython3 7.30.0

Funny thing is this issue actually ruined the notebook entirely. Even after I deleted the %%time command in the cell, the global variables were still removed after running the new cell 



Patch:
diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py
index ef572558438..ce8ceb196c8 100644
--- a/IPython/core/interactiveshell.py
+++ b/IPython/core/interactiveshell.py
@@ -2345,7 +2345,7 @@ def run_cell_magic(self, magic_name, line, cell):
                 magic_arg_s = self.var_expand(line, stack_depth)
             kwargs = {}
             if getattr(fn, "needs_local_scope", False):
-                kwargs['local_ns'] = sys._getframe(stack_depth).f_locals
+                kwargs['local_ns'] = self.user_ns
 
             with self.builtin_trap:
                 args = (magic_arg_s, cell)
diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py
index d58fa3c47d2..510750ea01a 100644
--- a/IPython/core/magics/execution.py
+++ b/IPython/core/magics/execution.py
@@ -1127,8 +1127,8 @@ def timeit(self, line='', cell=None, local_ns=None):
         ns = {}
         glob = self.shell.user_ns
         # handles global vars with same name as local vars. We store them in conflict_globs.
-        if local_ns is not None:
-            conflict_globs = {}
+        conflict_globs = {}
+        if local_ns and cell is None:
             for var_name, var_val in glob.items():
                 if var_name in local_ns:
                     conflict_globs[var_name] = var_val
@@ -1154,9 +1154,8 @@ def timeit(self, line='', cell=None, local_ns=None):
         timeit_result = TimeitResult(number, repeat, best, worst, all_runs, tc, precision)
 
         # Restore global vars from conflict_globs
-        if local_ns is not None:
-            if len(conflict_globs) > 0:
-                glob.update(conflict_globs)
+        if conflict_globs:
+           glob.update(conflict_globs)
                 
         if not quiet :
             # Check best timing is greater than zero to avoid a


Test patch:
diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py
index 7914d486ad1..f8ce2388769 100644
--- a/IPython/core/tests/test_magic.py
+++ b/IPython/core/tests/test_magic.py
@@ -419,6 +419,16 @@ def test_time3():
                     "run = 0\n"
                     "run += 1")
 
+def test_time_local_ns():
+    """
+    Test that local_ns is actually global_ns when running a cell magic
+    """
+    ip = get_ipython()
+    ip.run_cell("%%time\n"
+                "myvar = 1")
+    nt.assert_equal(ip.user_ns['myvar'], 1)
+    del ip.user_ns['myvar']
+
 def test_doctest_mode():
     "Toggle doctest_mode twice, it should be a no-op and run without error"
     _ip.magic('doctest_mode')