From 09897796b837b71c259543caec69cea5ff429ec5 2012-05-26 02:40:50 From: Fernando Perez Date: 2012-05-26 02:40:50 Subject: [PATCH] Fix remaining test failures. This completes the decoupling of the Magic class from the main InteractiveShell one. Finally! (it's been only 10 1/2 years...) At this point, all tests pass (modulo two failures in zmq that were there before and are not related to the magics work). We can now start the rest of the work for cell level magics, including a cleaner magic management architecture and breaking them up into several classes. --- diff --git a/IPython/core/magic.py b/IPython/core/magic.py index 54bf6de..d8fa23e 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -3045,11 +3045,11 @@ Defaulting color scheme to 'NoColor'""" dir_s = self.shell.dir_stack tgt = os.path.expanduser(unquote_filename(parameter_s)) - cwd = os.getcwdu().replace(self.home_dir,'~') + cwd = os.getcwdu().replace(self.shell.home_dir,'~') if tgt: self.magic_cd(parameter_s) dir_s.insert(0,cwd) - return self.magic_dirs() + return self.shell.magic('dirs') def magic_popd(self, parameter_s=''): """Change to directory popped off the top of the stack. @@ -3417,7 +3417,7 @@ Defaulting color scheme to 'NoColor'""" ptformatter.pprint = False disp_formatter.plain_text_only = True - shell.magic_xmode('Plain') + shell.magic('xmode Plain') else: # turn off pm.in_template, pm.in2_template, pm.out_template = dstore.prompt_templates @@ -3432,7 +3432,7 @@ Defaulting color scheme to 'NoColor'""" ptformatter.pprint = dstore.rc_pprint disp_formatter.plain_text_only = dstore.rc_plain_text_only - shell.magic_xmode(dstore.xmode) + shell.magic('xmode ' + dstore.xmode) # Store new mode and inform dstore.mode = bool(1-int(mode)) @@ -3487,7 +3487,8 @@ Defaulting color scheme to 'NoColor'""" """ opts, args = self.parse_options(parameter_s, 'n:') try: - filename = self.extension_manager.install_extension(args, opts.get('n')) + filename = self.shell.extension_manager.install_extension(args, + opts.get('n')) except ValueError as e: print e return @@ -3499,15 +3500,15 @@ Defaulting color scheme to 'NoColor'""" def magic_load_ext(self, module_str): """Load an IPython extension by its module name.""" - return self.extension_manager.load_extension(module_str) + return self.shell.extension_manager.load_extension(module_str) def magic_unload_ext(self, module_str): """Unload an IPython extension by its module name.""" - self.extension_manager.unload_extension(module_str) + self.shell.extension_manager.unload_extension(module_str) def magic_reload_ext(self, module_str): """Reload an IPython extension by its module name.""" - self.extension_manager.reload_extension(module_str) + self.shell.extension_manager.reload_extension(module_str) def magic_install_profiles(self, s): """%install_profiles has been deprecated.""" @@ -3681,9 +3682,10 @@ Defaulting color scheme to 'NoColor'""" if args.export: fname, name, format = current.parse_filename(args.filename) cells = [] - hist = list(self.history_manager.get_range()) + hist = list(self.shell.history_manager.get_range()) for session, prompt_number, input in hist[:-1]: - cells.append(current.new_code_cell(prompt_number=prompt_number, input=input)) + cells.append(current.new_code_cell(prompt_number=prompt_number, + input=input)) worksheet = current.new_worksheet(cells=cells) nb = current.new_notebook(name=name,worksheets=[worksheet]) with io.open(fname, 'w', encoding='utf-8') as f: diff --git a/IPython/core/tests/test_interactiveshell.py b/IPython/core/tests/test_interactiveshell.py index 21008e7..2ae07cf 100644 --- a/IPython/core/tests/test_interactiveshell.py +++ b/IPython/core/tests/test_interactiveshell.py @@ -343,7 +343,7 @@ class TestSafeExecfileNonAsciiPath(unittest.TestCase): def test_1(self): """Test safe_execfile with non-ascii path """ - _ip.shell.safe_execfile(self.fname, {}, raise_exceptions=True) + _ip.safe_execfile(self.fname, {}, raise_exceptions=True) class TestSystemRaw(unittest.TestCase): @@ -351,7 +351,7 @@ class TestSystemRaw(unittest.TestCase): """Test system_raw with non-ascii cmd """ cmd = ur'''python -c "'åäö'" ''' - _ip.shell.system_raw(cmd) + _ip.system_raw(cmd) def test__IPYTHON__(): diff --git a/IPython/core/tests/test_magic.py b/IPython/core/tests/test_magic.py index 530085a..0466474 100644 --- a/IPython/core/tests/test_magic.py +++ b/IPython/core/tests/test_magic.py @@ -51,7 +51,7 @@ def test_magic_parse_options(): """Test that we don't mangle paths when parsing magic options.""" ip = get_ipython() path = 'c:\\x' - opts = ip.parse_options('-f %s' % path,'f:')[0] + opts = ip._magic.parse_options('-f %s' % path,'f:')[0] # argv splitting is os-dependent if os.name == 'posix': expected = 'c:x' @@ -284,8 +284,8 @@ def test_parse_options(): """Tests for basic options parsing in magics.""" # These are only the most minimal of tests, more should be added later. At # the very least we check that basic text/unicode calls work OK. - nt.assert_equal(_ip.parse_options('foo', '')[1], 'foo') - nt.assert_equal(_ip.parse_options(u'foo', '')[1], u'foo') + nt.assert_equal(_ip._magic.parse_options('foo', '')[1], 'foo') + nt.assert_equal(_ip._magic.parse_options(u'foo', '')[1], u'foo') def test_dirops(): @@ -326,7 +326,7 @@ def test_reset_hard(): _ip.run_cell("a") nt.assert_equal(monitor, []) - _ip.magic_reset("-f") + _ip.magic("reset -f") nt.assert_equal(monitor, [1]) class TestXdel(tt.TempFileMixin): @@ -388,7 +388,7 @@ def test_whos(): def doctest_precision(): """doctest for %precision - In [1]: f = get_ipython().shell.display_formatter.formatters['text/plain'] + In [1]: f = get_ipython().display_formatter.formatters['text/plain'] In [2]: %precision 5 Out[2]: {u}'%.5f' @@ -422,7 +422,7 @@ def test_timeit_arguments(): "Test valid timeit arguments, should not cause SyntaxError (GH #1269)" _ip.magic("timeit ('#')") -@dec.skipif(_ip.magic_prun == _ip.profile_missing_notice) +@dec.skipif(_ip._magic.magic_prun == _ip._magic.profile_missing_notice) def test_prun_quotes(): "Test that prun does not clobber string escapes (GH #1302)" _ip.magic("prun -q x = '\t'") diff --git a/IPython/core/tests/test_prefilter.py b/IPython/core/tests/test_prefilter.py index 4da06b8..d14aba1 100644 --- a/IPython/core/tests/test_prefilter.py +++ b/IPython/core/tests/test_prefilter.py @@ -79,7 +79,7 @@ def test_issue_114(): msp = ip.prefilter_manager.multi_line_specials ip.prefilter_manager.multi_line_specials = False try: - for mgk in ip.lsmagic(): + for mgk in ip._magic.lsmagic(): raw = template % mgk yield nt.assert_equals(ip.prefilter(raw), raw) finally: diff --git a/IPython/frontend/terminal/interactiveshell.py b/IPython/frontend/terminal/interactiveshell.py index 49c88dd..58732fb 100644 --- a/IPython/frontend/terminal/interactiveshell.py +++ b/IPython/frontend/terminal/interactiveshell.py @@ -120,6 +120,133 @@ def rerun_pasted(shell, name='pasted_block'): shell.run_cell(b) +#------------------------------------------------------------------------ +# Terminal-specific magics +#------------------------------------------------------------------------ + +def magic_autoindent(self, parameter_s = ''): + """Toggle autoindent on/off (if available).""" + + self.shell.set_autoindent() + print "Automatic indentation is:",['OFF','ON'][self.shell.autoindent] + +@skip_doctest +def magic_cpaste(self, parameter_s=''): + """Paste & execute a pre-formatted code block from clipboard. + + You must terminate the block with '--' (two minus-signs) or Ctrl-D + alone on the line. You can also provide your own sentinel with '%paste + -s %%' ('%%' is the new sentinel for this operation) + + The block is dedented prior to execution to enable execution of method + definitions. '>' and '+' characters at the beginning of a line are + ignored, to allow pasting directly from e-mails, diff files and + doctests (the '...' continuation prompt is also stripped). The + executed block is also assigned to variable named 'pasted_block' for + later editing with '%edit pasted_block'. + + You can also pass a variable name as an argument, e.g. '%cpaste foo'. + This assigns the pasted block to variable 'foo' as string, without + dedenting or executing it (preceding >>> and + is still stripped) + + '%cpaste -r' re-executes the block previously entered by cpaste. + + Do not be alarmed by garbled output on Windows (it's a readline bug). + Just press enter and type -- (and press enter again) and the block + will be what was just pasted. + + IPython statements (magics, shell escapes) are not supported (yet). + + See also + -------- + paste: automatically pull code from clipboard. + + Examples + -------- + :: + + In [8]: %cpaste + Pasting code; enter '--' alone on the line to stop. + :>>> a = ["world!", "Hello"] + :>>> print " ".join(sorted(a)) + :-- + Hello world! + """ + + opts, name = self.parse_options(parameter_s, 'rs:', mode='string') + if 'r' in opts: + rerun_pasted(self.shell) + return + + sentinel = opts.get('s', '--') + block = strip_email_quotes(get_pasted_lines(sentinel)) + store_or_execute(self.shell, block, name) + + +def magic_paste(self, parameter_s=''): + """Paste & execute a pre-formatted code block from clipboard. + + The text is pulled directly from the clipboard without user + intervention and printed back on the screen before execution (unless + the -q flag is given to force quiet mode). + + The block is dedented prior to execution to enable execution of method + definitions. '>' and '+' characters at the beginning of a line are + ignored, to allow pasting directly from e-mails, diff files and + doctests (the '...' continuation prompt is also stripped). The + executed block is also assigned to variable named 'pasted_block' for + later editing with '%edit pasted_block'. + + You can also pass a variable name as an argument, e.g. '%paste foo'. + This assigns the pasted block to variable 'foo' as string, without + dedenting or executing it (preceding >>> and + is still stripped) + + Options + ------- + + -r: re-executes the block previously entered by cpaste. + + -q: quiet mode: do not echo the pasted text back to the terminal. + + IPython statements (magics, shell escapes) are not supported (yet). + + See also + -------- + cpaste: manually paste code into terminal until you mark its end. + """ + opts, name = self.parse_options(parameter_s, 'rq', mode='string') + if 'r' in opts: + rerun_pasted(self.shell) + return + try: + text = self.shell.hooks.clipboard_get() + block = strip_email_quotes(text.splitlines()) + except TryNext as clipboard_exc: + message = getattr(clipboard_exc, 'args') + if message: + error(message[0]) + else: + error('Could not get text from the clipboard.') + return + + # By default, echo back to terminal unless quiet mode is requested + if 'q' not in opts: + write = self.shell.write + write(self.shell.pycolorize(block)) + if not block.endswith('\n'): + write('\n') + write("## -- End pasted text --\n") + + store_or_execute(self.shell, block, name) + + +# Class-level: add a '%cls' magic only on Windows +if sys.platform == 'win32': + def magic_cls(self, s): + """Clear screen. + """ + os.system("cls") + #----------------------------------------------------------------------------- # Main class #----------------------------------------------------------------------------- @@ -531,134 +658,21 @@ class TerminalInteractiveShell(InteractiveShell): else: self.ask_exit() - #------------------------------------------------------------------------ - # Magic overrides - #------------------------------------------------------------------------ - # Once the base class stops inheriting from magic, this code needs to be - # moved into a separate machinery as well. For now, at least isolate here - # the magics which this class needs to implement differently from the base - # class, or that are unique to it. - - def magic_autoindent(self, parameter_s = ''): - """Toggle autoindent on/off (if available).""" - - self.shell.set_autoindent() - print "Automatic indentation is:",['OFF','ON'][self.shell.autoindent] - - @skip_doctest - def magic_cpaste(self, parameter_s=''): - """Paste & execute a pre-formatted code block from clipboard. - - You must terminate the block with '--' (two minus-signs) or Ctrl-D - alone on the line. You can also provide your own sentinel with '%paste - -s %%' ('%%' is the new sentinel for this operation) - - The block is dedented prior to execution to enable execution of method - definitions. '>' and '+' characters at the beginning of a line are - ignored, to allow pasting directly from e-mails, diff files and - doctests (the '...' continuation prompt is also stripped). The - executed block is also assigned to variable named 'pasted_block' for - later editing with '%edit pasted_block'. - - You can also pass a variable name as an argument, e.g. '%cpaste foo'. - This assigns the pasted block to variable 'foo' as string, without - dedenting or executing it (preceding >>> and + is still stripped) - - '%cpaste -r' re-executes the block previously entered by cpaste. - - Do not be alarmed by garbled output on Windows (it's a readline bug). - Just press enter and type -- (and press enter again) and the block - will be what was just pasted. - - IPython statements (magics, shell escapes) are not supported (yet). - - See also - -------- - paste: automatically pull code from clipboard. - - Examples - -------- - :: - - In [8]: %cpaste - Pasting code; enter '--' alone on the line to stop. - :>>> a = ["world!", "Hello"] - :>>> print " ".join(sorted(a)) - :-- - Hello world! - """ - - opts, name = self.parse_options(parameter_s, 'rs:', mode='string') - if 'r' in opts: - rerun_pasted(self.shell) - return - - sentinel = opts.get('s', '--') - block = strip_email_quotes(get_pasted_lines(sentinel)) - store_or_execute(self.shell, block, name) - - def magic_paste(self, parameter_s=''): - """Paste & execute a pre-formatted code block from clipboard. - - The text is pulled directly from the clipboard without user - intervention and printed back on the screen before execution (unless - the -q flag is given to force quiet mode). - - The block is dedented prior to execution to enable execution of method - definitions. '>' and '+' characters at the beginning of a line are - ignored, to allow pasting directly from e-mails, diff files and - doctests (the '...' continuation prompt is also stripped). The - executed block is also assigned to variable named 'pasted_block' for - later editing with '%edit pasted_block'. - - You can also pass a variable name as an argument, e.g. '%paste foo'. - This assigns the pasted block to variable 'foo' as string, without - dedenting or executing it (preceding >>> and + is still stripped) - - Options - ------- - - -r: re-executes the block previously entered by cpaste. - - -q: quiet mode: do not echo the pasted text back to the terminal. - - IPython statements (magics, shell escapes) are not supported (yet). + #------------------------------------------------------------------------- + # Things related to magics + #------------------------------------------------------------------------- - See also - -------- - cpaste: manually paste code into terminal until you mark its end. - """ - opts, name = self.parse_options(parameter_s, 'rq', mode='string') - if 'r' in opts: - rerun_pasted(self.shell) - return + def init_magics(self): + super(TerminalInteractiveShell, self).init_magics() + self.define_magic('autoindent', magic_autoindent) + self.define_magic('cpaste', magic_cpaste) + self.define_magic('paste', magic_paste) try: - text = self.shell.hooks.clipboard_get() - block = strip_email_quotes(text.splitlines()) - except TryNext as clipboard_exc: - message = getattr(clipboard_exc, 'args') - if message: - error(message[0]) - else: - error('Could not get text from the clipboard.') - return - - # By default, echo back to terminal unless quiet mode is requested - if 'q' not in opts: - write = self.shell.write - write(self.shell.pycolorize(block)) - if not block.endswith('\n'): - write('\n') - write("## -- End pasted text --\n") - - store_or_execute(self.shell, block, name) - - # Class-level: add a '%cls' magic only on Windows - if sys.platform == 'win32': - def magic_cls(self, s): - """Clear screen. - """ - os.system("cls") + magic_cls + except NameError: + pass + else: + self.define_magic('cls', magic_cls) def showindentationerror(self): super(TerminalInteractiveShell, self).showindentationerror() diff --git a/IPython/parallel/client/view.py b/IPython/parallel/client/view.py index ca29e84..90c73e4 100644 --- a/IPython/parallel/client/view.py +++ b/IPython/parallel/client/view.py @@ -815,7 +815,7 @@ class DirectView(View): else: pmagic = ip.plugin_manager.get_plugin('parallelmagic') if pmagic is None: - ip.magic_load_ext('parallelmagic') + ip.magic('load_ext parallelmagic') pmagic = ip.plugin_manager.get_plugin('parallelmagic') pmagic.active_view = self diff --git a/IPython/parallel/tests/test_view.py b/IPython/parallel/tests/test_view.py index 6c1a7a0..d8cc803 100644 --- a/IPython/parallel/tests/test_view.py +++ b/IPython/parallel/tests/test_view.py @@ -374,21 +374,21 @@ class TestView(ClusterTestCase, ParametricTestCase): v.activate() v.block=True - ip.magic_px('a=5') + ip.magic('px a=5') self.assertEquals(v['a'], 5) - ip.magic_px('a=10') + ip.magic('px a=10') self.assertEquals(v['a'], 10) sio = StringIO() savestdout = sys.stdout sys.stdout = sio # just 'print a' worst ~99% of the time, but this ensures that # the stdout message has arrived when the result is finished: - ip.magic_px('import sys,time;print (a); sys.stdout.flush();time.sleep(0.2)') + ip.magic('px import sys,time;print (a); sys.stdout.flush();time.sleep(0.2)') sys.stdout = savestdout buf = sio.getvalue() self.assertTrue('[stdout:' in buf, buf) self.assertTrue(buf.rstrip().endswith('10')) - self.assertRaisesRemote(ZeroDivisionError, ip.magic_px, '1/0') + self.assertRaisesRemote(ZeroDivisionError, ip.magic, 'px 1/0') def test_magic_px_nonblocking(self): ip = get_ipython() @@ -396,18 +396,18 @@ class TestView(ClusterTestCase, ParametricTestCase): v.activate() v.block=False - ip.magic_px('a=5') + ip.magic('px a=5') self.assertEquals(v['a'], 5) - ip.magic_px('a=10') + ip.magic('px a=10') self.assertEquals(v['a'], 10) sio = StringIO() savestdout = sys.stdout sys.stdout = sio - ip.magic_px('print a') + ip.magic('px print a') sys.stdout = savestdout buf = sio.getvalue() self.assertFalse('[stdout:%i]'%v.targets in buf) - ip.magic_px('1/0') + ip.magic('px 1/0') ar = v.get_result(-1) self.assertRaisesRemote(ZeroDivisionError, ar.get) @@ -420,12 +420,12 @@ class TestView(ClusterTestCase, ParametricTestCase): sio = StringIO() savestdout = sys.stdout sys.stdout = sio - ip.magic_autopx() + ip.magic('autopx') ip.run_cell('\n'.join(('a=5','b=10','c=0'))) ip.run_cell('b*=2') ip.run_cell('print (b)') ip.run_cell("b/c") - ip.magic_autopx() + ip.magic('autopx') sys.stdout = savestdout output = sio.getvalue().strip() self.assertTrue(output.startswith('%autopx enabled')) @@ -445,13 +445,13 @@ class TestView(ClusterTestCase, ParametricTestCase): sio = StringIO() savestdout = sys.stdout sys.stdout = sio - ip.magic_autopx() + ip.magic('autopx') ip.run_cell('\n'.join(('a=5','b=10','c=0'))) ip.run_cell('print (b)') ip.run_cell('import time; time.sleep(0.1)') ip.run_cell("b/c") ip.run_cell('b*=2') - ip.magic_autopx() + ip.magic('autopx') sys.stdout = savestdout output = sio.getvalue().strip() self.assertTrue(output.startswith('%autopx enabled')) @@ -472,10 +472,10 @@ class TestView(ClusterTestCase, ParametricTestCase): v['a'] = 111 ra = v['a'] - ar = ip.magic_result() + ar = ip.magic('result') self.assertEquals(ar.msg_ids, [v.history[-1]]) self.assertEquals(ar.get(), 111) - ar = ip.magic_result('-2') + ar = ip.magic('result -2') self.assertEquals(ar.msg_ids, [v.history[-2]]) def test_unicode_execute(self):