From 7ae1fe4225c0c11ccdd6eec758c23aa81ac88654 2013-07-13 00:44:18 From: MinRK Date: 2013-07-13 00:44:18 Subject: [PATCH] add `%matplotlib` and `shell.enable_matplotlib` Now `%matplotlib` / `enable_matplotlib` do the bulk of the work. `%pylab` / `enable_pylab` just add namespace population on top of that. Related changes: - remove `pylabtools.activate_pylab` - shorten backend message to just `using matplotlib backend: ` - only show backend message when magics are called with `auto`, never from Python methods or when backend is explicit. - pylab clobber warning is only displayed in the magic, not the Python method. --- diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 60f731f..da0c691 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -55,7 +55,6 @@ from IPython.core.macro import Macro from IPython.core.payload import PayloadManager from IPython.core.prefilter import PrefilterManager from IPython.core.profiledir import ProfileDir -from IPython.core.pylabtools import pylab_activate from IPython.core.prompts import PromptManager from IPython.lib.latextools import LaTeXTool from IPython.testing.skipdoctest import skip_doctest @@ -2832,6 +2831,51 @@ class InteractiveShell(SingletonConfigurable): def enable_gui(self, gui=None): raise NotImplementedError('Implement enable_gui in a subclass') + + def enable_matplotlib(self, gui=None): + """Enable interactive matplotlib and inline figure support. + + This takes the following steps: + + 1. select the appropriate eventloop and matplotlib backend + 2. set up matplotlib for interactive use with that backend + 3. configure formatters for inline figure display + 4. enable the selected gui eventloop + + Parameters + ---------- + gui : optional, string + If given, dictates the choice of matplotlib GUI backend to use + (should be one of IPython's supported backends, 'qt', 'osx', 'tk', + 'gtk', 'wx' or 'inline'), otherwise we use the default chosen by + matplotlib (as dictated by the matplotlib build-time options plus the + user's matplotlibrc configuration file). Note that not all backends + make sense in all contexts, for example a terminal ipython can't + display figures inline. + """ + from IPython.core import pylabtools as pt + gui, backend = pt.find_gui_and_backend(gui, self.pylab_gui_select) + + if gui != 'inline': + # If we have our first gui selection, store it + if self.pylab_gui_select is None: + self.pylab_gui_select = gui + # Otherwise if they are different + elif gui != self.pylab_gui_select: + print ('Warning: Cannot change to a different GUI toolkit: %s.' + ' Using %s instead.' % (gui, self.pylab_gui_select)) + gui, backend = pt.find_gui_and_backend(self.pylab_gui_select) + + pt.activate_matplotlib(backend) + pt.configure_inline_support(self, backend) + + # Now we must activate the gui pylab wants to use, and fix %run to take + # plot updates into account + self.enable_gui(gui) + self.magics_manager.registry['ExecutionMagics'].default_runner = \ + pt.mpl_runner(self.safe_execfile) + + return gui, backend def enable_pylab(self, gui=None, import_all=True, welcome_message=False): """Activate pylab support at runtime. @@ -2840,6 +2884,8 @@ class InteractiveShell(SingletonConfigurable): namespace all of numpy and pylab, and configures IPython to correctly interact with the GUI event loop. The GUI backend to be used can be optionally selected with the optional ``gui`` argument. + + This method only adds preloading the namespace to InteractiveShell.enable_matplotlib. Parameters ---------- @@ -2851,38 +2897,29 @@ class InteractiveShell(SingletonConfigurable): user's matplotlibrc configuration file). Note that not all backends make sense in all contexts, for example a terminal ipython can't display figures inline. + import_all : optional, bool, default: True + Whether to do `from numpy import *` and `from pylab import *` + in addition to module imports. + welcome_message : deprecated + This argument is ignored, no welcome message will be displayed. """ - from IPython.core.pylabtools import mpl_runner, backends + from IPython.core.pylabtools import import_pylab + + gui, backend = self.enable_matplotlib(gui) + # 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 = {} - try: - gui = pylab_activate(ns, gui, import_all, self, welcome_message=welcome_message) - except KeyError: - error("Backend '%s' not supported. Supported backends are: %s" - % (gui, " ".join(sorted(backends.keys())))) - return - except ImportError: - error("pylab mode doesn't work as matplotlib or its backend could not be imported." + \ - "\nIs it installed on the system?") - return + import_pylab(ns, import_all) # warn about clobbered names ignored = set(["__builtins__"]) both = set(ns).intersection(self.user_ns).difference(ignored) clobbered = [ name for name in both if self.user_ns[name] is not ns[name] ] - if clobbered: - warn("pylab import has clobbered these variables: %s" - "\n`%%pylab --no-import` prevents imports from pylab, numpy, etc." % clobbered - ) 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 - self.enable_gui(gui) - self.magics_manager.registry['ExecutionMagics'].default_runner = \ - mpl_runner(self.safe_execfile) + return gui, backend, clobbered #------------------------------------------------------------------------- # Utilities diff --git a/IPython/core/magics/pylab.py b/IPython/core/magics/pylab.py index 558bfe4..76e05b1 100644 --- a/IPython/core/magics/pylab.py +++ b/IPython/core/magics/pylab.py @@ -17,23 +17,13 @@ from IPython.config.application import Application from IPython.core import magic_arguments from IPython.core.magic import Magics, magics_class, line_magic from IPython.testing.skipdoctest import skip_doctest +from IPython.utils.warn import warn #----------------------------------------------------------------------------- # Magic implementation classes #----------------------------------------------------------------------------- -@magics_class -class PylabMagics(Magics): - """Magics related to matplotlib's pylab support""" - - @skip_doctest - @line_magic - @magic_arguments.magic_arguments() - @magic_arguments.argument( - '--no-import', action='store_true', default=None, - help="""Prevent IPython from populating the namespace""" - ) - @magic_arguments.argument( +magic_gui_arg = magic_arguments.argument( 'gui', nargs='?', help="""Name of the matplotlib backend to use ('qt', 'wx', 'gtk', 'osx', 'tk', 'inline', 'auto'). @@ -41,18 +31,24 @@ class PylabMagics(Magics): otherwise it will be matplotlib's default (which you can set in your matplotlib config file). """ - ) - def pylab(self, line=''): - """Load numpy and matplotlib to work interactively. - - %pylab [GUINAME] +) - This function lets you activate pylab (matplotlib, numpy and - interactive support) at any point during an IPython session. - - It will import at the top level numpy as np, pyplot as plt, matplotlib, - pylab and mlab, as well as all names from numpy and pylab. +@magics_class +class PylabMagics(Magics): + """Magics related to matplotlib's pylab support""" + + @skip_doctest + @line_magic + @magic_arguments.magic_arguments() + @magic_gui_arg + def matplotlib(self, line=''): + """Set up matplotlib to work interactively. + + This function lets you activate matplotlib interactive support + at any point during an IPython session. + It does not import anything into the interactive namespace. + If you are using the inline matplotlib backend for embedded figures, you can adjust its behavior via the %config magic:: @@ -64,34 +60,59 @@ class PylabMagics(Magics): # cells: In [2]: %config InlineBackend.close_figures = False - Parameters - ---------- - guiname : optional - One of the valid arguments to the %gui magic ('qt', 'wx', 'gtk', - 'osx' or 'tk'). If given, the corresponding Matplotlib backend is - used, otherwise matplotlib's default (which you can override in your - matplotlib config file) is used. - Examples -------- In this case, where the MPL default is TkAgg:: - In [2]: %pylab - - Welcome to pylab, a matplotlib-based Python environment. - Backend in use: TkAgg - For more information, type 'help(pylab)'. + In [2]: %matplotlib + Using matplotlib backend: TkAgg But you can explicitly request a different backend:: - In [3]: %pylab qt + In [3]: %matplotlib qt + """ + args = magic_arguments.parse_argstring(self.matplotlib, line) + gui, backend = self.shell.enable_matplotlib(args.gui) + self._show_matplotlib_backend(args.gui, backend) - Welcome to pylab, a matplotlib-based Python environment. - Backend in use: Qt4Agg - For more information, type 'help(pylab)'. + @skip_doctest + @line_magic + @magic_arguments.magic_arguments() + @magic_arguments.argument( + '--no-import-all', action='store_true', default=None, + help="""Prevent IPython from performing ``import *`` into the interactive namespace. + + The names that will still be added to the namespace if this flag is given:: + + numpy + matplotlib + np (numpy alias) + plt (matplotlib.pyplot alias) + pylab (from matplotlib) + pyplot (from matplotlib) + mlab (from matplotlib) + display (from IPython) + figsize (from IPython) + getfigs (from IPython) + + You can govern the default behavior with the + InteractiveShellApp.pylab_import_all configurable. + """ + ) + @magic_gui_arg + def pylab(self, line=''): + """Load numpy and matplotlib to work interactively. + + This function lets you activate pylab (matplotlib, numpy and + interactive support) at any point during an IPython session. + + It will import at the top level numpy as np, pyplot as plt, matplotlib, + pylab and mlab, as well as all names from numpy and pylab. + + See the %matplotlib magic for more details. """ args = magic_arguments.parse_argstring(self.pylab, line) - if args.no_import is None: + if args.no_import_all is None: # get default from Application if Application.initialized(): app = Application.instance() @@ -104,6 +125,17 @@ class PylabMagics(Magics): import_all = True else: # invert no-import flag - import_all = not args.no_import - - self.shell.enable_pylab(args.gui, import_all=import_all, welcome_message=True) + import_all = not args.no_import_all + + gui, backend, clobbered = self.shell.enable_pylab(args.gui, import_all=import_all) + self._show_matplotlib_backend(args.gui, backend) + if clobbered: + warn("pylab import has clobbered these variables: %s" % clobbered + + "\n`%pylab --no-import-all` prevents importing * from pylab and numpy" + ) + + def _show_matplotlib_backend(self, gui, backend): + """show matplotlib message backend message""" + if not gui or gui == 'auto': + print ("using matplotlib backend: %s" % backend) + diff --git a/IPython/core/pylabtools.py b/IPython/core/pylabtools.py index f675a2f..806139c 100644 --- a/IPython/core/pylabtools.py +++ b/IPython/core/pylabtools.py @@ -231,7 +231,7 @@ def activate_matplotlib(backend): import matplotlib matplotlib.interactive(True) - + # Matplotlib had a bug where even switch_backend could not force # the rcParam to update. This needs to be set *before* the module # magic of switch_backend(). @@ -257,7 +257,6 @@ def import_pylab(user_ns, import_all=True): Also imports a few names from IPython (figsize, display, getfigs) - The import_all parameter is included for backward compatibility, but ignored. """ # Import numpy as np/pyplot as plt are conventions we're trying to @@ -271,9 +270,10 @@ def import_pylab(user_ns, import_all=True): ) exec s in user_ns - s = ("from matplotlib.pylab import *\n" - "from numpy import *\n") - exec s in user_ns + if import_all: + s = ("from matplotlib.pylab import *\n" + "from numpy import *\n") + exec s in user_ns # IPython symbols to add user_ns['figsize'] = figsize @@ -283,7 +283,7 @@ def import_pylab(user_ns, import_all=True): user_ns['getfigs'] = getfigs -def configure_inline_support(shell, backend, user_ns=None): +def configure_inline_support(shell, backend): """Configure an IPython shell object for matplotlib use. Parameters @@ -291,10 +291,6 @@ def configure_inline_support(shell, backend, user_ns=None): shell : InteractiveShell instance backend : matplotlib backend - - user_ns : dict - A namespace where all configured variables will be placed. If not given, - the `user_ns` attribute of the shell object is used. """ # If using our svg payload backend, register the post-execution # function that will pick up the results for display. This can only be @@ -309,8 +305,6 @@ def configure_inline_support(shell, backend, user_ns=None): return from matplotlib import pyplot - user_ns = shell.user_ns if user_ns is None else user_ns - cfg = InlineBackend.instance(parent=shell) cfg.shell = shell if cfg not in shell.configurables: @@ -335,57 +329,5 @@ def configure_inline_support(shell, backend, user_ns=None): del shell._saved_rcParams # Setup the default figure format - fmt = cfg.figure_format - select_figure_format(shell, fmt) - - -def pylab_activate(user_ns, gui=None, import_all=True, shell=None, welcome_message=False): - """Activate pylab mode in the user's namespace. + select_figure_format(shell, cfg.figure_format) - Loads and initializes numpy, matplotlib and friends for interactive use. - - Parameters - ---------- - user_ns : dict - Namespace where the imports will occur. - - gui : optional, string - A valid gui name following the conventions of the %gui magic. - - import_all : optional, boolean - If true, an 'import *' is done from numpy and pylab. - - welcome_message : optional, boolean - If true, print a welcome message about pylab, which includes the backend - being used. - - Returns - ------- - The actual gui used (if not given as input, it was obtained from matplotlib - itself, and will be needed next to configure IPython's gui integration. - """ - pylab_gui_select = shell.pylab_gui_select if shell is not None else None - # Try to find the appropriate gui and backend for the settings - gui, backend = find_gui_and_backend(gui, pylab_gui_select) - if shell is not None and gui != 'inline': - # If we have our first gui selection, store it - if pylab_gui_select is None: - shell.pylab_gui_select = gui - # Otherwise if they are different - elif gui != pylab_gui_select: - print ('Warning: Cannot change to a different GUI toolkit: %s.' - ' Using %s instead.' % (gui, pylab_gui_select)) - gui, backend = find_gui_and_backend(pylab_gui_select) - activate_matplotlib(backend) - if import_all: - import_pylab(user_ns) - if shell is not None: - configure_inline_support(shell, backend, user_ns) - if welcome_message: - print """ -Welcome to pylab, a matplotlib-based Python environment [backend: %s]. -For more information, type 'help(pylab)'.""" % backend - # flush stdout, just to be safe - sys.stdout.flush() - - return gui diff --git a/IPython/core/shellapp.py b/IPython/core/shellapp.py index b1aae11..e98ec39 100644 --- a/IPython/core/shellapp.py +++ b/IPython/core/shellapp.py @@ -201,7 +201,9 @@ class InteractiveShellApp(Configurable): 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, welcome_message=True) + if self.pylab == "auto": + print ("using matplotlib backend: %s" % backend) + shell.enable_pylab(self.pylab, import_all=self.pylab_import_all) else: self.log.info("Enabling GUI event loop integration, " "toolkit=%s" % self.gui)