interactiveshell.py
558 lines
| 21.0 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r2761 | # -*- coding: utf-8 -*- | ||
"""Subclass of InteractiveShell for terminal based frontends.""" | ||||
#----------------------------------------------------------------------------- | ||||
# Copyright (C) 2001 Janko Hauser <jhauser@zscout.de> | ||||
# Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu> | ||||
# Copyright (C) 2008-2010 The IPython Development Team | ||||
# | ||||
# Distributed under the terms of the BSD License. The full license is in | ||||
# the file COPYING, distributed as part of this software. | ||||
#----------------------------------------------------------------------------- | ||||
#----------------------------------------------------------------------------- | ||||
# Imports | ||||
#----------------------------------------------------------------------------- | ||||
import __builtin__ | ||||
import bdb | ||||
from contextlib import nested | ||||
import os | ||||
import re | ||||
import sys | ||||
from IPython.core.error import TryNext | ||||
from IPython.core.usage import interactive_usage, default_banner | ||||
from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC | ||||
from IPython.lib.inputhook import enable_gui | ||||
from IPython.lib.pylabtools import pylab_activate | ||||
Fernando Perez
|
r3368 | from IPython.testing import decorators as testdec | ||
Brian Granger
|
r2761 | from IPython.utils.terminal import toggle_set_term_title, set_term_title | ||
from IPython.utils.process import abbrev_cwd | ||||
from IPython.utils.warn import warn | ||||
from IPython.utils.text import num_ini_spaces | ||||
MinRK
|
r3464 | from IPython.utils.traitlets import Int, Str, CBool, Unicode | ||
Brian Granger
|
r2761 | |||
#----------------------------------------------------------------------------- | ||||
# Utilities | ||||
#----------------------------------------------------------------------------- | ||||
def get_default_editor(): | ||||
try: | ||||
ed = os.environ['EDITOR'] | ||||
except KeyError: | ||||
if os.name == 'posix': | ||||
ed = 'vi' # the only one guaranteed to be there! | ||||
else: | ||||
ed = 'notepad' # same in Windows! | ||||
return ed | ||||
# store the builtin raw_input globally, and use this always, in case user code | ||||
# overwrites it (like wx.py.PyShell does) | ||||
raw_input_original = raw_input | ||||
#----------------------------------------------------------------------------- | ||||
# Main class | ||||
#----------------------------------------------------------------------------- | ||||
class TerminalInteractiveShell(InteractiveShell): | ||||
autoedit_syntax = CBool(False, config=True) | ||||
MinRK
|
r3464 | banner = Unicode('') | ||
banner1 = Unicode(default_banner, config=True) | ||||
banner2 = Unicode('', config=True) | ||||
Brian Granger
|
r2761 | confirm_exit = CBool(True, config=True) | ||
# This display_banner only controls whether or not self.show_banner() | ||||
# is called when mainloop/interact are called. The default is False | ||||
# because for the terminal based application, the banner behavior | ||||
# is controlled by Global.display_banner, which IPythonApp looks at | ||||
# to determine if *it* should call show_banner() by hand or not. | ||||
display_banner = CBool(False) # This isn't configurable! | ||||
embedded = CBool(False) | ||||
embedded_active = CBool(False) | ||||
MinRK
|
r3464 | editor = Unicode(get_default_editor(), config=True) | ||
pager = Unicode('less', config=True) | ||||
Brian Granger
|
r2761 | |||
screen_length = Int(0, config=True) | ||||
term_title = CBool(False, config=True) | ||||
def __init__(self, config=None, ipython_dir=None, user_ns=None, | ||||
user_global_ns=None, custom_exceptions=((),None), | ||||
usage=None, banner1=None, banner2=None, | ||||
display_banner=None): | ||||
super(TerminalInteractiveShell, self).__init__( | ||||
config=config, ipython_dir=ipython_dir, user_ns=user_ns, | ||||
user_global_ns=user_global_ns, custom_exceptions=custom_exceptions | ||||
) | ||||
self.init_term_title() | ||||
self.init_usage(usage) | ||||
self.init_banner(banner1, banner2, display_banner) | ||||
#------------------------------------------------------------------------- | ||||
# Things related to the terminal | ||||
#------------------------------------------------------------------------- | ||||
@property | ||||
def usable_screen_length(self): | ||||
if self.screen_length == 0: | ||||
return 0 | ||||
else: | ||||
num_lines_bot = self.separate_in.count('\n')+1 | ||||
return self.screen_length - num_lines_bot | ||||
def init_term_title(self): | ||||
# Enable or disable the terminal title. | ||||
if self.term_title: | ||||
toggle_set_term_title(True) | ||||
set_term_title('IPython: ' + abbrev_cwd()) | ||||
else: | ||||
toggle_set_term_title(False) | ||||
#------------------------------------------------------------------------- | ||||
Fernando Perez
|
r3003 | # Things related to aliases | ||
#------------------------------------------------------------------------- | ||||
def init_alias(self): | ||||
# The parent class defines aliases that can be safely used with any | ||||
# frontend. | ||||
super(TerminalInteractiveShell, self).init_alias() | ||||
# Now define aliases that only make sense on the terminal, because they | ||||
# need direct access to the console in a way that we can't emulate in | ||||
# GUI or web frontend | ||||
if os.name == 'posix': | ||||
aliases = [('clear', 'clear'), ('more', 'more'), ('less', 'less'), | ||||
('man', 'man')] | ||||
elif os.name == 'nt': | ||||
aliases = [('cls', 'cls')] | ||||
for name, cmd in aliases: | ||||
self.alias_manager.define_alias(name, cmd) | ||||
#------------------------------------------------------------------------- | ||||
Brian Granger
|
r2761 | # Things related to the banner and usage | ||
#------------------------------------------------------------------------- | ||||
def _banner1_changed(self): | ||||
self.compute_banner() | ||||
def _banner2_changed(self): | ||||
self.compute_banner() | ||||
def _term_title_changed(self, name, new_value): | ||||
self.init_term_title() | ||||
def init_banner(self, banner1, banner2, display_banner): | ||||
if banner1 is not None: | ||||
self.banner1 = banner1 | ||||
if banner2 is not None: | ||||
self.banner2 = banner2 | ||||
if display_banner is not None: | ||||
self.display_banner = display_banner | ||||
self.compute_banner() | ||||
def show_banner(self, banner=None): | ||||
if banner is None: | ||||
banner = self.banner | ||||
self.write(banner) | ||||
def compute_banner(self): | ||||
Fernando Perez
|
r3008 | self.banner = self.banner1 | ||
Brian Granger
|
r2761 | if self.profile: | ||
self.banner += '\nIPython profile: %s\n' % self.profile | ||||
if self.banner2: | ||||
Fernando Perez
|
r3008 | self.banner += '\n' + self.banner2 | ||
Brian Granger
|
r2761 | |||
def init_usage(self, usage=None): | ||||
if usage is None: | ||||
self.usage = interactive_usage | ||||
else: | ||||
self.usage = usage | ||||
#------------------------------------------------------------------------- | ||||
# Mainloop and code execution logic | ||||
#------------------------------------------------------------------------- | ||||
def mainloop(self, display_banner=None): | ||||
"""Start the mainloop. | ||||
If an optional banner argument is given, it will override the | ||||
internally created default banner. | ||||
""" | ||||
with nested(self.builtin_trap, self.display_trap): | ||||
while 1: | ||||
try: | ||||
self.interact(display_banner=display_banner) | ||||
#self.interact_with_readline() | ||||
# XXX for testing of a readline-decoupled repl loop, call | ||||
# interact_with_readline above | ||||
break | ||||
except KeyboardInterrupt: | ||||
# this should not be necessary, but KeyboardInterrupt | ||||
# handling seems rather unpredictable... | ||||
self.write("\nKeyboardInterrupt in interact()\n") | ||||
def interact(self, display_banner=None): | ||||
"""Closely emulate the interactive Python console.""" | ||||
# batch run -> do not interact | ||||
if self.exit_now: | ||||
return | ||||
if display_banner is None: | ||||
display_banner = self.display_banner | ||||
if display_banner: | ||||
self.show_banner() | ||||
Fernando Perez
|
r3080 | more = False | ||
Brian Granger
|
r2761 | |||
# Mark activity in the builtins | ||||
__builtin__.__dict__['__IPYTHON__active'] += 1 | ||||
if self.has_readline: | ||||
self.readline_startup_hook(self.pre_readline) | ||||
# exit_now is set by a call to %Exit or %Quit, through the | ||||
# ask_exit callback. | ||||
Fernando Perez
|
r3077 | |||
Brian Granger
|
r2761 | while not self.exit_now: | ||
self.hooks.pre_prompt_hook() | ||||
if more: | ||||
try: | ||||
prompt = self.hooks.generate_prompt(True) | ||||
except: | ||||
self.showtraceback() | ||||
if self.autoindent: | ||||
self.rl_do_indent = True | ||||
else: | ||||
try: | ||||
prompt = self.hooks.generate_prompt(False) | ||||
except: | ||||
self.showtraceback() | ||||
try: | ||||
Fernando Perez
|
r3080 | line = self.raw_input(prompt) | ||
Brian Granger
|
r2761 | if self.exit_now: | ||
# quick exit on sys.std[in|out] close | ||||
break | ||||
if self.autoindent: | ||||
self.rl_do_indent = False | ||||
except KeyboardInterrupt: | ||||
#double-guard against keyboardinterrupts during kbdint handling | ||||
try: | ||||
self.write('\nKeyboardInterrupt\n') | ||||
Thomas Kluyver
|
r3756 | self.input_splitter.reset() | ||
Fernando Perez
|
r3080 | more = False | ||
Brian Granger
|
r2761 | except KeyboardInterrupt: | ||
pass | ||||
except EOFError: | ||||
if self.autoindent: | ||||
self.rl_do_indent = False | ||||
if self.has_readline: | ||||
self.readline_startup_hook(None) | ||||
self.write('\n') | ||||
self.exit() | ||||
except bdb.BdbQuit: | ||||
warn('The Python debugger has exited with a BdbQuit exception.\n' | ||||
'Because of how pdb handles the stack, it is impossible\n' | ||||
'for IPython to properly format this particular exception.\n' | ||||
'IPython will resume normal operation.') | ||||
except: | ||||
# exceptions here are VERY RARE, but they can be triggered | ||||
# asynchronously by signal handlers, for example. | ||||
self.showtraceback() | ||||
else: | ||||
Fernando Perez
|
r3080 | self.input_splitter.push(line) | ||
more = self.input_splitter.push_accepts_more() | ||||
Brian Granger
|
r2761 | if (self.SyntaxTB.last_syntax_error and | ||
self.autoedit_syntax): | ||||
self.edit_syntax_error() | ||||
Fernando Perez
|
r3080 | if not more: | ||
Fernando Perez
|
r3085 | source_raw = self.input_splitter.source_raw_reset()[1] | ||
self.run_cell(source_raw) | ||||
Fernando Perez
|
r3080 | |||
Brian Granger
|
r2761 | # We are off again... | ||
__builtin__.__dict__['__IPYTHON__active'] -= 1 | ||||
# Turn off the exit flag, so the mainloop can be restarted if desired | ||||
self.exit_now = False | ||||
Fernando Perez
|
r3080 | def raw_input(self, prompt=''): | ||
"""Write a prompt and read a line. | ||||
The returned line does not include the trailing newline. | ||||
When the user enters the EOF key sequence, EOFError is raised. | ||||
Optional inputs: | ||||
- prompt(''): a string to be printed to prompt the user. | ||||
- continue_prompt(False): whether this line is the first one or a | ||||
continuation in a sequence of inputs. | ||||
""" | ||||
# Code run by the user may have modified the readline completer state. | ||||
# We must ensure that our completer is back in place. | ||||
if self.has_readline: | ||||
self.set_readline_completer() | ||||
try: | ||||
line = raw_input_original(prompt).decode(self.stdin_encoding) | ||||
except ValueError: | ||||
warn("\n********\nYou or a %run:ed script called sys.stdin.close()" | ||||
" or sys.stdout.close()!\nExiting IPython!") | ||||
self.ask_exit() | ||||
return "" | ||||
# Try to be reasonably smart about not re-indenting pasted input more | ||||
# than necessary. We do this by trimming out the auto-indent initial | ||||
# spaces, if the user's actual input started itself with whitespace. | ||||
if self.autoindent: | ||||
if num_ini_spaces(line) > self.indent_current_nsp: | ||||
line = line[self.indent_current_nsp:] | ||||
self.indent_current_nsp = 0 | ||||
return line | ||||
Brian Granger
|
r2761 | #------------------------------------------------------------------------- | ||
# Methods to support auto-editing of SyntaxErrors. | ||||
#------------------------------------------------------------------------- | ||||
def edit_syntax_error(self): | ||||
"""The bottom half of the syntax error handler called in the main loop. | ||||
Loop until syntax error is fixed or user cancels. | ||||
""" | ||||
while self.SyntaxTB.last_syntax_error: | ||||
# copy and clear last_syntax_error | ||||
err = self.SyntaxTB.clear_err_state() | ||||
if not self._should_recompile(err): | ||||
return | ||||
try: | ||||
# may set last_syntax_error again if a SyntaxError is raised | ||||
self.safe_execfile(err.filename,self.user_ns) | ||||
except: | ||||
self.showtraceback() | ||||
else: | ||||
try: | ||||
f = file(err.filename) | ||||
try: | ||||
# This should be inside a display_trap block and I | ||||
# think it is. | ||||
sys.displayhook(f.read()) | ||||
finally: | ||||
f.close() | ||||
except: | ||||
self.showtraceback() | ||||
def _should_recompile(self,e): | ||||
"""Utility routine for edit_syntax_error""" | ||||
if e.filename in ('<ipython console>','<input>','<string>', | ||||
'<console>','<BackgroundJob compilation>', | ||||
None): | ||||
return False | ||||
try: | ||||
if (self.autoedit_syntax and | ||||
not self.ask_yes_no('Return to editor to correct syntax error? ' | ||||
'[Y/n] ','y')): | ||||
return False | ||||
except EOFError: | ||||
return False | ||||
def int0(x): | ||||
try: | ||||
return int(x) | ||||
except TypeError: | ||||
return 0 | ||||
# always pass integer line and offset values to editor hook | ||||
try: | ||||
self.hooks.fix_error_editor(e.filename, | ||||
int0(e.lineno),int0(e.offset),e.msg) | ||||
except TryNext: | ||||
warn('Could not open editor') | ||||
return False | ||||
return True | ||||
#------------------------------------------------------------------------- | ||||
# Things related to GUI support and pylab | ||||
#------------------------------------------------------------------------- | ||||
def enable_pylab(self, gui=None): | ||||
"""Activate pylab support at runtime. | ||||
This turns on support for matplotlib, preloads into the interactive | ||||
namespace all of numpy and pylab, and configures IPython to correcdtly | ||||
interact with the GUI event loop. The GUI backend to be used can be | ||||
optionally selected with the optional :param:`gui` argument. | ||||
Parameters | ||||
---------- | ||||
gui : optional, string | ||||
If given, dictates the choice of matplotlib GUI backend to use | ||||
(should be one of IPython's supported backends, 'tk', 'qt', 'wx' or | ||||
'gtk'), otherwise we use the default chosen by matplotlib (as | ||||
dictated by the matplotlib build-time options plus the user's | ||||
matplotlibrc configuration file). | ||||
""" | ||||
# We want to prevent the loading of pylab to pollute the user's | ||||
# namespace as shown by the %who* magics, so we execute the activation | ||||
# code in an empty namespace, and we update *both* user_ns and | ||||
# user_ns_hidden with this information. | ||||
ns = {} | ||||
gui = pylab_activate(ns, gui) | ||||
self.user_ns.update(ns) | ||||
self.user_ns_hidden.update(ns) | ||||
# Now we must activate the gui pylab wants to use, and fix %run to take | ||||
# plot updates into account | ||||
enable_gui(gui) | ||||
self.magic_run = self._pylab_magic_run | ||||
#------------------------------------------------------------------------- | ||||
# Things related to exiting | ||||
#------------------------------------------------------------------------- | ||||
def ask_exit(self): | ||||
""" Ask the shell to exit. Can be overiden and used as a callback. """ | ||||
self.exit_now = True | ||||
def exit(self): | ||||
"""Handle interactive exit. | ||||
This method calls the ask_exit callback.""" | ||||
if self.confirm_exit: | ||||
if self.ask_yes_no('Do you really want to exit ([y]/n)?','y'): | ||||
self.ask_exit() | ||||
else: | ||||
self.ask_exit() | ||||
Fernando Perez
|
r2975 | |||
#------------------------------------------------------------------------ | ||||
# 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] | ||||
Fernando Perez
|
r3368 | @testdec.skip_doctest | ||
Fernando Perez
|
r2975 | def magic_cpaste(self, parameter_s=''): | ||
"""Paste & execute a pre-formatted code block from clipboard. | ||||
You must terminate the block with '--' (two minus-signs) 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. | ||||
Sathesh Chandra
|
r3340 | |||
Examples | ||||
-------- | ||||
:: | ||||
In [8]: %cpaste | ||||
Pasting code; enter '--' alone on the line to stop. | ||||
:>>> a = ["world!", "Hello"] | ||||
:>>> print " ".join(sorted(a)) | ||||
:-- | ||||
Hello world! | ||||
Fernando Perez
|
r2975 | """ | ||
opts,args = self.parse_options(parameter_s,'rs:',mode='string') | ||||
par = args.strip() | ||||
if opts.has_key('r'): | ||||
self._rerun_pasted() | ||||
return | ||||
sentinel = opts.get('s','--') | ||||
block = self._strip_pasted_lines_for_code( | ||||
self._get_pasted_lines(sentinel)) | ||||
self._execute_block(block, par) | ||||
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,args = self.parse_options(parameter_s,'rq',mode='string') | ||||
par = args.strip() | ||||
if opts.has_key('r'): | ||||
self._rerun_pasted() | ||||
return | ||||
text = self.shell.hooks.clipboard_get() | ||||
block = self._strip_pasted_lines_for_code(text.splitlines()) | ||||
# By default, echo back to terminal unless quiet mode is requested | ||||
if not opts.has_key('q'): | ||||
write = self.shell.write | ||||
write(self.shell.pycolorize(block)) | ||||
if not block.endswith('\n'): | ||||
write('\n') | ||||
write("## -- End pasted text --\n") | ||||
self._execute_block(block, par) | ||||
Brian Granger
|
r2761 | |||
InteractiveShellABC.register(TerminalInteractiveShell) | ||||