Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pylab fix #1052

Merged
merged 9 commits into from Nov 27, 2011
41 changes: 40 additions & 1 deletion IPython/core/interactiveshell.py
Expand Up @@ -62,6 +62,7 @@
from IPython.core.plugin import PluginManager
from IPython.core.prefilter import PrefilterManager, ESC_MAGIC
from IPython.core.profiledir import ProfileDir
from IPython.core.pylabtools import pylab_activate
from IPython.external.Itpl import ItplNS
from IPython.utils import PyColorize
from IPython.utils import io
Expand Down Expand Up @@ -2531,8 +2532,46 @@ def run_code(self, code_obj):
# Things related to GUI support and pylab
#-------------------------------------------------------------------------

def enable_gui(self, gui=None):
raise NotImplementedError('Implement enable_gui in a subclass')

def enable_pylab(self, gui=None, import_all=True):
raise NotImplementedError('Implement enable_pylab in a subclass')
"""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 correctly
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, '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.
"""

# 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)
except KeyError:
error("Backend %r not supported" % gui)
return
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.magic_run = self._pylab_magic_run

#-------------------------------------------------------------------------
# Utilities
Expand Down
24 changes: 15 additions & 9 deletions IPython/core/magic.py
Expand Up @@ -50,7 +50,7 @@
from IPython.core.macro import Macro
from IPython.core import magic_arguments, page
from IPython.core.prefilter import ESC_MAGIC
from IPython.lib.pylabtools import mpl_runner
from IPython.core.pylabtools import mpl_runner
from IPython.testing.skipdoctest import skip_doctest
from IPython.utils import py3compat
from IPython.utils.io import file_read, nlprint
Expand Down Expand Up @@ -3305,24 +3305,30 @@ def magic_gui(self, parameter_s=''):

This magic replaces IPython's threaded shells that were activated
using the (pylab/wthread/etc.) command line flags. GUI toolkits
can now be enabled, disabled and changed at runtime and keyboard
can now be enabled at runtime and keyboard
interrupts should work without any problems. The following toolkits
are supported: wxPython, PyQt4, PyGTK, and Tk::
are supported: wxPython, PyQt4, PyGTK, Tk and Cocoa (OSX)::

%gui wx # enable wxPython event loop integration
%gui qt4|qt # enable PyQt4 event loop integration
%gui gtk # enable PyGTK event loop integration
%gui tk # enable Tk event loop integration
%gui OSX # enable Cocoa event loop integration
# (requires %matplotlib 1.1)
%gui # disable all event loop integration

WARNING: after any of these has been called you can simply create
an application object, but DO NOT start the event loop yourself, as
we have already handled that.
"""
from IPython.lib.inputhook import enable_gui
opts, arg = self.parse_options(parameter_s, '')
if arg=='': arg = None
return enable_gui(arg)
try:
return self.enable_gui(arg)
except Exception as e:
# print simple error message, rather than traceback if we can't
# hook up the GUI
error(str(e))

def magic_load_ext(self, module_str):
"""Load an IPython extension by its module name."""
Expand Down Expand Up @@ -3416,9 +3422,9 @@ def magic_pylab(self, s):
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
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
Expand Down Expand Up @@ -3449,7 +3455,7 @@ def magic_pylab(self, s):
else:
import_all_status = True

self.shell.enable_pylab(s,import_all=import_all_status)
self.shell.enable_pylab(s, import_all=import_all_status)

def magic_tb(self, s):
"""Print the last traceback with the currently active exception mode.
Expand Down
92 changes: 51 additions & 41 deletions IPython/lib/pylabtools.py → IPython/core/pylabtools.py
Expand Up @@ -232,7 +232,8 @@ def activate_matplotlib(backend):
# For this, we wrap it into a decorator which adds a 'called' flag.
pylab.draw_if_interactive = flag_calls(pylab.draw_if_interactive)

def import_pylab(user_ns, backend, import_all=True, shell=None):

def import_pylab(user_ns, import_all=True):
"""Import the standard pylab symbols into user_ns."""

# Import numpy as np/pyplot as plt are conventions we're trying to
Expand All @@ -246,48 +247,57 @@ def import_pylab(user_ns, backend, import_all=True, shell=None):
)
exec s in user_ns

if shell is not None:
exec s in shell.user_ns_hidden
# If using our svg payload backend, register the post-execution
# function that will pick up the results for display. This can only be
# done with access to the real shell object.
#
from IPython.zmq.pylab.backend_inline import InlineBackend

cfg = InlineBackend.instance(config=shell.config)
cfg.shell = shell
if cfg not in shell.configurables:
shell.configurables.append(cfg)

if backend == backends['inline']:
from IPython.zmq.pylab.backend_inline import flush_figures
from matplotlib import pyplot
shell.register_post_execute(flush_figures)
# load inline_rc
pyplot.rcParams.update(cfg.rc)

# Add 'figsize' to pyplot and to the user's namespace
user_ns['figsize'] = pyplot.figsize = figsize
shell.user_ns_hidden['figsize'] = figsize

# Setup the default figure format
fmt = cfg.figure_format
select_figure_format(shell, fmt)

# The old pastefig function has been replaced by display
from IPython.core.display import display
# Add display and display_png to the user's namespace
user_ns['display'] = display
shell.user_ns_hidden['display'] = display
user_ns['getfigs'] = getfigs
shell.user_ns_hidden['getfigs'] = getfigs

if import_all:
s = ("from matplotlib.pylab import *\n"
"from numpy import *\n")
exec s in user_ns
if shell is not None:
exec s in shell.user_ns_hidden


def configure_shell(shell, backend, user_ns=None):
"""Configure an IPython shell object for matplotlib use.

Parameters
----------
shell : InteractiveShell instance
If None, this function returns immediately.

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 shell is None:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would just not call this function if shell is None - no need for it to conditionally do nothing if it was given inappropriate args.

return

user_ns = shell.user_ns if user_ns is None else user_ns

# If using our svg payload backend, register the post-execution
# function that will pick up the results for display. This can only be
# done with access to the real shell object.
from IPython.zmq.pylab.backend_inline import InlineBackend
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will make ipython --pylab fail in the absence of zmq.


cfg = InlineBackend.instance(config=shell.config)
cfg.shell = shell
if cfg not in shell.configurables:
shell.configurables.append(cfg)

if backend == backends['inline']:
from IPython.zmq.pylab.backend_inline import flush_figures
from matplotlib import pyplot
shell.register_post_execute(flush_figures)
# load inline_rc
pyplot.rcParams.update(cfg.rc)
# Add 'figsize' to pyplot and to the user's namespace
user_ns['figsize'] = pyplot.figsize = figsize

# Setup the default figure format
fmt = cfg.figure_format
select_figure_format(shell, fmt)

# The old pastefig function has been replaced by display
from IPython.core.display import display
# Add display and getfigs to the user's namespace
user_ns['display'] = display
user_ns['getfigs'] = getfigs


def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
Expand All @@ -313,8 +323,8 @@ def pylab_activate(user_ns, gui=None, import_all=True, shell=None):
"""
gui, backend = find_gui_and_backend(gui)
activate_matplotlib(backend)
import_pylab(user_ns, backend, import_all, shell)

import_pylab(user_ns, import_all)
configure_shell(shell, backend, user_ns)
print """
Welcome to pylab, a matplotlib-based Python environment [backend: %s].
For more information, type 'help(pylab)'.""" % backend
Expand Down
Expand Up @@ -20,6 +20,7 @@
import nose.tools as nt

from matplotlib import pyplot as plt
import numpy as np

# Our own imports
from IPython.testing import decorators as dec
Expand Down Expand Up @@ -52,3 +53,11 @@ def test_figure_to_svg():
plt.draw()
svg = pt.print_figure(fig, 'svg')[:100].lower()
yield nt.assert_true('doctype svg' in svg)


def test_import_pylab():
ip = get_ipython()
ns = {}
pt.import_pylab(ns, import_all=False)
nt.assert_true('plt' in ns)
nt.assert_equal(ns['np'], np)
53 changes: 8 additions & 45 deletions IPython/frontend/terminal/interactiveshell.py
Expand Up @@ -29,8 +29,7 @@
from IPython.core.error import TryNext
from IPython.core.usage import interactive_usage, default_banner
from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
from IPython.lib.inputhook import enable_gui
from IPython.lib.pylabtools import pylab_activate
from IPython.core.pylabtools import pylab_activate
from IPython.testing.skipdoctest import skip_doctest
from IPython.utils import py3compat
from IPython.utils.terminal import toggle_set_term_title, set_term_title
Expand Down Expand Up @@ -171,10 +170,13 @@ class TerminalInteractiveShell(InteractiveShell):
help="Enable auto setting the terminal title."
)

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):
# 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,
Expand Down Expand Up @@ -516,45 +518,6 @@ def int0(x):
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 = {}
try:
gui = pylab_activate(ns, gui, import_all)
except KeyError:
error("Backend %r not supported" % gui)
return
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
#-------------------------------------------------------------------------
Expand Down