Repository: ipython/ipython

Issue:
Regression : %kill_embedded does not work (broken by #6840 )
The Magic [`%kill_embedded`](http://ipython.readthedocs.org/en/stable/api/generated/IPython.terminal.embed.html?highlight=kill_embedded#IPython.terminal.embed.EmbeddedMagics.kill_embedded) flags the embedded shell instance so that it is not started on subsequent calls of `IPython.embed()`. However, fo this to work, we need to keep the instance in memory otherwise, we'll be flagging new instances all the time and never read the flags we've set.

In [`IPython/terminal/embed.py`](https://github.com/ipython/ipython/blob/master/IPython/terminal/embed.py#L262), we do several things :
- We save the instance that might be the current singleton instance of the `InteractiveShell` class
- We remove it from the singleton
- We get `InteractiveShellEmbed`'s instance (created if existing) and launch the shell
- As soon as it's ended, we delete the instance
- We replace the original shell instance as the singleton instance.

All these things were done by #6840 in an effort to make embed nestable (but then it broke `%kill_embeded`)

It's clear that the current behaviour makes it completely impossible for `%kill_embedded` to work as expected.

What would be preferred for a PR : to store the flag elsewhere or to improve #6840 to not destroy instances ?



Discussion:
I guess it depends whether `%kill_embed` should prevent future embeds at that nesting level, or all future embeds.

Probably the simplest thing would be to store the flag globally and prevent all future embeds in that process.

I guess so. In this case, we just need to :

``` python
# change 
self.shell.embedded_active = False
# to
self.shell.__cls__.embedded_active = False
```

and that might be enough for a fix. I'll see if I have time to submit a PR, but if you do, please feel free :)

Hum, marking that as 5.0. 

I think it is useful to use multiple `embed()`. One solution would be to add a `name=...` parameter to the constructor and `kill` based on the name. We could also "generate" a name based on `sys._getframe(1).f_code.co_filename+sys._getframe(1).f_lineno`, which would prevent reinvocation on the same line.


Resolving patch:
diff --git a/IPython/terminal/embed.py b/IPython/terminal/embed.py
--- a/IPython/terminal/embed.py
+++ b/IPython/terminal/embed.py
@@ -39,8 +39,7 @@ def kill_embedded(self, parameter_s=''):
         interfering again.
         """
 
-        kill = ask_yes_no("Are you sure you want to kill this embedded instance "
-                         "(y/n)? [y/N] ",'n')
+        kill = ask_yes_no("Are you sure you want to kill this embedded instance? [y/N] ",'n')
         if kill:
             self.shell.embedded_active = False
             print ("This embedded IPython will not reactivate anymore "
@@ -66,13 +65,25 @@ class InteractiveShellEmbed(TerminalInteractiveShell):
     dummy_mode = Bool(False)
     exit_msg = Unicode('')
     embedded = CBool(True)
-    embedded_active = CBool(True)
     should_raise = CBool(False)
     # Like the base class display_banner is not configurable, but here it
     # is True by default.
     display_banner = CBool(True)
     exit_msg = Unicode()
-    
+
+    _inactive_locations = set()
+
+    @property
+    def embedded_active(self):
+        return self._call_location_id not in InteractiveShellEmbed._inactive_locations
+
+    @embedded_active.setter
+    def embedded_active(self, value):
+        if value :
+            if self._call_location_id in InteractiveShellEmbed._inactive_locations:
+                InteractiveShellEmbed._inactive_locations.remove(self._call_location_id)
+        else:
+            InteractiveShellEmbed._inactive_locations.add(self._call_location_id)
 
     def __init__(self, **kw):
         
@@ -81,8 +92,13 @@ def __init__(self, **kw):
             warnings.warn("user_global_ns has been replaced by user_module. The\
                            parameter will be ignored, and removed in IPython 5.0", DeprecationWarning)
 
+        self._call_location_id =  kw.pop('_call_location_id', None)
+
         super(InteractiveShellEmbed,self).__init__(**kw)
 
+        if not self._call_location_id:
+            frame = sys._getframe(1)
+            self._call_location_id = '%s:%s' % (frame.f_code.co_filename, frame.f_lineno)
         # don't use the ipython crash handler so that user exceptions aren't
         # trapped
         sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors,
@@ -298,7 +314,8 @@ def embed(**kwargs):
     if saved_shell_instance is not None:
         cls = type(saved_shell_instance)
         cls.clear_instance()
-    shell = InteractiveShellEmbed.instance(**kwargs)
+    frame = sys._getframe(1)
+    shell = InteractiveShellEmbed.instance(_call_location_id='%s:%s' % (frame.f_code.co_filename, frame.f_lineno), **kwargs)
     shell(header=header, stack_depth=2, compile_flags=compile_flags)
     InteractiveShellEmbed.clear_instance()
     #restore previous instance


Test patch: