diff --git a/IPython/core/magics/basic.py b/IPython/core/magics/basic.py index f34610e..5fb2ba2 100644 --- a/IPython/core/magics/basic.py +++ b/IPython/core/magics/basic.py @@ -410,7 +410,7 @@ Defaulting color scheme to 'NoColor'""" %gui gtk # enable PyGTK event loop integration %gui gtk3 # enable Gtk3 event loop integration %gui tk # enable Tk event loop integration - %gui OSX # enable Cocoa event loop integration + %gui osx # enable Cocoa event loop integration # (requires %matplotlib 1.1) %gui # disable all event loop integration diff --git a/IPython/core/shellapp.py b/IPython/core/shellapp.py index 6dcdef2..b25c9d6 100644 --- a/IPython/core/shellapp.py +++ b/IPython/core/shellapp.py @@ -29,9 +29,12 @@ import sys from IPython.config.application import boolean_flag from IPython.config.configurable import Configurable from IPython.config.loader import Config +from IPython.core import pylabtools from IPython.utils import py3compat from IPython.utils.path import filefind -from IPython.utils.traitlets import Unicode, Instance, List, Bool +from IPython.utils.traitlets import ( + Unicode, Instance, List, Bool, CaselessStrEnum +) #----------------------------------------------------------------------------- # Aliases and Flags @@ -94,7 +97,11 @@ nosep_config.InteractiveShell.separate_out = '' nosep_config.InteractiveShell.separate_out2 = '' shell_flags['nosep']=(nosep_config, "Eliminate all spacing between prompts.") - +shell_flags['pylab'] = ( + {'InteractiveShellApp' : {'pylab' : 'auto'}}, + """Pre-load matplotlib and numpy for interactive use with + the default matplotlib backend.""" +) # it's possible we don't want short aliases for *all* of these: shell_aliases = dict( @@ -105,6 +112,8 @@ shell_aliases = dict( c='InteractiveShellApp.code_to_run', m='InteractiveShellApp.module_to_run', ext='InteractiveShellApp.extra_extension', + gui='InteractiveShellApp.gui', + pylab='InteractiveShellApp.pylab', ) shell_aliases['cache-size'] = 'InteractiveShell.cache_size' @@ -118,9 +127,14 @@ class InteractiveShellApp(Configurable): Provides configurables for loading extensions and executing files as part of configuring a Shell environment. - Provides init_path(), to be called before, and init_extensions() and - init_code() methods, to be called after init_shell(), which must be - implemented by subclasses. + The following methods should be called by the :meth:`initialize` method + of the subclass: + + - :meth:`init_path` + - :meth:`init_shell` (to be implemented by the subclass) + - :meth:`init_gui_pylab` + - :meth:`init_extensions` + - :meth:`init_code` """ extensions = List(Unicode, config=True, help="A list of dotted module names of IPython extensions to load." @@ -151,6 +165,15 @@ class InteractiveShellApp(Configurable): module_to_run = Unicode('', config=True, help="Run the module as a script." ) + gui = CaselessStrEnum(('qt', 'wx', 'gtk', 'glut', 'pyglet', 'osx'), config=True, + help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'glut', 'pyglet', 'osx')." + ) + pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'], + config=True, + help="""Pre-load matplotlib and numpy for interactive use, + selecting a particular matplotlib backend and loop integration. + """ + ) pylab_import_all = Bool(True, config=True, help="""If true, an 'import *' is done from numpy and pylab, when using pylab""" @@ -164,7 +187,25 @@ class InteractiveShellApp(Configurable): def init_shell(self): raise NotImplementedError("Override in subclasses") - + + def init_gui_pylab(self): + """Enable GUI event loop integration, taking pylab into account.""" + if self.gui or self.pylab: + shell = self.shell + try: + if self.pylab: + gui, backend = pylabtools.find_gui_and_backend(self.pylab) + self.log.info("Enabling GUI event loop integration, " + "toolkit=%s, pylab=%s" % (gui, self.pylab)) + shell.enable_pylab(gui, import_all=self.pylab_import_all) + else: + self.log.info("Enabling GUI event loop integration, " + "toolkit=%s" % self.gui) + shell.enable_gui(self.gui) + except Exception: + self.log.warn("GUI event loop or pylab initialization failed") + self.shell.showtraceback() + def init_extensions(self): """Load all IPython extensions in IPythonApp.extensions. diff --git a/IPython/frontend/terminal/console/app.py b/IPython/frontend/terminal/console/app.py index f1ada55..0de7ffe 100644 --- a/IPython/frontend/terminal/console/app.py +++ b/IPython/frontend/terminal/console/app.py @@ -52,8 +52,6 @@ flags = dict(flags) frontend_flags = dict(app_flags) # add TerminalIPApp flags: frontend_flags.update(term_flags) -# pylab is not frontend-specific in two-process IPython -frontend_flags.pop('pylab') # disable quick startup, as it won't propagate to the kernel anyway frontend_flags.pop('quick') # update full dict with frontend flags: diff --git a/IPython/frontend/terminal/ipapp.py b/IPython/frontend/terminal/ipapp.py index 638f243..310944d 100755 --- a/IPython/frontend/terminal/ipapp.py +++ b/IPython/frontend/terminal/ipapp.py @@ -171,22 +171,11 @@ frontend_flags['i'] = ( """If running code from the command line, become interactive afterwards. Note: can also be given simply as '-i.'""" ) -frontend_flags['pylab'] = ( - {'TerminalIPythonApp' : {'pylab' : 'auto'}}, - """Pre-load matplotlib and numpy for interactive use with - the default matplotlib backend.""" -) flags.update(frontend_flags) aliases = dict(base_aliases) aliases.update(shell_aliases) -# it's possible we don't want short aliases for *all* of these: -aliases.update(dict( - gui='TerminalIPythonApp.gui', - pylab='TerminalIPythonApp.pylab', -)) - #----------------------------------------------------------------------------- # Main classes and functions #----------------------------------------------------------------------------- @@ -246,15 +235,6 @@ class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp): self.load_config_file = lambda *a, **kw: None self.ignore_old_config=True - gui = CaselessStrEnum(('qt', 'wx', 'gtk', 'glut', 'pyglet'), config=True, - help="Enable GUI event loop integration ('qt', 'wx', 'gtk', 'glut', 'pyglet')." - ) - pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'auto'], - config=True, - help="""Pre-load matplotlib and numpy for interactive use, - selecting a particular matplotlib backend and loop integration. - """ - ) display_banner = Bool(True, config=True, help="Whether to display a banner upon starting IPython." ) @@ -344,34 +324,12 @@ class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp): # Make sure there is a space below the banner. if self.log_level <= logging.INFO: print - - def init_gui_pylab(self): - """Enable GUI event loop integration, taking pylab into account.""" - gui = self.gui - - # Using `pylab` will also require gui activation, though which toolkit - # to use may be chosen automatically based on mpl configuration. - if self.pylab: - activate = self.shell.enable_pylab - if self.pylab == 'auto': - gui = None - else: - gui = self.pylab - else: - # Enable only GUI integration, no pylab - activate = inputhook.enable_gui - - if gui or self.pylab: - try: - self.log.info("Enabling GUI event loop integration, " - "toolkit=%s, pylab=%s" % (gui, self.pylab) ) - if self.pylab: - activate(gui, import_all=self.pylab_import_all) - else: - activate(gui) - except: - self.log.warn("Error in enabling GUI event loop integration:") - self.shell.showtraceback() + def _pylab_changed(self, name, old, new): + """Replace --pylab='inline' with --pylab='auto'""" + if new == 'inline': + warn.warn("'inline' not available as pylab backend, " + "using 'auto' instead.\n") + self.pylab = 'auto' def start(self): if self.subapp is not None: diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py index b20ef16..a2f8c91 100755 --- a/IPython/zmq/ipkernel.py +++ b/IPython/zmq/ipkernel.py @@ -35,7 +35,6 @@ from zmq.eventloop import ioloop from zmq.eventloop.zmqstream import ZMQStream # Local imports -from IPython.core import pylabtools from IPython.config.configurable import Configurable from IPython.config.application import boolean_flag, catch_config_error from IPython.core.application import ProfileDir @@ -772,11 +771,6 @@ flags['pylab'] = ( aliases = dict(kernel_aliases) aliases.update(shell_aliases) -# it's possible we don't want short aliases for *all* of these: -aliases.update(dict( - pylab='IPKernelApp.pylab', -)) - #----------------------------------------------------------------------------- # The IPKernelApp class #----------------------------------------------------------------------------- @@ -787,20 +781,13 @@ class IPKernelApp(KernelApp, InteractiveShellApp): aliases = Dict(aliases) flags = Dict(flags) classes = [Kernel, ZMQInteractiveShell, ProfileDir, Session] - - # configurables - pylab = CaselessStrEnum(['tk', 'qt', 'wx', 'gtk', 'osx', 'inline', 'auto'], - config=True, - help="""Pre-load matplotlib and numpy for interactive use, - selecting a particular matplotlib backend and loop integration. - """ - ) - + @catch_config_error def initialize(self, argv=None): super(IPKernelApp, self).initialize(argv) self.init_path() self.init_shell() + self.init_gui_pylab() self.init_extensions() self.init_code() @@ -818,31 +805,28 @@ class IPKernelApp(KernelApp, InteractiveShellApp): self.kernel = kernel kernel.record_ports(self.ports) shell = kernel.shell - if self.pylab: - try: - gui, backend = pylabtools.find_gui_and_backend(self.pylab) - shell.enable_pylab(gui, import_all=self.pylab_import_all) - except Exception: - self.log.error("Pylab initialization failed", exc_info=True) - # print exception straight to stdout, because normally - # _showtraceback associates the reply with an execution, - # which means frontends will never draw it, as this exception - # is not associated with any execute request. - - # replace pyerr-sending traceback with stdout - _showtraceback = shell._showtraceback - def print_tb(etype, evalue, stb): - print ("Error initializing pylab, pylab mode will not " - "be active", file=io.stderr) - print (shell.InteractiveTB.stb2text(stb), file=io.stdout) - shell._showtraceback = print_tb - - # send the traceback over stdout - shell.showtraceback(tb_offset=0) - - # restore proper _showtraceback method - shell._showtraceback = _showtraceback - + + def init_gui_pylab(self): + """Enable GUI event loop integration, taking pylab into account.""" + + # Provide a wrapper for :meth:`InteractiveShellApp.init_gui_pylab` + # to ensure that any exception is printed straight to stderr. + # Normally _showtraceback associates the reply with an execution, + # which means frontends will never draw it, as this exception + # is not associated with any execute request. + + shell = self.shell + _showtraceback = shell._showtraceback + try: + # replace pyerr-sending traceback with stderr + def print_tb(etype, evalue, stb): + print ("GUI event loop or pylab initialization failed", + file=io.stderr) + print (shell.InteractiveTB.stb2text(stb), file=io.stderr) + shell._showtraceback = print_tb + InteractiveShellApp.init_gui_pylab(self) + finally: + shell._showtraceback = _showtraceback def init_shell(self): self.shell = self.kernel.shell