Skip to content

Commit

Permalink
Correctly handle "ipython --matplotlib=<whatever>"
Browse files Browse the repository at this point in the history
  • Loading branch information
ianthomas23 committed Apr 8, 2024
1 parent e666bc9 commit b1d9d89
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 35 deletions.
15 changes: 5 additions & 10 deletions IPython/core/magics/pylab.py
Expand Up @@ -93,17 +93,12 @@ def matplotlib(self, line=''):
"""
args = magic_arguments.parse_argstring(self.matplotlib, line)
if args.list:
from IPython.core.pylabtools import _matplotlib_manages_backends
from IPython.core.pylabtools import _list_matplotlib_backends_and_gui_loops

if _matplotlib_manages_backends():
from matplotlib.backends.registry import backend_registry

backends_list = backend_registry.list_all()
else:
from IPython.core.pylabtools import backends

backends_list = list(backends.keys())
print("Available matplotlib backends: %s" % backends_list)
print(
"Available matplotlib backends: %s"
% _list_matplotlib_backends_and_gui_loops()
)
else:
gui, backend = self.shell.enable_matplotlib(args.gui.lower() if isinstance(args.gui, str) else args.gui)
self._show_matplotlib_backend(args.gui, backend)
Expand Down
21 changes: 20 additions & 1 deletion IPython/core/pylabtools.py
Expand Up @@ -483,7 +483,8 @@ def _matplotlib_manages_backends() -> bool:
If it returns True, the caller can be sure that
matplotlib.backends.registry.backend_registry is available along with
member functions resolve_gui_or_backend, resolve_backend and list_all.
member functions resolve_gui_or_backend, resolve_backend, list_all, and
list_gui_frameworks.
"""
global _matplotlib_manages_backends_value
if _matplotlib_manages_backends_value is None:
Expand All @@ -497,3 +498,21 @@ def _matplotlib_manages_backends() -> bool:
_matplotlib_manages_backends_value = False

return _matplotlib_manages_backends_value


def _list_matplotlib_backends_and_gui_loops() -> list[str]:
"""Return list of all Matplotlib backends and GUI event loops.
This is the list returned by
%matplotlib --list
"""
if _matplotlib_manages_backends():
from matplotlib.backends.registry import backend_registry

ret = backend_registry.list_all() + backend_registry.list_gui_frameworks()
else:
from IPython.core import pylabtools

ret = list(pylabtools.backends.keys())

return sorted(["auto"] + ret)
89 changes: 65 additions & 24 deletions IPython/core/shellapp.py
Expand Up @@ -11,36 +11,45 @@
from itertools import chain
import os
import sys
import typing as t

from traitlets.config.application import boolean_flag
from traitlets.config.configurable import Configurable
from traitlets.config.loader import Config
from IPython.core.application import SYSTEM_CONFIG_DIRS, ENV_CONFIG_DIRS
from IPython.core import pylabtools
from IPython.utils.contexts import preserve_keys
from IPython.utils.path import filefind
from traitlets import (
Unicode, Instance, List, Bool, CaselessStrEnum, observe,
Unicode,
Instance,
List,
Bool,
CaselessStrEnum,
observe,
DottedObjectName,
Undefined,
)
from IPython.terminal import pt_inputhooks

#-----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Aliases and Flags
#-----------------------------------------------------------------------------
# -----------------------------------------------------------------------------

gui_keys = tuple(sorted(pt_inputhooks.backends) + sorted(pt_inputhooks.aliases))

backend_keys: list[str] = []

shell_flags = {}

addflag = lambda *args: shell_flags.update(boolean_flag(*args))
addflag('autoindent', 'InteractiveShell.autoindent',
'Turn on autoindenting.', 'Turn off autoindenting.'
addflag(
"autoindent",
"InteractiveShell.autoindent",
"Turn on autoindenting.",
"Turn off autoindenting.",
)
addflag('automagic', 'InteractiveShell.automagic',
"""Turn on the auto calling of magic commands. Type %%magic at the
addflag(
"automagic",
"InteractiveShell.automagic",
"""Turn on the auto calling of magic commands. Type %%magic at the
IPython prompt for more information.""",
'Turn off the auto calling of magic commands.'
)
Expand Down Expand Up @@ -96,6 +105,37 @@
)
shell_aliases['cache-size'] = 'InteractiveShell.cache_size'


# -----------------------------------------------------------------------------
# Traitlets
# -----------------------------------------------------------------------------


class MatplotlibBackendCaselessStrEnum(CaselessStrEnum):
"""An enum of Matplotlib backend strings where the case should be ignored.
Prior to Matplotlib 3.9.1 the list of valid backends is hardcoded in
pylabtools.backends. After that, Matplotlib manages backends.
The list of valid backends is determined when it is first needed to avoid
wasting unnecessary initialisation time.
"""

def __init__(
self: CaselessStrEnum[t.Any],
default_value: t.Any = Undefined,
**kwargs: t.Any,
) -> None:
super().__init__(None, default_value=default_value, **kwargs)

def __getattribute__(self, name):
if name == "values" and object.__getattribute__(self, name) is None:
from IPython.core.pylabtools import _list_matplotlib_backends_and_gui_loops

self.values = _list_matplotlib_backends_and_gui_loops()
return object.__getattribute__(self, name)


#-----------------------------------------------------------------------------
# Main classes and functions
#-----------------------------------------------------------------------------
Expand Down Expand Up @@ -155,30 +195,31 @@ class InteractiveShellApp(Configurable):
exec_lines = List(Unicode(),
help="""lines of code to run at IPython startup."""
).tag(config=True)
code_to_run = Unicode('',
help="Execute the given command string."
).tag(config=True)
module_to_run = Unicode('',
help="Run the module as a script."
code_to_run = Unicode("", help="Execute the given command string.").tag(config=True)
module_to_run = Unicode("", help="Run the module as a script.").tag(config=True)
gui = CaselessStrEnum(
gui_keys,
allow_none=True,
help="Enable GUI event loop integration with any of {0}.".format(gui_keys),
).tag(config=True)
gui = CaselessStrEnum(gui_keys, allow_none=True,
help="Enable GUI event loop integration with any of {0}.".format(gui_keys)
).tag(config=True)
matplotlib = CaselessStrEnum(backend_keys, allow_none=True,
matplotlib = MatplotlibBackendCaselessStrEnum(
allow_none=True,
help="""Configure matplotlib for interactive use with
the default matplotlib backend."""
the default matplotlib backend.""",
).tag(config=True)
pylab = CaselessStrEnum(backend_keys, allow_none=True,
pylab = MatplotlibBackendCaselessStrEnum(
allow_none=True,
help="""Pre-load matplotlib and numpy for interactive use,
selecting a particular matplotlib backend and loop integration.
"""
""",
).tag(config=True)
pylab_import_all = Bool(True,
pylab_import_all = Bool(
True,
help="""If true, IPython will populate the user namespace with numpy, pylab, etc.
and an ``import *`` is done from numpy and pylab, when using pylab mode.
When False, pylab mode should not import any names into the user namespace.
"""
""",
).tag(config=True)
ignore_cwd = Bool(
False,
Expand Down

0 comments on commit b1d9d89

Please sign in to comment.