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

PR: Unify kernel configuration #483

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions spyder_kernels/comms/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,10 @@ def register_comm_handlers(instance, frontend_comm):
frontend_comm.register_call_handler(
method_name, method)


def kernel_config(key):
"""Decorarot to mark function as kernel config for set_configuration."""
def config_decorator(fun):
fun._is_kernel_config = key
return fun
return config_decorator
165 changes: 87 additions & 78 deletions spyder_kernels/console/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
import spyder_kernels
from spyder_kernels.comms.frontendcomm import FrontendComm
from spyder_kernels.comms.decorators import (
register_comm_handlers, comm_handler)
register_comm_handlers, comm_handler, kernel_config)
from spyder_kernels.utils.iofuncs import iofunctions
from spyder_kernels.utils.mpl import automatic_backend, MPL_BACKENDS_TO_SPYDER
from spyder_kernels.utils.nsview import (
Expand Down Expand Up @@ -76,6 +76,18 @@ def __init__(self, *args, **kwargs):
# Socket to signal shell_stream locally
self.loopback_socket = None

# load configuration functions
self.load_config_methods()

def load_config_methods(self):
"""Load config methods"""
self._config_methods = {}
for instance in [self, self.shell]:
for method_name in instance.__class__.__dict__:
method = getattr(instance, method_name)
if hasattr(method, '_is_kernel_config'):
self._config_methods[method._is_kernel_config] = method

@property
def kernel_info(self):
# Used for checking correct version by spyder
Expand Down Expand Up @@ -125,11 +137,14 @@ def publish_state(self):
except Exception:
pass

def enable_faulthandler(self):
@kernel_config("faulthandler")
def enable_faulthandler(self, enable=True):
"""
Open a file to save the faulthandling and identifiers for
internal threads.
"""
if not enable:
return
fault_dir = None
if sys.platform.startswith('linux'):
# Do not use /tmp for temporary files
Expand Down Expand Up @@ -548,7 +563,7 @@ def get_mpl_interactive_backend(self):
# magic but not through our Preferences.
return -1

@comm_handler
@kernel_config("matplotlib")
def set_matplotlib_conf(self, conf):
"""Set matplotlib configuration"""
pylab_autoload_n = 'pylab/autoload'
Expand Down Expand Up @@ -623,14 +638,17 @@ def set_mpl_inline_bbox_inches(self, bbox_inches):
'InlineBackend.print_figure_kwargs', print_figure_kwargs)

# -- For completions
@kernel_config("jedi_completer")
def set_jedi_completer(self, use_jedi):
"""Enable/Disable jedi as the completer for the kernel."""
self._set_config_option('IPCompleter.use_jedi', use_jedi)

@kernel_config("greedy_completer")
def set_greedy_completer(self, use_greedy):
"""Enable/Disable greedy completer for the kernel."""
self._set_config_option('IPCompleter.greedy', use_greedy)

@kernel_config("autocall")
def set_autocall(self, autocall):
"""Enable/Disable autocall funtionality."""
self._set_config_option('ZMQInteractiveShell.autocall', autocall)
Expand All @@ -641,42 +659,25 @@ def set_configuration(self, conf):
"""Set kernel configuration"""
ret = {}
for key, value in conf.items():
if key == "cwd":
self._cwd_initialised = True
os.chdir(value)
self.publish_state()
elif key == "namespace_view_settings":
self.namespace_view_settings = value
self.publish_state()
elif key == "pdb":
self.shell.set_pdb_configuration(value)
elif key == "faulthandler":
if value:
ret[key] = self.enable_faulthandler()
elif key == "special_kernel":
try:
self.set_special_kernel(value)
except Exception:
ret["special_kernel_error"] = value
elif key == "color scheme":
self.set_color_scheme(value)
elif key == "jedi_completer":
self.set_jedi_completer(value)
elif key == "greedy_completer":
self.set_greedy_completer(value)
elif key == "autocall":
self.set_autocall(value)
elif key == "matplotlib":
self.set_matplotlib_conf(value)
elif key == "update_gui":
self.shell.update_gui_frontend = value
elif key == "wurlitzer":
if value:
self._load_wurlitzer()
elif key == "autoreload_magic":
self._autoreload_magic(value)
ret[key] = self._config_methods[key](value)
return ret

@kernel_config("update_gui")
def set_update_gui(self, update):
self.shell.update_gui_frontend = update

@kernel_config("namespace_view_settings")
def set_namespace_view_settings(self, namespace_view_settings):
self.namespace_view_settings = namespace_view_settings
self.publish_state()

@kernel_config("cwd")
def set_cwd(self, cwd):
self._cwd_initialised = True
os.chdir(cwd)
self.publish_state()

@kernel_config("color scheme")
def set_color_scheme(self, color_scheme):
if color_scheme == "dark":
# Needed to change the colors of tracebacks
Expand Down Expand Up @@ -712,57 +713,61 @@ def close_all_mpl_figures(self):
except:
pass

@kernel_config("special_kernel")
def set_special_kernel(self, special):
"""
Check if optional dependencies are available for special consoles.
"""
self.shell.special = None
if special is None:
return
try:
self.shell.special = None
if special is None:
return

if special == "pylab":
import matplotlib # noqa
exec("from pylab import *", self.shell.user_ns)
self.shell.special = special
return

if special == "sympy":
import sympy # noqa
sympy_init = "\n".join([
"from sympy import *",
"x, y, z, t = symbols('x y z t')",
"k, m, n = symbols('k m n', integer=True)",
"f, g, h = symbols('f g h', cls=Function)",
"init_printing()",
])
exec(sympy_init, self.shell.user_ns)
self.shell.special = special
return
if special == "pylab":
import matplotlib # noqa
exec("from pylab import *", self.shell.user_ns)
self.shell.special = special
return

if special == "cython":
import cython # noqa
if special == "sympy":
import sympy # noqa
sympy_init = "\n".join([
"from sympy import *",
"x, y, z, t = symbols('x y z t')",
"k, m, n = symbols('k m n', integer=True)",
"f, g, h = symbols('f g h', cls=Function)",
"init_printing()",
])
exec(sympy_init, self.shell.user_ns)
self.shell.special = special
return

# Import pyximport to enable Cython files support for
# import statement
import pyximport
pyx_setup_args = {}
if special == "cython":
import cython # noqa

# Add Numpy include dir to pyximport/distutils
try:
import numpy
pyx_setup_args['include_dirs'] = numpy.get_include()
except Exception:
pass
# Import pyximport to enable Cython files support for
# import statement
import pyximport
pyx_setup_args = {}

# Setup pyximport and enable Cython files reload
pyximport.install(setup_args=pyx_setup_args,
reload_support=True)
# Add Numpy include dir to pyximport/distutils
try:
import numpy
pyx_setup_args['include_dirs'] = numpy.get_include()
except Exception:
pass

self.shell.run_line_magic("reload_ext", "Cython")
self.shell.special = special
return
# Setup pyximport and enable Cython files reload
pyximport.install(setup_args=pyx_setup_args,
reload_support=True)

self.shell.run_line_magic("reload_ext", "Cython")
self.shell.special = special
return

raise NotImplementedError(f"{special}")
raise NotImplementedError(f"{special}")
except Exception:
return special

@comm_handler
def update_syspath(self, path_dict, new_path_dict):
Expand Down Expand Up @@ -1004,6 +1009,7 @@ def set_sympy_forecolor(self, background_color='dark'):
pass

# --- Others
@kernel_config("autoreload_magic")
def _autoreload_magic(self, enable):
"""Load %autoreload magic."""
try:
Expand All @@ -1016,9 +1022,12 @@ def _autoreload_magic(self, enable):
except Exception:
pass

def _load_wurlitzer(self):
@kernel_config("wurlitzer")
def _load_wurlitzer(self, enable=True):
"""Load wurlitzer extension."""
# Wurlitzer has no effect on Windows
if not enable:
return
if not os.name == 'nt':
# Enclose this in a try/except because if it fails the
# console will be totally unusable.
Expand Down
3 changes: 2 additions & 1 deletion spyder_kernels/console/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
from spyder_kernels.customize.namespace_manager import NamespaceManager
from spyder_kernels.customize.spyderpdb import SpyderPdb
from spyder_kernels.customize.code_runner import SpyderCodeRunner
from spyder_kernels.comms.decorators import comm_handler
from spyder_kernels.comms.decorators import comm_handler, kernel_config
from spyder_kernels.utils.mpl import automatic_backend


Expand Down Expand Up @@ -98,6 +98,7 @@ def enable_matplotlib(self, gui=None):
return gui, backend

# --- For Pdb namespace integration
@kernel_config("pdb")
def set_pdb_configuration(self, pdb_conf):
"""
Set Pdb configuration.
Expand Down
13 changes: 0 additions & 13 deletions spyder_kernels/console/start.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,6 @@ def kernel_config():
# Until we implement Issue 1052
spy_cfg.InteractiveShell.xmode = 'Plain'

# Jedi completer.
jedi_o = os.environ.get('SPY_JEDI_O') == 'True'
spy_cfg.IPCompleter.use_jedi = jedi_o

# Clear terminal arguments input.
# This needs to be done before adding the exec_lines that come from
# Spyder, to avoid deleting the sys module if users want to import
Expand Down Expand Up @@ -118,18 +114,9 @@ def kernel_config():
if is_module_installed('matplotlib'):
spy_cfg.IPKernelApp.matplotlib = "inline"

# Autocall
autocall_o = os.environ.get('SPY_AUTOCALL_O')
if autocall_o is not None:
spy_cfg.ZMQInteractiveShell.autocall = int(autocall_o)

# To handle the banner by ourselves
spy_cfg.ZMQInteractiveShell.banner1 = ''

# Greedy completer
greedy_o = os.environ.get('SPY_GREEDY_O') == 'True'
spy_cfg.IPCompleter.greedy = greedy_o

# Disable the new mechanism to capture and forward low-level output
# in IPykernel 6. For that we have Wurlitzer.
spy_cfg.IPKernelApp.capture_fd_output = False
Expand Down
Loading