Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1007 from fperez/paste_bug
Fix paste/cpaste bug and refactor/cleanup that code a lot. In fixing a pasting bug (mishandling of whitespace when input had prompts) it became clear the pasting code hadn't been updated when the new prefiltering machinery was added. Furthermore, the pasting magics are only for the terminal, but the code was in the base classes. This refactors and simplifies the pasting code, moving it to the terminal shell only, and removing unnecessary methods from the main class (using small utility functions instead). That will make it easier later to move them into dedicated magic objects when we have a chance to refactor the magic system.
- Loading branch information
Showing
4 changed files
with
252 additions
and
203 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
"""Tests for various magic functions specific to the terminal frontend. | ||
Needs to be run by nose (to make ipython session available). | ||
""" | ||
from __future__ import absolute_import | ||
|
||
#----------------------------------------------------------------------------- | ||
# Imports | ||
#----------------------------------------------------------------------------- | ||
|
||
import sys | ||
from StringIO import StringIO | ||
|
||
import nose.tools as nt | ||
|
||
from IPython.testing import decorators as dec | ||
from IPython.testing import tools as tt | ||
|
||
#----------------------------------------------------------------------------- | ||
# Test functions begin | ||
#----------------------------------------------------------------------------- | ||
|
||
def check_cpaste(code, should_fail=False): | ||
"""Execute code via 'cpaste' and ensure it was executed, unless | ||
should_fail is set. | ||
""" | ||
_ip.user_ns['code_ran'] = False | ||
|
||
src = StringIO() | ||
if not hasattr(src, 'encoding'): | ||
# IPython expects stdin to have an encoding attribute | ||
src.encoding = None | ||
src.write('\n') | ||
src.write(code) | ||
src.write('\n--\n') | ||
src.seek(0) | ||
|
||
stdin_save = sys.stdin | ||
sys.stdin = src | ||
|
||
try: | ||
context = tt.AssertPrints if should_fail else tt.AssertNotPrints | ||
with context("Traceback (most recent call last)"): | ||
_ip.magic('cpaste') | ||
|
||
if not should_fail: | ||
assert _ip.user_ns['code_ran'] | ||
finally: | ||
sys.stdin = stdin_save | ||
|
||
|
||
def test_cpaste(): | ||
"""Test cpaste magic""" | ||
|
||
def run(): | ||
"""Marker function: sets a flag when executed. | ||
""" | ||
_ip.user_ns['code_ran'] = True | ||
return 'run' # return string so '+ run()' doesn't result in success | ||
|
||
tests = {'pass': ["run()", | ||
"In [1]: run()", | ||
"In [1]: if 1:\n ...: run()", | ||
"> > > run()", | ||
">>> run()", | ||
" >>> run()", | ||
], | ||
|
||
'fail': ["1 + run()", | ||
"++ run()"]} | ||
|
||
_ip.user_ns['run'] = run | ||
|
||
for code in tests['pass']: | ||
check_cpaste(code) | ||
|
||
for code in tests['fail']: | ||
check_cpaste(code, should_fail=True) | ||
|
||
|
||
# Multiple tests for clipboard pasting | ||
def test_paste(): | ||
_ip = get_ipython() | ||
|
||
def paste(txt, flags='-q'): | ||
"""Paste input text, by default in quiet mode""" | ||
hooks.clipboard_get = lambda : txt | ||
_ip.magic('paste '+flags) | ||
|
||
# Inject fake clipboard hook but save original so we can restore it later | ||
hooks = _ip.hooks | ||
user_ns = _ip.user_ns | ||
original_clip = hooks.clipboard_get | ||
|
||
try: | ||
# Run tests with fake clipboard function | ||
user_ns.pop('x', None) | ||
paste('x=1') | ||
nt.assert_equal(user_ns['x'], 1) | ||
|
||
user_ns.pop('x', None) | ||
paste('>>> x=2') | ||
nt.assert_equal(user_ns['x'], 2) | ||
|
||
paste(""" | ||
>>> x = [1,2,3] | ||
>>> y = [] | ||
>>> for i in x: | ||
... y.append(i**2) | ||
... | ||
""") | ||
nt.assert_equal(user_ns['x'], [1,2,3]) | ||
nt.assert_equal(user_ns['y'], [1,4,9]) | ||
|
||
# Now, test that paste -r works | ||
user_ns.pop('x', None) | ||
nt.assert_false('x' in user_ns) | ||
_ip.magic('paste -r') | ||
nt.assert_equal(user_ns['x'], [1,2,3]) | ||
|
||
# Test pasting of email-quoted contents | ||
paste(""" | ||
>> def foo(x): | ||
>> return x + 1 | ||
>> x = foo(1.1) | ||
""") | ||
nt.assert_equal(user_ns['x'], 2.1) | ||
|
||
# Email again; some programs add a space also at each quoting level | ||
paste(""" | ||
> > def foo(x): | ||
> > return x + 1 | ||
> > x = foo(2.1) | ||
""") | ||
nt.assert_equal(user_ns['x'], 3.1) | ||
|
||
# Email quoting of interactive input | ||
paste(""" | ||
>> >>> def f(x): | ||
>> ... return x+1 | ||
>> ... | ||
>> >>> x = f(2.5) | ||
""") | ||
nt.assert_equal(user_ns['x'], 3.5) | ||
|
||
# Also test paste echoing, by temporarily faking the writer | ||
w = StringIO() | ||
writer = _ip.write | ||
_ip.write = w.write | ||
code = """ | ||
a = 100 | ||
b = 200""" | ||
try: | ||
paste(code,'') | ||
out = w.getvalue() | ||
finally: | ||
_ip.write = writer | ||
nt.assert_equal(user_ns['a'], 100) | ||
nt.assert_equal(user_ns['b'], 200) | ||
nt.assert_equal(out, code+"\n## -- End pasted text --\n") | ||
|
||
finally: | ||
# Restore original hook | ||
hooks.clipboard_get = original_clip |
Oops, something went wrong.