diff --git a/IPython/core/compilerop.py b/IPython/core/compilerop.py index ee0de31..e39ded6 100644 --- a/IPython/core/compilerop.py +++ b/IPython/core/compilerop.py @@ -90,7 +90,7 @@ class CachingCompiler(codeop.Compile): # Now, we must monkeypatch the linecache directly so that parts of the # stdlib that call it outside our control go through our codepath # (otherwise we'd lose our tracebacks). - linecache.checkcache = self.check_cache + linecache.checkcache = check_linecache_ipython def ast_parse(self, source, filename='', symbol='exec'): """Parse code to an AST with the current compiler flags active. @@ -134,11 +134,11 @@ class CachingCompiler(codeop.Compile): linecache._ipython_cache[name] = entry return name - def check_cache(self, *args): - """Call linecache.checkcache() safely protecting our cached values. - """ - # First call the orignal checkcache as intended - linecache._checkcache_ori(*args) - # Then, update back the cache with our data, so that tracebacks related - # to our compiled codes can be produced. - linecache.cache.update(linecache._ipython_cache) +def check_linecache_ipython(*args): + """Call linecache.checkcache() safely protecting our cached values. + """ + # First call the orignal checkcache as intended + linecache._checkcache_ori(*args) + # Then, update back the cache with our data, so that tracebacks related + # to our compiled codes can be produced. + linecache.cache.update(linecache._ipython_cache) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 89e0f53..95b1ea3 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -14,7 +14,6 @@ # Imports #----------------------------------------------------------------------------- -from __future__ import with_statement from __future__ import absolute_import from __future__ import print_function @@ -42,7 +41,7 @@ from IPython.core import ultratb from IPython.core.alias import AliasManager, AliasError from IPython.core.autocall import ExitAutocall from IPython.core.builtin_trap import BuiltinTrap -from IPython.core.compilerop import CachingCompiler +from IPython.core.compilerop import CachingCompiler, check_linecache_ipython from IPython.core.display_trap import DisplayTrap from IPython.core.displayhook import DisplayHook from IPython.core.displaypub import DisplayPublisher @@ -1533,7 +1532,7 @@ class InteractiveShell(SingletonConfigurable): self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain', color_scheme='NoColor', tb_offset = 1, - check_cache=self.compile.check_cache) + check_cache=check_linecache_ipython) # The instance will store a pointer to the system-wide exception hook, # so that runtime code (such as magics) can access it. This is because @@ -2518,7 +2517,7 @@ class InteractiveShell(SingletonConfigurable): # raised in user code. It would be nice if there were # versions of runlines, execfile that did raise, so # we could catch the errors. - self.run_cell(thefile.read(), store_history=False) + self.run_cell(thefile.read(), store_history=False, shell_futures=False) except: self.showtraceback() warn('Unknown failure executing file: <%s>' % fname) @@ -2552,7 +2551,7 @@ class InteractiveShell(SingletonConfigurable): self._current_cell_magic_body = None return self.run_cell_magic(magic_name, line, cell) - def run_cell(self, raw_cell, store_history=False, silent=False): + def run_cell(self, raw_cell, store_history=False, silent=False, shell_futures=True): """Run a complete IPython cell. Parameters @@ -2566,6 +2565,11 @@ class InteractiveShell(SingletonConfigurable): silent : bool If True, avoid side-effects, such as implicit displayhooks and and logging. silent=True forces store_history=False. + shell_futures : bool + If True, the code will share future statements with the interactive + shell. It will both be affected by previous __future__ imports, and + any __future__ imports in the code will affect the shell. If False, + __future__ imports are not shared in either direction. """ if (not raw_cell) or raw_cell.isspace(): return @@ -2583,6 +2587,11 @@ class InteractiveShell(SingletonConfigurable): self._current_cell_magic_body = \ ''.join(self.input_splitter.cell_magic_parts) cell = self.input_splitter.source_reset() + + # Our own compiler remembers the __future__ environment. If we want to + # run code with a separate __future__ environment, use the default + # compiler + compiler = self.compile if shell_futures else CachingCompiler() with self.builtin_trap: prefilter_failed = False @@ -2612,8 +2621,7 @@ class InteractiveShell(SingletonConfigurable): with self.display_trap: try: - code_ast = self.compile.ast_parse(cell, - filename=cell_name) + code_ast = compiler.ast_parse(cell, filename=cell_name) except IndentationError: self.showindentationerror() if store_history: @@ -2630,7 +2638,7 @@ class InteractiveShell(SingletonConfigurable): interactivity = "none" if silent else self.ast_node_interactivity self.run_ast_nodes(code_ast.body, cell_name, - interactivity=interactivity) + interactivity=interactivity, compiler=compiler) # Execute any registered post-execution functions. # unless we are silent @@ -2686,7 +2694,8 @@ class InteractiveShell(SingletonConfigurable): return ast.fix_missing_locations(node) - def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'): + def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr', + compiler=compile): """Run a sequence of AST nodes. The execution mode depends on the interactivity parameter. @@ -2703,6 +2712,9 @@ class InteractiveShell(SingletonConfigurable): will run the last node interactively only if it is an expression (i.e. expressions in loops or other blocks are not displayed. Other values for this parameter will raise a ValueError. + compiler : callable + A function with the same interface as the built-in compile(), to turn + the AST nodes into code objects. Default is the built-in compile(). """ if not nodelist: return @@ -2727,13 +2739,13 @@ class InteractiveShell(SingletonConfigurable): try: for i, node in enumerate(to_run_exec): mod = ast.Module([node]) - code = self.compile(mod, cell_name, "exec") + code = compiler(mod, cell_name, "exec") if self.run_code(code): return True for i, node in enumerate(to_run_interactive): mod = ast.Interactive([node]) - code = self.compile(mod, cell_name, "single") + code = compiler(mod, cell_name, "single") if self.run_code(code): return True diff --git a/IPython/core/tests/test_compilerop.py b/IPython/core/tests/test_compilerop.py index f586c33..125a567 100644 --- a/IPython/core/tests/test_compilerop.py +++ b/IPython/core/tests/test_compilerop.py @@ -67,7 +67,7 @@ def test_compiler_check_cache(): cp = compilerop.CachingCompiler() cp.cache('x=1', 99) # Ensure now that after clearing the cache, our entries survive - cp.check_cache() + linecache.checkcache() for k in linecache.cache: if k.startswith('= 3, "no differences with __future__ in py3") + def test_future_environment(self): + "Can we run code with & without the shell's __future__ imports?" + ip.run_cell("from __future__ import division") + ip.run_cell("a = 1/2", shell_futures=True) + self.assertEqual(ip.user_ns['a'], 0.5) + ip.run_cell("b = 1/2", shell_futures=False) + self.assertEqual(ip.user_ns['b'], 0) + + ip.compile.reset_compiler_flags() + # This shouldn't leak to the shell's compiler + ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False) + self.assertEqual(ip.user_ns['c'], 0.5) + ip.run_cell("d = 1/2", shell_futures=True) + self.assertEqual(ip.user_ns['d'], 0) class TestSafeExecfileNonAsciiPath(unittest.TestCase): diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index fafdbf9..4a7c8fb 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -827,7 +827,7 @@ def test_edit_interactive(): except code.InteractivelyDefined as e: nt.assert_equal(e.index, n) else: - nt.fail("Should have raised InteractivelyDefined") + raise AssertionError("Should have raised InteractivelyDefined") def test_edit_cell():