diff --git a/IPython/__init__.py b/IPython/__init__.py index aea962b..c8f33cf 100755 --- a/IPython/__init__.py +++ b/IPython/__init__.py @@ -40,10 +40,9 @@ sys.path.append(os.path.join(os.path.dirname(__file__), "extensions")) from .config.loader import Config from .core import release from .core.application import Application -from .core.ipapp import IPythonApp -from .core.embed import embed +from .frontend.terminal.embed import embed from .core.error import TryNext -from .core.iplib import InteractiveShell +from .core.interactiveshell import InteractiveShell from .testing import test from .lib import ( diff --git a/IPython/config/default/ipython_config.py b/IPython/config/default/ipython_config.py index c366614..5a6c2eb 100644 --- a/IPython/config/default/ipython_config.py +++ b/IPython/config/default/ipython_config.py @@ -47,15 +47,15 @@ c = get_config() # c.InteractiveShell.autocall = 1 -# c.InteractiveShell.autoedit_syntax = False +# c.TerminalInteractiveShell.autoedit_syntax = False # c.InteractiveShell.autoindent = True # c.InteractiveShell.automagic = False -# c.InteractiveShell.banner1 = 'This if for overriding the default IPython banner' +# c.TerminalTerminalInteractiveShell.banner1 = 'This if for overriding the default IPython banner' -# c.InteractiveShell.banner2 = "This is for extra banner text" +# c.TerminalTerminalInteractiveShell.banner2 = "This is for extra banner text" # c.InteractiveShell.cache_size = 1000 @@ -63,11 +63,11 @@ c = get_config() # c.InteractiveShell.color_info = True -# c.InteractiveShell.confirm_exit = True +# c.TerminalInteractiveShell.confirm_exit = True # c.InteractiveShell.deep_reload = False -# c.InteractiveShell.editor = 'nano' +# c.TerminalInteractiveShell.editor = 'nano' # c.InteractiveShell.logstart = True @@ -77,7 +77,7 @@ c = get_config() # c.InteractiveShell.object_info_string_level = 0 -# c.InteractiveShell.pager = 'less' +# c.TerminalInteractiveShell.pager = 'less' # c.InteractiveShell.pdb = False @@ -114,7 +114,7 @@ c = get_config() # c.InteractiveShell.readline_merge_completions = True # c.InteractiveShell.readline_omit__names = 0 -# c.InteractiveShell.screen_length = 0 +# c.TerminalInteractiveShell.screen_length = 0 # c.InteractiveShell.separate_in = '\n' # c.InteractiveShell.separate_out = '' @@ -124,7 +124,7 @@ c = get_config() # c.InteractiveShell.system_verbose = True -# c.InteractiveShell.term_title = False +# c.TerminalInteractiveShell.term_title = False # c.InteractiveShell.wildcards_case_sensitive = True diff --git a/IPython/core/alias.py b/IPython/core/alias.py index 506097a..59c936d 100644 --- a/IPython/core/alias.py +++ b/IPython/core/alias.py @@ -104,7 +104,7 @@ class AliasManager(Configurable): default_aliases = List(default_aliases(), config=True) user_aliases = List(default_value=[], config=True) - shell = Instance('IPython.core.iplib.InteractiveShellABC') + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') def __init__(self, shell=None, config=None): super(AliasManager, self).__init__(shell=shell, config=config) diff --git a/IPython/core/builtin_trap.py b/IPython/core/builtin_trap.py index 758d514..029b2ff 100755 --- a/IPython/core/builtin_trap.py +++ b/IPython/core/builtin_trap.py @@ -37,7 +37,7 @@ BuiltinUndefined = __BuiltinUndefined() class BuiltinTrap(Configurable): - shell = Instance('IPython.core.iplib.InteractiveShellABC') + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') def __init__(self, shell=None): super(BuiltinTrap, self).__init__(shell=shell, config=None) diff --git a/IPython/core/debugger.py b/IPython/core/debugger.py index 151a4b1..9f80d24 100644 --- a/IPython/core/debugger.py +++ b/IPython/core/debugger.py @@ -32,7 +32,7 @@ import sys from IPython.utils import PyColorize from IPython.core import ipapi from IPython.utils import coloransi -from IPython.utils.io import Term +import IPython.utils.io from IPython.core.excolors import exception_colors # See if we can use pydb. @@ -171,7 +171,7 @@ class Pdb(OldPdb): # Parent constructor: if has_pydb and completekey is None: - OldPdb.__init__(self,stdin=stdin,stdout=Term.cout) + OldPdb.__init__(self,stdin=stdin,stdout=IPython.utils.io.Term.cout) else: OldPdb.__init__(self,completekey,stdin,stdout) @@ -184,7 +184,7 @@ class Pdb(OldPdb): if self.is_pydb: - # iplib.py's ipalias seems to want pdb's checkline + # interactiveshell.py's ipalias seems to want pdb's checkline # which located in pydb.fn import pydb.fns self.checkline = lambda filename, lineno: \ @@ -279,7 +279,7 @@ class Pdb(OldPdb): def print_stack_entry(self,frame_lineno,prompt_prefix='\n-> ', context = 3): #frame, lineno = frame_lineno - print >>Term.cout, self.format_stack_entry(frame_lineno, '', context) + print >>IPython.utils.io.Term.cout, self.format_stack_entry(frame_lineno, '', context) # vds: >> frame, lineno = frame_lineno @@ -419,7 +419,7 @@ class Pdb(OldPdb): src.append(line) self.lineno = lineno - print >>Term.cout, ''.join(src) + print >>IPython.utils.io.Term.cout, ''.join(src) except KeyboardInterrupt: pass diff --git a/IPython/core/extensions.py b/IPython/core/extensions.py index bf1757e..a087b93 100644 --- a/IPython/core/extensions.py +++ b/IPython/core/extensions.py @@ -53,7 +53,7 @@ class ExtensionManager(Configurable): is added to ``sys.path`` automatically. """ - shell = Instance('IPython.core.iplib.InteractiveShellABC') + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') def __init__(self, shell=None, config=None): super(ExtensionManager, self).__init__(shell=shell, config=config) diff --git a/IPython/core/history.py b/IPython/core/history.py index e21a197..14ad06e 100644 --- a/IPython/core/history.py +++ b/IPython/core/history.py @@ -5,7 +5,8 @@ import fnmatch import os -from IPython.utils.io import Term, ask_yes_no +import IPython.utils.io +from IPython.utils.io import ask_yes_no from IPython.utils.warn import warn from IPython.core import ipapi @@ -62,7 +63,7 @@ def magic_history(self, parameter_s = ''): try: outfname = opts['f'] except KeyError: - outfile = Term.cout # default + outfile = IPython.utils.io.Term.cout # default # We don't want to close stdout at the end! close_at_end = False else: @@ -101,7 +102,7 @@ def magic_history(self, parameter_s = ''): init, final = map(int, args) else: warn('%hist takes 0, 1 or 2 arguments separated by spaces.') - print >> Term.cout, self.magic_hist.__doc__ + print >> IPython.utils.io.Term.cout, self.magic_hist.__doc__ return width = len(str(final)) diff --git a/IPython/core/hooks.py b/IPython/core/hooks.py index 97990c8..8639388 100644 --- a/IPython/core/hooks.py +++ b/IPython/core/hooks.py @@ -46,7 +46,7 @@ import sys from pprint import PrettyPrinter -from IPython.utils.io import Term +import IPython.utils.io from IPython.utils.process import shell from IPython.core.error import TryNext @@ -175,13 +175,13 @@ def result_display(self,arg): # So that multi-line strings line up with the left column of # the screen, instead of having the output prompt mess up # their first line. - Term.cout.write('\n') - print >>Term.cout, out + IPython.utils.io.Term.cout.write('\n') + print >>IPython.utils.io.Term.cout, out else: # By default, the interactive prompt uses repr() to display results, # so we should honor this. Users who'd rather use a different # mechanism can easily override this hook. - print >>Term.cout, repr(arg) + print >>IPython.utils.io.Term.cout, repr(arg) # the default display hook doesn't manipulate the value to put in history return None diff --git a/IPython/core/inputlist.py b/IPython/core/inputlist.py new file mode 100644 index 0000000..1e2a3e8 --- /dev/null +++ b/IPython/core/inputlist.py @@ -0,0 +1,14 @@ +class InputList(list): + """Class to store user input. + + It's basically a list, but slices return a string instead of a list, thus + allowing things like (assuming 'In' is an instance): + + exec In[4:7] + + or + + exec In[5:9] + In[14] + In[21:25]""" + + def __getslice__(self,i,j): + return ''.join(list.__getslice__(self,i,j)) diff --git a/IPython/core/iplib.py b/IPython/core/interactiveshell.py similarity index 74% rename from IPython/core/iplib.py rename to IPython/core/interactiveshell.py index a098db6..94a0fdf 100644 --- a/IPython/core/iplib.py +++ b/IPython/core/interactiveshell.py @@ -19,7 +19,6 @@ from __future__ import absolute_import import __builtin__ import abc -import bdb import codeop import exceptions import new @@ -39,35 +38,28 @@ from IPython.core.alias import AliasManager from IPython.core.builtin_trap import BuiltinTrap from IPython.config.configurable import Configurable from IPython.core.display_trap import DisplayTrap -from IPython.core.error import TryNext, UsageError +from IPython.core.error import UsageError from IPython.core.extensions import ExtensionManager from IPython.core.fakemodule import FakeModule, init_fakemod_dict +from IPython.core.inputlist import InputList from IPython.core.logger import Logger from IPython.core.magic import Magic from IPython.core.plugin import PluginManager from IPython.core.prefilter import PrefilterManager from IPython.core.prompts import CachedOutput -from IPython.core.usage import interactive_usage, default_banner import IPython.core.hooks from IPython.external.Itpl import ItplNS -from IPython.lib.inputhook import enable_gui -from IPython.lib.backgroundjobs import BackgroundJobManager -from IPython.lib.pylabtools import pylab_activate from IPython.utils import PyColorize from IPython.utils import pickleshare from IPython.utils.doctestreload import doctest_reload from IPython.utils.ipstruct import Struct -from IPython.utils.io import Term, ask_yes_no +import IPython.utils.io +from IPython.utils.io import ask_yes_no from IPython.utils.path import get_home_dir, get_ipython_dir, HomeDirError -from IPython.utils.process import ( - abbrev_cwd, - getoutput, - getoutputerror -) -# import IPython.utils.rlineimpl as readline +from IPython.utils.process import getoutput, getoutputerror from IPython.utils.strdispatch import StrDispatch from IPython.utils.syspathcontext import prepended_to_syspath -from IPython.utils.terminal import toggle_set_term_title, set_term_title +from IPython.utils.text import num_ini_spaces from IPython.utils.warn import warn, error, fatal from IPython.utils.traitlets import ( Int, Str, CBool, CaselessStrEnum, Enum, List, Unicode, Instance @@ -80,10 +72,6 @@ from IPython.utils.traitlets import ( # Globals #----------------------------------------------------------------------------- -# 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 - # compiled regexps for autoindent management dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass') @@ -91,18 +79,9 @@ dedent_re = re.compile(r'^\s+raise|^\s+return|^\s+pass') # Utilities #----------------------------------------------------------------------------- -ini_spaces_re = re.compile(r'^(\s+)') - - -def num_ini_spaces(strng): - """Return the number of initial spaces in a string""" - - ini_spaces = ini_spaces_re.match(strng) - if ini_spaces: - return ini_spaces.end() - else: - return 0 - +# 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 def softspace(file, newvalue): """Copied from code.py, to remove the dependency""" @@ -126,22 +105,6 @@ class SpaceInInput(exceptions.Exception): pass class Bunch: pass -class InputList(list): - """Class to store user input. - - It's basically a list, but slices return a string instead of a list, thus - allowing things like (assuming 'In' is an instance): - - exec In[4:7] - - or - - exec In[5:9] + In[14] + In[21:25]""" - - def __getslice__(self,i,j): - return ''.join(list.__getslice__(self,i,j)) - - class SyntaxTB(ultratb.ListTB): """Extension which holds some state: the last exception value""" @@ -160,17 +123,6 @@ class SyntaxTB(ultratb.ListTB): return e -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 - - def get_default_colors(): if sys.platform=='darwin': return "LightBG" @@ -201,28 +153,16 @@ class InteractiveShell(Configurable, Magic): """An enhanced, interactive shell for Python.""" autocall = Enum((0,1,2), default_value=1, config=True) - autoedit_syntax = CBool(False, config=True) + # TODO: remove all autoindent logic and put into frontends. + # We can't do this yet because even runlines uses the autoindent. autoindent = CBool(True, config=True) automagic = CBool(True, config=True) - banner = Str('') - banner1 = Str(default_banner, config=True) - banner2 = Str('', config=True) cache_size = Int(1000, config=True) color_info = CBool(True, config=True) colors = CaselessStrEnum(('NoColor','LightBG','Linux'), default_value=get_default_colors(), config=True) - confirm_exit = CBool(True, config=True) debug = CBool(False, config=True) deep_reload = CBool(False, 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) - editor = Str(get_default_editor(), config=True) filename = Str("") ipython_dir= Unicode('', config=True) # Set to get_ipython_dir() in __init__ logstart = CBool(False, config=True) @@ -230,7 +170,6 @@ class InteractiveShell(Configurable, Magic): logappend = Str('', config=True) object_info_string_level = Enum((0,1,2), default_value=0, config=True) - pager = Str('less', config=True) pdb = CBool(False, config=True) pprint = CBool(True, config=True) profile = Str('', config=True) @@ -240,6 +179,8 @@ class InteractiveShell(Configurable, Magic): prompts_pad_left = CBool(True, config=True) quiet = CBool(False, config=True) + # The readline stuff will eventually be moved to the terminal subclass + # but for now, we can't do that as readline is welded in everywhere. readline_use = CBool(True, config=True) readline_merge_completions = CBool(True, config=True) readline_omit__names = Enum((0,1,2), default_value=0, config=True) @@ -262,26 +203,17 @@ class InteractiveShell(Configurable, Magic): '"\C-u": unix-line-discard', ], allow_none=False, config=True) - screen_length = Int(0, config=True) - + # TODO: this part of prompt management should be moved to the frontends. # Use custom TraitTypes that convert '0'->'' and '\\n'->'\n' separate_in = SeparateStr('\n', config=True) separate_out = SeparateStr('', config=True) separate_out2 = SeparateStr('', config=True) - system_header = Str('IPython system call: ', config=True) system_verbose = CBool(False, config=True) - term_title = CBool(False, config=True) wildcards_case_sensitive = CBool(True, config=True) xmode = CaselessStrEnum(('Context','Plain', 'Verbose'), default_value='Context', config=True) - autoexec = List(allow_none=False) - - # class attribute to indicate whether the class supports threads or not. - # Subclasses with thread support should override this as needed. - isthreaded = False - # Subcomponents of InteractiveShell alias_manager = Instance('IPython.core.alias.AliasManager') prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') @@ -290,9 +222,8 @@ class InteractiveShell(Configurable, Magic): extension_manager = Instance('IPython.core.extensions.ExtensionManager') plugin_manager = Instance('IPython.core.plugin.PluginManager') - def __init__(self, config=None, ipython_dir=None, usage=None, + def __init__(self, config=None, ipython_dir=None, user_ns=None, user_global_ns=None, - banner1=None, banner2=None, display_banner=None, custom_exceptions=((),None)): # This is where traits with a config_key argument are updated @@ -302,9 +233,6 @@ class InteractiveShell(Configurable, Magic): # These are relatively independent and stateless self.init_ipython_dir(ipython_dir) self.init_instance_attrs() - self.init_term_title() - self.init_usage(usage) - self.init_banner(banner1, banner2, display_banner) # Create namespaces (user_ns, user_global_ns, etc.) self.init_create_namespaces(user_ns, user_global_ns) @@ -323,12 +251,16 @@ class InteractiveShell(Configurable, Magic): self.init_syntax_highlighting() self.init_hooks() self.init_pushd_popd_magic() + # TODO: init_io() needs to happen before init_traceback handlers + # because the traceback handlers hardcode the stdout/stderr streams. + # This logic in in debugger.Pdb and should eventually be changed. + self.init_io() self.init_traceback_handlers(custom_exceptions) self.init_user_ns() self.init_logger() self.init_alias() self.init_builtins() - + # pre_config_initialization self.init_shadow_hist() @@ -366,27 +298,10 @@ class InteractiveShell(Configurable, Magic): # Trait changed handlers #------------------------------------------------------------------------- - def _banner1_changed(self): - self.compute_banner() - - def _banner2_changed(self): - self.compute_banner() - def _ipython_dir_changed(self, name, new): if not os.path.isdir(new): os.makedirs(new, mode = 0777) - @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 _term_title_changed(self, name, new_value): - self.init_term_title() - def set_autoindent(self,value=None): """Set the autoindent flag, checking for readline support. @@ -421,7 +336,6 @@ class InteractiveShell(Configurable, Magic): self.config.Global.ipython_dir = self.ipython_dir def init_instance_attrs(self): - self.jobs = BackgroundJobManager() self.more = False # command compiler @@ -443,9 +357,6 @@ class InteractiveShell(Configurable, Magic): # item which gets cleared once run. self.code_to_run = None - # Flag to mark unconditional exit - self.exit_now = False - # Temporary files used for various purposes. Deleted at exit. self.tempfiles = [] @@ -459,20 +370,6 @@ class InteractiveShell(Configurable, Magic): # Indentation management self.indent_current_nsp = 0 - 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) - - def init_usage(self, usage=None): - if usage is None: - self.usage = interactive_usage - else: - self.usage = usage - def init_encoding(self): # Get system encoding at startup time. Certain terminals (like Emacs # under Win32 have it set to None, and we need to have a known valid @@ -519,6 +416,17 @@ class InteractiveShell(Configurable, Magic): 'NoColor', self.object_info_string_level) + def init_io(self): + import IPython.utils.io + if sys.platform == 'win32' and readline.have_readline and \ + self.readline_use: + Term = IPython.utils.io.IOTerm( + cout=readline._outputfile,cerr=readline._outputfile + ) + else: + Term = IPython.utils.io.IOTerm() + IPython.utils.io.Term = Term + def init_prompts(self): # Initialize cache, set in/out prompts and printing system self.outputcache = CachedOutput(self, @@ -550,31 +458,6 @@ class InteractiveShell(Configurable, Magic): warn("doctest module does not exist.") #------------------------------------------------------------------------- - # Things related to the banner - #------------------------------------------------------------------------- - - 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): - self.banner = self.banner1 + '\n' - if self.profile: - self.banner += '\nIPython profile: %s\n' % self.profile - if self.banner2: - self.banner += '\n' + self.banner2 + '\n' - - #------------------------------------------------------------------------- # Things related to injections into the sys module #------------------------------------------------------------------------- @@ -762,11 +645,6 @@ class InteractiveShell(Configurable, Magic): # notify the actual exception handlers self.InteractiveTB.call_pdb = val - if self.isthreaded: - try: - self.sys_excepthook.call_pdb = val - except: - warn('Failed to activate pdb for threaded exception handler') call_pdb = property(_get_call_pdb,_set_call_pdb,None, 'Control auto-activation of pdb at exceptions') @@ -1404,64 +1282,6 @@ class InteractiveShell(Configurable, Magic): value = msg, (filename, lineno, offset, line) self.SyntaxTB(etype,value,[]) - 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 ('','','', - '','', - 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 tab completion #------------------------------------------------------------------------- @@ -1641,13 +1461,12 @@ class InteractiveShell(Configurable, Magic): self.rl_next_input = s + # Maybe move this to the terminal subclass? def pre_readline(self): """readline hook to be used at the start of each line. Currently it handles auto-indent only.""" - #debugx('self.indent_current_nsp','pre_readline:') - if self.rl_do_indent: self.readline.insert_text(self._indent_current_str()) if self.rl_next_input is not None: @@ -1773,6 +1592,17 @@ class InteractiveShell(Configurable, Magic): self.plugin_manager = PluginManager(config=self.config) #------------------------------------------------------------------------- + # Things related to the prefilter + #------------------------------------------------------------------------- + + def init_prefilter(self): + self.prefilter_manager = PrefilterManager(shell=self, config=self.config) + # Ultimately this will be refactored in the new interpreter code, but + # for now, we should expose the main prefilter method (there's legacy + # code out there that may rely on this). + self.prefilter = self.prefilter_manager.prefilter_lines + + #------------------------------------------------------------------------- # Things related to the running of code #------------------------------------------------------------------------- @@ -1789,177 +1619,6 @@ class InteractiveShell(Configurable, Magic): with nested(self.builtin_trap,): return eval(expr, self.user_global_ns, self.user_ns) - 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): - - # if you run stuff with -c , raw hist is not updated - # ensure that it's in sync - if len(self.input_hist) != len (self.input_hist_raw): - self.input_hist_raw = InputList(self.input_hist) - - 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_prompt(self): - """ Print the prompt (in read-eval-print loop) - - Provided for those who want to implement their own read-eval-print loop (e.g. GUIs), not - used in standard IPython flow. - """ - if self.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() - self.write(prompt) - - def interact_handle_input(self,line): - """ Handle the input line (in read-eval-print loop) - - Provided for those who want to implement their own read-eval-print loop (e.g. GUIs), not - used in standard IPython flow. - """ - if line.lstrip() == line: - self.shadowhist.add(line.strip()) - lineout = self.prefilter_manager.prefilter_lines(line,self.more) - - if line.strip(): - if self.more: - self.input_hist_raw[-1] += '%s\n' % line - else: - self.input_hist_raw.append('%s\n' % line) - - - self.more = self.push_line(lineout) - if (self.SyntaxTB.last_syntax_error and - self.autoedit_syntax): - self.edit_syntax_error() - - def interact_with_readline(self): - """ Demo of using interact_handle_input, interact_prompt - - This is the main read-eval-print loop. If you need to implement your own (e.g. for GUI), - it should work like this. - """ - self.readline_startup_hook(self.pre_readline) - while not self.exit_now: - self.interact_prompt() - if self.more: - self.rl_do_indent = True - else: - self.rl_do_indent = False - line = raw_input_original().decode(self.stdin_encoding) - self.interact_handle_input(line) - - 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() - - more = 0 - - # 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. - - 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: - line = self.raw_input(prompt, more) - 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') - self.resetbuffer() - # keep cache in sync with the prompt counter: - self.outputcache.prompt_count -= 1 - - if self.autoindent: - self.indent_current_nsp = 0 - more = 0 - 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: - more = self.push_line(line) - if (self.SyntaxTB.last_syntax_error and - self.autoedit_syntax): - self.edit_syntax_error() - - # 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 - def safe_execfile(self, fname, *where, **kw): """A safe version of the builtin execfile(). @@ -2057,43 +1716,6 @@ class InteractiveShell(Configurable, Magic): except: self.showtraceback() warn('Unknown failure executing file: <%s>' % fname) - - def _is_secondary_block_start(self, s): - if not s.endswith(':'): - return False - if (s.startswith('elif') or - s.startswith('else') or - s.startswith('except') or - s.startswith('finally')): - return True - - def cleanup_ipy_script(self, script): - """Make a script safe for self.runlines() - - Currently, IPython is lines based, with blocks being detected by - empty lines. This is a problem for block based scripts that may - not have empty lines after blocks. This script adds those empty - lines to make scripts safe for running in the current line based - IPython. - """ - res = [] - lines = script.splitlines() - level = 0 - - for l in lines: - lstripped = l.lstrip() - stripped = l.strip() - if not stripped: - continue - newlevel = len(l) - len(lstripped) - if level > 0 and newlevel == 0 and \ - not self._is_secondary_block_start(stripped): - # add empty line - res.append('') - res.append(l) - level = newlevel - - return '\n'.join(res) + '\n' def runlines(self, lines, clean=False): """Run a string of one or more lines of source. @@ -2108,7 +1730,7 @@ class InteractiveShell(Configurable, Magic): lines = '\n'.join(lines) if clean: - lines = self.cleanup_ipy_script(lines) + lines = self._cleanup_ipy_script(lines) # We must start with a clean buffer, in case this is run from an # interactive IPython session (via a magic, for example). @@ -2272,6 +1894,47 @@ class InteractiveShell(Configurable, Magic): self.resetbuffer() return more + def resetbuffer(self): + """Reset the input buffer.""" + self.buffer[:] = [] + + def _is_secondary_block_start(self, s): + if not s.endswith(':'): + return False + if (s.startswith('elif') or + s.startswith('else') or + s.startswith('except') or + s.startswith('finally')): + return True + + def _cleanup_ipy_script(self, script): + """Make a script safe for self.runlines() + + Currently, IPython is lines based, with blocks being detected by + empty lines. This is a problem for block based scripts that may + not have empty lines after blocks. This script adds those empty + lines to make scripts safe for running in the current line based + IPython. + """ + res = [] + lines = script.splitlines() + level = 0 + + for l in lines: + lstripped = l.lstrip() + stripped = l.strip() + if not stripped: + continue + newlevel = len(l) - len(lstripped) + if level > 0 and newlevel == 0 and \ + not self._is_secondary_block_start(stripped): + # add empty line + res.append('') + res.append(l) + level = newlevel + + return '\n'.join(res) + '\n' + def _autoindent_update(self,line): """Keep track of the indent level.""" @@ -2290,91 +1953,12 @@ class InteractiveShell(Configurable, Magic): else: self.indent_current_nsp = 0 - def resetbuffer(self): - """Reset the input buffer.""" - self.buffer[:] = [] - - def raw_input(self,prompt='',continue_prompt=False): - """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. - """ - # growl.notify("raw_input: ", "prompt = %r\ncontinue_prompt = %s" % (prompt, continue_prompt)) - - # 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_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. - #debugx('self.buffer[-1]') - - if self.autoindent: - if num_ini_spaces(line) > self.indent_current_nsp: - line = line[self.indent_current_nsp:] - self.indent_current_nsp = 0 - - # store the unfiltered input before the user has any chance to modify - # it. - if line.strip(): - if continue_prompt: - self.input_hist_raw[-1] += '%s\n' % line - if self.has_readline and self.readline_use: - try: - histlen = self.readline.get_current_history_length() - if histlen > 1: - newhist = self.input_hist_raw[-1].rstrip() - self.readline.remove_history_item(histlen-1) - self.readline.replace_history_item(histlen-2, - newhist.encode(self.stdin_encoding)) - except AttributeError: - pass # re{move,place}_history_item are new in 2.4. - else: - self.input_hist_raw.append('%s\n' % line) - # only entries starting at first column go to shadow history - if line.lstrip() == line: - self.shadowhist.add(line.strip()) - elif not continue_prompt: - self.input_hist_raw.append('\n') - try: - lineout = self.prefilter_manager.prefilter_lines(line,continue_prompt) - except: - # blanket except, in case a user-defined prefilter crashes, so it - # can't take all of ipython with it. - self.showtraceback() - return '' - else: - return lineout - #------------------------------------------------------------------------- - # Things related to the prefilter + # Things related to GUI support and pylab #------------------------------------------------------------------------- - def init_prefilter(self): - self.prefilter_manager = PrefilterManager(shell=self, config=self.config) - # Ultimately this will be refactored in the new interpreter code, but - # for now, we should expose the main prefilter method (there's legacy - # code out there that may rely on this). - self.prefilter = self.prefilter_manager.prefilter_lines + def enable_pylab(self, gui=None): + raise NotImplementedError('Implement enable_pylab in a subclass') #------------------------------------------------------------------------- # Utilities @@ -2426,13 +2010,15 @@ class InteractiveShell(Configurable, Magic): tmp_file.close() return filename + # TODO: This should be removed when Term is refactored. def write(self,data): """Write a string to the default output""" - Term.cout.write(data) + IPython.utils.io.Term.cout.write(data) + # TODO: This should be removed when Term is refactored. def write_err(self,data): """Write a string to the default error output""" - Term.cerr.write(data) + IPython.utils.io.Term.cerr.write(data) def ask_yes_no(self,prompt,default=True): if self.quiet: @@ -2440,58 +2026,9 @@ class InteractiveShell(Configurable, Magic): return ask_yes_no(prompt,default) #------------------------------------------------------------------------- - # 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 IPython 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() - def atexit_operations(self): """This will be executed at the time of exit. diff --git a/IPython/core/ipapi.py b/IPython/core/ipapi.py index 24fe36d..8e5fef8 100644 --- a/IPython/core/ipapi.py +++ b/IPython/core/ipapi.py @@ -25,6 +25,6 @@ has been made into a component, this module will be sent to deathrow. def get(): """Get the global InteractiveShell instance.""" - from IPython.core.iplib import InteractiveShell - return InteractiveShell.instance() + from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell + return TerminalInteractiveShell.instance() diff --git a/IPython/core/macro.py b/IPython/core/macro.py index 8fb52a6..676e083 100644 --- a/IPython/core/macro.py +++ b/IPython/core/macro.py @@ -7,7 +7,7 @@ # the file COPYING, distributed as part of this software. #***************************************************************************** -from IPython.utils.io import Term +import IPython.utils.io from IPython.core.autocall import IPyAutocall class Macro(IPyAutocall): @@ -32,7 +32,7 @@ class Macro(IPyAutocall): return 'IPython.macro.Macro(%s)' % repr(self.value) def __call__(self,*args): - Term.cout.flush() + IPython.utils.io.Term.cout.flush() self._ip.user_ns['_margv'] = args self._ip.runlines(self.value) diff --git a/IPython/core/magic.py b/IPython/core/magic.py index b44a41a..5cd7429 100644 --- a/IPython/core/magic.py +++ b/IPython/core/magic.py @@ -58,7 +58,8 @@ from IPython.lib.pylabtools import mpl_runner from IPython.lib.inputhook import enable_gui from IPython.external.Itpl import itpl, printpl from IPython.testing import decorators as testdec -from IPython.utils.io import Term, file_read, nlprint +from IPython.utils.io import file_read, nlprint +import IPython.utils.io from IPython.utils.path import get_py_filename from IPython.utils.process import arg_split, abbrev_cwd from IPython.utils.terminal import set_term_title @@ -1699,7 +1700,7 @@ Currently the magic system has the following functions:\n""" # set the __file__ global in the script's namespace prog_ns['__file__'] = filename - # pickle fix. See iplib for an explanation. But we need to make sure + # pickle fix. See interactiveshell for an explanation. But we need to make sure # that, if we overwrite __main__, we replace it at the end main_mod_name = prog_ns['__name__'] @@ -2524,13 +2525,6 @@ Currently the magic system has the following functions:\n""" except: xmode_switch_err('user') - # threaded shells use a special handler in sys.excepthook - if shell.isthreaded: - try: - shell.sys_excepthook.set_mode(mode=new_mode) - except: - xmode_switch_err('threaded') - def magic_colors(self,parameter_s = ''): """Switch color scheme for prompts, info system and exception handlers. @@ -2585,13 +2579,6 @@ Defaulting color scheme to 'NoColor'""" except: color_switch_err('exception') - # threaded shells use a verbose traceback in sys.excepthook - if shell.isthreaded: - try: - shell.sys_excepthook.set_colors(scheme=new_scheme) - except: - color_switch_err('system exception handler') - # Set info (for 'object?') colors if shell.color_info: try: @@ -3107,7 +3094,7 @@ Defaulting color scheme to 'NoColor'""" # If all looks ok, proceed out,err = self.shell.getoutputerror(cmd) if err: - print >> Term.cerr,err + print >> IPython.utils.io.Term.cerr, err if opts.has_key('l'): out = SList(out.split('\n')) else: @@ -3157,52 +3144,9 @@ Defaulting color scheme to 'NoColor'""" if parameter_s: out,err = self.shell.getoutputerror(parameter_s) if err: - print >> Term.cerr,err + print >> IPython.utils.io.Term.cerr, err return SList(out.split('\n')) - def magic_bg(self, parameter_s=''): - """Run a job in the background, in a separate thread. - - For example, - - %bg myfunc(x,y,z=1) - - will execute 'myfunc(x,y,z=1)' in a background thread. As soon as the - execution starts, a message will be printed indicating the job - number. If your job number is 5, you can use - - myvar = jobs.result(5) or myvar = jobs[5].result - - to assign this result to variable 'myvar'. - - IPython has a job manager, accessible via the 'jobs' object. You can - type jobs? to get more information about it, and use jobs. to see - its attributes. All attributes not starting with an underscore are - meant for public use. - - In particular, look at the jobs.new() method, which is used to create - new jobs. This magic %bg function is just a convenience wrapper - around jobs.new(), for expression-based jobs. If you want to create a - new job with an explicit function object and arguments, you must call - jobs.new() directly. - - The jobs.new docstring also describes in detail several important - caveats associated with a thread-based model for background job - execution. Type jobs.new? for details. - - You can check the status of all jobs with jobs.status(). - - The jobs variable is set by IPython into the Python builtin namespace. - If you ever declare a variable named 'jobs', you will shadow this - name. You can either delete your global jobs variable to regain - access to the job manager, or make a new name and assign it manually - to the manager (stored in IPython's namespace). For example, to - assign the job manager to the Jobs name, use: - - Jobs = __builtins__.jobs""" - - self.shell.jobs.new(parameter_s,self.shell.user_ns) - def magic_r(self, parameter_s=''): """Repeat previous input. @@ -3327,10 +3271,10 @@ Defaulting color scheme to 'NoColor'""" def _get_pasted_lines(self, sentinel): """ Yield pasted lines until the user enters the given sentinel value. """ - from IPython.core import iplib + from IPython.core import interactiveshell print "Pasting code; enter '%s' alone on the line to stop." % sentinel while True: - l = iplib.raw_input_original(':') + l = interactiveshell.raw_input_original(':') if l == sentinel: return else: diff --git a/IPython/core/oinspect.py b/IPython/core/oinspect.py index 91ac5a5..723acae 100644 --- a/IPython/core/oinspect.py +++ b/IPython/core/oinspect.py @@ -30,7 +30,7 @@ import types from IPython.core.page import page from IPython.external.Itpl import itpl from IPython.utils import PyColorize -from IPython.utils.io import Term +import IPython.utils.io from IPython.utils.text import indent from IPython.utils.wildcard import list_namespace from IPython.utils.coloransi import * @@ -249,7 +249,7 @@ class Inspector: if output is None: self.noinfo('definition header',oname) else: - print >>Term.cout, header,self.format(output), + print >>IPython.utils.io.Term.cout, header,self.format(output), def pdoc(self,obj,oname='',formatter = None): """Print the docstring for any object. diff --git a/IPython/core/page.py b/IPython/core/page.py index f26075a..e8a234e 100755 --- a/IPython/core/page.py +++ b/IPython/core/page.py @@ -36,7 +36,7 @@ from IPython.core import ipapi from IPython.core.error import TryNext from IPython.utils.cursesimport import use_curses from IPython.utils.data import chop -from IPython.utils.io import Term +import IPython.utils.io from IPython.utils.process import xsys from IPython.utils.terminal import get_terminal_size @@ -56,18 +56,18 @@ def page_dumb(strng, start=0, screen_lines=25): out_ln = strng.splitlines()[start:] screens = chop(out_ln,screen_lines-1) if len(screens) == 1: - print >>Term.cout, os.linesep.join(screens[0]) + print >>IPython.utils.io.Term.cout, os.linesep.join(screens[0]) else: last_escape = "" for scr in screens[0:-1]: hunk = os.linesep.join(scr) - print >>Term.cout, last_escape + hunk + print >>IPython.utils.io.Term.cout, last_escape + hunk if not page_more(): return esc_list = esc_re.findall(hunk) if len(esc_list) > 0: last_escape = esc_list[-1] - print >>Term.cout, last_escape + os.linesep.join(screens[-1]) + print >>IPython.utils.io.Term.cout, last_escape + os.linesep.join(screens[-1]) def page(strng, start=0, screen_lines=0, pager_cmd=None): @@ -156,7 +156,7 @@ def page(strng, start=0, screen_lines=0, pager_cmd=None): #print 'numlines',numlines,'screenlines',screen_lines # dbg if numlines <= screen_lines : #print '*** normal print' # dbg - print >>Term.cout, str_toprint + print >>IPython.utils.io.Term.cout, str_toprint else: # Try to open pager and default to internal one if that fails. # All failure modes are tagged as 'retval=1', to match the return @@ -262,13 +262,13 @@ if os.name == 'nt' and os.environ.get('TERM','dumb') != 'emacs': @return: True if need print more lines, False if quit """ - Term.cout.write('---Return to continue, q to quit--- ') + IPython.utils.io.Term.cout.write('---Return to continue, q to quit--- ') ans = msvcrt.getch() if ans in ("q", "Q"): result = False else: result = True - Term.cout.write("\b"*37 + " "*37 + "\b"*37) + IPython.utils.io.Term.cout.write("\b"*37 + " "*37 + "\b"*37) return result else: def page_more(): diff --git a/IPython/core/prefilter.py b/IPython/core/prefilter.py index 50a8b67..a7bd9c6 100755 --- a/IPython/core/prefilter.py +++ b/IPython/core/prefilter.py @@ -36,7 +36,7 @@ from IPython.core.splitinput import split_user_input from IPython.core.page import page from IPython.utils.traitlets import List, Int, Any, Str, CBool, Bool, Instance -from IPython.utils.io import Term +import IPython.utils.io from IPython.utils.text import make_quoted_expr from IPython.utils.autoattr import auto_attr @@ -210,7 +210,7 @@ class PrefilterManager(Configurable): """ multi_line_specials = CBool(True, config=True) - shell = Instance('IPython.core.iplib.InteractiveShellABC') + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') def __init__(self, shell=None, config=None): super(PrefilterManager, self).__init__(shell=shell, config=config) @@ -453,7 +453,7 @@ class PrefilterTransformer(Configurable): priority = Int(100, config=True) # Transformers don't currently use shell or prefilter_manager, but as we # move away from checkers and handlers, they will need them. - shell = Instance('IPython.core.iplib.InteractiveShellABC') + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') enabled = Bool(True, config=True) @@ -561,7 +561,7 @@ class PrefilterChecker(Configurable): """Inspect an input line and return a handler for that line.""" priority = Int(100, config=True) - shell = Instance('IPython.core.iplib.InteractiveShellABC') + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') enabled = Bool(True, config=True) @@ -754,7 +754,7 @@ class PrefilterHandler(Configurable): handler_name = Str('normal') esc_strings = List([]) - shell = Instance('IPython.core.iplib.InteractiveShellABC') + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') prefilter_manager = Instance('IPython.core.prefilter.PrefilterManager') def __init__(self, shell=None, prefilter_manager=None, config=None): @@ -922,7 +922,7 @@ class AutoHandler(PrefilterHandler): # plain ascii works better w/ pyreadline, on some machines, so # we use it and only print uncolored rewrite if we have unicode rw = str(rw) - print >>Term.cout, rw + print >>IPython.utils.io.Term.cout, rw except UnicodeEncodeError: print "-------------->" + newcmd diff --git a/IPython/core/prompts.py b/IPython/core/prompts.py index 2220fdb..14231c8 100644 --- a/IPython/core/prompts.py +++ b/IPython/core/prompts.py @@ -25,7 +25,7 @@ from IPython.core.error import TryNext from IPython.utils import coloransi import IPython.utils.generics from IPython.utils.warn import warn -from IPython.utils.io import Term +import IPython.utils.io #**************************************************************************** #Color schemes for Prompts. @@ -537,7 +537,7 @@ class CachedOutput: except KeyError: pass if arg is not None: - cout_write = Term.cout.write # fast lookup + cout_write = IPython.utils.io.Term.cout.write # fast lookup # first handle the cache and counters # do not print output if input ends in ';' @@ -577,7 +577,7 @@ class CachedOutput: if self.logger.log_output: self.logger.log_write(repr(arg),'output') cout_write(self.output_sep2) - Term.cout.flush() + IPython.utils.io.Term.cout.flush() def _display(self,arg): """Default printer method, uses pprint. diff --git a/IPython/core/splitinput.py b/IPython/core/splitinput.py index ebf323f..99dc0b5 100644 --- a/IPython/core/splitinput.py +++ b/IPython/core/splitinput.py @@ -30,7 +30,7 @@ import re # RegExp for splitting line contents into pre-char//first word-method//rest. # For clarity, each group in on one line. -# WARNING: update the regexp if the escapes in iplib are changed, as they +# WARNING: update the regexp if the escapes in interactiveshell are changed, as they # are hardwired in. # Although it's not solely driven by the regex, note that: diff --git a/IPython/core/tests/test_imports.py b/IPython/core/tests/test_imports.py index 6257539..7f72a1b 100644 --- a/IPython/core/tests/test_imports.py +++ b/IPython/core/tests/test_imports.py @@ -25,8 +25,8 @@ def test_import_hooks(): def test_import_ipapi(): from IPython.core import ipapi -def test_import_iplib(): - from IPython.core import iplib +def test_import_interactiveshell(): + from IPython.core import interactiveshell def test_import_logger(): from IPython.core import logger @@ -40,9 +40,6 @@ def test_import_magic(): def test_import_oinspect(): from IPython.core import oinspect -def test_import_outputtrap(): - from IPython.core import outputtrap - def test_import_prefilter(): from IPython.core import prefilter diff --git a/IPython/core/tests/test_iplib.py b/IPython/core/tests/test_iplib.py index 8b4276c..d229ae7 100644 --- a/IPython/core/tests/test_iplib.py +++ b/IPython/core/tests/test_iplib.py @@ -1,4 +1,4 @@ -"""Tests for the key iplib module, where the main ipython class is defined. +"""Tests for the key interactiveshell module, where the main ipython class is defined. """ #----------------------------------------------------------------------------- # Module imports @@ -60,7 +60,7 @@ def test_reset(): # Tests for reporting of exceptions in various modes, handling of SystemExit, -# and %tb functionality. This is really a mix of testing ultraTB and iplib. +# and %tb functionality. This is really a mix of testing ultraTB and interactiveshell. def doctest_tb_plain(): """ diff --git a/IPython/core/ultratb.py b/IPython/core/ultratb.py index d5b3e22..40b74e1 100644 --- a/IPython/core/ultratb.py +++ b/IPython/core/ultratb.py @@ -95,7 +95,7 @@ from IPython.core import debugger, ipapi from IPython.core.display_trap import DisplayTrap from IPython.core.excolors import exception_colors from IPython.utils.data import uniq_stable -from IPython.utils.io import Term +import IPython.utils.io from IPython.utils.warn import info, error # Globals @@ -313,14 +313,11 @@ def _format_traceback_lines(lnum, index, lines, Colors, lvals=None,scheme=None): class TBTools: """Basic tools used by all traceback printer classes.""" - #: Default output stream, can be overridden at call time. A special value - #: of 'stdout' *as a string* can be given to force extraction of sys.stdout - #: at runtime. This allows testing exception printing with doctests, that - #: swap sys.stdout just at execution time. - #: Warning: be VERY careful to set this to one of the Term streams, NEVER - #: directly to sys.stdout/err, because under win32 the Term streams come from - #: pyreadline and know how to handle color correctly, whie stdout/err don't. - out_stream = Term.cerr + # This attribute us used in globalipapp.py to have stdout used for + # writting exceptions. This is needed so nose can trap them. This attribute + # should be None (the default, which will use IPython.utils.io.Term) or + # the string 'stdout' which will cause the override to sys.stdout. + out_stream = None def __init__(self,color_scheme = 'NoColor',call_pdb=False): # Whether to call the interactive pdb debugger after printing @@ -384,9 +381,9 @@ class ListTB(TBTools): TBTools.__init__(self,color_scheme = color_scheme,call_pdb=0) def __call__(self, etype, value, elist): - Term.cout.flush() - Term.cerr.write(self.text(etype,value,elist)) - Term.cerr.write('\n') + IPython.utils.io.Term.cout.flush() + IPython.utils.io.Term.cerr.write(self.text(etype,value,elist)) + IPython.utils.io.Term.cerr.write('\n') def text(self, etype, value, elist, context=5): """Return a color formatted string with the traceback info. @@ -535,10 +532,13 @@ class ListTB(TBTools): """ # This method needs to use __call__ from *this* class, not the one from # a subclass whose signature or behavior may be different - Term.cout.flush() - ostream = sys.stdout if self.out_stream == 'stdout' else Term.cerr + if self.out_stream == 'stdout': + ostream = sys.stdout + else: + ostream = IPython.utils.io.Term.cerr + ostream.flush() ostream.write(ListTB.text(self, etype, value, [])) - ostream.flush() + ostream.flush() def _some_str(self, value): # Lifted from traceback.py @@ -659,7 +659,7 @@ class VerboseTB(TBTools): # So far, I haven't been able to find an isolated example to # reproduce the problem. inspect_error() - traceback.print_exc(file=Term.cerr) + traceback.print_exc(file=IPython.utils.io.Term.cerr) info('\nUnfortunately, your original traceback can not be constructed.\n') return '' @@ -696,7 +696,7 @@ class VerboseTB(TBTools): # able to remove this try/except when 2.4 becomes a # requirement. Bug details at http://python.org/sf/1005466 inspect_error() - traceback.print_exc(file=Term.cerr) + traceback.print_exc(file=IPython.utils.io.Term.cerr) info("\nIPython's exception reporting continues...\n") if func == '?': @@ -717,7 +717,7 @@ class VerboseTB(TBTools): # and barfs out. At some point I should dig into this one # and file a bug report about it. inspect_error() - traceback.print_exc(file=Term.cerr) + traceback.print_exc(file=IPython.utils.io.Term.cerr) info("\nIPython's exception reporting continues...\n") call = tpl_call_fail % func @@ -910,9 +910,9 @@ class VerboseTB(TBTools): def handler(self, info=None): (etype, evalue, etb) = info or sys.exc_info() self.tb = etb - Term.cout.flush() - Term.cerr.write(self.text(etype, evalue, etb)) - Term.cerr.write('\n') + IPython.utils.io.Term.cout.flush() + IPython.utils.io.Term.cerr.write(self.text(etype, evalue, etb)) + IPython.utils.io.Term.cerr.write('\n') # Changed so an instance can just be called as VerboseTB_inst() and print # out the right info on its own. @@ -1032,8 +1032,11 @@ class AutoFormattedTB(FormattedTB): given at initialization time. """ if out is None: - out = sys.stdout if self.out_stream=='stdout' else self.out_stream - Term.cout.flush() + if self.out_stream == 'stdout': + out = sys.stdout + else: + out = IPython.utils.io.Term.cerr + out.flush() if tb_offset is not None: tb_offset, self.tb_offset = self.tb_offset, tb_offset out.write(self.text(etype, evalue, etb)) diff --git a/IPython/Shell.py b/IPython/deathrow/Shell.py similarity index 100% rename from IPython/Shell.py rename to IPython/deathrow/Shell.py diff --git a/IPython/deathrow/gui/wx/ipshell_nonblocking.py b/IPython/deathrow/gui/wx/ipshell_nonblocking.py index b8d4907..8644ade 100755 --- a/IPython/deathrow/gui/wx/ipshell_nonblocking.py +++ b/IPython/deathrow/gui/wx/ipshell_nonblocking.py @@ -24,7 +24,7 @@ import locale from thread_ex import ThreadEx from IPython.core import iplib -from IPython.utils.io import Term +import IPython.utils.io ############################################################################## class _Helper(object): diff --git a/IPython/deathrow/ipipe.py b/IPython/deathrow/ipipe.py index c440e6e..8c2766d 100644 --- a/IPython/deathrow/ipipe.py +++ b/IPython/deathrow/ipipe.py @@ -133,7 +133,7 @@ from IPython.external import simplegeneric from IPython.external import path try: - from IPython.utils.io import Term + import IPython.utils.io from IPython.utils import generics except ImportError: Term = None diff --git a/IPython/iplib.py b/IPython/deathrow/iplib.py similarity index 100% rename from IPython/iplib.py rename to IPython/deathrow/iplib.py diff --git a/IPython/scripts/ipython-wx b/IPython/deathrow/ipython-wx similarity index 100% rename from IPython/scripts/ipython-wx rename to IPython/deathrow/ipython-wx diff --git a/IPython/scripts/ipythonx b/IPython/deathrow/ipythonx similarity index 100% rename from IPython/scripts/ipythonx rename to IPython/deathrow/ipythonx diff --git a/IPython/deathrow/oldfrontend/prefilterfrontend.py b/IPython/deathrow/oldfrontend/prefilterfrontend.py index 327ea0a..20726e2 100644 --- a/IPython/deathrow/oldfrontend/prefilterfrontend.py +++ b/IPython/deathrow/oldfrontend/prefilterfrontend.py @@ -31,7 +31,7 @@ from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap -from IPython.utils.io import Term +import IPython.utils.io from linefrontendbase import LineFrontEndBase, common_prefix diff --git a/IPython/core/outputtrap.py b/IPython/deathrow/outputtrap.py similarity index 100% rename from IPython/core/outputtrap.py rename to IPython/deathrow/outputtrap.py diff --git a/IPython/deathrow/twshell.py b/IPython/deathrow/twshell.py index 3270d65..dd09e7e 100644 --- a/IPython/deathrow/twshell.py +++ b/IPython/deathrow/twshell.py @@ -14,7 +14,7 @@ from IPython.core.iplib import InteractiveShell from IPython.utils.ipstruct import Struct import Queue,thread,threading,signal from signal import signal, SIGINT -from IPython.utils.io import Term, ask_yes_no +import IPython.utils.io, ask_yes_no from IPython.utils.warn import warn, error from IPython.utils.decorators import flag_calls from IPython.core import shellglobals diff --git a/IPython/extensions/parallelmagic.py b/IPython/extensions/parallelmagic.py index 3f53468..1e2f482 100755 --- a/IPython/extensions/parallelmagic.py +++ b/IPython/extensions/parallelmagic.py @@ -36,7 +36,7 @@ class ParalleMagic(Plugin): active_multiengine_client = Any() verbose = Bool(False, config=True) - shell = Instance('IPython.core.iplib.InteractiveShellABC') + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') def __init__(self, shell=None, config=None): super(ParalleMagic, self).__init__(shell=shell, config=config) diff --git a/IPython/extensions/pretty.py b/IPython/extensions/pretty.py index 8f05b23..17a6e5c 100644 --- a/IPython/extensions/pretty.py +++ b/IPython/extensions/pretty.py @@ -39,7 +39,7 @@ from IPython.core.error import TryNext from IPython.external import pretty from IPython.core.plugin import Plugin from IPython.utils.traitlets import Bool, List, Instance -from IPython.utils.io import Term +import IPython.utils.io from IPython.utils.autoattr import auto_attr from IPython.utils.importstring import import_item @@ -55,7 +55,7 @@ class PrettyResultDisplay(Plugin): """A component for pretty printing on steroids.""" verbose = Bool(False, config=True) - shell = Instance('IPython.core.iplib.InteractiveShellABC') + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') # A list of (type, func_name), like # [(dict, 'my_dict_printer')] @@ -100,8 +100,8 @@ class PrettyResultDisplay(Plugin): # So that multi-line strings line up with the left column of # the screen, instead of having the output prompt mess up # their first line. - Term.cout.write('\n') - print >>Term.cout, out + IPython.utils.io.Term.cout.write('\n') + print >>IPython.utils.io.Term.cout, out else: raise TryNext diff --git a/IPython/extensions/tests/test_pretty.py b/IPython/extensions/tests/test_pretty.py index c7da78a..54dc9ba 100644 --- a/IPython/extensions/tests/test_pretty.py +++ b/IPython/extensions/tests/test_pretty.py @@ -18,7 +18,7 @@ Simple tests for :mod:`IPython.extensions.pretty`. from unittest import TestCase from IPython.config.configurable import Configurable -from IPython.core.iplib import InteractiveShellABC +from IPython.core.interactiveshell import InteractiveShellABC from IPython.extensions import pretty as pretty_ext from IPython.external import pretty from IPython.testing import decorators as dec diff --git a/IPython/frontend/terminal/__init__.py b/IPython/frontend/terminal/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/IPython/frontend/terminal/__init__.py diff --git a/IPython/core/embed.py b/IPython/frontend/terminal/embed.py similarity index 86% rename from IPython/core/embed.py rename to IPython/frontend/terminal/embed.py index fc68ec1..94a271f 100755 --- a/IPython/core/embed.py +++ b/IPython/frontend/terminal/embed.py @@ -30,8 +30,8 @@ import sys from contextlib import nested from IPython.core import ultratb -from IPython.core.iplib import InteractiveShell -from IPython.core.ipapp import load_default_config +from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell +from IPython.frontend.terminal.ipapp import load_default_config from IPython.utils.traitlets import Bool, Str, CBool from IPython.utils.io import ask_yes_no @@ -59,7 +59,7 @@ def kill_embedded(self,parameter_s=''): print "This embedded IPython will not reactivate anymore once you exit." -class InteractiveShellEmbed(InteractiveShell): +class InteractiveShellEmbed(TerminalInteractiveShell): dummy_mode = Bool(False) exit_msg = Str('') @@ -69,18 +69,19 @@ class InteractiveShellEmbed(InteractiveShell): # is True by default. display_banner = CBool(True) - def __init__(self, parent=None, config=None, ipython_dir=None, usage=None, - user_ns=None, user_global_ns=None, - banner1=None, banner2=None, display_banner=None, - custom_exceptions=((),None), exit_msg=''): + 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, exit_msg=u''): self.save_sys_ipcompleter() super(InteractiveShellEmbed,self).__init__( - parent=parent, config=config, ipython_dir=ipython_dir, usage=usage, - user_ns=user_ns, user_global_ns=user_global_ns, - banner1=banner1, banner2=banner2, display_banner=display_banner, - custom_exceptions=custom_exceptions) + config=config, ipython_dir=ipython_dir, user_ns=user_ns, + user_global_ns=user_global_ns, custom_exceptions=custom_exceptions, + usage=usage, banner1=banner1, banner2=banner2, + display_banner=display_banner + ) self.exit_msg = exit_msg self.define_magic("kill_embedded", kill_embedded) @@ -240,8 +241,7 @@ class InteractiveShellEmbed(InteractiveShell): _embedded_shell = None -def embed(header='', config=None, usage=None, banner1=None, banner2=None, - display_banner=True, exit_msg=''): +def embed(**kwargs): """Call this to embed IPython at the current point in your program. The first invocation of this will create an :class:`InteractiveShellEmbed` @@ -261,15 +261,12 @@ def embed(header='', config=None, usage=None, banner1=None, banner2=None, Full customization can be done by passing a :class:`Struct` in as the config argument. """ + config = kwargs.get('config') + header = kwargs.pop('header', u'') if config is None: config = load_default_config() - config.InteractiveShellEmbed = config.InteractiveShell + config.InteractiveShellEmbed = config.TerminalInteractiveShell global _embedded_shell if _embedded_shell is None: - _embedded_shell = InteractiveShellEmbed( - config=config, usage=usage, - banner1=banner1, banner2=banner2, - display_banner=display_banner, exit_msg=exit_msg - ) + _embedded_shell = InteractiveShellEmbed(**kwargs) _embedded_shell(header=header, stack_depth=2) - diff --git a/IPython/frontend/terminal/interactiveshell.py b/IPython/frontend/terminal/interactiveshell.py new file mode 100644 index 0000000..ec10d8c --- /dev/null +++ b/IPython/frontend/terminal/interactiveshell.py @@ -0,0 +1,523 @@ +# -*- coding: utf-8 -*- +"""Subclass of InteractiveShell for terminal based frontends.""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2001 Janko Hauser +# Copyright (C) 2001-2007 Fernando Perez. +# 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.inputlist import InputList +from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC +from IPython.lib.inputhook import enable_gui +from IPython.lib.pylabtools import pylab_activate +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 +from IPython.utils.traitlets import Int, Str, CBool + + +#----------------------------------------------------------------------------- +# 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) + banner = Str('') + banner1 = Str(default_banner, config=True) + banner2 = Str('', config=True) + 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) + editor = Str(get_default_editor(), config=True) + exit_now = CBool(False) + pager = Str('less', config=True) + + 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) + + #------------------------------------------------------------------------- + # 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): + self.banner = self.banner1 + '\n' + if self.profile: + self.banner += '\nIPython profile: %s\n' % self.profile + if self.banner2: + self.banner += '\n' + self.banner2 + '\n' + + 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): + + # if you run stuff with -c , raw hist is not updated + # ensure that it's in sync + if len(self.input_hist) != len (self.input_hist_raw): + self.input_hist_raw = InputList(self.input_hist) + + 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() + + more = 0 + + # 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. + + 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: + line = self.raw_input(prompt, more) + 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') + self.resetbuffer() + # keep cache in sync with the prompt counter: + self.outputcache.prompt_count -= 1 + + if self.autoindent: + self.indent_current_nsp = 0 + more = 0 + 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: + more = self.push_line(line) + if (self.SyntaxTB.last_syntax_error and + self.autoedit_syntax): + self.edit_syntax_error() + + # 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 + + def raw_input(self,prompt='',continue_prompt=False): + """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. + """ + # growl.notify("raw_input: ", "prompt = %r\ncontinue_prompt = %s" % (prompt, continue_prompt)) + + # 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_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. + #debugx('self.buffer[-1]') + + if self.autoindent: + if num_ini_spaces(line) > self.indent_current_nsp: + line = line[self.indent_current_nsp:] + self.indent_current_nsp = 0 + + # store the unfiltered input before the user has any chance to modify + # it. + if line.strip(): + if continue_prompt: + self.input_hist_raw[-1] += '%s\n' % line + if self.has_readline and self.readline_use: + try: + histlen = self.readline.get_current_history_length() + if histlen > 1: + newhist = self.input_hist_raw[-1].rstrip() + self.readline.remove_history_item(histlen-1) + self.readline.replace_history_item(histlen-2, + newhist.encode(self.stdin_encoding)) + except AttributeError: + pass # re{move,place}_history_item are new in 2.4. + else: + self.input_hist_raw.append('%s\n' % line) + # only entries starting at first column go to shadow history + if line.lstrip() == line: + self.shadowhist.add(line.strip()) + elif not continue_prompt: + self.input_hist_raw.append('\n') + try: + lineout = self.prefilter_manager.prefilter_lines(line,continue_prompt) + except: + # blanket except, in case a user-defined prefilter crashes, so it + # can't take all of ipython with it. + self.showtraceback() + return '' + else: + return lineout + + # TODO: The following three methods are an early attempt to refactor + # the main code execution logic. We don't use them, but they may be + # helpful when we refactor the code execution logic further. + # def interact_prompt(self): + # """ Print the prompt (in read-eval-print loop) + # + # Provided for those who want to implement their own read-eval-print loop (e.g. GUIs), not + # used in standard IPython flow. + # """ + # if self.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() + # self.write(prompt) + # + # def interact_handle_input(self,line): + # """ Handle the input line (in read-eval-print loop) + # + # Provided for those who want to implement their own read-eval-print loop (e.g. GUIs), not + # used in standard IPython flow. + # """ + # if line.lstrip() == line: + # self.shadowhist.add(line.strip()) + # lineout = self.prefilter_manager.prefilter_lines(line,self.more) + # + # if line.strip(): + # if self.more: + # self.input_hist_raw[-1] += '%s\n' % line + # else: + # self.input_hist_raw.append('%s\n' % line) + # + # + # self.more = self.push_line(lineout) + # if (self.SyntaxTB.last_syntax_error and + # self.autoedit_syntax): + # self.edit_syntax_error() + # + # def interact_with_readline(self): + # """ Demo of using interact_handle_input, interact_prompt + # + # This is the main read-eval-print loop. If you need to implement your own (e.g. for GUI), + # it should work like this. + # """ + # self.readline_startup_hook(self.pre_readline) + # while not self.exit_now: + # self.interact_prompt() + # if self.more: + # self.rl_do_indent = True + # else: + # self.rl_do_indent = False + # line = raw_input_original().decode(self.stdin_encoding) + # self.interact_handle_input(line) + + #------------------------------------------------------------------------- + # 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 ('','','', + '','', + 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() + + +InteractiveShellABC.register(TerminalInteractiveShell) diff --git a/IPython/core/ipapp.py b/IPython/frontend/terminal/ipapp.py similarity index 96% rename from IPython/core/ipapp.py rename to IPython/frontend/terminal/ipapp.py index 6733afe..d52052a 100755 --- a/IPython/core/ipapp.py +++ b/IPython/frontend/terminal/ipapp.py @@ -31,14 +31,14 @@ import sys from IPython.core import release from IPython.core.crashhandler import CrashHandler from IPython.core.application import Application, BaseAppConfigLoader -from IPython.core.iplib import InteractiveShell +from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell from IPython.config.loader import ( Config, PyFileConfigLoader ) from IPython.lib import inputhook from IPython.utils.path import filefind, get_ipython_dir -from . import usage +from IPython.core import usage #----------------------------------------------------------------------------- # Globals, utilities and helpers @@ -99,10 +99,10 @@ class IPAppConfigLoader(BaseAppConfigLoader): action='store_false', dest='InteractiveShell.automagic', help='Turn off the auto calling of magic commands.') paa('--autoedit-syntax', - action='store_true', dest='InteractiveShell.autoedit_syntax', + action='store_true', dest='TerminalInteractiveShell.autoedit_syntax', help='Turn on auto editing of files with syntax errors.') paa('--no-autoedit-syntax', - action='store_false', dest='InteractiveShell.autoedit_syntax', + action='store_false', dest='TerminalInteractiveShell.autoedit_syntax', help='Turn off auto editing of files with syntax errors.') paa('--banner', action='store_true', dest='Global.display_banner', @@ -143,13 +143,13 @@ class IPAppConfigLoader(BaseAppConfigLoader): action='store_false', dest='InteractiveShell.color_info', help="Disable using colors for info related things.") paa('--confirm-exit', - action='store_true', dest='InteractiveShell.confirm_exit', + action='store_true', dest='TerminalInteractiveShell.confirm_exit', help= """Set to confirm when you try to exit IPython with an EOF (Control-D in Unix, Control-Z/Enter in Windows). By typing 'exit', 'quit' or '%%Exit', you can force a direct exit without any confirmation.""") paa('--no-confirm-exit', - action='store_false', dest='InteractiveShell.confirm_exit', + action='store_false', dest='TerminalInteractiveShell.confirm_exit', help="Don't prompt the user when exiting.") paa('--deep-reload', action='store_true', dest='InteractiveShell.deep_reload', @@ -167,9 +167,9 @@ class IPAppConfigLoader(BaseAppConfigLoader): action='store_false', dest='InteractiveShell.deep_reload', help="Disable deep (recursive) reloading by default.") paa('--editor', - type=str, dest='InteractiveShell.editor', + type=str, dest='TerminalInteractiveShell.editor', help="Set the editor used by IPython (default to $EDITOR/vi/notepad).", - metavar='InteractiveShell.editor') + metavar='TerminalInteractiveShell.editor') paa('--log','-l', action='store_true', dest='InteractiveShell.logstart', help="Start logging to the default log file (./ipython_log.py).") @@ -228,7 +228,7 @@ class IPAppConfigLoader(BaseAppConfigLoader): action='store_false', dest='InteractiveShell.readline_use', help="Disable readline for command line usage.") paa('--screen-length','-sl', - type=int, dest='InteractiveShell.screen_length', + type=int, dest='TerminalInteractiveShell.screen_length', help= """Number of lines of your screen, used to control printing of very long strings. Strings longer than this number of lines will be sent @@ -239,7 +239,7 @@ class IPAppConfigLoader(BaseAppConfigLoader): internally). If for some reason this isn't working well (it needs curses support), specify it yourself. Otherwise don't change the default.""", - metavar='InteractiveShell.screen_length') + metavar='TerminalInteractiveShell.screen_length') paa('--separate-in','-si', type=str, dest='InteractiveShell.separate_in', help="Separator before input prompts. Default '\\n'.", @@ -256,10 +256,10 @@ class IPAppConfigLoader(BaseAppConfigLoader): action='store_true', dest='Global.nosep', help="Eliminate all spacing between prompts.") paa('--term-title', - action='store_true', dest='InteractiveShell.term_title', + action='store_true', dest='TerminalInteractiveShell.term_title', help="Enable auto setting the terminal title.") paa('--no-term-title', - action='store_false', dest='InteractiveShell.term_title', + action='store_false', dest='TerminalInteractiveShell.term_title', help="Disable auto setting the terminal title.") paa('--xmode', type=str, dest='InteractiveShell.xmode', @@ -476,7 +476,7 @@ class IPythonApp(Application): sys.path.insert(0, '') # Create an InteractiveShell instance. - self.shell = InteractiveShell.instance(config=self.master_config) + self.shell = TerminalInteractiveShell.instance(config=self.master_config) def post_construct(self): """Do actions after construct, but before starting the app.""" diff --git a/IPython/frontend/terminal/scripts/__init__.py b/IPython/frontend/terminal/scripts/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/IPython/frontend/terminal/scripts/__init__.py diff --git a/IPython/scripts/ipython b/IPython/frontend/terminal/scripts/ipython similarity index 75% rename from IPython/scripts/ipython rename to IPython/frontend/terminal/scripts/ipython index 65fec55..5a3546b 100755 --- a/IPython/scripts/ipython +++ b/IPython/frontend/terminal/scripts/ipython @@ -1,6 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from IPython.core.ipapp import launch_new_instance +from IPython.frontend.terminal.ipapp import launch_new_instance launch_new_instance() diff --git a/IPython/frontend/terminal/tests/__init__.py b/IPython/frontend/terminal/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/IPython/frontend/terminal/tests/__init__.py diff --git a/IPython/kernel/configobjfactory.py b/IPython/kernel/configobjfactory.py index b16dbfc..cd4d804 100644 --- a/IPython/kernel/configobjfactory.py +++ b/IPython/kernel/configobjfactory.py @@ -32,7 +32,7 @@ class IConfiguredObjectFactory(zi.Interface): subclass of :class:`IPython.config.configurable.Configurable`. """ - def __init__(config): + def __init__(config=None): """Get ready to configure the object using config.""" def create(): diff --git a/IPython/kernel/core/prompts.py b/IPython/kernel/core/prompts.py index fc583ea..ab36fa1 100644 --- a/IPython/kernel/core/prompts.py +++ b/IPython/kernel/core/prompts.py @@ -32,7 +32,7 @@ from IPython.external.Itpl import ItplNS from IPython.utils import coloransi from IPython.core import release from IPython.core.error import TryNext -from IPython.utils.io import Term +import IPython.utils.io from IPython.utils.warn import warn import IPython.utils.generics diff --git a/IPython/kernel/fcutil.py b/IPython/kernel/fcutil.py index b2fb012..624eb2c 100644 --- a/IPython/kernel/fcutil.py +++ b/IPython/kernel/fcutil.py @@ -212,8 +212,8 @@ class FCServiceFactory(AdaptedConfiguredObjectFactory): reuse_furls = Bool(False, config=True) interfaces = Instance(klass=Config, kw={}, allow_none=False, config=True) - def __init__(self, config, adaptee): - super(FCServiceFactory, self).__init__(config, adaptee) + def __init__(self, config=None, adaptee=None): + super(FCServiceFactory, self).__init__(config=config, adaptee=adaptee) self._check_reuse_furls() def _ip_changed(self, name, old, new): diff --git a/IPython/kernel/ipcontrollerapp.py b/IPython/kernel/ipcontrollerapp.py index 37cf583..24fb9a9 100755 --- a/IPython/kernel/ipcontrollerapp.py +++ b/IPython/kernel/ipcontrollerapp.py @@ -225,7 +225,7 @@ class IPControllerApp(ApplicationWithClusterDir): controller_service.setServiceParent(self.main_service) # The client tub and all its refereceables try: - csfactory = FCClientServiceFactory(self.master_config, controller_service) + csfactory = FCClientServiceFactory(config=self.master_config, adaptee=controller_service) except FURLError, e: log.err(e) self.exit(0) @@ -233,7 +233,7 @@ class IPControllerApp(ApplicationWithClusterDir): client_service.setServiceParent(self.main_service) # The engine tub try: - esfactory = FCEngineServiceFactory(self.master_config, controller_service) + esfactory = FCEngineServiceFactory(config=self.master_config, adaptee=controller_service) except FURLError, e: log.err(e) self.exit(0) diff --git a/IPython/lib/demo.py b/IPython/lib/demo.py index 398eca3..580588f 100644 --- a/IPython/lib/demo.py +++ b/IPython/lib/demo.py @@ -176,7 +176,8 @@ import shlex import sys from IPython.utils.PyColorize import Parser -from IPython.utils.io import file_read, file_readlines, Term +from IPython.utils.io import file_read, file_readlines +import IPython.utils.io from IPython.utils.text import marquee __all__ = ['Demo','IPythonDemo','LineDemo','IPythonLineDemo','DemoError'] @@ -318,7 +319,7 @@ class Demo(object): if index is None: if self.finished: - print >>Term.cout, 'Demo finished. Use .reset() if you want to rerun it.' + print >>IPython.utils.io.Term.cout, 'Demo finished. Use .reset() if you want to rerun it.' return None index = self.block_index else: @@ -387,9 +388,9 @@ class Demo(object): if index is None: return - print >>Term.cout, self.marquee('<%s> block # %s (%s remaining)' % + print >>IPython.utils.io.Term.cout, self.marquee('<%s> block # %s (%s remaining)' % (self.title,index,self.nblocks-index-1)) - print >>Term.cout,(self.src_blocks_colored[index]) + print >>IPython.utils.io.Term.cout,(self.src_blocks_colored[index]) sys.stdout.flush() def show_all(self): @@ -402,12 +403,12 @@ class Demo(object): marquee = self.marquee for index,block in enumerate(self.src_blocks_colored): if silent[index]: - print >>Term.cout, marquee('<%s> SILENT block # %s (%s remaining)' % + print >>IPython.utils.io.Term.cout, marquee('<%s> SILENT block # %s (%s remaining)' % (title,index,nblocks-index-1)) else: - print >>Term.cout, marquee('<%s> block # %s (%s remaining)' % + print >>IPython.utils.io.Term.cout, marquee('<%s> block # %s (%s remaining)' % (title,index,nblocks-index-1)) - print >>Term.cout, block, + print >>IPython.utils.io.Term.cout, block, sys.stdout.flush() def runlines(self,source): @@ -432,18 +433,18 @@ class Demo(object): next_block = self.src_blocks[index] self.block_index += 1 if self._silent[index]: - print >>Term.cout, marquee('Executing silent block # %s (%s remaining)' % + print >>IPython.utils.io.Term.cout, marquee('Executing silent block # %s (%s remaining)' % (index,self.nblocks-index-1)) else: self.pre_cmd() self.show(index) if self.auto_all or self._auto[index]: - print >>Term.cout, marquee('output:') + print >>IPython.utils.io.Term.cout, marquee('output:') else: - print >>Term.cout, marquee('Press to quit, to execute...'), + print >>IPython.utils.io.Term.cout, marquee('Press to quit, to execute...'), ans = raw_input().strip() if ans: - print >>Term.cout, marquee('Block NOT executed') + print >>IPython.utils.io.Term.cout, marquee('Block NOT executed') return try: save_argv = sys.argv @@ -461,10 +462,10 @@ class Demo(object): if self.block_index == self.nblocks: mq1 = self.marquee('END OF DEMO') if mq1: - # avoid spurious print >>Term.cout,s if empty marquees are used - print >>Term.cout - print >>Term.cout, mq1 - print >>Term.cout, self.marquee('Use .reset() if you want to rerun it.') + # avoid spurious print >>IPython.utils.io.Term.cout,s if empty marquees are used + print >>IPython.utils.io.Term.cout + print >>IPython.utils.io.Term.cout, mq1 + print >>IPython.utils.io.Term.cout, self.marquee('Use .reset() if you want to rerun it.') self.finished = True # These methods are meant to be overridden by subclasses who may wish to diff --git a/IPython/testing/globalipapp.py b/IPython/testing/globalipapp.py index efae6e7..d508d55 100644 --- a/IPython/testing/globalipapp.py +++ b/IPython/testing/globalipapp.py @@ -114,8 +114,7 @@ def start_ipython(): return start_ipython.already_called = True - # Ok, first time we're called, go ahead - from IPython.core import iplib + from IPython.frontend.terminal import interactiveshell def xsys(cmd): """Execute a command and print its output. @@ -136,7 +135,7 @@ def start_ipython(): config = tools.default_config() # Create and initialize our test-friendly IPython instance. - shell = iplib.InteractiveShell.instance( + shell = interactiveshell.TerminalInteractiveShell.instance( config=config, user_ns=ipnsdict(), user_global_ns={} ) diff --git a/IPython/testing/iptest.py b/IPython/testing/iptest.py index 76ac761..a39e53b 100644 --- a/IPython/testing/iptest.py +++ b/IPython/testing/iptest.py @@ -153,10 +153,6 @@ def make_exclude(): ipjoin = lambda *paths: pjoin('IPython', *paths) exclusions = [ipjoin('external'), - # Deprecated old Shell and iplib modules, skip to avoid - # warnings - ipjoin('Shell'), - ipjoin('iplib'), pjoin('IPython_doctest_plugin'), ipjoin('quarantine'), ipjoin('deathrow'), diff --git a/IPython/testing/plugin/Makefile b/IPython/testing/plugin/Makefile index db82075..6f999a3 100644 --- a/IPython/testing/plugin/Makefile +++ b/IPython/testing/plugin/Makefile @@ -8,7 +8,7 @@ NOSE=nosetests -vvs --with-ipdoctest --doctest-tests --doctest-extension=txt \ SRC=ipdoctest.py setup.py ../decorators.py # Default target for clean 'make' -default: iplib +default: interactiveshell # The actual plugin installation plugin: IPython_doctest_plugin.egg-info @@ -39,8 +39,8 @@ magic: plugin excolors: plugin $(NOSE) IPython.core.excolors -iplib: plugin - $(NOSE) IPython.core.iplib +interactiveshell: plugin + $(NOSE) IPython.core.interactiveshell strd: plugin $(NOSE) IPython.core.strdispatch @@ -61,7 +61,7 @@ sr: rtest strd base: dtest rtest test strd deco -quick: base iplib ipipe +quick: base interactiveshell ipipe all: base ipython diff --git a/IPython/testing/tools.py b/IPython/testing/tools.py index a6dc9da..8622a6b 100644 --- a/IPython/testing/tools.py +++ b/IPython/testing/tools.py @@ -164,9 +164,9 @@ def default_argv(): def default_config(): """Return a config object with good defaults for testing.""" config = Config() - config.InteractiveShell.colors = 'NoColor' - config.InteractiveShell.term_title = False, - config.InteractiveShell.autocall = 0 + config.TerminalInteractiveShell.colors = 'NoColor' + config.TerminalTerminalInteractiveShell.term_title = False, + config.TerminalInteractiveShell.autocall = 0 return config diff --git a/IPython/utils/io.py b/IPython/utils/io.py index 30408e2..e38445b 100644 --- a/IPython/utils/io.py +++ b/IPython/utils/io.py @@ -65,21 +65,10 @@ class IOTerm: # In the future, having IPython channel all its I/O operations through # this class will make it easier to embed it into other environments which # are not a normal terminal (such as a GUI-based shell) - def __init__(self,cin=None,cout=None,cerr=None): - self.cin = IOStream(cin,sys.stdin) - self.cout = IOStream(cout,sys.stdout) - self.cerr = IOStream(cerr,sys.stderr) - - -# Global variable to be used for all I/O -Term = IOTerm() - - -import IPython.utils.rlineimpl as readline -# Remake Term to use the readline i/o facilities -if sys.platform == 'win32' and readline.have_readline: - - Term = IOTerm(cout=readline._outputfile,cerr=readline._outputfile) + def __init__(self, cin=None, cout=None, cerr=None): + self.cin = IOStream(cin, sys.stdin) + self.cout = IOStream(cout, sys.stdout) + self.cerr = IOStream(cerr, sys.stderr) class Tee(object): diff --git a/IPython/utils/process.py b/IPython/utils/process.py index 2d8919c..3a2ac78 100644 --- a/IPython/utils/process.py +++ b/IPython/utils/process.py @@ -77,7 +77,7 @@ def find_cmd(cmd): from IPython.utils.path import get_ipython_module_path from IPython.utils.process import pycmd2argv - argv = pycmd2argv(get_ipython_module_path('IPython.core.ipapp')) + argv = pycmd2argv(get_ipython_module_path('IPython.frontend.terminal.ipapp')) Parameters ---------- diff --git a/IPython/utils/tests/test_path.py b/IPython/utils/tests/test_path.py index 1715960..db9a8a8 100644 --- a/IPython/utils/tests/test_path.py +++ b/IPython/utils/tests/test_path.py @@ -255,7 +255,7 @@ def test_get_ipython_package_dir(): def test_get_ipython_module_path(): - ipapp_path = path.get_ipython_module_path('IPython.core.ipapp') + ipapp_path = path.get_ipython_module_path('IPython.frontend.terminal.ipapp') nt.assert_true(os.path.isfile(ipapp_path)) diff --git a/IPython/utils/text.py b/IPython/utils/text.py index e354574..169a0e4 100644 --- a/IPython/utils/text.py +++ b/IPython/utils/text.py @@ -471,3 +471,14 @@ def marquee(txt='',width=78,mark='*'): return '%s %s %s' % (marks,txt,marks) +ini_spaces_re = re.compile(r'^(\s+)') + +def num_ini_spaces(strng): + """Return the number of initial spaces in a string""" + + ini_spaces = ini_spaces_re.match(strng) + if ini_spaces: + return ini_spaces.end() + else: + return 0 + diff --git a/IPython/utils/warn.py b/IPython/utils/warn.py index 772faed..bc9edd3 100644 --- a/IPython/utils/warn.py +++ b/IPython/utils/warn.py @@ -16,7 +16,7 @@ Utilities for warnings. Shoudn't we just use the built in warnings module. import sys -from IPython.utils.io import Term +import IPython.utils.io #----------------------------------------------------------------------------- # Code @@ -25,7 +25,7 @@ from IPython.utils.io import Term def warn(msg,level=2,exit_val=1): """Standard warning printer. Gives formatting consistency. - Output is sent to Term.cerr (sys.stderr by default). + Output is sent to IPython.utils.io.Term.cerr (sys.stderr by default). Options: @@ -41,9 +41,9 @@ def warn(msg,level=2,exit_val=1): if level>0: header = ['','','WARNING: ','ERROR: ','FATAL ERROR: '] - print >> Term.cerr, '%s%s' % (header[level],msg) + print >> IPython.utils.io.Term.cerr, '%s%s' % (header[level],msg) if level == 4: - print >> Term.cerr,'Exiting.\n' + print >> IPython.utils.io.Term.cerr,'Exiting.\n' sys.exit(exit_val) diff --git a/IPython/zmq/displayhook.py b/IPython/zmq/displayhook.py new file mode 100644 index 0000000..f37f30a --- /dev/null +++ b/IPython/zmq/displayhook.py @@ -0,0 +1,22 @@ +import __builtin__ + +from session import extract_header + +class DisplayHook(object): + + def __init__(self, session, pub_socket): + self.session = session + self.pub_socket = pub_socket + self.parent_header = {} + + def __call__(self, obj): + if obj is None: + return + + __builtin__._ = obj + msg = self.session.msg(u'pyout', {u'data':repr(obj)}, + parent=self.parent_header) + self.pub_socket.send_json(msg) + + def set_parent(self, parent): + self.parent_header = extract_header(parent) \ No newline at end of file diff --git a/IPython/zmq/exitpoller.py b/IPython/zmq/exitpoller.py new file mode 100644 index 0000000..6f55703 --- /dev/null +++ b/IPython/zmq/exitpoller.py @@ -0,0 +1,42 @@ +import os +import time +from threading import Thread + + +class ExitPollerUnix(Thread): + """ A Unix-specific daemon thread that terminates the program immediately + when the parent process no longer exists. + """ + + def __init__(self): + super(ExitPollerUnix, self).__init__() + self.daemon = True + + def run(self): + # We cannot use os.waitpid because it works only for child processes. + from errno import EINTR + while True: + try: + if os.getppid() == 1: + os._exit(1) + time.sleep(1.0) + except OSError, e: + if e.errno == EINTR: + continue + raise + +class ExitPollerWindows(Thread): + """ A Windows-specific daemon thread that terminates the program immediately + when a Win32 handle is signaled. + """ + + def __init__(self, handle): + super(ExitPollerWindows, self).__init__() + self.daemon = True + self.handle = handle + + def run(self): + from _subprocess import WaitForSingleObject, WAIT_OBJECT_0, INFINITE + result = WaitForSingleObject(self.handle, INFINITE) + if result == WAIT_OBJECT_0: + os._exit(1) \ No newline at end of file diff --git a/IPython/zmq/iostream.py b/IPython/zmq/iostream.py new file mode 100644 index 0000000..8ba2d7a --- /dev/null +++ b/IPython/zmq/iostream.py @@ -0,0 +1,77 @@ +import sys +import time +from cStringIO import StringIO + +from session import extract_header, Message + +#----------------------------------------------------------------------------- +# Stream classes +#----------------------------------------------------------------------------- + +class OutStream(object): + """A file like object that publishes the stream to a 0MQ PUB socket.""" + + # The time interval between automatic flushes, in seconds. + flush_interval = 0.05 + + def __init__(self, session, pub_socket, name): + self.session = session + self.pub_socket = pub_socket + self.name = name + self.parent_header = {} + self._new_buffer() + + def set_parent(self, parent): + self.parent_header = extract_header(parent) + + def close(self): + self.pub_socket = None + + def flush(self): + if self.pub_socket is None: + raise ValueError(u'I/O operation on closed file') + else: + data = self._buffer.getvalue() + if data: + content = {u'name':self.name, u'data':data} + msg = self.session.msg(u'stream', content=content, + parent=self.parent_header) + print>>sys.__stdout__, Message(msg) + self.pub_socket.send_json(msg) + + self._buffer.close() + self._new_buffer() + + def isatty(self): + return False + + def next(self): + raise IOError('Read not supported on a write only stream.') + + def read(self, size=-1): + raise IOError('Read not supported on a write only stream.') + + def readline(self, size=-1): + raise IOError('Read not supported on a write only stream.') + + def write(self, string): + if self.pub_socket is None: + raise ValueError('I/O operation on closed file') + else: + self._buffer.write(string) + current_time = time.time() + if self._start <= 0: + self._start = current_time + elif current_time - self._start > self.flush_interval: + self.flush() + + def writelines(self, sequence): + if self.pub_socket is None: + raise ValueError('I/O operation on closed file') + else: + for string in sequence: + self.write(string) + + def _new_buffer(self): + self._buffer = StringIO() + self._start = -1 \ No newline at end of file diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py new file mode 100755 index 0000000..82a048c --- /dev/null +++ b/IPython/zmq/ipkernel.py @@ -0,0 +1,367 @@ +#!/usr/bin/env python +"""A simple interactive kernel that talks to a frontend over 0MQ. + +Things to do: + +* Implement `set_parent` logic. Right before doing exec, the Kernel should + call set_parent on all the PUB objects with the message about to be executed. +* Implement random port and security key logic. +* Implement control messages. +* Implement event loop and poll version. +""" + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +# Standard library imports. +import __builtin__ +from code import CommandCompiler +import os +import sys +import time +import traceback + +# System library imports. +import zmq + +# Local imports. +from IPython.config.configurable import Configurable +from IPython.zmq.zmqshell import ZMQInteractiveShell +from IPython.external.argparse import ArgumentParser +from IPython.utils.traitlets import Instance +from IPython.zmq.session import Session, Message +from completer import KernelCompleter +from iostream import OutStream +from displayhook import DisplayHook +from exitpoller import ExitPollerUnix, ExitPollerWindows + +#----------------------------------------------------------------------------- +# Main kernel class +#----------------------------------------------------------------------------- + +class Kernel(Configurable): + + shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') + session = Instance('IPython.zmq.session.Session') + reply_socket = Instance('zmq.Socket') + pub_socket = Instance('zmq.Socket') + req_socket = Instance('zmq.Socket') + + def __init__(self, **kwargs): + super(Kernel, self).__init__(**kwargs) + self.shell = ZMQInteractiveShell.instance() + + # Build dict of handlers for message types + msg_types = [ 'execute_request', 'complete_request', + 'object_info_request' ] + self.handlers = {} + for msg_type in msg_types: + self.handlers[msg_type] = getattr(self, msg_type) + + def abort_queue(self): + while True: + try: + ident = self.reply_socket.recv(zmq.NOBLOCK) + except zmq.ZMQError, e: + if e.errno == zmq.EAGAIN: + break + else: + assert self.reply_socket.rcvmore(), "Unexpected missing message part." + msg = self.reply_socket.recv_json() + print>>sys.__stdout__, "Aborting:" + print>>sys.__stdout__, Message(msg) + msg_type = msg['msg_type'] + reply_type = msg_type.split('_')[0] + '_reply' + reply_msg = self.session.msg(reply_type, {'status' : 'aborted'}, msg) + print>>sys.__stdout__, Message(reply_msg) + self.reply_socket.send(ident,zmq.SNDMORE) + self.reply_socket.send_json(reply_msg) + # We need to wait a bit for requests to come in. This can probably + # be set shorter for true asynchronous clients. + time.sleep(0.1) + + def execute_request(self, ident, parent): + try: + code = parent[u'content'][u'code'] + except: + print>>sys.__stderr__, "Got bad msg: " + print>>sys.__stderr__, Message(parent) + return + pyin_msg = self.session.msg(u'pyin',{u'code':code}, parent=parent) + self.pub_socket.send_json(pyin_msg) + + try: + # Replace raw_input. Note that is not sufficient to replace + # raw_input in the user namespace. + raw_input = lambda prompt='': self.raw_input(prompt, ident, parent) + __builtin__.raw_input = raw_input + + # Configure the display hook. + sys.displayhook.set_parent(parent) + + self.shell.runlines(code) + # exec comp_code in self.user_ns, self.user_ns + except: + etype, evalue, tb = sys.exc_info() + tb = traceback.format_exception(etype, evalue, tb) + exc_content = { + u'status' : u'error', + u'traceback' : tb, + u'ename' : unicode(etype.__name__), + u'evalue' : unicode(evalue) + } + exc_msg = self.session.msg(u'pyerr', exc_content, parent) + self.pub_socket.send_json(exc_msg) + reply_content = exc_content + else: + reply_content = {'status' : 'ok'} + + # Flush output before sending the reply. + sys.stderr.flush() + sys.stdout.flush() + + # Send the reply. + reply_msg = self.session.msg(u'execute_reply', reply_content, parent) + print>>sys.__stdout__, Message(reply_msg) + self.reply_socket.send(ident, zmq.SNDMORE) + self.reply_socket.send_json(reply_msg) + if reply_msg['content']['status'] == u'error': + self.abort_queue() + + def raw_input(self, prompt, ident, parent): + # Flush output before making the request. + sys.stderr.flush() + sys.stdout.flush() + + # Send the input request. + content = dict(prompt=prompt) + msg = self.session.msg(u'input_request', content, parent) + self.req_socket.send_json(msg) + + # Await a response. + reply = self.req_socket.recv_json() + try: + value = reply['content']['value'] + except: + print>>sys.__stderr__, "Got bad raw_input reply: " + print>>sys.__stderr__, Message(parent) + value = '' + return value + + def complete_request(self, ident, parent): + matches = {'matches' : self.complete(parent), + 'status' : 'ok'} + completion_msg = self.session.send(self.reply_socket, 'complete_reply', + matches, parent, ident) + print >> sys.__stdout__, completion_msg + + def complete(self, msg): + return self.shell.complete(msg.content.line) + + def object_info_request(self, ident, parent): + context = parent['content']['oname'].split('.') + object_info = self.object_info(context) + msg = self.session.send(self.reply_socket, 'object_info_reply', + object_info, parent, ident) + print >> sys.__stdout__, msg + + def object_info(self, context): + symbol, leftover = self.symbol_from_context(context) + if symbol is not None and not leftover: + doc = getattr(symbol, '__doc__', '') + else: + doc = '' + object_info = dict(docstring = doc) + return object_info + + def symbol_from_context(self, context): + if not context: + return None, context + + base_symbol_string = context[0] + symbol = self.shell.user_ns.get(base_symbol_string, None) + if symbol is None: + symbol = __builtin__.__dict__.get(base_symbol_string, None) + if symbol is None: + return None, context + + context = context[1:] + for i, name in enumerate(context): + new_symbol = getattr(symbol, name, None) + if new_symbol is None: + return symbol, context[i:] + else: + symbol = new_symbol + + return symbol, [] + + def start(self): + while True: + ident = self.reply_socket.recv() + assert self.reply_socket.rcvmore(), "Missing message part." + msg = self.reply_socket.recv_json() + omsg = Message(msg) + print>>sys.__stdout__ + print>>sys.__stdout__, omsg + handler = self.handlers.get(omsg.msg_type, None) + if handler is None: + print >> sys.__stderr__, "UNKNOWN MESSAGE TYPE:", omsg + else: + handler(ident, omsg) + +#----------------------------------------------------------------------------- +# Kernel main and launch functions +#----------------------------------------------------------------------------- + +def bind_port(socket, ip, port): + """ Binds the specified ZMQ socket. If the port is less than zero, a random + port is chosen. Returns the port that was bound. + """ + connection = 'tcp://%s' % ip + if port <= 0: + port = socket.bind_to_random_port(connection) + else: + connection += ':%i' % port + socket.bind(connection) + return port + + +def main(): + """ Main entry point for launching a kernel. + """ + # Parse command line arguments. + parser = ArgumentParser() + parser.add_argument('--ip', type=str, default='127.0.0.1', + help='set the kernel\'s IP address [default: local]') + parser.add_argument('--xrep', type=int, metavar='PORT', default=0, + help='set the XREP channel port [default: random]') + parser.add_argument('--pub', type=int, metavar='PORT', default=0, + help='set the PUB channel port [default: random]') + parser.add_argument('--req', type=int, metavar='PORT', default=0, + help='set the REQ channel port [default: random]') + if sys.platform == 'win32': + parser.add_argument('--parent', type=int, metavar='HANDLE', + default=0, help='kill this process if the process ' + 'with HANDLE dies') + else: + parser.add_argument('--parent', action='store_true', + help='kill this process if its parent dies') + namespace = parser.parse_args() + + # Create a context, a session, and the kernel sockets. + print >>sys.__stdout__, "Starting the kernel..." + context = zmq.Context() + session = Session(username=u'kernel') + + reply_socket = context.socket(zmq.XREP) + xrep_port = bind_port(reply_socket, namespace.ip, namespace.xrep) + print >>sys.__stdout__, "XREP Channel on port", xrep_port + + pub_socket = context.socket(zmq.PUB) + pub_port = bind_port(pub_socket, namespace.ip, namespace.pub) + print >>sys.__stdout__, "PUB Channel on port", pub_port + + req_socket = context.socket(zmq.XREQ) + req_port = bind_port(req_socket, namespace.ip, namespace.req) + print >>sys.__stdout__, "REQ Channel on port", req_port + + # Redirect input streams. This needs to be done before the Kernel is done + # because currently the Kernel creates a ZMQInteractiveShell, which + # holds references to sys.stdout and sys.stderr. + sys.stdout = OutStream(session, pub_socket, u'stdout') + sys.stderr = OutStream(session, pub_socket, u'stderr') + # Set a displayhook. + sys.displayhook = DisplayHook(session, pub_socket) + + # Create the kernel. + kernel = Kernel( + session=session, reply_socket=reply_socket, + pub_socket=pub_socket, req_socket=req_socket + ) + + # Configure this kernel/process to die on parent termination, if necessary. + if namespace.parent: + if sys.platform == 'win32': + poller = ExitPollerWindows(namespace.parent) + else: + poller = ExitPollerUnix() + poller.start() + + # Start the kernel mainloop. + kernel.start() + + +def launch_kernel(xrep_port=0, pub_port=0, req_port=0, independent=False): + """ Launches a localhost kernel, binding to the specified ports. + + Parameters + ---------- + xrep_port : int, optional + The port to use for XREP channel. + + pub_port : int, optional + The port to use for the SUB channel. + + req_port : int, optional + The port to use for the REQ (raw input) channel. + + independent : bool, optional (default False) + If set, the kernel process is guaranteed to survive if this process + dies. If not set, an effort is made to ensure that the kernel is killed + when this process dies. Note that in this case it is still good practice + to kill kernels manually before exiting. + + Returns + ------- + A tuple of form: + (kernel_process, xrep_port, pub_port, req_port) + where kernel_process is a Popen object and the ports are integers. + """ + import socket + from subprocess import Popen + + # Find open ports as necessary. + ports = [] + ports_needed = int(xrep_port <= 0) + int(pub_port <= 0) + int(req_port <= 0) + for i in xrange(ports_needed): + sock = socket.socket() + sock.bind(('', 0)) + ports.append(sock) + for i, sock in enumerate(ports): + port = sock.getsockname()[1] + sock.close() + ports[i] = port + if xrep_port <= 0: + xrep_port = ports.pop(0) + if pub_port <= 0: + pub_port = ports.pop(0) + if req_port <= 0: + req_port = ports.pop(0) + + # Spawn a kernel. + command = 'from IPython.zmq.ipkernel import main; main()' + arguments = [ sys.executable, '-c', command, '--xrep', str(xrep_port), + '--pub', str(pub_port), '--req', str(req_port) ] + if independent: + if sys.platform == 'win32': + proc = Popen(['start', '/b'] + arguments, shell=True) + else: + proc = Popen(arguments, preexec_fn=lambda: os.setsid()) + else: + if sys.platform == 'win32': + from _subprocess import DuplicateHandle, GetCurrentProcess, \ + DUPLICATE_SAME_ACCESS + pid = GetCurrentProcess() + handle = DuplicateHandle(pid, pid, pid, 0, + True, # Inheritable by new processes. + DUPLICATE_SAME_ACCESS) + proc = Popen(arguments + ['--parent', str(int(handle))]) + else: + proc = Popen(arguments + ['--parent']) + + return proc, xrep_port, pub_port, req_port + + +if __name__ == '__main__': + main() diff --git a/IPython/zmq/kernelmanager.py b/IPython/zmq/kernelmanager.py index 7becccc..2efc1da 100644 --- a/IPython/zmq/kernelmanager.py +++ b/IPython/zmq/kernelmanager.py @@ -30,7 +30,7 @@ from zmq.eventloop import ioloop # Local imports. from IPython.utils.traitlets import HasTraits, Any, Instance, Type, TCPAddress -from kernel import launch_kernel +from ipkernel import launch_kernel from session import Session #----------------------------------------------------------------------------- diff --git a/IPython/zmq/kernel.py b/IPython/zmq/pykernel.py similarity index 62% rename from IPython/zmq/kernel.py rename to IPython/zmq/pykernel.py index 46d1956..a4856f5 100755 --- a/IPython/zmq/kernel.py +++ b/IPython/zmq/pykernel.py @@ -3,7 +3,6 @@ Things to do: -* Finish implementing `raw_input`. * Implement `set_parent` logic. Right before doing exec, the Kernel should call set_parent on all the PUB objects with the message about to be executed. * Implement random port and security key logic. @@ -18,10 +17,8 @@ Things to do: # Standard library imports. import __builtin__ from code import CommandCompiler -from cStringIO import StringIO import os import sys -from threading import Thread import time import traceback @@ -30,100 +27,16 @@ import zmq # Local imports. from IPython.external.argparse import ArgumentParser -from session import Session, Message, extract_header +from session import Session, Message from completer import KernelCompleter +from iostream import OutStream +from displayhook import DisplayHook +from exitpoller import ExitPollerUnix, ExitPollerWindows #----------------------------------------------------------------------------- -# Kernel and stream classes +# Main kernel class #----------------------------------------------------------------------------- -class OutStream(object): - """A file like object that publishes the stream to a 0MQ PUB socket.""" - - # The time interval between automatic flushes, in seconds. - flush_interval = 0.05 - - def __init__(self, session, pub_socket, name): - self.session = session - self.pub_socket = pub_socket - self.name = name - self.parent_header = {} - self._new_buffer() - - def set_parent(self, parent): - self.parent_header = extract_header(parent) - - def close(self): - self.pub_socket = None - - def flush(self): - if self.pub_socket is None: - raise ValueError(u'I/O operation on closed file') - else: - data = self._buffer.getvalue() - if data: - content = {u'name':self.name, u'data':data} - msg = self.session.msg(u'stream', content=content, - parent=self.parent_header) - print>>sys.__stdout__, Message(msg) - self.pub_socket.send_json(msg) - - self._buffer.close() - self._new_buffer() - - def isatty(self): - return False - - def next(self): - raise IOError('Read not supported on a write only stream.') - - def read(self, size=-1): - raise IOError('Read not supported on a write only stream.') - - def readline(self, size=-1): - raise IOError('Read not supported on a write only stream.') - - def write(self, string): - if self.pub_socket is None: - raise ValueError('I/O operation on closed file') - else: - self._buffer.write(string) - current_time = time.time() - if self._start <= 0: - self._start = current_time - elif current_time - self._start > self.flush_interval: - self.flush() - - def writelines(self, sequence): - if self.pub_socket is None: - raise ValueError('I/O operation on closed file') - else: - for string in sequence: - self.write(string) - - def _new_buffer(self): - self._buffer = StringIO() - self._start = -1 - - -class DisplayHook(object): - - def __init__(self, session, pub_socket): - self.session = session - self.pub_socket = pub_socket - self.parent_header = {} - - def __call__(self, obj): - if obj is not None: - __builtin__._ = obj - msg = self.session.msg(u'pyout', {u'data':repr(obj)}, - parent=self.parent_header) - self.pub_socket.send_json(msg) - - def set_parent(self, parent): - self.parent_header = extract_header(parent) - - class Kernel(object): # The global kernel instance. @@ -267,7 +180,6 @@ class Kernel(object): exec comp_code in self.user_ns, self.user_ns except: - result = u'error' etype, evalue, tb = sys.exc_info() tb = traceback.format_exception(etype, evalue, tb) exc_content = { @@ -388,45 +300,6 @@ class Kernel(object): # Kernel main and launch functions #----------------------------------------------------------------------------- -class ExitPollerUnix(Thread): - """ A Unix-specific daemon thread that terminates the program immediately - when the parent process no longer exists. - """ - - def __init__(self): - super(ExitPollerUnix, self).__init__() - self.daemon = True - - def run(self): - # We cannot use os.waitpid because it works only for child processes. - from errno import EINTR - while True: - try: - if os.getppid() == 1: - os._exit(1) - time.sleep(1.0) - except OSError, e: - if e.errno == EINTR: - continue - raise - -class ExitPollerWindows(Thread): - """ A Windows-specific daemon thread that terminates the program immediately - when a Win32 handle is signaled. - """ - - def __init__(self, handle): - super(ExitPollerWindows, self).__init__() - self.daemon = True - self.handle = handle - - def run(self): - from _subprocess import WaitForSingleObject, WAIT_OBJECT_0, INFINITE - result = WaitForSingleObject(self.handle, INFINITE) - if result == WAIT_OBJECT_0: - os._exit(1) - - def bind_port(socket, ip, port): """ Binds the specified ZMQ socket. If the port is zero, a random port is chosen. Returns the port that was bound. @@ -566,7 +439,7 @@ def launch_kernel(xrep_port=0, pub_port=0, req_port=0, req_port = ports.pop(0) # Build the kernel launch command. - command = 'from IPython.zmq.kernel import main; main()' + command = 'from IPython.zmq.pykernel import main; main()' arguments = [ sys.executable, '-c', command, '--xrep', str(xrep_port), '--pub', str(pub_port), '--req', str(req_port) ] if pylab: diff --git a/IPython/zmq/zmqshell.py b/IPython/zmq/zmqshell.py new file mode 100644 index 0000000..62ba5d0 --- /dev/null +++ b/IPython/zmq/zmqshell.py @@ -0,0 +1,31 @@ +import sys +from subprocess import Popen, PIPE +from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC + + +class ZMQInteractiveShell(InteractiveShell): + """A subclass of InteractiveShell for ZMQ.""" + + def system(self, cmd): + cmd = self.var_expand(cmd, depth=2) + sys.stdout.flush() + sys.stderr.flush() + p = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE) + for line in p.stdout.read().split('\n'): + if len(line) > 0: + print line + for line in p.stderr.read().split('\n'): + if len(line) > 0: + print line + return p.wait() + + def init_io(self): + # This will just use sys.stdout and sys.stderr. If you want to + # override sys.stdout and sys.stderr themselves, you need to do that + # *before* instantiating this class, because Term holds onto + # references to the underlying streams. + import IPython.utils.io + Term = IPython.utils.io.IOTerm() + IPython.utils.io.Term = Term + +InteractiveShellABC.register(ZMQInteractiveShell) diff --git a/ipython.py b/ipython.py index ae5c5ea..29db095 100755 --- a/ipython.py +++ b/ipython.py @@ -13,4 +13,6 @@ this_dir = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, this_dir) # Now proceed with execution -execfile(os.path.join(this_dir, 'IPython', 'scripts', 'ipython')) +execfile(os.path.join( + this_dir, 'IPython', 'frontend', 'terminal', 'scripts', 'ipython' +)) diff --git a/scripts/ipython_win_post_install.py b/scripts/ipython_win_post_install.py index a965ee8..606fd7f 100755 --- a/scripts/ipython_win_post_install.py +++ b/scripts/ipython_win_post_install.py @@ -35,8 +35,6 @@ def install(): 'ipcontroller', 'ipengine', 'ipcluster', - 'ipythonx', - 'ipython-wx', 'irunner' ] scripts = pjoin(prefix,'scripts') diff --git a/setup.py b/setup.py index 2798796..01174e1 100755 --- a/setup.py +++ b/setup.py @@ -207,12 +207,11 @@ if 'setuptools' in sys.modules: setuptools_extra_args['zip_safe'] = False setuptools_extra_args['entry_points'] = { 'console_scripts': [ - 'ipython = IPython.core.ipapp:launch_new_instance', + 'ipython = IPython.frontend.terminal.ipapp:launch_new_instance', 'pycolor = IPython.utils.PyColorize:main', 'ipcontroller = IPython.kernel.ipcontrollerapp:launch_new_instance', 'ipengine = IPython.kernel.ipengineapp:launch_new_instance', 'ipcluster = IPython.kernel.ipclusterapp:launch_new_instance', - 'ipythonx = IPython.frontend.wx.ipythonx:main', 'iptest = IPython.testing.iptest:main', 'irunner = IPython.lib.irunner:main' ] diff --git a/setupbase.py b/setupbase.py index 9542911..a4afdae 100644 --- a/setupbase.py +++ b/setupbase.py @@ -111,6 +111,7 @@ def find_packages(): add_package(packages, 'frontend') add_package(packages, 'frontend.qt') add_package(packages, 'frontend.qt.console') + add_package(packages, 'frontend.terminal', config=False, tests=True, scripts=True) add_package(packages, 'kernel', config=False, tests=True, scripts=True) add_package(packages, 'kernel.core', config=False, tests=True) add_package(packages, 'lib', tests=True) @@ -254,12 +255,11 @@ def find_scripts(): """ kernel_scripts = pjoin('IPython','kernel','scripts') main_scripts = pjoin('IPython','scripts') + frontend_terminal_scripts = pjoin('IPython','frontend','terminal','scripts') scripts = [pjoin(kernel_scripts, 'ipengine'), pjoin(kernel_scripts, 'ipcontroller'), pjoin(kernel_scripts, 'ipcluster'), - pjoin(main_scripts, 'ipython'), - pjoin(main_scripts, 'ipythonx'), - pjoin(main_scripts, 'ipython-wx'), + pjoin(frontend_terminal_scripts, 'ipython'), pjoin(main_scripts, 'pycolor'), pjoin(main_scripts, 'irunner'), pjoin(main_scripts, 'iptest')