Skip to content

Commit

Permalink
Merge pull request #466 from impact27/group_config_calls
Browse files Browse the repository at this point in the history
PR: Simplify kernel configuration
  • Loading branch information
ccordoba12 authored Nov 15, 2023
2 parents 8ef4340 + 3a6be2d commit d282829
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 294 deletions.
6 changes: 4 additions & 2 deletions spyder_kernels/comms/frontendcomm.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,11 @@ def _check_comm_reply(self):
"""
Send comm message to frontend to check if the iopub channel is ready
"""
if len(self._pending_comms) == 0:
# Make sure the length doesn't change during iteration
pending_comms = list(self._pending_comms.values())
if len(pending_comms) == 0:
return
for comm in self._pending_comms.values():
for comm in pending_comms:
self._notify_comm_ready(comm)
self.kernel.io_loop.call_later(1, self._check_comm_reply)

Expand Down
5 changes: 1 addition & 4 deletions spyder_kernels/comms/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,8 @@ def __call__(self, string):
if not self._warning_shown:
self._warning_shown = True

# Don't print handler name for `show_mpl_backend_errors`
# because we have a specific message for it.
# request_pdb_stop is expected to print messages.
if self._name not in [
'show_mpl_backend_errors', 'request_pdb_stop']:
if self._name not in ['request_pdb_stop']:
self._write(
"\nOutput from spyder call " + repr(self._name) + ":\n"
)
Expand Down
247 changes: 174 additions & 73 deletions spyder_kernels/console/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@
from zmq.utils.garbage import gc

# Local imports
import spyder_kernels
from spyder_kernels.comms.frontendcomm import FrontendComm
from spyder_kernels.comms.decorators import (
register_comm_handlers, comm_handler)
from spyder_kernels.utils.iofuncs import iofunctions
from spyder_kernels.utils.mpl import (
MPL_BACKENDS_FROM_SPYDER, MPL_BACKENDS_TO_SPYDER, INLINE_FIGURE_FORMATS)
from spyder_kernels.utils.mpl import automatic_backend, MPL_BACKENDS_TO_SPYDER
from spyder_kernels.utils.nsview import (
get_remote_data, make_remote_view, get_size)
from spyder_kernels.console.shell import SpyderShell
Expand Down Expand Up @@ -65,7 +65,6 @@ def __init__(self, *args, **kwargs):
register_comm_handlers(self.shell, self.frontend_comm)

self.namespace_view_settings = {}
self._mpl_backend_error = None
self.faulthandler_handle = None
self._cwd_initialised = False

Expand All @@ -77,6 +76,18 @@ def __init__(self, *args, **kwargs):
# Socket to signal shell_stream locally
self.loopback_socket = None

@property
def kernel_info(self):
# Used for checking correct version by spyder
infos = super().kernel_info
infos.update({
"spyder_kernels_info": (
spyder_kernels.__version__,
sys.executable
)
})
return infos

# -- Public API -----------------------------------------------------------
def frontend_call(self, blocking=False, broadcast=True,
timeout=None, callback=None, display_error=False):
Expand Down Expand Up @@ -114,7 +125,6 @@ def publish_state(self):
except Exception:
pass

@comm_handler
def enable_faulthandler(self):
"""
Open a file to save the faulthandling and identifiers for
Expand All @@ -140,6 +150,11 @@ def enable_faulthandler(self):
faulthandler.enable(self.faulthandler_handle)
return self.faulthandler_handle.name, main_id, system_ids

@comm_handler
def safe_exec(self, filename):
"""Safely execute a file using IPKernelApp._exec_file."""
self.parent._exec_file(filename)

@comm_handler
def get_fault_text(self, fault_filename, main_id, ignore_ids):
"""Get fault text from old run."""
Expand Down Expand Up @@ -260,11 +275,6 @@ def get_current_frames(self, ignore_internal_threads=True):
return frames

# --- For the Variable Explorer
@comm_handler
def set_namespace_view_settings(self, settings):
"""Set namespace_view_settings."""
self.namespace_view_settings = settings

@comm_handler
def get_namespace_view(self, frame=None):
"""
Expand Down Expand Up @@ -529,7 +539,7 @@ def get_mpl_interactive_backend(self):
if framework is None:
# Since no interactive backend has been set yet, this is
# equivalent to having the inline one.
return 0
return 'inline'
elif framework in mapping:
return MPL_BACKENDS_TO_SPYDER[mapping[framework]]
else:
Expand All @@ -538,25 +548,42 @@ def get_mpl_interactive_backend(self):
# magic but not through our Preferences.
return -1

def set_matplotlib_backend(self, backend, pylab=False):
"""Set matplotlib backend given a Spyder backend option."""
mpl_backend = MPL_BACKENDS_FROM_SPYDER[str(backend)]
self._set_mpl_backend(mpl_backend, pylab=pylab)

def set_mpl_inline_figure_format(self, figure_format):
"""Set the inline figure format to use with matplotlib."""
mpl_figure_format = INLINE_FIGURE_FORMATS[figure_format]
self._set_config_option(
'InlineBackend.figure_format', mpl_figure_format)

def set_mpl_inline_resolution(self, resolution):
"""Set inline figure resolution."""
self._set_mpl_inline_rc_config('figure.dpi', resolution)
@comm_handler
def set_matplotlib_conf(self, conf):
"""Set matplotlib configuration"""
pylab_autoload_n = 'pylab/autoload'
pylab_backend_n = 'pylab/backend'
figure_format_n = 'pylab/inline/figure_format'
resolution_n = 'pylab/inline/resolution'
width_n = 'pylab/inline/width'
height_n = 'pylab/inline/height'
bbox_inches_n = 'pylab/inline/bbox_inches'
inline_backend = 'inline'

if pylab_autoload_n in conf or pylab_backend_n in conf:
self._set_mpl_backend(
conf.get(pylab_backend_n, inline_backend),
pylab=conf.get(pylab_autoload_n, False)
)

if figure_format_n in conf:
self._set_config_option(
'InlineBackend.figure_format',
conf[figure_format_n]
)

if resolution_n in conf:
self._set_mpl_inline_rc_config('figure.dpi', conf[resolution_n])

if width_n in conf and height_n in conf:
self._set_mpl_inline_rc_config(
'figure.figsize',
(conf[width_n], conf[height_n])
)

if bbox_inches_n in conf:
self.set_mpl_inline_bbox_inches(conf[bbox_inches_n])

def set_mpl_inline_figure_size(self, width, height):
"""Set inline figure size."""
value = (width, height)
self._set_mpl_inline_rc_config('figure.figsize', value)

def set_mpl_inline_bbox_inches(self, bbox_inches):
"""
Expand Down Expand Up @@ -599,11 +626,54 @@ def set_autocall(self, autocall):

# --- Additional methods
@comm_handler
def set_cwd(self, dirname):
"""Set current working directory."""
self._cwd_initialised = True
os.chdir(dirname)
self.publish_state()
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)
return ret

def set_color_scheme(self, color_scheme):
if color_scheme == "dark":
# Needed to change the colors of tracebacks
self.shell.run_line_magic("colors", "linux")
self.set_sympy_forecolor(background_color='dark')
elif color_scheme == "light":
self.shell.run_line_magic("colors", "lightbg")
self.set_sympy_forecolor(background_color='light')

def get_cwd(self):
"""Get current working directory."""
Expand Down Expand Up @@ -631,28 +701,57 @@ def close_all_mpl_figures(self):
except:
pass

@comm_handler
def is_special_kernel_valid(self):
def set_special_kernel(self, special):
"""
Check if optional dependencies are available for special consoles.
"""
try:
if os.environ.get('SPY_AUTOLOAD_PYLAB_O') == 'True':
import matplotlib
elif os.environ.get('SPY_SYMPY_O') == 'True':
import sympy
elif os.environ.get('SPY_RUN_CYTHON') == 'True':
import cython
except Exception:
# Use Exception instead of ImportError here because modules can
# fail to be imported due to a lot of issues.
if os.environ.get('SPY_AUTOLOAD_PYLAB_O') == 'True':
return u'matplotlib'
elif os.environ.get('SPY_SYMPY_O') == 'True':
return u'sympy'
elif os.environ.get('SPY_RUN_CYTHON') == 'True':
return u'cython'
return None
self.shell.special = None
if special is None:
return

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

if special == "sympy":
import sympy
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 == "cython":
import cython

# Import pyximport to enable Cython files support for
# import statement
import pyximport
pyx_setup_args = {}

# Add Numpy include dir to pyximport/distutils
try:
import numpy
pyx_setup_args['include_dirs'] = numpy.get_include()
except Exception:
pass

# 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}")

@comm_handler
def update_syspath(self, path_dict, new_path_dict):
Expand Down Expand Up @@ -805,6 +904,9 @@ def _set_mpl_backend(self, backend, pylab=False):

magic = 'pylab' if pylab else 'matplotlib'

if backend == "auto":
backend = automatic_backend()

error = None
try:
# This prevents Matplotlib to automatically set the backend, which
Expand Down Expand Up @@ -843,8 +945,8 @@ def _set_mpl_backend(self, backend, pylab=False):
error = generic_error.format(err) + '\n\n' + additional_info
except Exception:
error = generic_error.format(traceback.format_exc())

self._mpl_backend_error = error
if error:
print(error)

def _set_config_option(self, option, value):
"""
Expand Down Expand Up @@ -876,31 +978,30 @@ def _set_mpl_inline_rc_config(self, option, value):
# Needed in case matplolib isn't installed
pass

@comm_handler
def show_mpl_backend_errors(self):
"""Show Matplotlib backend errors after the prompt is ready."""
if self._mpl_backend_error is not None:
print(self._mpl_backend_error) # spyder: test-skip

@comm_handler
def set_sympy_forecolor(self, background_color='dark'):
"""Set SymPy forecolor depending on console background."""
if os.environ.get('SPY_SYMPY_O') == 'True':
try:
from sympy import init_printing
if background_color == 'dark':
init_printing(forecolor='White', ip=self.shell)
elif background_color == 'light':
init_printing(forecolor='Black', ip=self.shell)
except Exception:
pass
if self.shell.special != "sympy":
return

try:
from sympy import init_printing
if background_color == 'dark':
init_printing(forecolor='White', ip=self.shell)
elif background_color == 'light':
init_printing(forecolor='Black', ip=self.shell)
except Exception:
pass

# --- Others
def _load_autoreload_magic(self):
def _autoreload_magic(self, enable):
"""Load %autoreload magic."""
try:
self.shell.run_line_magic('reload_ext', 'autoreload')
self.shell.run_line_magic('autoreload', '2')
if enable:
self.shell.run_line_magic('reload_ext', 'autoreload')
self.shell.run_line_magic('autoreload', "2")
else:
self.shell.run_line_magic('autoreload', "off")

except Exception:
pass

Expand Down
Loading

0 comments on commit d282829

Please sign in to comment.