diff --git a/IPython/terminal/embed.py b/IPython/terminal/embed.py index 045925c..b2f3408 100644 --- a/IPython/terminal/embed.py +++ b/IPython/terminal/embed.py @@ -14,6 +14,7 @@ import warnings from IPython.core import ultratb, compilerop from IPython.core.magic import Magics, magics_class, line_magic from IPython.core.interactiveshell import DummyMod +from IPython.core.interactiveshell import InteractiveShell from IPython.terminal.interactiveshell import TerminalInteractiveShell from IPython.terminal.ipapp import load_default_config @@ -192,7 +193,8 @@ class InteractiveShellEmbed(TerminalInteractiveShell): # like _ih and get_ipython() into the local namespace, but delete them # later. if local_ns is not None: - self.user_ns = local_ns + reentrant_local_ns = {k: v for (k, v) in local_ns.items() if k not in self.user_ns_hidden.keys()} + self.user_ns = reentrant_local_ns self.init_user_ns() # Compiler flags @@ -208,8 +210,8 @@ class InteractiveShellEmbed(TerminalInteractiveShell): # now, purge out the local namespace of IPython's hidden variables. if local_ns is not None: - for name in self.user_ns_hidden: - local_ns.pop(name, None) + local_ns.update({k: v for (k, v) in self.user_ns.items() if k not in self.user_ns_hidden.keys()}) + # Restore original namespace so shell can shut down when we exit. self.user_module = orig_user_module @@ -249,5 +251,28 @@ def embed(**kwargs): config = load_default_config() config.InteractiveShellEmbed = config.TerminalInteractiveShell kwargs['config'] = config + #save ps1/ps2 if defined + ps1 = None + ps2 = None + try: + ps1 = sys.ps1 + ps2 = sys.ps2 + except AttributeError: + pass + #save previous instance + saved_shell_instance = InteractiveShell._instance + if saved_shell_instance is not None: + cls = type(saved_shell_instance) + cls.clear_instance() shell = InteractiveShellEmbed.instance(**kwargs) shell(header=header, stack_depth=2, compile_flags=compile_flags) + InteractiveShellEmbed.clear_instance() + #restore previous instance + if saved_shell_instance is not None: + cls = type(saved_shell_instance) + cls.clear_instance() + for subclass in cls._walk_mro(): + subclass._instance = saved_shell_instance + if ps1 is not None: + sys.ps1 = ps1 + sys.ps2 = ps2 diff --git a/IPython/terminal/tests/test_embed.py b/IPython/terminal/tests/test_embed.py index 70a4508..ec51316 100644 --- a/IPython/terminal/tests/test_embed.py +++ b/IPython/terminal/tests/test_embed.py @@ -16,11 +16,13 @@ import sys import nose.tools as nt from IPython.utils.process import process_handler from IPython.utils.tempdir import NamedFileInTemporaryDirectory +from IPython.testing.decorators import skip_win32 #----------------------------------------------------------------------------- # Tests #----------------------------------------------------------------------------- + _sample_embed = b""" from __future__ import print_function import IPython @@ -55,3 +57,69 @@ def test_ipython_embed(): nt.assert_in('IPython', std) nt.assert_in('bye!', std) +@skip_win32 +def test_nest_embed(): + """test that `IPython.embed()` is nestable""" + from IPython.external import pexpect + ipy_prompt = r']:' #ansi color codes give problems matching beyond this + + + child = pexpect.spawn('%s -m IPython'%(sys.executable, )) + child.expect(ipy_prompt) + child.sendline("from __future__ import print_function") + child.expect(ipy_prompt) + child.sendline("import IPython") + child.expect(ipy_prompt) + child.sendline("ip0 = get_ipython()") + #enter first nested embed + child.sendline("IPython.embed()") + #skip the banner until we get to a prompt + try: + prompted = -1 + while prompted != 0: + prompted = child.expect([ipy_prompt, '\r\n']) + except pexpect.TIMEOUT as e: + print(e) + #child.interact() + child.sendline("embed1 = get_ipython()"); child.expect(ipy_prompt) + child.sendline("print('true' if embed1 is not ip0 else 'false')") + assert(child.expect(['true\r\n', 'false\r\n']) == 0) + child.expect(ipy_prompt) + child.sendline("print('true' if IPython.get_ipython() is embed1 else 'false')") + assert(child.expect(['true\r\n', 'false\r\n']) == 0) + child.expect(ipy_prompt) + #enter second nested embed + child.sendline("IPython.embed()") + #skip the banner until we get to a prompt + try: + prompted = -1 + while prompted != 0: + prompted = child.expect([ipy_prompt, '\r\n']) + except pexpect.TIMEOUT as e: + print(e) + #child.interact() + child.sendline("embed2 = get_ipython()"); child.expect(ipy_prompt) + child.sendline("print('true' if embed2 is not embed1 else 'false')") + assert(child.expect(['true\r\n', 'false\r\n']) == 0) + child.expect(ipy_prompt) + child.sendline("print('true' if embed2 is IPython.get_ipython() else 'false')") + assert(child.expect(['true\r\n', 'false\r\n']) == 0) + child.expect(ipy_prompt) + child.sendline('exit') + #back at first embed + child.expect(ipy_prompt) + child.sendline("print('true' if get_ipython() is embed1 else 'false')") + assert(child.expect(['true\r\n', 'false\r\n']) == 0) + child.expect(ipy_prompt) + child.sendline("print('true' if IPython.get_ipython() is embed1 else 'false')") + assert(child.expect(['true\r\n', 'false\r\n']) == 0) + child.expect(ipy_prompt) + child.sendline('exit') + #back at launching scope + child.expect(ipy_prompt) + child.sendline("print('true' if get_ipython() is ip0 else 'false')") + assert(child.expect(['true\r\n', 'false\r\n']) == 0) + child.expect(ipy_prompt) + child.sendline("print('true' if IPython.get_ipython() is ip0 else 'false')") + assert(child.expect(['true\r\n', 'false\r\n']) == 0) + child.expect(ipy_prompt)