Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

__future__ environments #1671

Merged
merged 5 commits into from

2 participants

Thomas Kluyver Min RK
Thomas Kluyver
Owner

So far, this just closes the .ipy hole, so .py and .ipy files have the same behaviour with respect to future, both for %run and as startup files.

There was some discussion on the mailing list about whether %run should share the shell's __future__, or only %run -i. Either way, this will need a bit more thought, because it interacts with the other options that can be passed to %run, especially for profiling and debugging.

%rerun and macros will share the shell's futures - I think this is expected.

Min RK
Owner

I think this behavior does match my expectations, thanks!

Min RK
Owner

Can you rebase? One of the big merges touched the run_cell signature as well.

Thomas Kluyver
Owner

Will do, although it ma

Thomas Kluyver takluyver closed this
Thomas Kluyver takluyver reopened this
Thomas Kluyver
Owner

I'm not sure why I closed this one - looks like I hit the wrong button somewhere. Reopened and rebased.

Thomas Kluyver
Owner

The Travis failure appears to be a problem with Travis, not the code.

Thomas Kluyver
Owner

Test results for commit 0de3a99 merged into master (cdc7e66)
Platform: linux2

  • python2.7: Failed, log at https://gist.github.com/4530575 (libraries not available: azure oct2py pymongo)
  • python3.2: OK (libraries not available: azure oct2py pymongo wx wx.aui)

Not available for testing: python2.6

Thomas Kluyver
Owner

That failure is some problem with PySide/PyQt, unrelated to this.

Min RK
Owner

This looks good to me. Want to add a test?

Thomas Kluyver
Owner

I added a test, and it turned up a minor issue, which required a couple more API changes. I guess it shows how important it is to test everything.

I also noticed a coincidental failure (caused by a file in the working directory where I run the test) was trying to use a now-nonexistant nose.tools.fail() function, so I changed that.

Thomas Kluyver takluyver commented on the diff
IPython/core/compilerop.py
@@ -90,7 +90,7 @@ def __init__(self):
# 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
Thomas Kluyver Owner

I've made this a standalone function, so that linecache isn't holding a reference to the last CachingCompiler() we created. It didn't need to be a method in the first place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Min RK
Owner

This looks good to me. Anything ore you want to add / confirm?

Thomas Kluyver
Owner

Nope, that's great. Merging now.

Thomas Kluyver takluyver merged commit 0aacf47 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 13, 2013
  1. Thomas Kluyver

    Merge pull request #2782 from ellbur/run-breakpoints

    takluyver authored
    Allow the %run magic with '-b' to specify a file.
Commits on Jan 14, 2013
  1. Thomas Kluyver

    Add shell_futures parameter to run_cell to allow running code without…

    takluyver authored
    … sharing __future__ imports.
Commits on Jan 17, 2013
  1. Thomas Kluyver

    Test __future__ environments

    takluyver authored
  2. Thomas Kluyver
  3. Thomas Kluyver

    Fix unrelated test.

    takluyver authored
    nose.tools no longer has a fail() function
This page is out of date. Refresh to see the latest.
18 IPython/core/compilerop.py
View
@@ -90,7 +90,7 @@ def __init__(self):
# 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
Thomas Kluyver Owner

I've made this a standalone function, so that linecache isn't holding a reference to the last CachingCompiler() we created. It didn't need to be a method in the first place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
def ast_parse(self, source, filename='<unknown>', symbol='exec'):
"""Parse code to an AST with the current compiler flags active.
@@ -134,11 +134,11 @@ def cache(self, code, number=0):
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)
34 IPython/core/interactiveshell.py
View
@@ -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.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 @@ def init_traceback_handlers(self, custom_exceptions):
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
@@ -2514,7 +2513,7 @@ def safe_execfile_ipy(self, fname):
# 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)
@@ -2548,7 +2547,7 @@ def _run_cached_cell_magic(self, magic_name, line):
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
@@ -2562,6 +2561,11 @@ def run_cell(self, raw_cell, store_history=False, silent=False):
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
@@ -2579,6 +2583,11 @@ def run_cell(self, raw_cell, store_history=False, silent=False):
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
@@ -2608,8 +2617,7 @@ def run_cell(self, raw_cell, store_history=False, silent=False):
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:
@@ -2626,7 +2634,7 @@ def run_cell(self, raw_cell, store_history=False, silent=False):
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
@@ -2682,7 +2690,8 @@ def transform_ast(self, node):
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.
@@ -2699,6 +2708,9 @@ def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'):
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
@@ -2723,13 +2735,13 @@ def run_ast_nodes(self, nodelist, cell_name, interactivity='last_expr'):
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
2  IPython/core/tests/test_compilerop.py
View
@@ -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('<ipython-input-99'):
break
16 IPython/core/tests/test_interactiveshell.py
View
@@ -384,6 +384,22 @@ def my_handler(shell, etype, value, tb, tb_offset=None):
finally:
# Reset the custom exception hook
ip.set_custom_exc((), None)
+
+ @skipif(sys.version_info[0] >= 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):
2  IPython/core/tests/test_magic.py
View
@@ -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():
Something went wrong with that request. Please try again.