Skip to content

Commit

Permalink
magickernel: give better error messages when call to magic fails; ker…
Browse files Browse the repository at this point in the history
…nel_magic: move monkeypatch to magickernel; parallel_magic: clean up and make work for general code; time tested to make sure it is running in parallel
  • Loading branch information
Doug Blank committed Sep 10, 2014
1 parent 24a4a6d commit b951a44
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 28 deletions.
17 changes: 9 additions & 8 deletions jupyter_kernel/magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@ def call_magic(self, mtype, name, code, args):
args = []

try:
func(*args, **kwargs)
except TypeError:
try:
func(*args, **kwargs)
except TypeError:
func(old_args)
except Exception:
msg = "Error in calling magic '%s' on %s" % (name, mtype)
self.kernel.Error(msg)
self.kernel.Error(self.get_help(mtype, name))
# return dummy magic to end processing:
return Magic(self.kernel)
except Exception as exc:
msg = "Error in calling magic '%s' on %s:\n %s\n args: %s\n kwargs: %s" % (
name, mtype, str(exc), args, kwargs)
self.kernel.Error(msg)
self.kernel.Error(self.get_help(mtype, name))
# return dummy magic to end processing:
return Magic(self.kernel)
return self

def get_help(self, mtype, name, level=0):
Expand Down
16 changes: 16 additions & 0 deletions jupyter_kernel/magickernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import imp
import re
import inspect
import logging


class MagicKernel(Kernel):
Expand All @@ -22,6 +23,12 @@ class MagicKernel(Kernel):

def __init__(self, *args, **kwargs):
super(MagicKernel, self).__init__(*args, **kwargs)
if self.log is None:
# This occurs if we call as a stand-alone kernel
# (eg, not as a process)
# FIXME: take care of input/output, eg StringIO
# make work without a session
self.log = logging.Logger(".magickernel")
self.sticky_magics = {}
self._i = None
self._ii = None
Expand All @@ -43,6 +50,15 @@ def __init__(self, *args, **kwargs):
jupyter_kernel.JUPYTER_INSTANCE = self
self.set_variable("get_jupyter", jupyter_kernel.get_jupyter)

@classmethod
def subkernel(cls, kernel):
"""
FIXME: monkeypatch to Make this kernel class be a subkernel to another.
"""
cls.log = kernel.log
cls.iopub_socket = kernel.iopub_socket
cls._parent_header = kernel._parent_header

#####################################
# Methods which provide kernel - specific behavior

Expand Down
6 changes: 3 additions & 3 deletions jupyter_kernel/magics/kernel_magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ def line_kernel(self, module_name, class_name, kernel_name="default"):
"""
module = __import__(module_name)
class_ = getattr(module, class_name)
class_.log = logging.Logger(".kernel")
class_.iopub_socket = self.kernel.iopub_socket
class_._parent_header = self.kernel._parent_header
# FIXME: monkeypatch to replace methods of class
# with methods of instance
class_.subkernel(self.kernel)
self.kernels[kernel_name] = class_()
self.retval = self.kernels[kernel_name]

Expand Down
86 changes: 69 additions & 17 deletions jupyter_kernel/magics/parallel_magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,63 +5,112 @@
from jupyter_kernel import Magic, option
import logging

class Slice(object):
"""Utility class for making slice ranges."""
def __getitem__(self, item):
return item

slicer = Slice() ## instance to parse slices

class ParallelMagic(Magic):
client = None
view = None
module_name = None
class_name = None
kernel_name = None
ids = None
retval = None

@option(
'-k', '--kernel_name', action='store', default="default",
help='arbitrary name given to reference kernel'
)
@option(
'-i', '--ids', action='store', default=None,
help='the machine ids to use from the cluster'
)
def line_parallel(self, module_name, class_name, kernel_name="default", ids=None):
"""
%parallel MODULE CLASS NAME ids[...] - construct an interface to the cluster.
%parallel MODULE CLASS [-k NAME] [-i [...]] - construct an interface to the cluster.
Example:
%parallel bash_kernel BashKernel
%parallel bash_kernel BashKernel bash [0, 2, 4]
%parallel bash_kernel BashKernel -k bash
%parallel bash_kernel BashKernel --i [0,2:5,9,...]
Use %px or %%px to send code to the cluster.
"""
from IPython.parallel import Client
self.client = Client()
if ids is None:
self.view = self.client[:]
# ids[:] = slice(None, None, None)
# ids[1:3] = slice(1, 3, None)
# ids[1:3:1] = slice(1, 3, 1)
# ids[1, 2, ...] = [1, 2, Ellipsis]
# ids[1, 2:4, ...] = [1, slice(2, 4, None), Ellipsis]
else:
# ids[:] = slice(None, None, None)
# ids[1:3] = slice(1, 3, None)
# ids[1:3:1] = slice(1, 3, 1)
# ids[1, 2, ...] = [1, 2, Ellipsis]
# ids[1, 2:4, ...] = [1, slice(2, 4, None), Ellipsis]
try:
ids_slice = eval("slicer%s" % ids) # slicer[0,...,7]
except:
ids_slice = slicer[:]
if isinstance(ids_slice, (slice, int)):
self.view = self.client[ids_slice]
else: # tuple of indexes/slices
# TEST: can we do this?
# FIXME: if so, handle Ellipsis
view = []
for item in ids_slice:
view.append(self.client[item])
self.view = view
self.module_name = module_name
self.class_name = class_name
self.kernel_name = kernel_name
self.view.execute("""
try:
kernels
except:
kernels = {}
from %(module_name)s import %(class_name)s
import logging
%(class_name)s.log = logging.Logger(".kernel")
kernel = %(class_name)s()
kernels['%(kernel_name)s'] = %(class_name)s()
""" % {"module_name": module_name,
"class_name": class_name}, block=True)
"class_name": class_name,
"kernel_name": kernel_name},
block=True)
self.retval = None

## px --kernel NAME
def line_px(self, expression):
@option(
'-k', '--kernel_name', action='store', default=None,
help='kernel name given to use for execution'
)
def line_px(self, expression, kernel_name=None):
"""
%px EXPRESSION - send EXPRESSION to the cluster.
Example:
%px sys.version
%px (define x 42)
%px -k scheme (define x 42)
%px x
Use %parallel to initialize the cluster.
"""
self.retval = self.view["kernel.do_execute_direct(\"%s\")" % expression.replace('"', '\\"')]
if kernel_name is None:
kernel_name = self.kernel_name
self.retval = self.view["kernels['%s'].do_execute_direct(\"%s\")" % (
kernel_name, self._clean_code(expression))]

def _clean_code(self, expr):
return expr.strip().replace('"', '\\"').replace("\n", "\\n")

## px --kernel NAME
def cell_px(self):
@option(
'-k', '--kernel_name', action='store', default=None,
help='kernel name given to use for execution'
)
def cell_px(self, kernel_name=None):
"""
%%px - send cell to the cluster.
Expand All @@ -72,7 +121,10 @@ def cell_px(self):
Use %parallel to initialize the cluster.
"""
self.retval = self.view["kernel.do_execute_direct(\"%s\")" % self.code.replace('"', '\\"')]
if kernel_name is None:
kernel_name = self.kernel_name
self.retval = self.view["kernels['%s'].do_execute_direct(\"%s\")" % (
kernel_name, self._clean_code(self.code))]
self.evaluate = False

def post_process(self, retval):
Expand Down

0 comments on commit b951a44

Please sign in to comment.