class PrefilterManager(Configurable): """Main prefilter component. The IPython prefilter is run on all user input before it is run. The prefilter consumes lines of input and produces transformed lines of input. The iplementation consists of two phases: 1. Transformers 2. Checkers and handlers Over time, we plan on deprecating the checkers and handlers and doing everything in the transformers. The transformers are instances of :class:`PrefilterTransformer` and have a single method :meth:`transform` that takes a line and returns a transformed line. The transformation can be accomplished using any tool, but our current ones use regular expressions for speed. After all the transformers have been run, the line is fed to the checkers, which are instances of :class:`PrefilterChecker`. The line is passed to the :meth:`check` method, which either returns `None` or a :class:`PrefilterHandler` instance. If `None` is returned, the other checkers are tried. If an :class:`PrefilterHandler` instance is returned, the line is passed to the :meth:`handle` method of the returned handler and no further checkers are tried. Both transformers and checkers have a `priority` attribute, that determines the order in which they are called. Smaller priorities are tried first. Both transformers and checkers also have `enabled` attribute, which is a boolean that determines if the instance is used. Users or developers can change the priority or enabled attribute of transformers or checkers, but they must call the :meth:`sort_checkers` or :meth:`sort_transformers` method after changing the priority. """ multi_line_specials = CBool(True, config=True) shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') def __init__(self, shell=None, config=None): super(PrefilterManager, self).__init__(shell=shell, config=config) self.shell = shell self.init_transformers() self.init_handlers() self.init_checkers() #------------------------------------------------------------------------- # API for managing transformers #------------------------------------------------------------------------- def init_transformers(self): """Create the default transformers.""" self._transformers = [] for transformer_cls in _default_transformers: transformer_cls(shell=self.shell, prefilter_manager=self, config=self.config) def sort_transformers(self): """Sort the transformers by priority. This must be called after the priority of a transformer is changed. The :meth:`register_transformer` method calls this automatically. """ self._transformers.sort(key=lambda x: x.priority) @property def transformers(self): """Return a list of checkers, sorted by priority.""" return self._transformers def register_transformer(self, transformer): """Register a transformer instance.""" if transformer not in self._transformers: self._transformers.append(transformer) self.sort_transformers() def unregister_transformer(self, transformer): """Unregister a transformer instance.""" if transformer in self._transformers: self._transformers.remove(transformer) #------------------------------------------------------------------------- # API for managing checkers #------------------------------------------------------------------------- def init_checkers(self): """Create the default checkers.""" self._checkers = [] for checker in _default_checkers: checker(shell=self.shell, prefilter_manager=self, config=self.config) def sort_checkers(self): """Sort the checkers by priority. This must be called after the priority of a checker is changed. The :meth:`register_checker` method calls this automatically. """ self._checkers.sort(key=lambda x: x.priority) @property def checkers(self): """Return a list of checkers, sorted by priority.""" return self._checkers def register_checker(self, checker): """Register a checker instance.""" if checker not in self._checkers: self._checkers.append(checker) self.sort_checkers() def unregister_checker(self, checker): """Unregister a checker instance.""" if checker in self._checkers: self._checkers.remove(checker) #------------------------------------------------------------------------- # API for managing checkers #------------------------------------------------------------------------- def init_handlers(self): """Create the default handlers.""" self._handlers = {} self._esc_handlers = {} for handler in _default_handlers: handler(shell=self.shell, prefilter_manager=self, config=self.config) @property def handlers(self): """Return a dict of all the handlers.""" return self._handlers def register_handler(self, name, handler, esc_strings): """Register a handler instance by name with esc_strings.""" self._handlers[name] = handler for esc_str in esc_strings: self._esc_handlers[esc_str] = handler def unregister_handler(self, name, handler, esc_strings): """Unregister a handler instance by name with esc_strings.""" try: del self._handlers[name] except KeyError: pass for esc_str in esc_strings: h = self._esc_handlers.get(esc_str) if h is handler: del self._esc_handlers[esc_str] def get_handler_by_name(self, name): """Get a handler by its name.""" return self._handlers.get(name) def get_handler_by_esc(self, esc_str): """Get a handler by its escape string.""" return self._esc_handlers.get(esc_str) #------------------------------------------------------------------------- # Main prefiltering API #------------------------------------------------------------------------- def prefilter_line_info(self, line_info): """Prefilter a line that has been converted to a LineInfo object. This implements the checker/handler part of the prefilter pipe. """ # print "prefilter_line_info: ", line_info handler = self.find_handler(line_info) return handler.handle(line_info) def find_handler(self, line_info): """Find a handler for the line_info by trying checkers.""" for checker in self.checkers: if checker.enabled: handler = checker.check(line_info) if handler: return handler return self.get_handler_by_name('normal') def transform_line(self, line, continue_prompt): """Calls the enabled transformers in order of increasing priority.""" for transformer in self.transformers: if transformer.enabled: line = transformer.transform(line, continue_prompt) return line def prefilter_line(self, line, continue_prompt=False): """Prefilter a single input line as text. This method prefilters a single line of text by calling the transformers and then the checkers/handlers. """ # print "prefilter_line: ", line, continue_prompt # All handlers *must* return a value, even if it's blank (''). # save the line away in case we crash, so the post-mortem handler can # record it self.shell._last_input_line = line if not line: # Return immediately on purely empty lines, so that if the user # previously typed some whitespace that started a continuation # prompt, he can break out of that loop with just an empty line. # This is how the default python prompt works. return '' # At this point, we invoke our transformers. if not continue_prompt or (continue_prompt and self.multi_line_specials): line = self.transform_line(line, continue_prompt) # Now we compute line_info for the checkers and handlers line_info = LineInfo(line, continue_prompt) # the input history needs to track even empty lines stripped = line.strip() normal_handler = self.get_handler_by_name('normal') if not stripped: return normal_handler.handle(line_info) # special handlers are only allowed for single line statements if continue_prompt and not self.multi_line_specials: return normal_handler.handle(line_info) prefiltered = self.prefilter_line_info(line_info) # print "prefiltered line: %r" % prefiltered return prefiltered def prefilter_lines(self, lines, continue_prompt=False): """Prefilter multiple input lines of text. This is the main entry point for prefiltering multiple lines of input. This simply calls :meth:`prefilter_line` for each line of input. This covers cases where there are multiple lines in the user entry, which is the case when the user goes back to a multiline history entry and presses enter. """ llines = lines.rstrip('\n').split('\n') # We can get multiple lines in one shot, where multiline input 'blends' # into one line, in cases like recalling from the readline history # buffer. We need to make sure that in such cases, we correctly # communicate downstream which line is first and which are continuation # ones. if len(llines) > 1: out = '\n'.join([ self.prefilter_line(line, lnum > 0) for lnum, line in enumerate(llines) ]) else: out = self.prefilter_line(llines[0], continue_prompt) return out
class Completer(Configurable): greedy = CBool(False, config=True, help="""Activate greedy completion This will enable completion on elements of lists, results of function calls, etc., but can be unsafe because the code is actually evaluated on TAB. """ ) def __init__(self, namespace=None, global_namespace=None, config=None, **kwargs): """Create a new completer for the command line. Completer(namespace=ns,global_namespace=ns2) -> completer instance. If unspecified, the default namespace where completions are performed is __main__ (technically, __main__.__dict__). Namespaces should be given as dictionaries. An optional second namespace can be given. This allows the completer to handle cases where both the local and global scopes need to be distinguished. Completer instances should be used as the completion mechanism of readline via the set_completer() call: readline.set_completer(Completer(my_namespace).complete) """ # Don't bind to namespace quite yet, but flag whether the user wants a # specific namespace or to use __main__.__dict__. This will allow us # to bind to __main__.__dict__ at completion time, not now. if namespace is None: self.use_main_ns = 1 else: self.use_main_ns = 0 self.namespace = namespace # The global namespace, if given, can be bound directly if global_namespace is None: self.global_namespace = {} else: self.global_namespace = global_namespace super(Completer, self).__init__(config=config, **kwargs) def complete(self, text, state): """Return the next possible completion for 'text'. This is called successively with state == 0, 1, 2, ... until it returns None. The completion should begin with 'text'. """ if self.use_main_ns: self.namespace = __main__.__dict__ if state == 0: if "." in text: self.matches = self.attr_matches(text) else: self.matches = self.global_matches(text) try: return self.matches[state] except IndexError: return None def global_matches(self, text): """Compute matches when text is a simple name. Return a list of all keywords, built-in functions and names currently defined in self.namespace or self.global_namespace that match. """ #print 'Completer->global_matches, txt=%r' % text # dbg matches = [] match_append = matches.append n = len(text) for lst in [keyword.kwlist, __builtin__.__dict__.keys(), self.namespace.keys(), self.global_namespace.keys()]: for word in lst: if word[:n] == text and word != "__builtins__": match_append(word) return matches def attr_matches(self, text): """Compute matches when text contains a dot. Assuming the text is of the form NAME.NAME....[NAME], and is evaluatable in self.namespace or self.global_namespace, it will be evaluated and its attributes (as revealed by dir()) are used as possible completions. (For class instances, class members are are also considered.) WARNING: this can still invoke arbitrary C code, if an object with a __getattr__ hook is evaluated. """ #io.rprint('Completer->attr_matches, txt=%r' % text) # dbg # Another option, seems to work great. Catches things like ''.<tab> m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text) if m: expr, attr = m.group(1, 3) elif self.greedy: m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer) if not m2: return [] expr, attr = m2.group(1,2) else: return [] try: obj = eval(expr, self.namespace) except: try: obj = eval(expr, self.global_namespace) except: return [] if self.limit_to__all__ and hasattr(obj, '__all__'): words = get__all__entries(obj) else: words = dir2(obj) try: words = generics.complete_object(obj, words) except TryNext: pass except Exception: # Silence errors from completion function #raise # dbg pass # Build match list to return n = len(attr) res = ["%s.%s" % (expr, w) for w in words if w[:n] == attr ] return res
class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp): name = 'ipython-qtconsole' description = """ The IPython QtConsole. This launches a Console-style application using Qt. It is not a full console, in that launched terminal subprocesses will not be able to accept input. The QtConsole supports various extra features beyond the Terminal IPython shell, such as inline plotting with matplotlib, via: ipython qtconsole --pylab=inline as well as saving your session as HTML, and printing the output. """ examples = _examples classes = [ IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session ] flags = Dict(flags) aliases = Dict(aliases) frontend_flags = Any(qt_flags) frontend_aliases = Any(qt_aliases) kernel_manager_class = QtKernelManager stylesheet = Unicode('', config=True, help="path to a custom CSS stylesheet") plain = CBool( False, config=True, help= "Use a plaintext widget instead of rich text (plain can't print/save)." ) def _pure_changed(self, name, old, new): kind = 'plain' if self.plain else 'rich' self.config.ConsoleWidget.kind = kind if self.pure: self.widget_factory = FrontendWidget elif self.plain: self.widget_factory = IPythonWidget else: self.widget_factory = RichIPythonWidget _plain_changed = _pure_changed # the factory for creating a widget widget_factory = Any(RichIPythonWidget) def parse_command_line(self, argv=None): super(IPythonQtConsoleApp, self).parse_command_line(argv) self.build_kernel_argv(argv) def new_frontend_master(self): """ Create and return new frontend attached to new kernel, launched on localhost. """ ip = self.ip if self.ip in LOCAL_IPS else LOCALHOST kernel_manager = QtKernelManager( ip=ip, connection_file=self._new_connection_file(), config=self.config, ) # start the kernel kwargs = dict(ipython=not self.pure) kwargs['extra_arguments'] = self.kernel_argv kernel_manager.start_kernel(**kwargs) kernel_manager.start_channels() widget = self.widget_factory(config=self.config, local_kernel=True) widget.kernel_manager = kernel_manager widget._existing = False widget._may_close = True widget._confirm_exit = self.confirm_exit return widget def new_frontend_slave(self, current_widget): """Create and return a new frontend attached to an existing kernel. Parameters ---------- current_widget : IPythonWidget The IPythonWidget whose kernel this frontend is to share """ kernel_manager = QtKernelManager( connection_file=current_widget.kernel_manager.connection_file, config=self.config, ) kernel_manager.load_connection_file() kernel_manager.start_channels() widget = self.widget_factory(config=self.config, local_kernel=False) widget._existing = True widget._may_close = False widget._confirm_exit = False widget.kernel_manager = kernel_manager return widget def init_qt_elements(self): # Create the widget. self.app = QtGui.QApplication([]) base_path = os.path.abspath(os.path.dirname(__file__)) icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg') self.app.icon = QtGui.QIcon(icon_path) QtGui.QApplication.setWindowIcon(self.app.icon) local_kernel = (not self.existing) or self.ip in LOCAL_IPS self.widget = self.widget_factory(config=self.config, local_kernel=local_kernel) self.widget._existing = self.existing self.widget._may_close = not self.existing self.widget._confirm_exit = self.confirm_exit self.widget.kernel_manager = self.kernel_manager self.window = MainWindow( self.app, confirm_exit=self.confirm_exit, new_frontend_factory=self.new_frontend_master, slave_frontend_factory=self.new_frontend_slave, ) self.window.log = self.log self.window.add_tab_with_frontend(self.widget) self.window.init_menu_bar() self.window.setWindowTitle('Python' if self.pure else 'IPython') def init_colors(self): """Configure the coloring of the widget""" # Note: This will be dramatically simplified when colors # are removed from the backend. if self.pure: # only IPythonWidget supports styling return # parse the colors arg down to current known labels try: colors = self.config.ZMQInteractiveShell.colors except AttributeError: colors = None try: style = self.config.IPythonWidget.syntax_style except AttributeError: style = None # find the value for colors: if colors: colors = colors.lower() if colors in ('lightbg', 'light'): colors = 'lightbg' elif colors in ('dark', 'linux'): colors = 'linux' else: colors = 'nocolor' elif style: if style == 'bw': colors = 'nocolor' elif styles.dark_style(style): colors = 'linux' else: colors = 'lightbg' else: colors = None # Configure the style. widget = self.widget if style: widget.style_sheet = styles.sheet_from_template(style, colors) widget.syntax_style = style widget._syntax_style_changed() widget._style_sheet_changed() elif colors: # use a default style widget.set_default_style(colors=colors) else: # this is redundant for now, but allows the widget's # defaults to change widget.set_default_style() if self.stylesheet: # we got an expicit stylesheet if os.path.isfile(self.stylesheet): with open(self.stylesheet) as f: sheet = f.read() widget.style_sheet = sheet widget._style_sheet_changed() else: raise IOError("Stylesheet %r not found." % self.stylesheet) def init_signal(self): """allow clean shutdown on sigint""" signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2)) # need a timer, so that QApplication doesn't block until a real # Qt event fires (can require mouse movement) # timer trick from http://stackoverflow.com/q/4938723/938949 timer = QtCore.QTimer() # Let the interpreter run each 200 ms: timer.timeout.connect(lambda: None) timer.start(200) # hold onto ref, so the timer doesn't get cleaned up self._sigint_timer = timer @catch_config_error def initialize(self, argv=None): super(IPythonQtConsoleApp, self).initialize(argv) IPythonConsoleApp.initialize(self, argv) self.init_qt_elements() self.init_colors() self.init_signal() def start(self): # draw the window self.window.show() self.window.raise_() # Start the application main loop. self.app.exec_()
class TerminalInteractiveShell(InteractiveShell): autoedit_syntax = CBool(False, config=True, help="auto editing of files with syntax errors.") banner = Unicode('') banner1 = Unicode( default_banner, config=True, help="""The part of the banner to be printed before the profile""") banner2 = Unicode( '', config=True, help="""The part of the banner to be printed after the profile""") confirm_exit = CBool( True, config=True, 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' or 'quit', you can force a direct exit without any confirmation.""", ) # 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 = Unicode( get_default_editor(), config=True, help="Set the editor used by IPython (default to $EDITOR/vi/notepad).") pager = Unicode('less', config=True, help="The shell program to be used for paging.") screen_length = Int( 0, config=True, 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 through a pager instead of directly printed. The default value for this is 0, which means IPython will auto-detect your screen size every time it needs to print certain potentially long strings (this doesn't change the behavior of the 'print' keyword, it's only triggered internally). If for some reason this isn't working well (it needs curses support), specify it yourself. Otherwise don't change the default.""", ) term_title = CBool(False, config=True, help="Enable auto setting the terminal title.") def __init__(self, config=None, ipython_dir=None, profile_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, profile_dir=profile_dir, user_ns=user_ns, user_global_ns=user_global_ns, custom_exceptions=custom_exceptions) # use os.system instead of utils.process.system by default, except on Windows if os.name == 'nt': self.system = self.system_piped else: self.system = self.system_raw 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 aliases #------------------------------------------------------------------------- def init_alias(self): # The parent class defines aliases that can be safely used with any # frontend. super(TerminalInteractiveShell, self).init_alias() # Now define aliases that only make sense on the terminal, because they # need direct access to the console in a way that we can't emulate in # GUI or web frontend if os.name == 'posix': aliases = [('clear', 'clear'), ('more', 'more'), ('less', 'less'), ('man', 'man')] elif os.name == 'nt': aliases = [('cls', 'cls')] for name, cmd in aliases: self.alias_manager.define_alias(name, cmd) #------------------------------------------------------------------------- # 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 if self.profile and self.profile != 'default': self.banner += '\nIPython profile: %s\n' % self.profile if self.banner2: self.banner += '\n' + self.banner2 def init_usage(self, usage=None): if usage is None: self.usage = interactive_usage else: self.usage = usage #------------------------------------------------------------------------- # Mainloop and code execution logic #------------------------------------------------------------------------- def mainloop(self, display_banner=None): """Start the mainloop. If an optional banner argument is given, it will override the internally created default banner. """ with nested(self.builtin_trap, self.display_trap): while 1: try: self.interact(display_banner=display_banner) #self.interact_with_readline() # XXX for testing of a readline-decoupled repl loop, call # interact_with_readline above break except KeyboardInterrupt: # this should not be necessary, but KeyboardInterrupt # handling seems rather unpredictable... self.write("\nKeyboardInterrupt in interact()\n") def interact(self, display_banner=None): """Closely emulate the interactive Python console.""" # batch run -> do not interact if self.exit_now: return if display_banner is None: display_banner = self.display_banner if isinstance(display_banner, basestring): self.show_banner(display_banner) elif display_banner: self.show_banner() more = False # 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) 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.input_splitter.reset() more = False 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: self.input_splitter.push(line) more = self.input_splitter.push_accepts_more() if (self.SyntaxTB.last_syntax_error and self.autoedit_syntax): self.edit_syntax_error() if not more: source_raw = self.input_splitter.source_raw_reset()[1] self.run_cell(source_raw) # 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=''): """Write a prompt and read a line. The returned line does not include the trailing newline. When the user enters the EOF key sequence, EOFError is raised. Optional inputs: - prompt(''): a string to be printed to prompt the user. - continue_prompt(False): whether this line is the first one or a continuation in a sequence of inputs. """ # Code run by the user may have modified the readline completer state. # We must ensure that our completer is back in place. if self.has_readline: self.set_readline_completer() try: line = raw_input_original(prompt).decode(self.stdin_encoding) except ValueError: warn("\n********\nYou or a %run:ed script called sys.stdin.close()" " or sys.stdout.close()!\nExiting IPython!") self.ask_exit() return "" # Try to be reasonably smart about not re-indenting pasted input more # than necessary. We do this by trimming out the auto-indent initial # spaces, if the user's actual input started itself with whitespace. if self.autoindent: if num_ini_spaces(line) > self.indent_current_nsp: line = line[self.indent_current_nsp:] self.indent_current_nsp = 0 return line #------------------------------------------------------------------------- # Methods to support auto-editing of SyntaxErrors. #------------------------------------------------------------------------- def edit_syntax_error(self): """The bottom half of the syntax error handler called in the main loop. Loop until syntax error is fixed or user cancels. """ while self.SyntaxTB.last_syntax_error: # copy and clear last_syntax_error err = self.SyntaxTB.clear_err_state() if not self._should_recompile(err): return try: # may set last_syntax_error again if a SyntaxError is raised self.safe_execfile(err.filename, self.user_ns) except: self.showtraceback() else: try: f = file(err.filename) try: # This should be inside a display_trap block and I # think it is. sys.displayhook(f.read()) finally: f.close() except: self.showtraceback() def _should_recompile(self, e): """Utility routine for edit_syntax_error""" if e.filename in ('<ipython console>', '<input>', '<string>', '<console>', '<BackgroundJob compilation>', None): return False try: if (self.autoedit_syntax and not self.ask_yes_no( 'Return to editor to correct syntax error? ' '[Y/n] ', 'y')): return False except EOFError: return False def int0(x): try: return int(x) except TypeError: return 0 # always pass integer line and offset values to editor hook try: self.hooks.fix_error_editor(e.filename, int0(e.lineno), int0(e.offset), e.msg) except TryNext: warn('Could not open editor') return False return True #------------------------------------------------------------------------- # Things related to GUI support and pylab #------------------------------------------------------------------------- def enable_pylab(self, gui=None, import_all=True): """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, import_all) 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() #------------------------------------------------------------------------ # Magic overrides #------------------------------------------------------------------------ # Once the base class stops inheriting from magic, this code needs to be # moved into a separate machinery as well. For now, at least isolate here # the magics which this class needs to implement differently from the base # class, or that are unique to it. def magic_autoindent(self, parameter_s=''): """Toggle autoindent on/off (if available).""" self.shell.set_autoindent() print "Automatic indentation is:", ['OFF', 'ON'][self.shell.autoindent] @skip_doctest def magic_cpaste(self, parameter_s=''): """Paste & execute a pre-formatted code block from clipboard. You must terminate the block with '--' (two minus-signs) alone on the line. You can also provide your own sentinel with '%paste -s %%' ('%%' is the new sentinel for this operation) The block is dedented prior to execution to enable execution of method definitions. '>' and '+' characters at the beginning of a line are ignored, to allow pasting directly from e-mails, diff files and doctests (the '...' continuation prompt is also stripped). The executed block is also assigned to variable named 'pasted_block' for later editing with '%edit pasted_block'. You can also pass a variable name as an argument, e.g. '%cpaste foo'. This assigns the pasted block to variable 'foo' as string, without dedenting or executing it (preceding >>> and + is still stripped) '%cpaste -r' re-executes the block previously entered by cpaste. Do not be alarmed by garbled output on Windows (it's a readline bug). Just press enter and type -- (and press enter again) and the block will be what was just pasted. IPython statements (magics, shell escapes) are not supported (yet). See also -------- paste: automatically pull code from clipboard. Examples -------- :: In [8]: %cpaste Pasting code; enter '--' alone on the line to stop. :>>> a = ["world!", "Hello"] :>>> print " ".join(sorted(a)) :-- Hello world! """ opts, args = self.parse_options(parameter_s, 'rs:', mode='string') par = args.strip() if opts.has_key('r'): self._rerun_pasted() return sentinel = opts.get('s', '--') block = self._strip_pasted_lines_for_code( self._get_pasted_lines(sentinel)) self._execute_block(block, par) def magic_paste(self, parameter_s=''): """Paste & execute a pre-formatted code block from clipboard. The text is pulled directly from the clipboard without user intervention and printed back on the screen before execution (unless the -q flag is given to force quiet mode). The block is dedented prior to execution to enable execution of method definitions. '>' and '+' characters at the beginning of a line are ignored, to allow pasting directly from e-mails, diff files and doctests (the '...' continuation prompt is also stripped). The executed block is also assigned to variable named 'pasted_block' for later editing with '%edit pasted_block'. You can also pass a variable name as an argument, e.g. '%paste foo'. This assigns the pasted block to variable 'foo' as string, without dedenting or executing it (preceding >>> and + is still stripped) Options ------- -r: re-executes the block previously entered by cpaste. -q: quiet mode: do not echo the pasted text back to the terminal. IPython statements (magics, shell escapes) are not supported (yet). See also -------- cpaste: manually paste code into terminal until you mark its end. """ opts, args = self.parse_options(parameter_s, 'rq', mode='string') par = args.strip() if opts.has_key('r'): self._rerun_pasted() return text = self.shell.hooks.clipboard_get() block = self._strip_pasted_lines_for_code(text.splitlines()) # By default, echo back to terminal unless quiet mode is requested if not opts.has_key('q'): write = self.shell.write write(self.shell.pycolorize(block)) if not block.endswith('\n'): write('\n') write("## -- End pasted text --\n") self._execute_block(block, par) def showindentationerror(self): super(TerminalInteractiveShell, self).showindentationerror() print( "If you want to paste code into IPython, try the %paste magic function." )
class MongoNotebookManager(NotebookManager): #Useless variable that is required unfortunately notebook_dir = Unicode(u"", config=True) mongo_uri = Unicode( 'mongodb://localhost:27017/', config=True, help= "The URI to connect to the MongoDB instance. Defaults to 'mongodb://localhost:27017/'" ) replica_set = Unicode('', config=True, help="Replica set for mongodb, if any") database_name = Unicode( 'ipython', config=True, help="Defines the database in mongodb in which to store the collections" ) notebook_collection = Unicode( 'notebooks', config=True, help="Defines the collection in mongodb in which to store the notebooks" ) checkpoint_collection = Unicode( 'checkpoints', config=True, help="The collection name in which to keep notebook checkpoints") checkpoints_history = CBool('checkpoints_history', config=True, help="Save all checkpoints or keep only last") def __init__(self, **kwargs): super(MongoNotebookManager, self).__init__(**kwargs) if len(self.replica_set) == 0: self._conn = self._connect_server() else: self._conn = self._connect_replica_set() def get_notebook_names(self, path=''): """List all notebook names in the notebook dir and path.""" path = path.strip('/') spec = {'path': path, 'type': 'notebook'} fields = {'name': 1} notebooks = list( self._connect_collection(self.notebook_collection).find( spec, fields)) names = [n['name'] for n in notebooks] return names def path_exists(self, path): """Does the API-style path (directory) actually exist? Parameters ---------- path : string The path to check. This is an API path (`/` separated, relative to base notebook-dir). Returns ------- exists : bool Whether the path is indeed a directory. """ path = path.strip('/') if path != '': spec = {'path': path} count = self._connect_collection( self.notebook_collection).find(spec).count() else: count = 1 return count > 0 def is_hidden(self, path): #Nothing is hidden return False def notebook_exists(self, name, path=''): path = path.strip('/') spec = {'path': path, 'name': name, 'type': 'notebook'} count = self._connect_collection( self.notebook_collection).find(spec).count() return count == 1 def list_dirs(self, path): path = path.strip('/') if path == '': prefix = '' else: prefix = path + '/' spec = {'path': prefix, 'type': 'directory'} fields = {'name': 1} notebooks = list( self._connect_collection(self.notebook_collection).find( spec, fields)) names = [ n['name'].lstrip(prefix) for n in notebooks if '/' not in n['name'] ] dirs = [self.get_dir_model(name, path) for name in names] dirs = sorted(dirs, key=sort_key) return dirs def get_dir_model(self, name, path=''): path = path.strip('/') spec = {'path': path, 'name': name, 'type': 'directory'} fields = {'lastModified': 1, 'created': 1} notebook = self._connect_collection(self.notebook_collection).find_one( spec, fields) if notebook == None: raise IOError('directory does not exist: %r' % (path + '|' + name)) last_modified = notebook['lastModified'] created = notebook['created'] # Create the notebook model. model = {} model['name'] = name model['path'] = path model['last_modified'] = last_modified model['created'] = created model['type'] = 'directory' return model def list_notebooks(self, path): path = path.strip('/') notebook_names = self.get_notebook_names(path) notebooks = [ self.get_notebook(name, path, content=False) for name in notebook_names if self.should_list(name) ] notebooks = sorted(notebooks, key=sort_key) return notebooks def get_notebook(self, name, path='', content=True): path = path.strip('/') if not self.notebook_exists(name=name, path=path): raise web.HTTPError(404, u'Notebook does not exist: %s' % name) spec = {'path': path, 'name': name, 'type': 'notebook'} fields = {'lastModified': 1, 'created': 1} if content: fields['content'] = 1 notebook = self._connect_collection(self.notebook_collection).find_one( spec, fields) last_modified = notebook['lastModified'] created = notebook['created'] # Create the notebook model. model = {} model['name'] = name model['path'] = path model['last_modified'] = last_modified model['created'] = created model['type'] = 'notebook' if content: with StringIO(notebook['content']) as f: nb = current.read(f, u'json') self.mark_trusted_cells(nb, name, path) model['content'] = nb return model def create_notebook(self, model=None, path=''): """Create a new notebook and return its model with no content.""" path = path.strip('/') if model is None: model = {} if 'content' not in model: metadata = current.new_metadata(name=u'') model['content'] = current.new_notebook(metadata=metadata) if 'name' not in model: model['name'] = self.increment_filename('Untitled', path) model['path'] = path model['type'] = 'notebook' model = self.save_notebook(model, model['name'], model['path']) return model def save_notebook(self, model, name='', path=''): path = path.strip('/') if 'content' not in model: raise web.HTTPError(400, u'No notebook JSON data provided') # One checkpoint should always exist if self.notebook_exists( name, path) and not self.list_checkpoints(name, path): self.create_checkpoint(name, path) new_path = model.get('path', path).strip('/') new_name = model.get('name', name) if path != new_path or name != new_name: self.rename_notebook(name, path, new_name, new_path) # Save the notebook file nb = current.to_notebook_json(model['content']) self.check_and_sign(nb, new_name, new_path) if 'name' in nb['metadata']: nb['metadata']['name'] = u'' try: with StringIO() as f: current.write(nb, f, u'json') spec = {'path': path, 'name': name} data = { '$set': { 'type': 'notebook', 'content': f.getvalue(), 'lastModified': datetime.datetime.now(), } } f.close() if 'created' in model: data['$set']['created'] = model['created'] else: data['$set']['created'] = datetime.datetime.now() notebook = self._connect_collection( self.notebook_collection).update(spec, data, upsert=True) except Exception as e: raise web.HTTPError( 400, u'Unexpected error while autosaving notebook: %s' % (e)) model = self.get_notebook(new_name, new_path, content=False) return model def update_notebook(self, model, name, path=''): path = path.strip('/') new_name = model.get('name', name) new_path = model.get('path', path).strip('/') if path != new_path or name != new_name: self.rename_notebook(name, path, new_name, new_path) model = self.get_notebook(new_name, new_path, content=False) return model def delete_notebook(self, name, path=''): path = path.strip('/') spec = {'path': path, 'name': name} fields = { 'name': 1, } notebook = self._connect_collection(self.notebook_collection).find_one( spec, fields) if not notebook: raise web.HTTPError(404, u'Notebook does not exist: %s' % name) # clear checkpoints self._connect_collection(self.checkpoint_collection).remove(spec) self._connect_collection(self.notebook_collection).remove(spec) def rename_notebook(self, old_name, old_path, new_name, new_path): old_path = old_path.strip('/') new_path = new_path.strip('/') if new_name == old_name and new_path == old_path: return # Should we proceed with the move? spec = {'path': new_path, 'name': new_name} fields = { 'name': 1, } notebook = self._connect_collection(self.notebook_collection).find_one( spec, fields) if notebook != None: raise web.HTTPError( 409, u'Notebook with name already exists: %s' % new_name) # Move the notebook file try: spec = {'path': old_path, 'name': old_name} modify = {'$set': {'path': new_path, 'name': new_name}} self._connect_collection(self.notebook_collection).update( spec, modify) except Exception as e: raise web.HTTPError( 500, u'Unknown error renaming notebook: %s %s' % (old_os_path, e)) # Move the checkpoints spec = {'path': old_path, 'name': old_name} modify = {'$set': {'path': new_path, 'name': new_name}} self._connect_collection(self.checkpoint_collection).update(spec, modify, multi=True) # public checkpoint API def create_checkpoint(self, name, path=''): path = path.strip('/') spec = {'path': path, 'name': name} notebook = self._connect_collection( self.notebook_collection).find_one(spec) chid = notebook['_id'] del notebook['_id'] cp_id = str( self._connect_collection( self.checkpoint_collection).find(spec).count()) if self.checkpoints_history: spec['cp'] = cp_id else: notebook['cp'] = cp_id spec['id'] = chid newnotebook = {'$set': notebook} last_modified = notebook["lastModified"] self._connect_collection(self.checkpoint_collection).update( spec, newnotebook, upsert=True) # return the checkpoint info return dict(id=cp_id, last_modified=last_modified) def list_checkpoints(self, name, path=''): path = path.strip('/') spec = { 'path': path, 'name': name, } checkpoints = list( self._connect_collection(self.checkpoint_collection).find(spec)) return [ dict(id=c['cp'], last_modified=c['lastModified']) for c in checkpoints ] def restore_checkpoint(self, checkpoint_id, name, path=''): path = path.strip('/') spec = {'path': path, 'name': name, 'cp': checkpoint_id} checkpoint = self._connect_collection( self.checkpoint_collection).find_one(spec) if checkpoint == None: raise web.HTTPError( 404, u'Notebook checkpoint does not exist: %s-%s' % (name, checkpoint_id)) del spec['cp'] del checkpoint['cp'] del checkpoint['_id'] checkpoint = {'$set': checkpoint} self._connect_collection(self.notebook_collection).update(spec, checkpoint, upsert=True) def delete_checkpoint(self, checkpoint_id, name, path=''): path = path.strip('/') spec = {'path': path, 'name': name, 'cp': checkpoint_id} checkpoint = self._connect_collection( self.checkpoint_collection).find_one(spec) if checkpoint == None: raise web.HTTPError( 404, u'Notebook checkpoint does not exist: %s%s-%s' % (path, name, checkpoint_id)) self._connect_collection(self.checkpoint_collection).remove(spec) def info_string(self): return "Serving notebooks from mongodb" def get_kernel_path(self, name, path='', model=None): return os.path.join(self.notebook_dir, path) #mongodb related functions def _connect_server(self): return MongoProxy(pymongo.MongoClient(self.mongo_uri)) def _connect_replica_set(self): return MongoProxy( pymongo.MongoReplicaSetClient(self.mongo_uri, self._replicaSet)) def _connect_collection(self, collection): if not self._conn.alive(): if len(self.replica_set) == 0: self._conn = self._connect_server() else: self._conn = self._connectReplicaSet() return self._conn[self.database_name][collection]
class InteractiveShellEmbed(TerminalInteractiveShell): dummy_mode = Bool(False) exit_msg = Unicode('') embedded = CBool(True) embedded_active = CBool(True) # Like the base class display_banner is not configurable, but here it # is True by default. display_banner = CBool(True) def __init__(self, config=None, ipython_dir=None, user_ns=None, user_module=None, custom_exceptions=((), None), usage=None, banner1=None, banner2=None, display_banner=None, exit_msg=u'', user_global_ns=None): if user_global_ns is not None: warnings.warn( "user_global_ns has been replaced by user_module. The\ parameter will be ignored.", DeprecationWarning) super(InteractiveShellEmbed, self).__init__(config=config, ipython_dir=ipython_dir, user_ns=user_ns, user_module=user_module, custom_exceptions=custom_exceptions, usage=usage, banner1=banner1, banner2=banner2, display_banner=display_banner) self.exit_msg = exit_msg # don't use the ipython crash handler so that user exceptions aren't # trapped sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors, mode=self.xmode, call_pdb=self.pdb) def init_sys_modules(self): pass def init_magics(self): super(InteractiveShellEmbed, self).init_magics() self.register_magics(EmbeddedMagics) def __call__(self, header='', local_ns=None, module=None, dummy=None, stack_depth=1, global_ns=None, compile_flags=None): """Activate the interactive interpreter. __call__(self,header='',local_ns=None,module=None,dummy=None) -> Start the interpreter shell with the given local and global namespaces, and optionally print a header string at startup. The shell can be globally activated/deactivated using the dummy_mode attribute. This allows you to turn off a shell used for debugging globally. However, *each* time you call the shell you can override the current state of dummy_mode with the optional keyword parameter 'dummy'. For example, if you set dummy mode on with IPShell.dummy_mode = True, you can still have a specific call work by making it as IPShell(dummy=False). """ # If the user has turned it off, go away if not self.embedded_active: return # Normal exits from interactive mode set this flag, so the shell can't # re-enter (it checks this variable at the start of interactive mode). self.exit_now = False # Allow the dummy parameter to override the global __dummy_mode if dummy or (dummy != 0 and self.dummy_mode): return if self.has_readline: self.set_readline_completer() # self.banner is auto computed if header: self.old_banner2 = self.banner2 self.banner2 = self.banner2 + '\n' + header + '\n' else: self.old_banner2 = '' # Call the embedding code with a stack depth of 1 so it can skip over # our call and get the original caller's namespaces. self.mainloop(local_ns, module, stack_depth=stack_depth, global_ns=global_ns, compile_flags=compile_flags) self.banner2 = self.old_banner2 if self.exit_msg is not None: print(self.exit_msg) def mainloop(self, local_ns=None, module=None, stack_depth=0, display_banner=None, global_ns=None, compile_flags=None): """Embeds IPython into a running python program. Parameters ---------- local_ns, module Working local namespace (a dict) and module (a module or similar object). If given as None, they are automatically taken from the scope where the shell was called, so that program variables become visible. stack_depth : int How many levels in the stack to go to looking for namespaces (when local_ns or module is None). This allows an intermediate caller to make sure that this function gets the namespace from the intended level in the stack. By default (0) it will get its locals and globals from the immediate caller. compile_flags A bit field identifying the __future__ features that are enabled, as passed to the builtin :func:`compile` function. If given as None, they are automatically taken from the scope where the shell was called. """ if (global_ns is not None) and (module is None): warnings.warn("global_ns is deprecated, use module instead.", DeprecationWarning) module = DummyMod() module.__dict__ = global_ns # Get locals and globals from caller if ((local_ns is None or module is None or compile_flags is None) and self.default_user_namespaces): call_frame = sys._getframe(stack_depth).f_back if local_ns is None: local_ns = call_frame.f_locals if module is None: global_ns = call_frame.f_globals module = sys.modules[global_ns['__name__']] if compile_flags is None: compile_flags = (call_frame.f_code.co_flags & compilerop.PyCF_MASK) # Save original namespace and module so we can restore them after # embedding; otherwise the shell doesn't shut down correctly. orig_user_module = self.user_module orig_user_ns = self.user_ns orig_compile_flags = self.compile.flags # Update namespaces and fire up interpreter # The global one is easy, we can just throw it in if module is not None: self.user_module = module # But the user/local one is tricky: ipython needs it to store internal # data, but we also need the locals. We'll throw our hidden variables # like _ih and get_ipython() into the local namespace, but delete them # later. if local_ns is not None: self.user_ns = local_ns self.init_user_ns() # Compiler flags if compile_flags is not None: self.compile.flags = compile_flags # Patch for global embedding to make sure that things don't overwrite # user globals accidentally. Thanks to Richard <*****@*****.**> # FIXME. Test this a bit more carefully (the if.. is new) # N.B. This can't now ever be called. Not sure what it was for. # And now, since it wasn't called in the previous version, I'm # commenting out these lines so they can't be called with my new changes # --TK, 2011-12-10 #if local_ns is None and module is None: # self.user_global_ns.update(__main__.__dict__) # make sure the tab-completer has the correct frame information, so it # actually completes using the frame's locals/globals self.set_completer_frame() with self.builtin_trap, self.display_trap: self.interact(display_banner=display_banner) # now, purge out the local namespace of IPython's hidden variables. if local_ns is not None: for name in self.user_ns_hidden: local_ns.pop(name, None) # Restore original namespace so shell can shut down when we exit. self.user_module = orig_user_module self.user_ns = orig_user_ns self.compile.flags = orig_compile_flags
class IPythonQtConsoleApp(BaseIPythonApplication, IPythonConsoleApp): name = 'ipython-qtconsole' description = """ The IPython QtConsole. This launches a Console-style application using Qt. It is not a full console, in that launched terminal subprocesses will not be able to accept input. The QtConsole supports various extra features beyond the Terminal IPython shell, such as inline plotting with matplotlib, via: ipython qtconsole --matplotlib=inline as well as saving your session as HTML, and printing the output. """ examples = _examples classes = [IPythonWidget] + IPythonConsoleApp.classes flags = Dict(flags) aliases = Dict(aliases) frontend_flags = Any(qt_flags) frontend_aliases = Any(qt_aliases) kernel_client_class = QtKernelClient kernel_manager_class = QtKernelManager stylesheet = Unicode('', config=True, help="path to a custom CSS stylesheet") hide_menubar = CBool(False, config=True, help="Start the console window with the menu bar hidden.") maximize = CBool(False, config=True, help="Start the console window maximized.") plain = CBool(False, config=True, help="Use a plaintext widget instead of rich text (plain can't print/save).") display_banner = CBool(True, config=True, help="Whether to display a banner upon starting the QtConsole." ) def _plain_changed(self, name, old, new): kind = 'plain' if new else 'rich' self.config.ConsoleWidget.kind = kind if new: self.widget_factory = IPythonWidget else: self.widget_factory = RichIPythonWidget # the factory for creating a widget widget_factory = Any(RichIPythonWidget) def parse_command_line(self, argv=None): super(IPythonQtConsoleApp, self).parse_command_line(argv) self.build_kernel_argv(argv) def new_frontend_master(self): """ Create and return new frontend attached to new kernel, launched on localhost. """ kernel_manager = self.kernel_manager_class( connection_file=self._new_connection_file(), parent=self, autorestart=True, ) # start the kernel kwargs = {} # FIXME: remove special treatment of IPython kernels if self.kernel_manager.ipython_kernel: kwargs['extra_arguments'] = self.kernel_argv kernel_manager.start_kernel(**kwargs) kernel_manager.client_factory = self.kernel_client_class kernel_client = kernel_manager.client() kernel_client.start_channels(shell=True, iopub=True) widget = self.widget_factory(config=self.config, local_kernel=True) self.init_colors(widget) widget.kernel_manager = kernel_manager widget.kernel_client = kernel_client widget._existing = False widget._may_close = True widget._confirm_exit = self.confirm_exit widget._display_banner = self.display_banner return widget def new_frontend_slave(self, current_widget): """Create and return a new frontend attached to an existing kernel. Parameters ---------- current_widget : IPythonWidget The IPythonWidget whose kernel this frontend is to share """ kernel_client = self.kernel_client_class( connection_file=current_widget.kernel_client.connection_file, config = self.config, ) kernel_client.load_connection_file() kernel_client.start_channels() widget = self.widget_factory(config=self.config, local_kernel=False) self.init_colors(widget) widget._existing = True widget._may_close = False widget._confirm_exit = False widget._display_banner = self.display_banner widget.kernel_client = kernel_client widget.kernel_manager = current_widget.kernel_manager return widget def init_qt_app(self): # separate from qt_elements, because it must run first self.app = QtGui.QApplication([]) def init_qt_elements(self): # Create the widget. base_path = os.path.abspath(os.path.dirname(__file__)) icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg') self.app.icon = QtGui.QIcon(icon_path) QtGui.QApplication.setWindowIcon(self.app.icon) ip = self.ip local_kernel = (not self.existing) or is_local_ip(ip) self.widget = self.widget_factory(config=self.config, local_kernel=local_kernel) self.init_colors(self.widget) self.widget._existing = self.existing self.widget._may_close = not self.existing self.widget._confirm_exit = self.confirm_exit self.widget._display_banner = self.display_banner self.widget.kernel_manager = self.kernel_manager self.widget.kernel_client = self.kernel_client self.window = MainWindow(self.app, confirm_exit=self.confirm_exit, new_frontend_factory=self.new_frontend_master, slave_frontend_factory=self.new_frontend_slave, ) self.window.log = self.log self.window.add_tab_with_frontend(self.widget) self.window.init_magic_helper() self.window.init_menu_bar() # Ignore on OSX, where there is always a menu bar if sys.platform != 'darwin' and self.hide_menubar: self.window.menuBar().setVisible(False) self.window.setWindowTitle('IPython') def init_colors(self, widget): """Configure the coloring of the widget""" # Note: This will be dramatically simplified when colors # are removed from the backend. # parse the colors arg down to current known labels cfg = self.config colors = cfg.ZMQInteractiveShell.colors if 'ZMQInteractiveShell.colors' in cfg else None style = cfg.IPythonWidget.syntax_style if 'IPythonWidget.syntax_style' in cfg else None sheet = cfg.IPythonWidget.style_sheet if 'IPythonWidget.style_sheet' in cfg else None # find the value for colors: if colors: colors=colors.lower() if colors in ('lightbg', 'light'): colors='lightbg' elif colors in ('dark', 'linux'): colors='linux' else: colors='nocolor' elif style: if style=='bw': colors='nocolor' elif styles.dark_style(style): colors='linux' else: colors='lightbg' else: colors=None # Configure the style if style: widget.style_sheet = styles.sheet_from_template(style, colors) widget.syntax_style = style widget._syntax_style_changed() widget._style_sheet_changed() elif colors: # use a default dark/light/bw style widget.set_default_style(colors=colors) if self.stylesheet: # we got an explicit stylesheet if os.path.isfile(self.stylesheet): with open(self.stylesheet) as f: sheet = f.read() else: raise IOError("Stylesheet %r not found." % self.stylesheet) if sheet: widget.style_sheet = sheet widget._style_sheet_changed() def init_signal(self): """allow clean shutdown on sigint""" signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2)) # need a timer, so that QApplication doesn't block until a real # Qt event fires (can require mouse movement) # timer trick from http://stackoverflow.com/q/4938723/938949 timer = QtCore.QTimer() # Let the interpreter run each 200 ms: timer.timeout.connect(lambda: None) timer.start(200) # hold onto ref, so the timer doesn't get cleaned up self._sigint_timer = timer @catch_config_error def initialize(self, argv=None): self.init_qt_app() super(IPythonQtConsoleApp, self).initialize(argv) IPythonConsoleApp.initialize(self,argv) self.init_qt_elements() self.init_signal() def start(self): # draw the window if self.maximize: self.window.showMaximized() else: self.window.show() self.window.raise_() # Start the application main loop. self.app.exec_()
class IPythonConsoleApp(ConnectionFileMixin): name = 'ipython-console-mixin' description = """ The IPython Mixin Console. This class contains the common portions of console client (QtConsole, ZMQ-based terminal console, etc). It is not a full console, in that launched terminal subprocesses will not be able to accept input. The Console using this mixing supports various extra features beyond the single-process Terminal IPython shell, such as connecting to existing kernel, via: ipython <appname> --existing as well as tunnel via SSH """ classes = classes flags = Dict(flags) aliases = Dict(aliases) kernel_manager_class = KernelManager kernel_client_class = BlockingKernelClient kernel_argv = List(Unicode) # frontend flags&aliases to be stripped when building kernel_argv frontend_flags = Any(app_flags) frontend_aliases = Any(app_aliases) # create requested profiles by default, if they don't exist: auto_create = CBool(True) # connection info: sshserver = Unicode( '', config=True, help="""The SSH server to use to connect to the kernel.""") sshkey = Unicode( '', config=True, help="""Path to the ssh key to use for logging in to the ssh server.""" ) def _connection_file_default(self): return 'kernel-%i.json' % os.getpid() existing = CUnicode('', config=True, help="""Connect to an already running kernel""") kernel_name = Unicode('python', config=True, help="""The name of the default kernel to start.""") confirm_exit = CBool( True, config=True, help=""" Set to display confirmation dialog on exit. You can always use 'exit' or 'quit', to force a direct exit without any confirmation.""", ) def build_kernel_argv(self, argv=None): """build argv to be passed to kernel subprocess""" if argv is None: argv = sys.argv[1:] self.kernel_argv = swallow_argv(argv, self.frontend_aliases, self.frontend_flags) def init_connection_file(self): """find the connection file, and load the info if found. The current working directory and the current profile's security directory will be searched for the file if it is not given by absolute path. When attempting to connect to an existing kernel and the `--existing` argument does not match an existing file, it will be interpreted as a fileglob, and the matching file in the current profile's security dir with the latest access time will be used. After this method is called, self.connection_file contains the *full path* to the connection file, never just its name. """ if self.existing: try: cf = find_connection_file(self.existing) except Exception: self.log.critical( "Could not find existing kernel connection file %s", self.existing) self.exit(1) self.log.debug("Connecting to existing kernel: %s" % cf) self.connection_file = cf else: # not existing, check if we are going to write the file # and ensure that self.connection_file is a full path, not just the shortname try: cf = find_connection_file(self.connection_file) except Exception: # file might not exist if self.connection_file == os.path.basename( self.connection_file): # just shortname, put it in security dir cf = os.path.join(self.profile_dir.security_dir, self.connection_file) else: cf = self.connection_file self.connection_file = cf try: self.connection_file = filefind( self.connection_file, ['.', self.profile_dir.security_dir]) except IOError: self.log.debug("Connection File not found: %s", self.connection_file) return # should load_connection_file only be used for existing? # as it is now, this allows reusing ports if an existing # file is requested try: self.load_connection_file() except Exception: self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True) self.exit(1) def init_ssh(self): """set up ssh tunnels, if needed.""" if not self.existing or (not self.sshserver and not self.sshkey): return self.load_connection_file() transport = self.transport ip = self.ip if transport != 'tcp': self.log.error("Can only use ssh tunnels with TCP sockets, not %s", transport) sys.exit(-1) if self.sshkey and not self.sshserver: # specifying just the key implies that we are connecting directly self.sshserver = ip ip = localhost() # build connection dict for tunnels: info = dict(ip=ip, shell_port=self.shell_port, iopub_port=self.iopub_port, stdin_port=self.stdin_port, hb_port=self.hb_port) self.log.info("Forwarding connections to %s via %s" % (ip, self.sshserver)) # tunnels return a new set of ports, which will be on localhost: self.ip = localhost() try: newports = tunnel_to_kernel(info, self.sshserver, self.sshkey) except: # even catch KeyboardInterrupt self.log.error("Could not setup tunnels", exc_info=True) self.exit(1) self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports cf = self.connection_file base, ext = os.path.splitext(cf) base = os.path.basename(base) self.connection_file = os.path.basename(base) + '-ssh' + ext self.log.info("To connect another client via this tunnel, use:") self.log.info("--existing %s" % self.connection_file) def _new_connection_file(self): cf = '' while not cf: # we don't need a 128b id to distinguish kernels, use more readable # 48b node segment (12 hex chars). Users running more than 32k simultaneous # kernels can subclass. ident = str(uuid.uuid4()).split('-')[-1] cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident) # only keep if it's actually new. Protect against unlikely collision # in 48b random search space cf = cf if not os.path.exists(cf) else '' return cf def init_kernel_manager(self): # Don't let Qt or ZMQ swallow KeyboardInterupts. if self.existing: self.kernel_manager = None return signal.signal(signal.SIGINT, signal.SIG_DFL) # Create a KernelManager and start a kernel. try: self.kernel_manager = self.kernel_manager_class( ip=self.ip, session=self.session, transport=self.transport, shell_port=self.shell_port, iopub_port=self.iopub_port, stdin_port=self.stdin_port, hb_port=self.hb_port, connection_file=self.connection_file, kernel_name=self.kernel_name, parent=self, ipython_dir=self.ipython_dir, ) except NoSuchKernel: self.log.critical("Could not find kernel %s", self.kernel_name) self.exit(1) self.kernel_manager.client_factory = self.kernel_client_class self.kernel_manager.start_kernel(extra_arguments=self.kernel_argv) atexit.register(self.kernel_manager.cleanup_ipc_files) if self.sshserver: # ssh, write new connection file self.kernel_manager.write_connection_file() # in case KM defaults / ssh writing changes things: km = self.kernel_manager self.shell_port = km.shell_port self.iopub_port = km.iopub_port self.stdin_port = km.stdin_port self.hb_port = km.hb_port self.connection_file = km.connection_file atexit.register(self.kernel_manager.cleanup_connection_file) def init_kernel_client(self): if self.kernel_manager is not None: self.kernel_client = self.kernel_manager.client() else: self.kernel_client = self.kernel_client_class( session=self.session, ip=self.ip, transport=self.transport, shell_port=self.shell_port, iopub_port=self.iopub_port, stdin_port=self.stdin_port, hb_port=self.hb_port, connection_file=self.connection_file, parent=self, ) self.kernel_client.start_channels() def initialize(self, argv=None): """ Classes which mix this class in should call: IPythonConsoleApp.initialize(self,argv) """ self.init_connection_file() default_secure(self.config) self.init_ssh() self.init_kernel_manager() self.init_kernel_client()
class InteractiveShellEmbed(TerminalInteractiveShell): dummy_mode = Bool(False) exit_msg = Unicode('') embedded = CBool(True) embedded_active = CBool(True) # Like the base class display_banner is not configurable, but here it # is True by default. display_banner = CBool(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, exit_msg=u''): super(InteractiveShellEmbed,self).__init__( 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) # don't use the ipython crash handler so that user exceptions aren't # trapped sys.excepthook = ultratb.FormattedTB(color_scheme=self.colors, mode=self.xmode, call_pdb=self.pdb) def init_sys_modules(self): pass def __call__(self, header='', local_ns=None, global_ns=None, dummy=None, stack_depth=1): """Activate the interactive interpreter. __call__(self,header='',local_ns=None,global_ns,dummy=None) -> Start the interpreter shell with the given local and global namespaces, and optionally print a header string at startup. The shell can be globally activated/deactivated using the set/get_dummy_mode methods. This allows you to turn off a shell used for debugging globally. However, *each* time you call the shell you can override the current state of dummy_mode with the optional keyword parameter 'dummy'. For example, if you set dummy mode on with IPShell.set_dummy_mode(1), you can still have a specific call work by making it as IPShell(dummy=0). The optional keyword parameter dummy controls whether the call actually does anything. """ # If the user has turned it off, go away if not self.embedded_active: return # Normal exits from interactive mode set this flag, so the shell can't # re-enter (it checks this variable at the start of interactive mode). self.exit_now = False # Allow the dummy parameter to override the global __dummy_mode if dummy or (dummy != 0 and self.dummy_mode): return if self.has_readline: self.set_readline_completer() # self.banner is auto computed if header: self.old_banner2 = self.banner2 self.banner2 = self.banner2 + '\n' + header + '\n' else: self.old_banner2 = '' # Call the embedding code with a stack depth of 1 so it can skip over # our call and get the original caller's namespaces. self.mainloop(local_ns, global_ns, stack_depth=stack_depth) self.banner2 = self.old_banner2 if self.exit_msg is not None: print self.exit_msg def mainloop(self, local_ns=None, global_ns=None, stack_depth=0, display_banner=None): """Embeds IPython into a running python program. Input: - header: An optional header message can be specified. - local_ns, global_ns: working namespaces. If given as None, the IPython-initialized one is updated with __main__.__dict__, so that program variables become visible but user-specific configuration remains possible. - stack_depth: specifies how many levels in the stack to go to looking for namespaces (when local_ns and global_ns are None). This allows an intermediate caller to make sure that this function gets the namespace from the intended level in the stack. By default (0) it will get its locals and globals from the immediate caller. Warning: it's possible to use this in a program which is being run by IPython itself (via %run), but some funny things will happen (a few globals get overwritten). In the future this will be cleaned up, as there is no fundamental reason why it can't work perfectly.""" # Get locals and globals from caller if local_ns is None or global_ns is None: call_frame = sys._getframe(stack_depth).f_back if local_ns is None: local_ns = call_frame.f_locals if global_ns is None: global_ns = call_frame.f_globals # Update namespaces and fire up interpreter # The global one is easy, we can just throw it in self.user_global_ns = global_ns # but the user/local one is tricky: ipython needs it to store internal # data, but we also need the locals. We'll copy locals in the user # one, but will track what got copied so we can delete them at exit. # This is so that a later embedded call doesn't see locals from a # previous call (which most likely existed in a separate scope). local_varnames = local_ns.keys() self.user_ns.update(local_ns) #self.user_ns['local_ns'] = local_ns # dbg # Patch for global embedding to make sure that things don't overwrite # user globals accidentally. Thanks to Richard <*****@*****.**> # FIXME. Test this a bit more carefully (the if.. is new) if local_ns is None and global_ns is None: self.user_global_ns.update(__main__.__dict__) # make sure the tab-completer has the correct frame information, so it # actually completes using the frame's locals/globals self.set_completer_frame() with nested(self.builtin_trap, self.display_trap): self.interact(display_banner=display_banner) # now, purge out the user namespace from anything we might have added # from the caller's local namespace delvar = self.user_ns.pop for var in local_varnames: delvar(var,None)
class NXConsoleApp(BaseIPythonApplication, IPythonConsoleApp): name = 'ipython-qtconsole' description = """ The NeXpy Console. This launches a Console-style application using Qt. The console is embedded in a GUI that contains a tree view of all NXroot groups and a matplotlib plotting pane. It also has all the added benefits of an IPython Qt Console with multiline editing, autocompletion, tooltips, command line histories and the ability to save your session as HTML or print the output. """ classes = [IPythonWidget] + IPythonConsoleApp.classes flags = Dict(flags) aliases = Dict(aliases) frontend_flags = Any(qt_flags) frontend_aliases = Any(qt_aliases) stylesheet = Unicode('', config=True, help="path to a custom CSS stylesheet") plain = CBool( False, config=True, help= "Use a plaintext widget instead of rich text (plain can't print/save)." ) def _plain_changed(self, name, old, new): kind = 'plain' if new else 'rich' self.config.ConsoleWidget.kind = kind if new: self.widget_factory = IPythonWidget else: self.widget_factory = RichIPythonWidget # the factory for creating a widget widget_factory = Any(RichIPythonWidget) def parse_command_line(self, argv=None): super(NXConsoleApp, self).parse_command_line(argv) self.build_kernel_argv(argv) def init_dir(self): """Initialize NeXpy home directory""" home_dir = os.path.realpath(os.path.expanduser('~')) nexpy_dir = os.path.join(home_dir, '.nexpy') if not os.path.exists(nexpy_dir): parent = os.path.dirname(nexpy_dir) if not os.access(parent, os.W_OK): nexpy_dir = tempfile.mkdtemp() else: os.mkdir(nexpy_dir) for subdirectory in ['functions', 'plugins', 'readers', 'scripts']: directory = os.path.join(nexpy_dir, subdirectory) if not os.path.exists(directory): os.mkdir(directory) global _nexpy_dir _nexpy_dir = nexpy_dir def init_log(self): value = os.getenv("NEXPY_LOG") if value == None: log_file = os.path.join(_nexpy_dir, 'nexpy.log') hdlr = logging.handlers.RotatingFileHandler(log_file, maxBytes=50000, backupCount=5) fmt = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' fmtr = logging.Formatter(fmt, None) logging.root.setLevel(logging.INFO) else: hdlr = logging.StreamHandler(stream=sys.stdout) fmt = '%(levelname)s %(module)s.%(funcName)s() %(message)s' fmtr = logging.Formatter(fmt) try: logging.root.setLevel(logging.__dict__[value]) except KeyError: print 'Invalid log level:', value sys.exit(1) hdlr.setFormatter(fmtr) logging.root.addHandler(hdlr) logging.info('NeXpy launched') logging.debug('Log level is: ' + str(value)) def init_tree(self): """Initialize the NeXus tree used in the tree view.""" global _tree self.tree = NXtree() _tree = self.tree def init_gui(self): """Initialize the GUI.""" self.app = guisupport.get_app_qt4() self.window = MainWindow(self.app, self.tree, config=self.config) self.window.log = self.log global _mainwindow _mainwindow = self.window if 'svg' in QtGui.QImageReader.supportedImageFormats(): self.app.icon = QtGui.QIcon( pkg_resources.resource_filename('nexpy.gui', 'resources/icon/NeXpy.svg')) else: self.app.icon = QtGui.QIcon( pkg_resources.resource_filename('nexpy.gui', 'resources/icon/NeXpy.png')) QtGui.QApplication.setWindowIcon(self.app.icon) def init_shell(self): """Initialize imports in the shell.""" global _shell _shell = self.window.user_ns s = ("import nexusformat.nexus as nx\n" "from nexusformat.nexus import NXgroup, NXfield, NXattr, NXlink\n" "from nexusformat.nexus import *\n" "import nexpy\n" "from nexpy.gui.plotview import NXPlotView") exec s in self.window.user_ns s = "" for _class in nxclasses: s = "%s=nx.%s\n" % (_class, _class) + s exec s in self.window.user_ns try: f = open( os.path.join(os.path.expanduser('~'), '.nexpy', 'config.py')) s = ''.join(f.readlines()) exec s in self.window.user_ns except: s = ("import sys\n" "import os\n" "import h5py as h5\n" "import numpy as np\n" "import numpy.ma as ma\n" "import scipy as sp\n" "import matplotlib as mpl\n" "from matplotlib import pylab, mlab, pyplot\n" "plt = pyplot") exec s in self.window.user_ns try: print sys.argv[1] fname = os.path.expanduser(sys.argv[1]) name = _mainwindow.treeview.tree.get_name(fname) _mainwindow.treeview.tree[name] = self.window.user_ns[ name] = nxload(fname) _mainwindow.treeview.select_node(_mainwindow.treeview.tree[name]) logging.info("NeXus file '%s' opened as workspace '%s'" % (fname, name)) self.window.user_ns[name].plot() except: pass def init_colors(self): """Configure the coloring of the widget""" # Note: This will be dramatically simplified when colors # are removed from the backend. # Configure the style. self.window.console.set_default_style() def init_signal(self): """allow clean shutdown on sigint""" signal.signal(signal.SIGINT, lambda sig, frame: self.exit(-2)) # need a timer, so that QApplication doesn't block until a real # Qt event fires (can require mouse movement) # timer trick from http://stackoverflow.com/q/4938723/938949 timer = QtCore.QTimer() # Let the interpreter run each 200 ms: timer.timeout.connect(lambda: None) timer.start(200) # hold onto ref, so the timer doesn't get cleaned up self._sigint_timer = timer @catch_config_error def initialize(self, argv=None): super(NXConsoleApp, self).initialize(argv) self.init_dir() self.init_log() self.init_tree() self.init_gui() self.init_shell() self.init_colors() self.init_signal() def start(self): # draw the window self.window.show() self.window.raise_() # Start the application main loop. guisupport.start_event_loop_qt4(self.app)
class ZMQInteractiveShell(InteractiveShell): """A subclass of InteractiveShell for ZMQ.""" displayhook_class = Type(ZMQShellDisplayHook) display_pub_class = Type(ZMQDisplayPublisher) # Override the traitlet in the parent class, because there's no point using # readline for the kernel. Can be removed when the readline code is moved # to the terminal frontend. colors_force = CBool(True) readline_use = CBool(False) # autoindent has no meaning in a zmqshell, and attempting to enable it # will print a warning in the absence of readline. autoindent = CBool(False) exiter = Instance(ZMQExitAutocall) def _exiter_default(self): return ZMQExitAutocall(self) keepkernel_on_exit = None # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no # interactive input being read; we provide event loop support in ipkernel from .eventloops import enable_gui enable_gui = staticmethod(enable_gui) def init_environment(self): """Configure the user's environment. """ env = os.environ # These two ensure 'ls' produces nice coloring on BSD-derived systems env['TERM'] = 'xterm-color' env['CLICOLOR'] = '1' # Since normal pagers don't work at all (over pexpect we don't have # single-key control of the subprocess), try to disable paging in # subprocesses as much as possible. env['PAGER'] = 'cat' env['GIT_PAGER'] = 'cat' # And install the payload version of page. install_payload_page() def auto_rewrite_input(self, cmd): """Called to show the auto-rewritten input for autocall and friends. FIXME: this payload is currently not correctly processed by the frontend. """ new = self.prompt_manager.render('rewrite') + cmd payload = dict( source= 'IPython.zmq.zmqshell.ZMQInteractiveShell.auto_rewrite_input', transformed_input=new, ) self.payload_manager.write_payload(payload) def ask_exit(self): """Engage the exit actions.""" payload = dict( source='IPython.zmq.zmqshell.ZMQInteractiveShell.ask_exit', exit=True, keepkernel=self.keepkernel_on_exit, ) self.payload_manager.write_payload(payload) def _showtraceback(self, etype, evalue, stb): exc_content = { u'traceback': stb, u'ename': unicode(etype.__name__), u'evalue': unicode(evalue) } dh = self.displayhook # Send exception info over pub socket for other clients than the caller # to pick up exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header) # FIXME - Hack: store exception info in shell object. Right now, the # caller is reading this info after the fact, we need to fix this logic # to remove this hack. Even uglier, we need to store the error status # here, because in the main loop, the logic that sets it is being # skipped because runlines swallows the exceptions. exc_content[u'status'] = u'error' self._reply_content = exc_content # /FIXME return exc_content #------------------------------------------------------------------------ # Magic overrides #------------------------------------------------------------------------ # Once the base class stops inheriting from magic, this code needs to be # moved into a separate machinery as well. For now, at least isolate here # the magics which this class needs to implement differently from the base # class, or that are unique to it. def magic_doctest_mode(self, parameter_s=''): """Toggle doctest mode on and off. This mode is intended to make IPython behave as much as possible like a plain Python shell, from the perspective of how its prompts, exceptions and output look. This makes it easy to copy and paste parts of a session into doctests. It does so by: - Changing the prompts to the classic ``>>>`` ones. - Changing the exception reporting mode to 'Plain'. - Disabling pretty-printing of output. Note that IPython also supports the pasting of code snippets that have leading '>>>' and '...' prompts in them. This means that you can paste doctests from files or docstrings (even if they have leading whitespace), and the code will execute correctly. You can then use '%history -t' to see the translated history; this will give you the input after removal of all the leading prompts and whitespace, which can be pasted back into an editor. With these features, you can switch into this mode easily whenever you need to do testing and changes to doctests, without having to leave your existing IPython session. """ from IPython.utils.ipstruct import Struct # Shorthands shell = self.shell disp_formatter = self.shell.display_formatter ptformatter = disp_formatter.formatters['text/plain'] # dstore is a data store kept in the instance metadata bag to track any # changes we make, so we can undo them later. dstore = shell.meta.setdefault('doctest_mode', Struct()) save_dstore = dstore.setdefault # save a few values we'll need to recover later mode = save_dstore('mode', False) save_dstore('rc_pprint', ptformatter.pprint) save_dstore('rc_plain_text_only', disp_formatter.plain_text_only) save_dstore('xmode', shell.InteractiveTB.mode) if mode == False: # turn on ptformatter.pprint = False disp_formatter.plain_text_only = True shell.magic_xmode('Plain') else: # turn off ptformatter.pprint = dstore.rc_pprint disp_formatter.plain_text_only = dstore.rc_plain_text_only shell.magic_xmode(dstore.xmode) # Store new mode and inform on console dstore.mode = bool(1 - int(mode)) mode_label = ['OFF', 'ON'][dstore.mode] print('Doctest mode is:', mode_label) # Send the payload back so that clients can modify their prompt display payload = dict( source= 'IPython.zmq.zmqshell.ZMQInteractiveShell.magic_doctest_mode', mode=dstore.mode) self.payload_manager.write_payload(payload) @skip_doctest def magic_edit(self, parameter_s='', last_call=['', '']): """Bring up an editor and execute the resulting code. Usage: %edit [options] [args] %edit runs an external text editor. You will need to set the command for this editor via the ``TerminalInteractiveShell.editor`` option in your configuration file before it will work. This command allows you to conveniently edit multi-line code right in your IPython session. If called without arguments, %edit opens up an empty editor with a temporary file and will execute the contents of this file when you close it (don't forget to save it!). Options: -n <number>: open the editor at a specified line number. By default, the IPython editor hook uses the unix syntax 'editor +N filename', but you can configure this by providing your own modified hook if your favorite editor supports line-number specifications with a different syntax. -p: this will call the editor with the same data as the previous time it was used, regardless of how long ago (in your current session) it was. -r: use 'raw' input. This option only applies to input taken from the user's history. By default, the 'processed' history is used, so that magics are loaded in their transformed version to valid Python. If this option is given, the raw input as typed as the command line is used instead. When you exit the editor, it will be executed by IPython's own processor. -x: do not execute the edited code immediately upon exit. This is mainly useful if you are editing programs which need to be called with command line arguments, which you can then do using %run. Arguments: If arguments are given, the following possibilites exist: - The arguments are numbers or pairs of colon-separated numbers (like 1 4:8 9). These are interpreted as lines of previous input to be loaded into the editor. The syntax is the same of the %macro command. - If the argument doesn't start with a number, it is evaluated as a variable and its contents loaded into the editor. You can thus edit any string which contains python code (including the result of previous edits). - If the argument is the name of an object (other than a string), IPython will try to locate the file where it was defined and open the editor at the point where it is defined. You can use `%edit function` to load an editor exactly at the point where 'function' is defined, edit it and have the file be executed automatically. If the object is a macro (see %macro for details), this opens up your specified editor with a temporary file containing the macro's data. Upon exit, the macro is reloaded with the contents of the file. Note: opening at an exact line is only supported under Unix, and some editors (like kedit and gedit up to Gnome 2.8) do not understand the '+NUMBER' parameter necessary for this feature. Good editors like (X)Emacs, vi, jed, pico and joe all do. - If the argument is not found as a variable, IPython will look for a file with that name (adding .py if necessary) and load it into the editor. It will execute its contents with execfile() when you exit, loading any code in the file into your interactive namespace. After executing your code, %edit will return as output the code you typed in the editor (except when it was an existing file). This way you can reload the code in further invocations of %edit as a variable, via _<NUMBER> or Out[<NUMBER>], where <NUMBER> is the prompt number of the output. Note that %edit is also available through the alias %ed. This is an example of creating a simple function inside the editor and then modifying it. First, start up the editor: In [1]: ed Editing... done. Executing edited code... Out[1]: 'def foo():n print "foo() was defined in an editing session"n' We can then call the function foo(): In [2]: foo() foo() was defined in an editing session Now we edit foo. IPython automatically loads the editor with the (temporary) file where foo() was previously defined: In [3]: ed foo Editing... done. Executing edited code... And if we call foo() again we get the modified version: In [4]: foo() foo() has now been changed! Here is an example of how to edit a code snippet successive times. First we call the editor: In [5]: ed Editing... done. Executing edited code... hello Out[5]: "print 'hello'n" Now we call it again with the previous output (stored in _): In [6]: ed _ Editing... done. Executing edited code... hello world Out[6]: "print 'hello world'n" Now we call it with the output #8 (stored in _8, also as Out[8]): In [7]: ed _8 Editing... done. Executing edited code... hello again Out[7]: "print 'hello again'n" """ opts, args = self.parse_options(parameter_s, 'prn:') try: filename, lineno, _ = self._find_edit_target(args, opts, last_call) except MacroToEdit as e: # TODO: Implement macro editing over 2 processes. print("Macro editing not yet implemented in 2-process model.") return # Make sure we send to the client an absolute path, in case the working # directory of client and kernel don't match filename = os.path.abspath(filename) payload = { 'source': 'IPython.zmq.zmqshell.ZMQInteractiveShell.edit_magic', 'filename': filename, 'line_number': lineno } self.payload_manager.write_payload(payload) # A few magics that are adapted to the specifics of using pexpect and a # remote terminal def magic_clear(self, arg_s): """Clear the terminal.""" if os.name == 'posix': self.shell.system("clear") else: self.shell.system("cls") if os.name == 'nt': # This is the usual name in windows magic_cls = magic_clear # Terminal pagers won't work over pexpect, but we do have our own pager def magic_less(self, arg_s): """Show a file through the pager. Files ending in .py are syntax-highlighted.""" cont = open(arg_s).read() if arg_s.endswith('.py'): cont = self.shell.pycolorize(cont) page.page(cont) magic_more = magic_less # Man calls a pager, so we also need to redefine it if os.name == 'posix': def magic_man(self, arg_s): """Find the man page for the given command and display in pager.""" page.page( self.shell.getoutput('man %s | col -b' % arg_s, split=False)) # FIXME: this is specific to the GUI, so we should let the gui app load # magics at startup that are only for the gui. Once the gui app has proper # profile and configuration management, we can have it initialize a kernel # with a special config file that provides these. def magic_guiref(self, arg_s): """Show a basic reference about the GUI console.""" from IPython.core.usage import gui_reference page.page(gui_reference, auto_html=True) def magic_connect_info(self, arg_s): """Print information for connecting other clients to this kernel It will print the contents of this session's connection file, as well as shortcuts for local clients. In the simplest case, when called from the most recently launched kernel, secondary clients can be connected, simply with: $> ipython <app> --existing """ from IPython.core.application import BaseIPythonApplication as BaseIPApp if BaseIPApp.initialized(): app = BaseIPApp.instance() security_dir = app.profile_dir.security_dir profile = app.profile else: profile = 'default' security_dir = '' try: connection_file = get_connection_file() info = get_connection_info(unpack=False) except Exception as e: error("Could not get connection info: %r" % e) return # add profile flag for non-default profile profile_flag = "--profile %s" % profile if profile != 'default' else "" # if it's in the security dir, truncate to basename if security_dir == os.path.dirname(connection_file): connection_file = os.path.basename(connection_file) print(info + '\n') print("Paste the above JSON into a file, and connect with:\n" " $> ipython <app> --existing <file>\n" "or, if you are local, you can connect with just:\n" " $> ipython <app> --existing {0} {1}\n" "or even just:\n" " $> ipython <app> --existing {1}\n" "if this is the most recent IPython session you have started.". format(connection_file, profile_flag)) def magic_qtconsole(self, arg_s): """Open a qtconsole connected to this kernel. Useful for connecting a qtconsole to running notebooks, for better debugging. """ try: p = connect_qtconsole(argv=arg_split(arg_s, os.name == 'posix')) except Exception as e: error("Could not start qtconsole: %r" % e) return def set_next_input(self, text): """Send the specified text to the frontend to be presented at the next input cell.""" payload = dict( source='IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input', text=text) self.payload_manager.write_payload(payload)
class IPythonQtConsoleApp(BaseIPythonApplication): name = 'ipython-qtconsole' default_config_file_name = 'ipython_config.py' description = """ The IPython QtConsole. This launches a Console-style application using Qt. It is not a full console, in that launched terminal subprocesses will not be able to accept input. The QtConsole supports various extra features beyond the Terminal IPython shell, such as inline plotting with matplotlib, via: ipython qtconsole --pylab=inline as well as saving your session as HTML, and printing the output. """ examples = _examples classes = [ IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session ] flags = Dict(flags) aliases = Dict(aliases) kernel_argv = List(Unicode) # create requested profiles by default, if they don't exist: auto_create = CBool(True) # connection info: ip = Unicode(LOCALHOST, config=True, help="""Set the kernel\'s IP address [default localhost]. If the IP address is something other than localhost, then Consoles on other machines will be able to connect to the Kernel, so be careful!""") sshserver = Unicode( '', config=True, help="""The SSH server to use to connect to the kernel.""") sshkey = Unicode( '', config=True, help="""Path to the ssh key to use for logging in to the ssh server.""" ) hb_port = Int(0, config=True, help="set the heartbeat port [default: random]") shell_port = Int(0, config=True, help="set the shell (XREP) port [default: random]") iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]") stdin_port = Int(0, config=True, help="set the stdin (XREQ) port [default: random]") connection_file = Unicode( '', config=True, help= """JSON file in which to store connection info [default: kernel-<pid>.json] This file will contain the IP, ports, and authentication key needed to connect clients to this kernel. By default, this file will be created in the security-dir of the current profile, but can be specified by absolute path. """) def _connection_file_default(self): return 'kernel-%i.json' % os.getpid() existing = Unicode('', config=True, help="""Connect to an already running kernel""") stylesheet = Unicode('', config=True, help="path to a custom CSS stylesheet") pure = CBool(False, config=True, help="Use a pure Python kernel instead of an IPython kernel.") plain = CBool( False, config=True, help= "Use a plaintext widget instead of rich text (plain can't print/save)." ) def _pure_changed(self, name, old, new): kind = 'plain' if self.plain else 'rich' self.config.ConsoleWidget.kind = kind if self.pure: self.widget_factory = FrontendWidget elif self.plain: self.widget_factory = IPythonWidget else: self.widget_factory = RichIPythonWidget _plain_changed = _pure_changed confirm_exit = CBool( True, config=True, help=""" Set to display confirmation dialog on exit. You can always use 'exit' or 'quit', to force a direct exit without any confirmation.""", ) # the factory for creating a widget widget_factory = Any(RichIPythonWidget) def parse_command_line(self, argv=None): super(IPythonQtConsoleApp, self).parse_command_line(argv) if argv is None: argv = sys.argv[1:] self.kernel_argv = list(argv) # copy # kernel should inherit default config file from frontend self.kernel_argv.append("--KernelApp.parent_appname='%s'" % self.name) # Scrub frontend-specific flags swallow_next = False was_flag = False # copy again, in case some aliases have the same name as a flag # argv = list(self.kernel_argv) for a in argv: if swallow_next: swallow_next = False # last arg was an alias, remove the next one # *unless* the last alias has a no-arg flag version, in which # case, don't swallow the next arg if it's also a flag: if not (was_flag and a.startswith('-')): self.kernel_argv.remove(a) continue if a.startswith('-'): split = a.lstrip('-').split('=') alias = split[0] if alias in qt_aliases: self.kernel_argv.remove(a) if len(split) == 1: # alias passed with arg via space swallow_next = True # could have been a flag that matches an alias, e.g. `existing` # in which case, we might not swallow the next arg was_flag = alias in qt_flags elif alias in qt_flags: # strip flag, but don't swallow next, as flags don't take args self.kernel_argv.remove(a) def init_connection_file(self): """find the connection file, and load the info if found. The current working directory and the current profile's security directory will be searched for the file if it is not given by absolute path. When attempting to connect to an existing kernel and the `--existing` argument does not match an existing file, it will be interpreted as a fileglob, and the matching file in the current profile's security dir with the latest access time will be used. """ if self.existing: try: cf = find_connection_file(self.existing) except Exception: self.log.critical( "Could not find existing kernel connection file %s", self.existing) self.exit(1) self.log.info("Connecting to existing kernel: %s" % cf) self.connection_file = cf # should load_connection_file only be used for existing? # as it is now, this allows reusing ports if an existing # file is requested try: self.load_connection_file() except Exception: self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True) self.exit(1) def load_connection_file(self): """load ip/port/hmac config from JSON connection file""" # this is identical to KernelApp.load_connection_file # perhaps it can be centralized somewhere? try: fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir]) except IOError: self.log.debug("Connection File not found: %s", self.connection_file) return self.log.debug(u"Loading connection file %s", fname) with open(fname) as f: s = f.read() cfg = json.loads(s) if self.ip == LOCALHOST and 'ip' in cfg: # not overridden by config or cl_args self.ip = cfg['ip'] for channel in ('hb', 'shell', 'iopub', 'stdin'): name = channel + '_port' if getattr(self, name) == 0 and name in cfg: # not overridden by config or cl_args setattr(self, name, cfg[name]) if 'key' in cfg: self.config.Session.key = str_to_bytes(cfg['key']) def init_ssh(self): """set up ssh tunnels, if needed.""" if not self.sshserver and not self.sshkey: return if self.sshkey and not self.sshserver: # specifying just the key implies that we are connecting directly self.sshserver = self.ip self.ip = LOCALHOST # build connection dict for tunnels: info = dict(ip=self.ip, shell_port=self.shell_port, iopub_port=self.iopub_port, stdin_port=self.stdin_port, hb_port=self.hb_port) self.log.info("Forwarding connections to %s via %s" % (self.ip, self.sshserver)) # tunnels return a new set of ports, which will be on localhost: self.ip = LOCALHOST try: newports = tunnel_to_kernel(info, self.sshserver, self.sshkey) except: # even catch KeyboardInterrupt self.log.error("Could not setup tunnels", exc_info=True) self.exit(1) self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports cf = self.connection_file base, ext = os.path.splitext(cf) base = os.path.basename(base) self.connection_file = os.path.basename(base) + '-ssh' + ext self.log.critical("To connect another client via this tunnel, use:") self.log.critical("--existing %s" % self.connection_file) def _new_connection_file(self): return os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % uuid.uuid4()) def init_kernel_manager(self): # Don't let Qt or ZMQ swallow KeyboardInterupts. signal.signal(signal.SIGINT, signal.SIG_DFL) sec = self.profile_dir.security_dir try: cf = filefind(self.connection_file, ['.', sec]) except IOError: # file might not exist if self.connection_file == os.path.basename(self.connection_file): # just shortname, put it in security dir cf = os.path.join(sec, self.connection_file) else: cf = self.connection_file # Create a KernelManager and start a kernel. self.kernel_manager = QtKernelManager( ip=self.ip, shell_port=self.shell_port, iopub_port=self.iopub_port, stdin_port=self.stdin_port, hb_port=self.hb_port, connection_file=cf, config=self.config, ) # start the kernel if not self.existing: kwargs = dict(ipython=not self.pure) kwargs['extra_arguments'] = self.kernel_argv self.kernel_manager.start_kernel(**kwargs) elif self.sshserver: # ssh, write new connection file self.kernel_manager.write_connection_file() self.kernel_manager.start_channels() def new_frontend_master(self): """ Create and return new frontend attached to new kernel, launched on localhost. """ ip = self.ip if self.ip in LOCAL_IPS else LOCALHOST kernel_manager = QtKernelManager( ip=ip, connection_file=self._new_connection_file(), config=self.config, ) # start the kernel kwargs = dict(ipython=not self.pure) kwargs['extra_arguments'] = self.kernel_argv kernel_manager.start_kernel(**kwargs) kernel_manager.start_channels() widget = self.widget_factory(config=self.config, local_kernel=True) widget.kernel_manager = kernel_manager widget._existing = False widget._may_close = True widget._confirm_exit = self.confirm_exit return widget def new_frontend_slave(self, current_widget): """Create and return a new frontend attached to an existing kernel. Parameters ---------- current_widget : IPythonWidget The IPythonWidget whose kernel this frontend is to share """ kernel_manager = QtKernelManager( connection_file=current_widget.kernel_manager.connection_file, config=self.config, ) kernel_manager.load_connection_file() kernel_manager.start_channels() widget = self.widget_factory(config=self.config, local_kernel=False) widget._existing = True widget._may_close = False widget._confirm_exit = False widget.kernel_manager = kernel_manager return widget def init_qt_elements(self): # Create the widget. self.app = QtGui.QApplication([]) base_path = os.path.abspath(os.path.dirname(__file__)) icon_path = os.path.join(base_path, 'resources', 'icon', 'IPythonConsole.svg') self.app.icon = QtGui.QIcon(icon_path) QtGui.QApplication.setWindowIcon(self.app.icon) local_kernel = (not self.existing) or self.ip in LOCAL_IPS self.widget = self.widget_factory(config=self.config, local_kernel=local_kernel) self.widget._existing = self.existing self.widget._may_close = not self.existing self.widget._confirm_exit = self.confirm_exit self.widget.kernel_manager = self.kernel_manager self.window = MainWindow( self.app, confirm_exit=self.confirm_exit, new_frontend_factory=self.new_frontend_master, slave_frontend_factory=self.new_frontend_slave, ) self.window.log = self.log self.window.add_tab_with_frontend(self.widget) self.window.init_menu_bar() self.window.setWindowTitle('Python' if self.pure else 'IPython') def init_colors(self): """Configure the coloring of the widget""" # Note: This will be dramatically simplified when colors # are removed from the backend. if self.pure: # only IPythonWidget supports styling return # parse the colors arg down to current known labels try: colors = self.config.ZMQInteractiveShell.colors except AttributeError: colors = None try: style = self.config.IPythonWidget.syntax_style except AttributeError: style = None # find the value for colors: if colors: colors = colors.lower() if colors in ('lightbg', 'light'): colors = 'lightbg' elif colors in ('dark', 'linux'): colors = 'linux' else: colors = 'nocolor' elif style: if style == 'bw': colors = 'nocolor' elif styles.dark_style(style): colors = 'linux' else: colors = 'lightbg' else: colors = None # Configure the style. widget = self.widget if style: widget.style_sheet = styles.sheet_from_template(style, colors) widget.syntax_style = style widget._syntax_style_changed() widget._style_sheet_changed() elif colors: # use a default style widget.set_default_style(colors=colors) else: # this is redundant for now, but allows the widget's # defaults to change widget.set_default_style() if self.stylesheet: # we got an expicit stylesheet if os.path.isfile(self.stylesheet): with open(self.stylesheet) as f: sheet = f.read() widget.style_sheet = sheet widget._style_sheet_changed() else: raise IOError("Stylesheet %r not found." % self.stylesheet) @catch_config_error def initialize(self, argv=None): super(IPythonQtConsoleApp, self).initialize(argv) self.init_connection_file() default_secure(self.config) self.init_ssh() self.init_kernel_manager() self.init_qt_elements() self.init_colors() def start(self): # draw the window self.window.show() # Start the application main loop. self.app.exec_()
class IPCompleter(Completer): """Extension of the completer class with IPython-specific features""" def _greedy_changed(self, name, old, new): """update the splitter and readline delims when greedy is changed""" if new: self.splitter.delims = GREEDY_DELIMS else: self.splitter.delims = DELIMS if self.readline: self.readline.set_completer_delims(self.splitter.delims) merge_completions = CBool( True, config=True, help="""Whether to merge completion results into a single list If False, only the completion results from the first non-empty completer will be returned. """) omit__names = Enum( (0, 1, 2), default_value=2, config=True, help="""Instruct the completer to omit private method names Specifically, when completing on ``object.<tab>``. When 2 [default]: all names that start with '_' will be excluded. When 1: all 'magic' names (``__foo__``) will be excluded. When 0: nothing will be excluded. """) limit_to__all__ = CBool( default_value=False, config=True, help="""Instruct the completer to use __all__ for the completion Specifically, when completing on ``object.<tab>``. When True: only those names in obj.__all__ will be included. When False [default]: the __all__ attribute is ignored """) def __init__(self, shell=None, namespace=None, global_namespace=None, use_readline=True, config=None, **kwargs): """IPCompleter() -> completer Return a completer object suitable for use by the readline library via readline.set_completer(). Inputs: - shell: a pointer to the ipython shell itself. This is needed because this completer knows about magic functions, and those can only be accessed via the ipython instance. - namespace: an optional dict where completions are performed. - global_namespace: secondary optional dict for completions, to handle cases (such as IPython embedded inside functions) where both Python scopes are visible. use_readline : bool, optional If true, use the readline library. This completer can still function without readline, though in that case callers must provide some extra information on each call about the current line.""" self.magic_escape = ESC_MAGIC self.splitter = CompletionSplitter() # Readline configuration, only used by the rlcompleter method. if use_readline: # We store the right version of readline so that later code import IPython.utils.rlineimpl as readline self.readline = readline else: self.readline = None # _greedy_changed() depends on splitter and readline being defined: Completer.__init__(self, namespace=namespace, global_namespace=global_namespace, config=config, **kwargs) # List where completion matches will be stored self.matches = [] self.shell = shell # Regexp to split filenames with spaces in them self.space_name_re = re.compile(r'([^\\] )') # Hold a local ref. to glob.glob for speed self.glob = glob.glob # Determine if we are running on 'dumb' terminals, like (X)Emacs # buffers, to avoid completion problems. term = os.environ.get('TERM', 'xterm') self.dumb_terminal = term in ['dumb', 'emacs'] # Special handling of backslashes needed in win32 platforms if sys.platform == "win32": self.clean_glob = self._clean_glob_win32 else: self.clean_glob = self._clean_glob #regexp to parse docstring for function signature self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*') self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)') #use this if positional argument name is also needed #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)') # All active matcher routines for completion self.matchers = [ self.python_matches, self.file_matches, self.magic_matches, self.python_func_kw_matches, self.dict_key_matches, ] def all_completions(self, text): """ Wrapper around the complete method for the benefit of emacs and pydb. """ return self.complete(text)[1] def _clean_glob(self, text): return self.glob("%s*" % text) def _clean_glob_win32(self, text): return [f.replace("\\", "/") for f in self.glob("%s*" % text)] def file_matches(self, text): """Match filenames, expanding ~USER type strings. Most of the seemingly convoluted logic in this completer is an attempt to handle filenames with spaces in them. And yet it's not quite perfect, because Python's readline doesn't expose all of the GNU readline details needed for this to be done correctly. For a filename with a space in it, the printed completions will be only the parts after what's already been typed (instead of the full completions, as is normally done). I don't think with the current (as of Python 2.3) Python readline it's possible to do better.""" #io.rprint('Completer->file_matches: <%r>' % text) # dbg # chars that require escaping with backslash - i.e. chars # that readline treats incorrectly as delimiters, but we # don't want to treat as delimiters in filename matching # when escaped with backslash if text.startswith('!'): text = text[1:] text_prefix = '!' else: text_prefix = '' text_until_cursor = self.text_until_cursor # track strings with open quotes open_quotes = has_open_quotes(text_until_cursor) if '(' in text_until_cursor or '[' in text_until_cursor: lsplit = text else: try: # arg_split ~ shlex.split, but with unicode bugs fixed by us lsplit = arg_split(text_until_cursor)[-1] except ValueError: # typically an unmatched ", or backslash without escaped char. if open_quotes: lsplit = text_until_cursor.split(open_quotes)[-1] else: return [] except IndexError: # tab pressed on empty line lsplit = "" if not open_quotes and lsplit != protect_filename(lsplit): # if protectables are found, do matching on the whole escaped name has_protectables = True text0, text = text, lsplit else: has_protectables = False text = os.path.expanduser(text) if text == "": return [text_prefix + protect_filename(f) for f in self.glob("*")] # Compute the matches from the filesystem m0 = self.clean_glob(text.replace('\\', '')) if has_protectables: # If we had protectables, we need to revert our changes to the # beginning of filename so that we don't double-write the part # of the filename we have so far len_lsplit = len(lsplit) matches = [ text_prefix + text0 + protect_filename(f[len_lsplit:]) for f in m0 ] else: if open_quotes: # if we have a string with an open quote, we don't need to # protect the names at all (and we _shouldn't_, as it # would cause bugs when the filesystem call is made). matches = m0 else: matches = [text_prefix + protect_filename(f) for f in m0] #io.rprint('mm', matches) # dbg # Mark directories in input list by appending '/' to their names. matches = [x + '/' if os.path.isdir(x) else x for x in matches] return matches def magic_matches(self, text): """Match magics""" #print 'Completer->magic_matches:',text,'lb',self.text_until_cursor # dbg # Get all shell magics now rather than statically, so magics loaded at # runtime show up too. lsm = self.shell.magics_manager.lsmagic() line_magics = lsm['line'] cell_magics = lsm['cell'] pre = self.magic_escape pre2 = pre + pre # Completion logic: # - user gives %%: only do cell magics # - user gives %: do both line and cell magics # - no prefix: do both # In other words, line magics are skipped if the user gives %% explicitly bare_text = text.lstrip(pre) comp = [pre2 + m for m in cell_magics if m.startswith(bare_text)] if not text.startswith(pre2): comp += [pre + m for m in line_magics if m.startswith(bare_text)] return comp def python_matches(self, text): """Match attributes or global python names""" #io.rprint('Completer->python_matches, txt=%r' % text) # dbg if "." in text: try: matches = self.attr_matches(text) if text.endswith('.') and self.omit__names: if self.omit__names == 1: # true if txt is _not_ a __ name, false otherwise: no__name = ( lambda txt: re.match(r'.*\.__.*?__', txt) is None) else: # true if txt is _not_ a _ name, false otherwise: no__name = (lambda txt: re.match( r'\._.*?', txt[txt.rindex('.'):]) is None) matches = filter(no__name, matches) except NameError: # catches <undefined attributes>.<tab> matches = [] else: matches = self.global_matches(text) return matches def _default_arguments_from_docstring(self, doc): """Parse the first line of docstring for call signature. Docstring should be of the form 'min(iterable[, key=func])\n'. It can also parse cython docstring of the form 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'. """ if doc is None: return [] #care only the firstline line = doc.lstrip().splitlines()[0] #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*') #'min(iterable[, key=func])\n' -> 'iterable[, key=func]' sig = self.docstring_sig_re.search(line) if sig is None: return [] # iterable[, key=func]' -> ['iterable[' ,' key=func]'] sig = sig.groups()[0].split(',') ret = [] for s in sig: #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)') ret += self.docstring_kwd_re.findall(s) return ret def _default_arguments(self, obj): """Return the list of default arguments of obj if it is callable, or empty list otherwise.""" call_obj = obj ret = [] if inspect.isbuiltin(obj): pass elif not (inspect.isfunction(obj) or inspect.ismethod(obj)): if inspect.isclass(obj): #for cython embededsignature=True the constructor docstring #belongs to the object itself not __init__ ret += self._default_arguments_from_docstring( getattr(obj, '__doc__', '')) # for classes, check for __init__,__new__ call_obj = (getattr(obj, '__init__', None) or getattr(obj, '__new__', None)) # for all others, check if they are __call__able elif hasattr(obj, '__call__'): call_obj = obj.__call__ ret += self._default_arguments_from_docstring( getattr(call_obj, '__doc__', '')) try: args, _, _1, defaults = inspect.getargspec(call_obj) if defaults: ret += args[-len(defaults):] except TypeError: pass return list(set(ret)) def python_func_kw_matches(self, text): """Match named parameters (kwargs) of the last open function""" if "." in text: # a parameter cannot be dotted return [] try: regexp = self.__funcParamsRegex except AttributeError: regexp = self.__funcParamsRegex = re.compile( r''' '.*?(?<!\\)' | # single quoted strings or ".*?(?<!\\)" | # double quoted strings or \w+ | # identifier \S # other characters ''', re.VERBOSE | re.DOTALL) # 1. find the nearest identifier that comes before an unclosed # parenthesis before the cursor # e.g. for "foo (1+bar(x), pa<cursor>,a=1)", the candidate is "foo" tokens = regexp.findall(self.text_until_cursor) tokens.reverse() iterTokens = iter(tokens) openPar = 0 for token in iterTokens: if token == ')': openPar -= 1 elif token == '(': openPar += 1 if openPar > 0: # found the last unclosed parenthesis break else: return [] # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" ) ids = [] isId = re.compile(r'\w+$').match while True: try: ids.append(next(iterTokens)) if not isId(ids[-1]): ids.pop() break if not next(iterTokens) == '.': break except StopIteration: break # lookup the candidate callable matches either using global_matches # or attr_matches for dotted names if len(ids) == 1: callableMatches = self.global_matches(ids[0]) else: callableMatches = self.attr_matches('.'.join(ids[::-1])) argMatches = [] for callableMatch in callableMatches: try: namedArgs = self._default_arguments( eval(callableMatch, self.namespace)) except: continue for namedArg in namedArgs: if namedArg.startswith(text): argMatches.append("%s=" % namedArg) return argMatches def dict_key_matches(self, text): "Match string keys in a dictionary, after e.g. 'foo[' " def get_keys(obj): # Only allow completion for known in-memory dict-like types if isinstance(obj, dict) or\ _safe_isinstance(obj, 'pandas', 'DataFrame'): try: return list(obj.keys()) except Exception: return [] elif _safe_isinstance(obj, 'numpy', 'ndarray') or\ _safe_isinstance(obj, 'numpy', 'void'): return obj.dtype.names or [] return [] try: regexps = self.__dict_key_regexps except AttributeError: dict_key_re_fmt = r'''(?x) ( # match dict-referring expression wrt greedy setting %s ) \[ # open bracket \s* # and optional whitespace ([uUbB]? # string prefix (r not handled) (?: # unclosed string '(?:[^']|(?<!\\)\\')* | "(?:[^"]|(?<!\\)\\")* ) )? $ ''' regexps = self.__dict_key_regexps = { False: re.compile(dict_key_re_fmt % ''' # identifiers separated by . (?!\d)\w+ (?:\.(?!\d)\w+)* '''), True: re.compile(dict_key_re_fmt % ''' .+ ''') } match = regexps[self.greedy].search(self.text_until_cursor) if match is None: return [] expr, prefix = match.groups() try: obj = eval(expr, self.namespace) except Exception: try: obj = eval(expr, self.global_namespace) except Exception: return [] keys = get_keys(obj) if not keys: return keys closing_quote, token_offset, matches = match_dict_keys(keys, prefix) if not matches: return matches # get the cursor position of # - the text being completed # - the start of the key text # - the start of the completion text_start = len(self.text_until_cursor) - len(text) if prefix: key_start = match.start(2) completion_start = key_start + token_offset else: key_start = completion_start = match.end() # grab the leading prefix, to make sure all completions start with `text` if text_start > key_start: leading = '' else: leading = text[text_start:completion_start] # the index of the `[` character bracket_idx = match.end(1) # append closing quote and bracket as appropriate # this is *not* appropriate if the opening quote or bracket is outside # the text given to this method suf = '' continuation = self.line_buffer[len(self.text_until_cursor):] if key_start > text_start and closing_quote: # quotes were opened inside text, maybe close them if continuation.startswith(closing_quote): continuation = continuation[len(closing_quote):] else: suf += closing_quote if bracket_idx > text_start: # brackets were opened inside text, maybe close them if not continuation.startswith(']'): suf += ']' return [leading + k + suf for k in matches] def unicode_name_matches(self, text): u"""Match Latex-like syntax for unicode characters base on the name of the character. This does \\GREEK SMALL LETTER ETA -> η Works only on valid python 3 identifier, or on combining characters that will combine to form a valid identifier. Used on Python 3 only. """ slashpos = text.rfind('\\') if slashpos > -1: s = text[slashpos + 1:] try: unic = unicodedata.lookup(s) # allow combining chars if ('a' + unic).isidentifier(): return '\\' + s, [unic] except KeyError as e: pass return u'', [] def latex_matches(self, text): u"""Match Latex syntax for unicode characters. This does both \\alp -> \\alpha and \\alpha -> α Used on Python 3 only. """ slashpos = text.rfind('\\') if slashpos > -1: s = text[slashpos:] if s in latex_symbols: # Try to complete a full latex symbol to unicode # \\alpha -> α return s, [latex_symbols[s]] else: # If a user has partially typed a latex symbol, give them # a full list of options \al -> [\aleph, \alpha] matches = [k for k in latex_symbols if k.startswith(s)] return s, matches return u'', [] def dispatch_custom_completer(self, text): #io.rprint("Custom! '%s' %s" % (text, self.custom_completers)) # dbg line = self.line_buffer if not line.strip(): return None # Create a little structure to pass all the relevant information about # the current completion to any custom completer. event = Bunch() event.line = line event.symbol = text cmd = line.split(None, 1)[0] event.command = cmd event.text_until_cursor = self.text_until_cursor #print "\ncustom:{%s]\n" % event # dbg # for foo etc, try also to find completer for %foo if not cmd.startswith(self.magic_escape): try_magic = self.custom_completers.s_matches(self.magic_escape + cmd) else: try_magic = [] for c in itertools.chain( self.custom_completers.s_matches(cmd), try_magic, self.custom_completers.flat_matches(self.text_until_cursor)): #print "try",c # dbg try: res = c(event) if res: # first, try case sensitive match withcase = [r for r in res if r.startswith(text)] if withcase: return withcase # if none, then case insensitive ones are ok too text_low = text.lower() return [r for r in res if r.lower().startswith(text_low)] except TryNext: pass return None def complete(self, text=None, line_buffer=None, cursor_pos=None): """Find completions for the given text and line context. Note that both the text and the line_buffer are optional, but at least one of them must be given. Parameters ---------- text : string, optional Text to perform the completion on. If not given, the line buffer is split using the instance's CompletionSplitter object. line_buffer : string, optional If not given, the completer attempts to obtain the current line buffer via readline. This keyword allows clients which are requesting for text completions in non-readline contexts to inform the completer of the entire text. cursor_pos : int, optional Index of the cursor in the full line buffer. Should be provided by remote frontends where kernel has no access to frontend state. Returns ------- text : str Text that was actually used in the completion. matches : list A list of completion matches. """ # io.rprint('\nCOMP1 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg # if the cursor position isn't given, the only sane assumption we can # make is that it's at the end of the line (the common case) if cursor_pos is None: cursor_pos = len(line_buffer) if text is None else len(text) if PY3: base_text = text if not line_buffer else line_buffer[:cursor_pos] latex_text, latex_matches = self.latex_matches(base_text) if latex_matches: return latex_text, latex_matches name_text = '' name_matches = [] for meth in (self.unicode_name_matches, back_latex_name_matches, back_unicode_name_matches): name_text, name_matches = meth(base_text) if name_text: return name_text, name_matches # if text is either None or an empty string, rely on the line buffer if not text: text = self.splitter.split_line(line_buffer, cursor_pos) # If no line buffer is given, assume the input text is all there was if line_buffer is None: line_buffer = text self.line_buffer = line_buffer self.text_until_cursor = self.line_buffer[:cursor_pos] # io.rprint('COMP2 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg # Start with a clean slate of completions self.matches[:] = [] custom_res = self.dispatch_custom_completer(text) if custom_res is not None: # did custom completers produce something? self.matches = custom_res else: # Extend the list of completions with the results of each # matcher, so we return results to the user from all # namespaces. if self.merge_completions: self.matches = [] for matcher in self.matchers: try: self.matches.extend(matcher(text)) except: # Show the ugly traceback if the matcher causes an # exception, but do NOT crash the kernel! sys.excepthook(*sys.exc_info()) else: for matcher in self.matchers: self.matches = matcher(text) if self.matches: break # FIXME: we should extend our api to return a dict with completions for # different types of objects. The rlcomplete() method could then # simply collapse the dict into a list for readline, but we'd have # richer completion semantics in other evironments. # use penalize_magics_key to put magics after variables with same name self.matches = sorted(set(self.matches), key=penalize_magics_key) #io.rprint('COMP TEXT, MATCHES: %r, %r' % (text, self.matches)) # dbg return text, self.matches def rlcomplete(self, text, state): """Return the state-th possible completion for 'text'. This is called successively with state == 0, 1, 2, ... until it returns None. The completion should begin with 'text'. Parameters ---------- text : string Text to perform the completion on. state : int Counter used by readline. """ if state == 0: self.line_buffer = line_buffer = self.readline.get_line_buffer() cursor_pos = self.readline.get_endidx() #io.rprint("\nRLCOMPLETE: %r %r %r" % # (text, line_buffer, cursor_pos) ) # dbg # if there is only a tab on a line with only whitespace, instead of # the mostly useless 'do you want to see all million completions' # message, just do the right thing and give the user his tab! # Incidentally, this enables pasting of tabbed text from an editor # (as long as autoindent is off). # It should be noted that at least pyreadline still shows file # completions - is there a way around it? # don't apply this on 'dumb' terminals, such as emacs buffers, so # we don't interfere with their own tab-completion mechanism. if not (self.dumb_terminal or line_buffer.strip()): self.readline.insert_text('\t') sys.stdout.flush() return None # Note: debugging exceptions that may occur in completion is very # tricky, because readline unconditionally silences them. So if # during development you suspect a bug in the completion code, turn # this flag on temporarily by uncommenting the second form (don't # flip the value in the first line, as the '# dbg' marker can be # automatically detected and is used elsewhere). DEBUG = False #DEBUG = True # dbg if DEBUG: try: self.complete(text, line_buffer, cursor_pos) except: import traceback traceback.print_exc() else: # The normal production version is here # This method computes the self.matches array self.complete(text, line_buffer, cursor_pos) try: return self.matches[state] except IndexError: return None
class IPCompleter(Completer): """Extension of the completer class with IPython-specific features""" def _greedy_changed(self, name, old, new): """update the splitter and readline delims when greedy is changed""" if new: self.splitter.delims = GREEDY_DELIMS else: self.splitter.delims = DELIMS if self.readline: self.readline.set_completer_delims(self.splitter.delims) merge_completions = CBool(True, config=True, help="""Whether to merge completion results into a single list If False, only the completion results from the first non-empty completer will be returned. """ ) omit__names = Enum((0,1,2), default_value=2, config=True, help="""Instruct the completer to omit private method names Specifically, when completing on ``object.<tab>``. When 2 [default]: all names that start with '_' will be excluded. When 1: all 'magic' names (``__foo__``) will be excluded. When 0: nothing will be excluded. """ ) limit_to__all__ = CBool(default_value=False, config=True, help="""Instruct the completer to use __all__ for the completion Specifically, when completing on ``object.<tab>``. When True: only those names in obj.__all__ will be included. When False [default]: the __all__ attribute is ignored """ ) def __init__(self, shell=None, namespace=None, global_namespace=None, alias_table=None, use_readline=True, config=None, **kwargs): """IPCompleter() -> completer Return a completer object suitable for use by the readline library via readline.set_completer(). Inputs: - shell: a pointer to the ipython shell itself. This is needed because this completer knows about magic functions, and those can only be accessed via the ipython instance. - namespace: an optional dict where completions are performed. - global_namespace: secondary optional dict for completions, to handle cases (such as IPython embedded inside functions) where both Python scopes are visible. - If alias_table is supplied, it should be a dictionary of aliases to complete. use_readline : bool, optional If true, use the readline library. This completer can still function without readline, though in that case callers must provide some extra information on each call about the current line.""" self.magic_escape = ESC_MAGIC self.splitter = CompletionSplitter() # Readline configuration, only used by the rlcompleter method. if use_readline: # We store the right version of readline so that later code import IPython.utils.rlineimpl as readline self.readline = readline else: self.readline = None # _greedy_changed() depends on splitter and readline being defined: Completer.__init__(self, namespace=namespace, global_namespace=global_namespace, config=config, **kwargs) # List where completion matches will be stored self.matches = [] self.shell = shell if alias_table is None: alias_table = {} self.alias_table = alias_table # Regexp to split filenames with spaces in them self.space_name_re = re.compile(r'([^\\] )') # Hold a local ref. to glob.glob for speed self.glob = glob.glob # Determine if we are running on 'dumb' terminals, like (X)Emacs # buffers, to avoid completion problems. term = os.environ.get('TERM','xterm') self.dumb_terminal = term in ['dumb','emacs'] # Special handling of backslashes needed in win32 platforms if sys.platform == "win32": self.clean_glob = self._clean_glob_win32 else: self.clean_glob = self._clean_glob # All active matcher routines for completion self.matchers = [self.python_matches, self.file_matches, self.magic_matches, self.alias_matches, self.python_func_kw_matches, ] def all_completions(self, text): """ Wrapper around the complete method for the benefit of emacs and pydb. """ return self.complete(text)[1] def _clean_glob(self,text): return self.glob("%s*" % text) def _clean_glob_win32(self,text): return [f.replace("\\","/") for f in self.glob("%s*" % text)] def file_matches(self, text): """Match filenames, expanding ~USER type strings. Most of the seemingly convoluted logic in this completer is an attempt to handle filenames with spaces in them. And yet it's not quite perfect, because Python's readline doesn't expose all of the GNU readline details needed for this to be done correctly. For a filename with a space in it, the printed completions will be only the parts after what's already been typed (instead of the full completions, as is normally done). I don't think with the current (as of Python 2.3) Python readline it's possible to do better.""" #io.rprint('Completer->file_matches: <%r>' % text) # dbg # chars that require escaping with backslash - i.e. chars # that readline treats incorrectly as delimiters, but we # don't want to treat as delimiters in filename matching # when escaped with backslash if text.startswith('!'): text = text[1:] text_prefix = '!' else: text_prefix = '' text_until_cursor = self.text_until_cursor # track strings with open quotes open_quotes = has_open_quotes(text_until_cursor) if '(' in text_until_cursor or '[' in text_until_cursor: lsplit = text else: try: # arg_split ~ shlex.split, but with unicode bugs fixed by us lsplit = arg_split(text_until_cursor)[-1] except ValueError: # typically an unmatched ", or backslash without escaped char. if open_quotes: lsplit = text_until_cursor.split(open_quotes)[-1] else: return [] except IndexError: # tab pressed on empty line lsplit = "" if not open_quotes and lsplit != protect_filename(lsplit): # if protectables are found, do matching on the whole escaped name has_protectables = True text0,text = text,lsplit else: has_protectables = False text = os.path.expanduser(text) if text == "": return [text_prefix + protect_filename(f) for f in self.glob("*")] # Compute the matches from the filesystem m0 = self.clean_glob(text.replace('\\','')) if has_protectables: # If we had protectables, we need to revert our changes to the # beginning of filename so that we don't double-write the part # of the filename we have so far len_lsplit = len(lsplit) matches = [text_prefix + text0 + protect_filename(f[len_lsplit:]) for f in m0] else: if open_quotes: # if we have a string with an open quote, we don't need to # protect the names at all (and we _shouldn't_, as it # would cause bugs when the filesystem call is made). matches = m0 else: matches = [text_prefix + protect_filename(f) for f in m0] #io.rprint('mm', matches) # dbg # Mark directories in input list by appending '/' to their names. matches = [x+'/' if os.path.isdir(x) else x for x in matches] return matches def magic_matches(self, text): """Match magics""" #print 'Completer->magic_matches:',text,'lb',self.text_until_cursor # dbg # Get all shell magics now rather than statically, so magics loaded at # runtime show up too. lsm = self.shell.magics_manager.lsmagic() line_magics = lsm['line'] cell_magics = lsm['cell'] pre = self.magic_escape pre2 = pre+pre # Completion logic: # - user gives %%: only do cell magics # - user gives %: do both line and cell magics # - no prefix: do both # In other words, line magics are skipped if the user gives %% explicitly bare_text = text.lstrip(pre) comp = [ pre2+m for m in cell_magics if m.startswith(bare_text)] if not text.startswith(pre2): comp += [ pre+m for m in line_magics if m.startswith(bare_text)] return comp def alias_matches(self, text): """Match internal system aliases""" #print 'Completer->alias_matches:',text,'lb',self.text_until_cursor # dbg # if we are not in the first 'item', alias matching # doesn't make sense - unless we are starting with 'sudo' command. main_text = self.text_until_cursor.lstrip() if ' ' in main_text and not main_text.startswith('sudo'): return [] text = os.path.expanduser(text) aliases = self.alias_table.keys() if text == '': return aliases else: return [a for a in aliases if a.startswith(text)] def python_matches(self,text): """Match attributes or global python names""" #io.rprint('Completer->python_matches, txt=%r' % text) # dbg if "." in text: try: matches = self.attr_matches(text) if text.endswith('.') and self.omit__names: if self.omit__names == 1: # true if txt is _not_ a __ name, false otherwise: no__name = (lambda txt: re.match(r'.*\.__.*?__',txt) is None) else: # true if txt is _not_ a _ name, false otherwise: no__name = (lambda txt: re.match(r'.*\._.*?',txt) is None) matches = filter(no__name, matches) except NameError: # catches <undefined attributes>.<tab> matches = [] else: matches = self.global_matches(text) return matches def _default_arguments(self, obj): """Return the list of default arguments of obj if it is callable, or empty list otherwise.""" if not (inspect.isfunction(obj) or inspect.ismethod(obj)): # for classes, check for __init__,__new__ if inspect.isclass(obj): obj = (getattr(obj,'__init__',None) or getattr(obj,'__new__',None)) # for all others, check if they are __call__able elif hasattr(obj, '__call__'): obj = obj.__call__ # XXX: is there a way to handle the builtins ? try: args,_,_1,defaults = inspect.getargspec(obj) if defaults: return args[-len(defaults):] except TypeError: pass return [] def python_func_kw_matches(self,text): """Match named parameters (kwargs) of the last open function""" if "." in text: # a parameter cannot be dotted return [] try: regexp = self.__funcParamsRegex except AttributeError: regexp = self.__funcParamsRegex = re.compile(r''' '.*?(?<!\\)' | # single quoted strings or ".*?(?<!\\)" | # double quoted strings or \w+ | # identifier \S # other characters ''', re.VERBOSE | re.DOTALL) # 1. find the nearest identifier that comes before an unclosed # parenthesis before the cursor # e.g. for "foo (1+bar(x), pa<cursor>,a=1)", the candidate is "foo" tokens = regexp.findall(self.text_until_cursor) tokens.reverse() iterTokens = iter(tokens); openPar = 0 for token in iterTokens: if token == ')': openPar -= 1 elif token == '(': openPar += 1 if openPar > 0: # found the last unclosed parenthesis break else: return [] # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" ) ids = [] isId = re.compile(r'\w+$').match while True: try: ids.append(iterTokens.next()) if not isId(ids[-1]): ids.pop(); break if not iterTokens.next() == '.': break except StopIteration: break # lookup the candidate callable matches either using global_matches # or attr_matches for dotted names if len(ids) == 1: callableMatches = self.global_matches(ids[0]) else: callableMatches = self.attr_matches('.'.join(ids[::-1])) argMatches = [] for callableMatch in callableMatches: try: namedArgs = self._default_arguments(eval(callableMatch, self.namespace)) except: continue for namedArg in namedArgs: if namedArg.startswith(text): argMatches.append("%s=" %namedArg) return argMatches def dispatch_custom_completer(self, text): #io.rprint("Custom! '%s' %s" % (text, self.custom_completers)) # dbg line = self.line_buffer if not line.strip(): return None # Create a little structure to pass all the relevant information about # the current completion to any custom completer. event = Bunch() event.line = line event.symbol = text cmd = line.split(None,1)[0] event.command = cmd event.text_until_cursor = self.text_until_cursor #print "\ncustom:{%s]\n" % event # dbg # for foo etc, try also to find completer for %foo if not cmd.startswith(self.magic_escape): try_magic = self.custom_completers.s_matches( self.magic_escape + cmd) else: try_magic = [] for c in itertools.chain(self.custom_completers.s_matches(cmd), try_magic, self.custom_completers.flat_matches(self.text_until_cursor)): #print "try",c # dbg try: res = c(event) if res: # first, try case sensitive match withcase = [r for r in res if r.startswith(text)] if withcase: return withcase # if none, then case insensitive ones are ok too text_low = text.lower() return [r for r in res if r.lower().startswith(text_low)] except TryNext: pass return None def complete(self, text=None, line_buffer=None, cursor_pos=None): """Find completions for the given text and line context. This is called successively with state == 0, 1, 2, ... until it returns None. The completion should begin with 'text'. Note that both the text and the line_buffer are optional, but at least one of them must be given. Parameters ---------- text : string, optional Text to perform the completion on. If not given, the line buffer is split using the instance's CompletionSplitter object. line_buffer : string, optional If not given, the completer attempts to obtain the current line buffer via readline. This keyword allows clients which are requesting for text completions in non-readline contexts to inform the completer of the entire text. cursor_pos : int, optional Index of the cursor in the full line buffer. Should be provided by remote frontends where kernel has no access to frontend state. Returns ------- text : str Text that was actually used in the completion. matches : list A list of completion matches. """ #io.rprint('\nCOMP1 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg # if the cursor position isn't given, the only sane assumption we can # make is that it's at the end of the line (the common case) if cursor_pos is None: cursor_pos = len(line_buffer) if text is None else len(text) # if text is either None or an empty string, rely on the line buffer if not text: text = self.splitter.split_line(line_buffer, cursor_pos) # If no line buffer is given, assume the input text is all there was if line_buffer is None: line_buffer = text self.line_buffer = line_buffer self.text_until_cursor = self.line_buffer[:cursor_pos] #io.rprint('COMP2 %r %r %r' % (text, line_buffer, cursor_pos)) # dbg # Start with a clean slate of completions self.matches[:] = [] custom_res = self.dispatch_custom_completer(text) if custom_res is not None: # did custom completers produce something? self.matches = custom_res else: # Extend the list of completions with the results of each # matcher, so we return results to the user from all # namespaces. if self.merge_completions: self.matches = [] for matcher in self.matchers: try: self.matches.extend(matcher(text)) except: # Show the ugly traceback if the matcher causes an # exception, but do NOT crash the kernel! sys.excepthook(*sys.exc_info()) else: for matcher in self.matchers: self.matches = matcher(text) if self.matches: break # FIXME: we should extend our api to return a dict with completions for # different types of objects. The rlcomplete() method could then # simply collapse the dict into a list for readline, but we'd have # richer completion semantics in other evironments. self.matches = sorted(set(self.matches)) #io.rprint('COMP TEXT, MATCHES: %r, %r' % (text, self.matches)) # dbg return text, self.matches def rlcomplete(self, text, state): """Return the state-th possible completion for 'text'. This is called successively with state == 0, 1, 2, ... until it returns None. The completion should begin with 'text'. Parameters ---------- text : string Text to perform the completion on. state : int Counter used by readline. """ if state==0: self.line_buffer = line_buffer = self.readline.get_line_buffer() cursor_pos = self.readline.get_endidx() #io.rprint("\nRLCOMPLETE: %r %r %r" % # (text, line_buffer, cursor_pos) ) # dbg # if there is only a tab on a line with only whitespace, instead of # the mostly useless 'do you want to see all million completions' # message, just do the right thing and give the user his tab! # Incidentally, this enables pasting of tabbed text from an editor # (as long as autoindent is off). # It should be noted that at least pyreadline still shows file # completions - is there a way around it? # don't apply this on 'dumb' terminals, such as emacs buffers, so # we don't interfere with their own tab-completion mechanism. if not (self.dumb_terminal or line_buffer.strip()): self.readline.insert_text('\t') sys.stdout.flush() return None # Note: debugging exceptions that may occur in completion is very # tricky, because readline unconditionally silences them. So if # during development you suspect a bug in the completion code, turn # this flag on temporarily by uncommenting the second form (don't # flip the value in the first line, as the '# dbg' marker can be # automatically detected and is used elsewhere). DEBUG = False #DEBUG = True # dbg if DEBUG: try: self.complete(text, line_buffer, cursor_pos) except: import traceback; traceback.print_exc() else: # The normal production version is here # This method computes the self.matches array self.complete(text, line_buffer, cursor_pos) try: return self.matches[state] except IndexError: return None
class IPythonConsoleApp(Configurable): name = 'ipython-console-mixin' default_config_file_name = 'ipython_config.py' description = """ The IPython Mixin Console. This class contains the common portions of console client (QtConsole, ZMQ-based terminal console, etc). It is not a full console, in that launched terminal subprocesses will not be able to accept input. The Console using this mixing supports various extra features beyond the single-process Terminal IPython shell, such as connecting to existing kernel, via: ipython <appname> --existing as well as tunnel via SSH """ classes = classes flags = Dict(flags) aliases = Dict(aliases) kernel_manager_class = BlockingKernelManager kernel_argv = List(Unicode) # frontend flags&aliases to be stripped when building kernel_argv frontend_flags = Any(app_flags) frontend_aliases = Any(app_aliases) # create requested profiles by default, if they don't exist: auto_create = CBool(True) # connection info: ip = Unicode(LOCALHOST, config=True, help="""Set the kernel\'s IP address [default localhost]. If the IP address is something other than localhost, then Consoles on other machines will be able to connect to the Kernel, so be careful!""") sshserver = Unicode( '', config=True, help="""The SSH server to use to connect to the kernel.""") sshkey = Unicode( '', config=True, help="""Path to the ssh key to use for logging in to the ssh server.""" ) hb_port = Int(0, config=True, help="set the heartbeat port [default: random]") shell_port = Int(0, config=True, help="set the shell (ROUTER) port [default: random]") iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]") stdin_port = Int(0, config=True, help="set the stdin (DEALER) port [default: random]") connection_file = Unicode( '', config=True, help= """JSON file in which to store connection info [default: kernel-<pid>.json] This file will contain the IP, ports, and authentication key needed to connect clients to this kernel. By default, this file will be created in the security-dir of the current profile, but can be specified by absolute path. """) def _connection_file_default(self): return 'kernel-%i.json' % os.getpid() existing = CUnicode('', config=True, help="""Connect to an already running kernel""") confirm_exit = CBool( True, config=True, help=""" Set to display confirmation dialog on exit. You can always use 'exit' or 'quit', to force a direct exit without any confirmation.""", ) def build_kernel_argv(self, argv=None): """build argv to be passed to kernel subprocess""" if argv is None: argv = sys.argv[1:] self.kernel_argv = swallow_argv(argv, self.frontend_aliases, self.frontend_flags) # kernel should inherit default config file from frontend self.kernel_argv.append("--KernelApp.parent_appname='%s'" % self.name) def init_connection_file(self): """find the connection file, and load the info if found. The current working directory and the current profile's security directory will be searched for the file if it is not given by absolute path. When attempting to connect to an existing kernel and the `--existing` argument does not match an existing file, it will be interpreted as a fileglob, and the matching file in the current profile's security dir with the latest access time will be used. After this method is called, self.connection_file contains the *full path* to the connection file, never just its name. """ if self.existing: try: cf = find_connection_file(self.existing) except Exception: self.log.critical( "Could not find existing kernel connection file %s", self.existing) self.exit(1) self.log.info("Connecting to existing kernel: %s" % cf) self.connection_file = cf else: # not existing, check if we are going to write the file # and ensure that self.connection_file is a full path, not just the shortname try: cf = find_connection_file(self.connection_file) except Exception: # file might not exist if self.connection_file == os.path.basename( self.connection_file): # just shortname, put it in security dir cf = os.path.join(self.profile_dir.security_dir, self.connection_file) else: cf = self.connection_file self.connection_file = cf # should load_connection_file only be used for existing? # as it is now, this allows reusing ports if an existing # file is requested try: self.load_connection_file() except Exception: self.log.error("Failed to load connection file: %r", self.connection_file, exc_info=True) self.exit(1) def load_connection_file(self): """load ip/port/hmac config from JSON connection file""" # this is identical to KernelApp.load_connection_file # perhaps it can be centralized somewhere? try: fname = filefind(self.connection_file, ['.', self.profile_dir.security_dir]) except IOError: self.log.debug("Connection File not found: %s", self.connection_file) return self.log.debug(u"Loading connection file %s", fname) with open(fname) as f: s = f.read() cfg = json.loads(s) if self.ip == LOCALHOST and 'ip' in cfg: # not overridden by config or cl_args self.ip = cfg['ip'] for channel in ('hb', 'shell', 'iopub', 'stdin'): name = channel + '_port' if getattr(self, name) == 0 and name in cfg: # not overridden by config or cl_args setattr(self, name, cfg[name]) if 'key' in cfg: self.config.Session.key = str_to_bytes(cfg['key']) def init_ssh(self): """set up ssh tunnels, if needed.""" if not self.sshserver and not self.sshkey: return if self.sshkey and not self.sshserver: # specifying just the key implies that we are connecting directly self.sshserver = self.ip self.ip = LOCALHOST # build connection dict for tunnels: info = dict(ip=self.ip, shell_port=self.shell_port, iopub_port=self.iopub_port, stdin_port=self.stdin_port, hb_port=self.hb_port) self.log.info("Forwarding connections to %s via %s" % (self.ip, self.sshserver)) # tunnels return a new set of ports, which will be on localhost: self.ip = LOCALHOST try: newports = tunnel_to_kernel(info, self.sshserver, self.sshkey) except: # even catch KeyboardInterrupt self.log.error("Could not setup tunnels", exc_info=True) self.exit(1) self.shell_port, self.iopub_port, self.stdin_port, self.hb_port = newports cf = self.connection_file base, ext = os.path.splitext(cf) base = os.path.basename(base) self.connection_file = os.path.basename(base) + '-ssh' + ext self.log.critical("To connect another client via this tunnel, use:") self.log.critical("--existing %s" % self.connection_file) def _new_connection_file(self): cf = '' while not cf: # we don't need a 128b id to distinguish kernels, use more readable # 48b node segment (12 hex chars). Users running more than 32k simultaneous # kernels can subclass. ident = str(uuid.uuid4()).split('-')[-1] cf = os.path.join(self.profile_dir.security_dir, 'kernel-%s.json' % ident) # only keep if it's actually new. Protect against unlikely collision # in 48b random search space cf = cf if not os.path.exists(cf) else '' return cf def init_kernel_manager(self): # Don't let Qt or ZMQ swallow KeyboardInterupts. signal.signal(signal.SIGINT, signal.SIG_DFL) # Create a KernelManager and start a kernel. self.kernel_manager = self.kernel_manager_class( ip=self.ip, shell_port=self.shell_port, iopub_port=self.iopub_port, stdin_port=self.stdin_port, hb_port=self.hb_port, connection_file=self.connection_file, config=self.config, ) # start the kernel if not self.existing: self.kernel_manager.start_kernel(extra_arguments=self.kernel_argv) elif self.sshserver: # ssh, write new connection file self.kernel_manager.write_connection_file() atexit.register(self.kernel_manager.cleanup_connection_file) self.kernel_manager.start_channels() def initialize(self, argv=None): """ Classes which mix this class in should call: IPythonConsoleApp.initialize(self,argv) """ self.init_connection_file() default_secure(self.config) self.init_ssh() self.init_kernel_manager()
class TerminalInteractiveShell(InteractiveShell): autoedit_syntax = CBool(False, config=True, help="auto editing of files with syntax errors.") banner = Unicode('') banner1 = Unicode( default_banner, config=True, help="""The part of the banner to be printed before the profile""") banner2 = Unicode( '', config=True, help="""The part of the banner to be printed after the profile""") confirm_exit = CBool( True, config=True, 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' or 'quit', you can force a direct exit without any confirmation.""", ) # 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 = Unicode( get_default_editor(), config=True, help="Set the editor used by IPython (default to $EDITOR/vi/notepad).") pager = Unicode('less', config=True, help="The shell program to be used for paging.") screen_length = Integer( 0, config=True, 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 through a pager instead of directly printed. The default value for this is 0, which means IPython will auto-detect your screen size every time it needs to print certain potentially long strings (this doesn't change the behavior of the 'print' keyword, it's only triggered internally). If for some reason this isn't working well (it needs curses support), specify it yourself. Otherwise don't change the default.""", ) term_title = CBool(False, config=True, help="Enable auto setting the terminal title.") # In the terminal, GUI control is done via PyOS_InputHook from IPython.lib.inputhook import enable_gui enable_gui = staticmethod(enable_gui) def __init__(self, config=None, ipython_dir=None, profile_dir=None, user_ns=None, user_module=None, custom_exceptions=((), None), usage=None, banner1=None, banner2=None, display_banner=None): super(TerminalInteractiveShell, self).__init__(config=config, profile_dir=profile_dir, user_ns=user_ns, user_module=user_module, custom_exceptions=custom_exceptions) # use os.system instead of utils.process.system by default, # because piped system doesn't make sense in the Terminal: self.system = self.system_raw 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 aliases #------------------------------------------------------------------------- def init_alias(self): # The parent class defines aliases that can be safely used with any # frontend. super(TerminalInteractiveShell, self).init_alias() # Now define aliases that only make sense on the terminal, because they # need direct access to the console in a way that we can't emulate in # GUI or web frontend if os.name == 'posix': aliases = [('clear', 'clear'), ('more', 'more'), ('less', 'less'), ('man', 'man')] elif os.name == 'nt': aliases = [('cls', 'cls')] for name, cmd in aliases: self.alias_manager.define_alias(name, cmd) #------------------------------------------------------------------------- # 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 if self.profile and self.profile != 'default': self.banner += '\nIPython profile: %s\n' % self.profile if self.banner2: self.banner += '\n' + self.banner2 def init_usage(self, usage=None): if usage is None: self.usage = interactive_usage else: self.usage = usage #------------------------------------------------------------------------- # Mainloop and code execution logic #------------------------------------------------------------------------- def mainloop(self, display_banner=None): """Start the mainloop. If an optional banner argument is given, it will override the internally created default banner. """ with nested(self.builtin_trap, self.display_trap): while 1: try: self.interact(display_banner=display_banner) #self.interact_with_readline() # XXX for testing of a readline-decoupled repl loop, call # interact_with_readline above break except KeyboardInterrupt: # this should not be necessary, but KeyboardInterrupt # handling seems rather unpredictable... self.write("\nKeyboardInterrupt in interact()\n") def _replace_rlhist_multiline(self, source_raw, hlen_before_cell): """Store multiple lines as a single entry in history""" # do nothing without readline or disabled multiline if not self.has_readline or not self.multiline_history: return hlen_before_cell # windows rl has no remove_history_item if not hasattr(self.readline, "remove_history_item"): return hlen_before_cell # skip empty cells if not source_raw.rstrip(): return hlen_before_cell # nothing changed do nothing, e.g. when rl removes consecutive dups hlen = self.readline.get_current_history_length() if hlen == hlen_before_cell: return hlen_before_cell for i in range(hlen - hlen_before_cell): self.readline.remove_history_item(hlen - i - 1) stdin_encoding = get_stream_enc(sys.stdin, 'utf-8') self.readline.add_history( py3compat.unicode_to_str(source_raw.rstrip(), stdin_encoding)) return self.readline.get_current_history_length() 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 isinstance(display_banner, basestring): self.show_banner(display_banner) elif display_banner: self.show_banner() more = False if self.has_readline: self.readline_startup_hook(self.pre_readline) hlen_b4_cell = self.readline.get_current_history_length() else: hlen_b4_cell = 0 # 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.prompt_manager.render('in2') except: self.showtraceback() if self.autoindent: self.rl_do_indent = True else: try: prompt = self.separate_in + self.prompt_manager.render( 'in') except: self.showtraceback() try: line = self.raw_input(prompt) 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') source_raw = self.input_splitter.source_raw_reset()[1] hlen_b4_cell = \ self._replace_rlhist_multiline(source_raw, hlen_b4_cell) more = False 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: self.input_splitter.push(line) more = self.input_splitter.push_accepts_more() if (self.SyntaxTB.last_syntax_error and self.autoedit_syntax): self.edit_syntax_error() if not more: source_raw = self.input_splitter.source_raw_reset()[1] self.run_cell(source_raw, store_history=True) hlen_b4_cell = \ self._replace_rlhist_multiline(source_raw, hlen_b4_cell) # Turn off the exit flag, so the mainloop can be restarted if desired self.exit_now = False def raw_input(self, prompt=''): """Write a prompt and read a line. The returned line does not include the trailing newline. When the user enters the EOF key sequence, EOFError is raised. Optional inputs: - prompt(''): a string to be printed to prompt the user. - continue_prompt(False): whether this line is the first one or a continuation in a sequence of inputs. """ # Code run by the user may have modified the readline completer state. # We must ensure that our completer is back in place. if self.has_readline: self.set_readline_completer() try: line = py3compat.str_to_unicode(self.raw_input_original(prompt)) except ValueError: warn("\n********\nYou or a %run:ed script called sys.stdin.close()" " or sys.stdout.close()!\nExiting IPython!") self.ask_exit() return "" # Try to be reasonably smart about not re-indenting pasted input more # than necessary. We do this by trimming out the auto-indent initial # spaces, if the user's actual input started itself with whitespace. if self.autoindent: if num_ini_spaces(line) > self.indent_current_nsp: line = line[self.indent_current_nsp:] self.indent_current_nsp = 0 return line #------------------------------------------------------------------------- # 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 = open(err.filename) try: # This should be inside a display_trap block and I # think it is. sys.displayhook(f.read()) finally: f.close() except: self.showtraceback() def _should_recompile(self, e): """Utility routine for edit_syntax_error""" if e.filename in ('<ipython console>', '<input>', '<string>', '<console>', '<BackgroundJob compilation>', None): return False try: if (self.autoedit_syntax and not self.ask_yes_no( 'Return to editor to correct syntax error? ' '[Y/n] ', 'y')): return False except EOFError: return False def int0(x): try: return int(x) except TypeError: return 0 # always pass integer line and offset values to editor hook try: self.hooks.fix_error_editor(e.filename, int0(e.lineno), int0(e.offset), e.msg) except TryNext: warn('Could not open editor') return False return True #------------------------------------------------------------------------- # Things related to 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() #------------------------------------------------------------------------- # Things related to magics #------------------------------------------------------------------------- def init_magics(self): super(TerminalInteractiveShell, self).init_magics() self.register_magics(TerminalMagics) def showindentationerror(self): super(TerminalInteractiveShell, self).showindentationerror() print( "If you want to paste code into IPython, try the " "%paste and %cpaste magic functions.")
class ZMQInteractiveShell(InteractiveShell): """A subclass of InteractiveShell for ZMQ.""" displayhook_class = Type(ZMQShellDisplayHook) display_pub_class = Type(ZMQDisplayPublisher) data_pub_class = Type(ZMQDataPublisher) kernel = Any() parent_header = Any() # Override the traitlet in the parent class, because there's no point using # readline for the kernel. Can be removed when the readline code is moved # to the terminal frontend. colors_force = CBool(True) readline_use = CBool(False) # autoindent has no meaning in a zmqshell, and attempting to enable it # will print a warning in the absence of readline. autoindent = CBool(False) exiter = Instance(ZMQExitAutocall) def _exiter_default(self): return ZMQExitAutocall(self) def _exit_now_changed(self, name, old, new): """stop eventloop when exit_now fires""" if new: loop = ioloop.IOLoop.instance() loop.add_timeout(time.time() + 0.1, loop.stop) keepkernel_on_exit = None # Over ZeroMQ, GUI control isn't done with PyOS_InputHook as there is no # interactive input being read; we provide event loop support in ipkernel @staticmethod def enable_gui(gui): from .eventloops import enable_gui as real_enable_gui try: real_enable_gui(gui) except ValueError as e: raise UsageError("%s" % e) def init_environment(self): """Configure the user's environment. """ env = os.environ # These two ensure 'ls' produces nice coloring on BSD-derived systems env['TERM'] = 'xterm-color' env['CLICOLOR'] = '1' # Since normal pagers don't work at all (over pexpect we don't have # single-key control of the subprocess), try to disable paging in # subprocesses as much as possible. env['PAGER'] = 'cat' env['GIT_PAGER'] = 'cat' # And install the payload version of page. install_payload_page() def auto_rewrite_input(self, cmd): """Called to show the auto-rewritten input for autocall and friends. FIXME: this payload is currently not correctly processed by the frontend. """ new = self.prompt_manager.render('rewrite') + cmd payload = dict( source='auto_rewrite_input', transformed_input=new, ) self.payload_manager.write_payload(payload) def ask_exit(self): """Engage the exit actions.""" self.exit_now = True payload = dict( source='ask_exit', exit=True, keepkernel=self.keepkernel_on_exit, ) self.payload_manager.write_payload(payload) def _showtraceback(self, etype, evalue, stb): # try to preserve ordering of tracebacks and print statements sys.stdout.flush() sys.stderr.flush() exc_content = { u'traceback': stb, u'ename': unicode_type(etype.__name__), u'evalue': py3compat.safe_unicode(evalue), } dh = self.displayhook # Send exception info over pub socket for other clients than the caller # to pick up topic = None if dh.topic: topic = dh.topic.replace(b'pyout', b'pyerr') exc_msg = dh.session.send(dh.pub_socket, u'pyerr', json_clean(exc_content), dh.parent_header, ident=topic) # FIXME - Hack: store exception info in shell object. Right now, the # caller is reading this info after the fact, we need to fix this logic # to remove this hack. Even uglier, we need to store the error status # here, because in the main loop, the logic that sets it is being # skipped because runlines swallows the exceptions. exc_content[u'status'] = u'error' self._reply_content = exc_content # /FIXME return exc_content def set_next_input(self, text): """Send the specified text to the frontend to be presented at the next input cell.""" payload = dict(source='set_next_input', text=text) self.payload_manager.write_payload(payload) def set_parent(self, parent): """Set the parent header for associating output with its triggering input""" self.parent_header = parent self.displayhook.set_parent(parent) self.display_pub.set_parent(parent) self.data_pub.set_parent(parent) try: sys.stdout.set_parent(parent) except AttributeError: pass try: sys.stderr.set_parent(parent) except AttributeError: pass def get_parent(self): return self.parent_header #------------------------------------------------------------------------- # Things related to magics #------------------------------------------------------------------------- def init_magics(self): super(ZMQInteractiveShell, self).init_magics() self.register_magics(KernelMagics) self.magics_manager.register_alias('ed', 'edit') def init_comms(self): self.comm_manager = CommManager(shell=self, parent=self) self.configurables.append(self.comm_manager)
class PyDevTerminalInteractiveShell(TerminalInteractiveShell): banner1 = Unicode(default_pydev_banner, config=True, help="""The part of the banner to be printed before the profile""" ) # TODO term_title: (can PyDev's title be changed???, see terminal.py for where to inject code, in particular set_term_title as used by %cd) # for now, just disable term_title term_title = CBool(False) # Note in version 0.11 there is no guard in the IPython code about displaying a # warning, so with 0.11 you get: # WARNING: Readline services not available or not loaded. # WARNING: The auto-indent feature requires the readline library # Disable readline, readline type code is all handled by PyDev (on Java side) readline_use = CBool(False) # autoindent has no meaning in PyDev (PyDev always handles that on the Java side), # and attempting to enable it will print a warning in the absence of readline. autoindent = CBool(False) # Force console to not give warning about color scheme choice and default to NoColor. # TODO It would be nice to enable colors in PyDev but: # - The PyDev Console (Eclipse Console) does not support the full range of colors, so the # effect isn't as nice anyway at the command line # - If done, the color scheme should default to LightBG, but actually be dependent on # any settings the user has (such as if a dark theme is in use, then Linux is probably # a better theme). colors_force = CBool(True) colors = Unicode("NoColor") # In the PyDev Console, GUI control is done via hookable XML-RPC server @staticmethod def enable_gui(gui=None, app=None): """Switch amongst GUI input hooks by name. """ # Deferred import from pydev_ipython.inputhook import enable_gui as real_enable_gui try: return real_enable_gui(gui, app) except ValueError as e: raise UsageError("%s" % e) #------------------------------------------------------------------------- # Things related to hooks #------------------------------------------------------------------------- def init_hooks(self): super(PyDevTerminalInteractiveShell, self).init_hooks() self.set_hook('show_in_pager', show_in_pager) #------------------------------------------------------------------------- # Things related to exceptions #------------------------------------------------------------------------- def showtraceback(self, exc_tuple=None, filename=None, tb_offset=None, exception_only=False): # IPython does a lot of clever stuff with Exceptions. However mostly # it is related to IPython running in a terminal instead of an IDE. # (e.g. it prints out snippets of code around the stack trace) # PyDev does a lot of clever stuff too, so leave exception handling # with default print_exc that PyDev can parse and do its clever stuff # with (e.g. it puts links back to the original source code) import traceback;traceback.print_exc() #------------------------------------------------------------------------- # Things related to text completion #------------------------------------------------------------------------- # The way to construct an IPCompleter changed in most versions, # so we have a custom, per version implementation of the construction def _new_completer_011(self): return PyDevIPCompleter(self, self.user_ns, self.user_global_ns, self.readline_omit__names, self.alias_manager.alias_table, self.has_readline) def _new_completer_012(self): completer = PyDevIPCompleter(shell=self, namespace=self.user_ns, global_namespace=self.user_global_ns, alias_table=self.alias_manager.alias_table, use_readline=self.has_readline, config=self.config, ) self.configurables.append(completer) return completer def _new_completer_100(self): completer = PyDevIPCompleter(shell=self, namespace=self.user_ns, global_namespace=self.user_global_ns, alias_table=self.alias_manager.alias_table, use_readline=self.has_readline, parent=self, ) self.configurables.append(completer) return completer def _new_completer_200(self): # As of writing this, IPython 2.0.0 is in dev mode so subject to change completer = PyDevIPCompleter(shell=self, namespace=self.user_ns, global_namespace=self.user_global_ns, use_readline=self.has_readline, parent=self, ) self.configurables.append(completer) return completer def init_completer(self): """Initialize the completion machinery. This creates a completer that provides the completions that are IPython specific. We use this to supplement PyDev's core code completions. """ # PyDev uses its own completer and custom hooks so that it uses # most completions from PyDev's core completer which provides # extra information. # See getCompletions for where the two sets of results are merged from IPython.core.completerlib import magic_run_completer, cd_completer try: from IPython.core.completerlib import reset_completer except ImportError: # reset_completer was added for rel-0.13 reset_completer = None if IPythonRelease._version_major >= 2: self.Completer = self._new_completer_200() elif IPythonRelease._version_major >= 1: self.Completer = self._new_completer_100() elif IPythonRelease._version_minor >= 12: self.Completer = self._new_completer_012() else: self.Completer = self._new_completer_011() # Add custom completers to the basic ones built into IPCompleter sdisp = self.strdispatchers.get('complete_command', StrDispatch()) self.strdispatchers['complete_command'] = sdisp self.Completer.custom_completers = sdisp self.set_hook('complete_command', magic_run_completer, str_key='%run') self.set_hook('complete_command', cd_completer, str_key='%cd') if reset_completer: self.set_hook('complete_command', reset_completer, str_key='%reset') # Only configure readline if we truly are using readline. IPython can # do tab-completion over the network, in GUIs, etc, where readline # itself may be absent if self.has_readline: self.set_readline_completer() #------------------------------------------------------------------------- # Things related to aliases #------------------------------------------------------------------------- def init_alias(self): # InteractiveShell defines alias's we want, but TerminalInteractiveShell defines # ones we don't. So don't use super and instead go right to InteractiveShell InteractiveShell.init_alias(self) #------------------------------------------------------------------------- # Things related to exiting #------------------------------------------------------------------------- def ask_exit(self): """ Ask the shell to exit. Can be overiden and used as a callback. """ # TODO PyDev's console does not have support from the Python side to exit # the console. If user forces the exit (with sys.exit()) then the console # simply reports errors. e.g.: # >>> import sys # >>> sys.exit() # Failed to create input stream: Connection refused # >>> # Console already exited with value: 0 while waiting for an answer. # Error stream: # Output stream: # >>> # # Alternatively if you use the non-IPython shell this is what happens # >>> exit() # <type 'exceptions.SystemExit'>:None # >>> # <type 'exceptions.SystemExit'>:None # >>> # super(PyDevTerminalInteractiveShell, self).ask_exit() print('To exit the PyDev Console, terminate the console within Eclipse.') #------------------------------------------------------------------------- # Things related to magics #------------------------------------------------------------------------- def init_magics(self): super(PyDevTerminalInteractiveShell, self).init_magics()
class IPythonQtConsoleApp(BaseIPythonApplication): name = 'ipython-qtconsole' default_config_file_name='ipython_config.py' description = """ The IPython QtConsole. This launches a Console-style application using Qt. It is not a full console, in that launched terminal subprocesses will not be able to accept input. The QtConsole supports various extra features beyond the Terminal IPython shell, such as inline plotting with matplotlib, via: ipython qtconsole --pylab=inline as well as saving your session as HTML, and printing the output. """ examples = _examples classes = [IPKernelApp, IPythonWidget, ZMQInteractiveShell, ProfileDir, Session] flags = Dict(flags) aliases = Dict(aliases) kernel_argv = List(Unicode) # create requested profiles by default, if they don't exist: auto_create = CBool(True) # connection info: ip = Unicode(LOCALHOST, config=True, help="""Set the kernel\'s IP address [default localhost]. If the IP address is something other than localhost, then Consoles on other machines will be able to connect to the Kernel, so be careful!""" ) hb_port = Int(0, config=True, help="set the heartbeat port [default: random]") shell_port = Int(0, config=True, help="set the shell (XREP) port [default: random]") iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]") stdin_port = Int(0, config=True, help="set the stdin (XREQ) port [default: random]") existing = CBool(False, config=True, help="Whether to connect to an already running Kernel.") stylesheet = Unicode('', config=True, help="path to a custom CSS stylesheet") pure = CBool(False, config=True, help="Use a pure Python kernel instead of an IPython kernel.") plain = CBool(False, config=True, help="Use a plaintext widget instead of rich text (plain can't print/save).") def _pure_changed(self, name, old, new): kind = 'plain' if self.plain else 'rich' self.config.ConsoleWidget.kind = kind if self.pure: self.widget_factory = FrontendWidget elif self.plain: self.widget_factory = IPythonWidget else: self.widget_factory = RichIPythonWidget _plain_changed = _pure_changed confirm_exit = CBool(True, config=True, help=""" Set to display confirmation dialog on exit. You can always use 'exit' or 'quit', to force a direct exit without any confirmation.""", ) # the factory for creating a widget widget_factory = Any(RichIPythonWidget) def parse_command_line(self, argv=None): super(IPythonQtConsoleApp, self).parse_command_line(argv) if argv is None: argv = sys.argv[1:] self.kernel_argv = list(argv) # copy # kernel should inherit default config file from frontend self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name) # scrub frontend-specific flags for a in argv: if a.startswith('-'): key = a.lstrip('-').split('=')[0] if key in qt_flags: self.kernel_argv.remove(a) def init_kernel_manager(self): # Don't let Qt or ZMQ swallow KeyboardInterupts. signal.signal(signal.SIGINT, signal.SIG_DFL) # Create a KernelManager and start a kernel. self.kernel_manager = QtKernelManager( shell_address=(self.ip, self.shell_port), sub_address=(self.ip, self.iopub_port), stdin_address=(self.ip, self.stdin_port), hb_address=(self.ip, self.hb_port), config=self.config ) # start the kernel if not self.existing: kwargs = dict(ip=self.ip, ipython=not self.pure) kwargs['extra_arguments'] = self.kernel_argv self.kernel_manager.start_kernel(**kwargs) self.kernel_manager.start_channels() def init_qt_elements(self): # Create the widget. self.app = QtGui.QApplication([]) local_kernel = (not self.existing) or self.ip in LOCAL_IPS self.widget = self.widget_factory(config=self.config, local_kernel=local_kernel) self.widget.kernel_manager = self.kernel_manager self.window = MainWindow(self.app, self.widget, self.existing, may_close=local_kernel, confirm_exit=self.confirm_exit) self.window.setWindowTitle('Python' if self.pure else 'IPython') def init_colors(self): """Configure the coloring of the widget""" # Note: This will be dramatically simplified when colors # are removed from the backend. if self.pure: # only IPythonWidget supports styling return # parse the colors arg down to current known labels try: colors = self.config.ZMQInteractiveShell.colors except AttributeError: colors = None try: style = self.config.IPythonWidget.colors except AttributeError: style = None # find the value for colors: if colors: colors=colors.lower() if colors in ('lightbg', 'light'): colors='lightbg' elif colors in ('dark', 'linux'): colors='linux' else: colors='nocolor' elif style: if style=='bw': colors='nocolor' elif styles.dark_style(style): colors='linux' else: colors='lightbg' else: colors=None # Configure the style. widget = self.widget if style: widget.style_sheet = styles.sheet_from_template(style, colors) widget.syntax_style = style widget._syntax_style_changed() widget._style_sheet_changed() elif colors: # use a default style widget.set_default_style(colors=colors) else: # this is redundant for now, but allows the widget's # defaults to change widget.set_default_style() if self.stylesheet: # we got an expicit stylesheet if os.path.isfile(self.stylesheet): with open(self.stylesheet) as f: sheet = f.read() widget.style_sheet = sheet widget._style_sheet_changed() else: raise IOError("Stylesheet %r not found."%self.stylesheet) def initialize(self, argv=None): super(IPythonQtConsoleApp, self).initialize(argv) self.init_kernel_manager() self.init_qt_elements() self.init_colors() def start(self): # draw the window self.window.show() # Start the application main loop. self.app.exec_()