Esempio n. 1
0
class SingularKernel(Kernel):
    implementation = "jupyter_singular_wrapper"
    implementation_version = __version__

    def _replace_get_ipython(self):
        new_kernel = own_ipython(self)
        global kernel_object_for_ipython
        kernel_object_for_ipython = new_kernel

    @property
    def language_version(self):
        m = version_pat.search(self.banner)
        return m.group(1)

    _banner = None

    @property
    def banner(self):
        if self._banner is None:
            self._banner = "Singular Jupyter kernel"
        return self._banner

    language_info = {
        "name": "Singular",
        "codemirror_mode": "singular",  # note that this does not exist yet
        "mimetype": "text/x-singular",
        "file_extension": ".singular",
    }

    help_links = [
        {"text": "Singular manual", "url": "http://www.singular.uni-kl.de/Manual/latest/index.htm"},
        {"text": "Singular examples", "url": "http://www.singular.uni-kl.de/Manual/latest/sing_842.htm"},
        {"text": "Singular library", "url": "http://www.singular.uni-kl.de/Manual/latest/sing_926.htm"},
        {"text": "Singular index", "url": "http://www.singular.uni-kl.de/Manual/latest/sing_2336.htm"},
    ]

    def __init__(self, **kwargs):
        Kernel.__init__(self, **kwargs)
        self._replace_get_ipython()
        self.comm_manager = CommManager(shell=None, parent=self, kernel=self)

        self.shell_handlers["comm_open"] = self.comm_manager.comm_open
        self.shell_handlers["comm_msg"] = self.comm_manager.comm_msg
        self.shell_handlers["comm_close"] = self.comm_manager.comm_close

        self.comm_manager.register_target("ipython.widget", Widget.handle_comm_opened)
        self._start_singular()

    def _start_singular(self):
        sig = signal.signal(signal.SIGINT, signal.SIG_DFL)
        signal.signal(signal.SIGINT, sig)
        InitializeSingular(which("Singular"))

    def _check_for_plot(self, code):
        return "plot_jupyter" in code

    def _process_python(self, code):
        if code.find("@python") == -1 and code.find("@widget") == -1:
            return False
        exec(code[7:], globals(), locals())
        return True

    def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False):

        default = {"status": "ok", "execution_count": self.execution_count, "payload": [], "user_expressions": {}}

        if not code.strip():
            return default

        if self._process_python(code):
            return default

        interrupted = False

        code_stripped = code.rstrip()
        output = RunSingularCommand(code_stripped)

        output_error = output[0]
        output_string = output[1]

        if not output_error:
            if not silent:
                if self._check_for_plot(code_stripped):

                    filename_image = output_string.rstrip()

                    with open(filename_image, "rb") as imageFile:
                        image_string = base64.b64encode(imageFile.read()).decode()

                    stream_content = {
                        "source": "singular",
                        "data": {"image/jpeg": image_string},
                        "metadata": {"image/jpeg": {"width": 400, "height": 400}},
                    }
                    self.send_response(self.iopub_socket, "display_data", stream_content)
                elif output_string.strip() != "":
                    stream_content = {"execution_count": self.execution_count, "data": {"text/plain": output_string}}
                    self.send_response(self.iopub_socket, "execute_result", stream_content)

            return {"status": "ok", "execution_count": self.execution_count, "payload": [], "user_expressions": {}}

        else:

            stream_content = {"execution_count": self.execution_count, "data": {"text/plain": "Error:"}}
            self.send_response(self.iopub_socket, "execute_result", stream_content)
            stream_content = {"execution_count": self.execution_count, "data": {"text/plain": output_string}}
            self.send_response(self.iopub_socket, "execute_result", stream_content)

            stream_content = {
                "execution_count": self.execution_count,
                "ename": "",
                "evalue": output_string,
                "traceback": [],
            }
            self.send_response(self.iopub_socket, "error", stream_content)
            return {
                "status": "error",
                "execution_count": self.execution_count,
                "ename": "",
                "evalue": output_string,
                "traceback": [],
            }

    def do_complete(self, code, cursor_pos):
        code = code[:cursor_pos]
        default = {"matches": [], "cursor_start": 0, "cursor_end": cursor_pos, "metadata": dict(), "status": "ok"}

        token = code.encode("utf-8")
        start = cursor_pos - len(token)
        completion_list = GetSingularCompletion(code, start, cursor_pos)

        if not completion_list:
            return default

        return {
            "matches": sorted(completion_list),
            "cursor_start": start,
            "cursor_end": cursor_pos,
            "metadata": dict(),
            "status": "ok",
        }

    def do_is_complete(self, code):
        code = code.rstrip()
        if code[-1] == ";":
            return {"status": "complete"}
        else:
            return {"status": "incomplete", "indent": "  "}
Esempio n. 2
0
class MetaKernel(Kernel):
    """The base MetaKernel class."""

    app_name = 'metakernel'
    identifier_regex = r'[^\d\W][\w\.]*'
    func_call_regex = r'([^\d\W][\w\.]*)\([^\)\()]*\Z'
    magic_prefixes = dict(magic='%', shell='!', help='?')
    help_suffix = '?'
    help_links = [
        {
            'text': "MetaKernel Magics",
            'url': "https://metakernel.readthedocs.io/en/latest/source/README.html",
        },
    ]
    language_info = {
        # 'mimetype': 'text/x-python',
        # 'name': 'python',
        # ------ If different from 'language':
        # 'codemirror_mode': {
        #    "version": 2,
        #    "name": "ipython"
        # }
        # 'pygments_lexer': 'language',
        # 'version'       : "x.y.z",
        # 'file_extension': '.py',
        'help_links': help_links,
    }
    plot_settings = Dict(dict(backend='inline')).tag(config=True)

    meta_kernel = None

    @classmethod
    def run_as_main(cls, *args, **kwargs):
        """Launch or install a metakernel.

        Modules implementing a metakernel subclass can use the following lines:

            if __name__ == '__main__':
                MetaKernelSubclass.run_as_main()
        """
        kwargs['app_name'] = cls.app_name
        MetaKernelApp.launch_instance(kernel_class=cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        super(MetaKernel, self).__init__(*args, **kwargs)
        if MetaKernel.meta_kernel is None:
            MetaKernel.meta_kernel = self
        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(".metakernel")
        else:
            # Write has already been set
            try:
                sys.stdout.write = self.Write
            except:
                pass  # Can't change stdout
        self.redirect_to_log = False
        self.shell = None
        self.sticky_magics = OrderedDict()
        self._i = None
        self._ii = None
        self._iii = None
        self._ = None
        self.__ = None
        self.___ = None
        self.max_hist_cache = 1000
        self.hist_cache = []
        self.comm_manager = CommManager(shell=None, parent=self,
                                        kernel=self)
        self.comm_manager.register_target('ipython.widget',
            lazy_import_handle_comm_opened)

        self.hist_file = get_history_file(self)
        self.parser = Parser(self.identifier_regex, self.func_call_regex,
                             self.magic_prefixes, self.help_suffix)
        comm_msg_types = ['comm_open', 'comm_msg', 'comm_close']
        for msg_type in comm_msg_types:
            self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
        self._ipy_formatter = IPythonDisplayFormatter()
        self.env = {}
        self.reload_magics()
        # provide a way to get the current instance
        self.set_variable("kernel", self)
        # Run command line filenames, if given:
        if self.parent is not None and self.parent.extra_args:
            level = self.log.level
            self.log.setLevel("INFO")
            self.redirect_to_log = True
            self.Write("Executing files...")
            for filename in self.parent.extra_args:
                self.Write("    %s..." % filename)
                try:
                    self.do_execute_file(filename)
                except Exception as exc:
                    self.log.info("    %s" % (exc,))
            self.Write("Executing files: done!")
            self.log.setLevel(level)
            self.redirect_to_log = False

    def makeSubkernel(self, kernel):
        """
        Run this method in an IPython kernel to set
        this kernel's input/output settings.
        """
        from IPython import get_ipython
        from IPython.display import display
        shell = get_ipython()
        if shell: # we are running under an IPython kernel
            self.session = shell.kernel.session
            self.Display = display
            self.send_response = self._send_shell_response
        else:
            self.session = kernel.session
            self.send_response = kernel.send_response
            self.Display = kernel.Display

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

    def set_variable(self, name, value):
        """
        Set a variable to a Python-typed value.
        """
        pass

    def get_variable(self, name):
        """
        Lookup a variable name and return a Python-typed value.
        """
        pass

    def repr(self, item):
        """The repr of the kernel."""
        return repr(item)

    def get_usage(self):
        """Get the usage statement for the kernel."""
        return "This is a usage statement."

    def get_kernel_help_on(self, info, level=0, none_on_fail=False):
        """Get help on an object.  Called by the help magic."""
        if none_on_fail:
            return None
        else:
            return "Sorry, no help is available on '%s'." % info['code']

    def handle_plot_settings(self):
        """Handle the current plot settings"""
        pass

    def get_local_magics_dir(self):
        """
        Returns the path to local magics dir (eg ~/.ipython/metakernel/magics)
        """
        base = get_ipython_dir()
        return os.path.join(base, 'metakernel', 'magics')

    def get_completions(self, info):
        """
        Get completions from kernel based on info dict.
        """
        return []

    def do_execute_direct(self, code, silent=False):
        """
        Execute code in the kernel language.
        """
        pass

    def do_execute_file(self, filename):
        """
        Default code for running a file. Just opens the file, and sends
        the text to do_execute_direct.
        """
        code = "".join(open(filename).readlines())
        return self.do_execute_direct(code)

    def do_execute_meta(self, code):
        """
        Execute meta code in the kernel. This uses the execute infrastructure
        but allows JavaScript to talk directly to the kernel bypassing normal
        processing.

        When responding to the %%debug magic, the step and reset meta
        commands can answer with a string in the format:

        "highlight: [start_line, start_col, end_line, end_col]"

        for highlighting expressions in the frontend.
        """
        if code == "reset":
            raise Exception("This kernel does not implement this meta command")
        elif code == "stop":
            raise Exception("This kernel does not implement this meta command")
        elif code == "step":
            raise Exception("This kernel does not implement this meta command")
        elif code.startswith("inspect "):
            raise Exception("This kernel does not implement this meta command")
        else:
            raise Exception("Unknown meta command: '%s'" % code)

    def initialize_debug(self, code):
        """
        This function is used with the %%debug magic for highlighting
        lines of code, and for initializing debug functions.

        Return the empty string if highlighting is not supported.
        """
        #return "highlight: [%s, %s, %s, %s]" % (line1, col1, line2, col2)
        return ""

    def do_function_direct(self, function_name, arg):
        """
        Call a function in the kernel language with args (as a single item).
        """
        f = self.do_execute_direct(function_name)
        return f(arg)

    def restart_kernel(self):
        """Restart the kernel"""
        pass

    ############################################
    # Implement base class methods

    def do_execute(self, code, silent=False, store_history=True, user_expressions=None,
                   allow_stdin=False):
        """Handle code execution.

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#execute
        """
        # Set the ability for the kernel to get standard-in:
        self._allow_stdin = allow_stdin
        # Create a default response:
        self.kernel_resp = {
            'status': 'ok',
            # The base class increments the execution count
            'execution_count': self.execution_count,
            'payload': [],
            'user_expressions': {},
        }

        # TODO: remove this when IPython fixes this
        # This happens at startup when the language is set to python
        if '_usage.page_guiref' in code:
            return self.kernel_resp

        if code and store_history:
            self.hist_cache.append(code.strip())

        if not code.strip():
            return self.kernel_resp

        info = self.parse_code(code)
        self.payload = []
        retval = None

        if info['magic'] and info['magic']['name'] == 'help':

            if info['magic']['type'] == 'line':
                level = 0
            else:
                level = 1
            text = self.get_help_on(code, level)
            if text:
                content = {
                    "start_line_number": 0,
                    "source": "page",
                }
                if isinstance(text, dict):
                    content["data"] = text ## {mime-type: ..., mime-type:...}
                    self.log.debug(str(text))
                else:
                    content["data"] = {"text/plain": text}
                    self.log.debug(text)
                self.payload = [content]

        elif info['magic'] or self.sticky_magics:
            retval = None
            if self.sticky_magics:
                magics, code = _split_magics_code(code, self.magic_prefixes)
                code = magics + self._get_sticky_magics() + code
            stack = []
            # Handle magics:
            magic = None
            prefixes = ((self.magic_prefixes['shell'],
                         self.magic_prefixes['magic']))
            while code.startswith(prefixes):
                magic = self.get_magic(code)
                if magic is not None:
                    stack.append(magic)
                    code = magic.get_code()
                    # signal to exit, maybe error or no block
                    if not magic.evaluate:
                        break
                else:
                    break
            # Execute code, if any:
            if ((magic is None or magic.evaluate) and code.strip() != ""):
                if code.startswith("~~META~~:"):
                    retval = self.do_execute_meta(code[9:].strip())
                else:
                    retval = self.do_execute_direct(code)
            # Post-process magics:
            for magic in reversed(stack):
                retval = magic.post_process(retval)
        else:
            if code.startswith("~~META~~:"):
                retval = self.do_execute_meta(code[9:].strip())
            else:
                retval = self.do_execute_direct(code)

        self.post_execute(retval, code, silent)

        if 'payload' in self.kernel_resp:
            self.kernel_resp['payload'] = self.payload

        return self.kernel_resp

    def post_execute(self, retval, code, silent):
        """Post-execution actions

        Handle special kernel variables and display response if not silent.
        """
        # Handle in's
        self.set_variable("_iii", self._iii)
        self.set_variable("_ii", self._ii)
        self.set_variable("_i", code)
        self.set_variable("_i" + str(self.execution_count), code)
        self._iii = self._ii
        self._ii = code
        if (retval is not None):
            # --------------------------------------
            # Handle out's (only when non-null)
            self.set_variable("___", self.___)
            self.set_variable("__", self.__)
            self.set_variable("_", retval)
            self.set_variable("_" + str(self.execution_count), retval)
            self.___ = self.__
            self.__ = retval
            self.log.debug(retval)
            if isinstance(retval, ExceptionWrapper):
                self.kernel_resp['status'] = 'error'
                content = {
                    'traceback':  retval.traceback,
                    'evalue': retval.evalue,
                    'ename': retval.ename,
                }
                self.kernel_resp.update(content)
                if not silent:
                    self.send_response(self.iopub_socket, 'error', content)
            else:
                try:
                    data = _formatter(retval, self.repr)
                except Exception as e:
                    self.Error(e)
                    return
                content = {
                    'execution_count': self.execution_count,
                    'data': data[0],
                    'metadata': data[1],
                }
                if not silent:
                    if Widget and isinstance(retval, Widget):
                        self.Display(retval)
                        return
                    self.send_response(self.iopub_socket, 'execute_result', content)

    def do_history(self, hist_access_type, output, raw, session=None,
                   start=None, stop=None, n=None, pattern=None, unique=False):
        """
        Access history at startup.

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#history
        """
        with open(self.hist_file) as fid:
            self.hist_cache = json.loads(fid.read() or "[]")
        return {'history': [(None, None, h) for h in self.hist_cache]}

    def do_shutdown(self, restart):
        """
        Shut down the app gracefully, saving history.

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#kernel-shutdown
        """
        if self.hist_file:
            with open(self.hist_file, "w") as fid:
                json.dump(self.hist_cache[-self.max_hist_cache:], fid)
        if restart:
            self.Print("Restarting kernel...")
            self.restart_kernel()
            self.reload_magics()
            self.Print("Done!")
        return {'status': 'ok', 'restart': restart}

    def do_is_complete(self, code):
        """
        Given code as string, returns dictionary with 'status' representing
        whether code is ready to evaluate. Possible values for status are:

           'complete'   - ready to evaluate
           'incomplete' - not yet ready
           'invalid'    - invalid code
           'unknown'    - unknown; the default unless overridden

        Optionally, if 'status' is 'incomplete', you may indicate
        an indentation string.

        Example:

            return {'status' : 'incomplete',
                    'indent': ' ' * 4}

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#code-completeness
        """
        if code.startswith(self.magic_prefixes['magic']):
            ## force requirement to end with an empty line
            if code.endswith("\n"):
                return {'status' : 'complete'}
            else:
                return {'status' : 'incomplete'}
        # otherwise, how to know is complete?
        elif code.endswith("\n"):
            return {'status' : 'complete'}
        else:
            return {'status' : 'incomplete'}

    def do_complete(self, code, cursor_pos):
        """Handle code completion for the kernel.

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#completion
        """
        info = self.parse_code(code, 0, cursor_pos)
        content = {
            'matches': [],
            'cursor_start': info['start'],
            'cursor_end': info['end'],
            'status': 'ok'
        }

        matches = info['path_matches']

        if info['magic']:

            # if the last line contains another magic, use that
            line_info = self.parse_code(info['line'])
            if line_info['magic']:
                info = line_info

            if info['magic']['type'] == 'line':
                magics = self.line_magics
            else:
                magics = self.cell_magics

            if info['magic']['name'] in magics:
                magic = magics[info['magic']['name']]
                info = info['magic']
                if info['type'] == 'cell' and info['code']:
                    info = self.parse_code(info['code'])
                else:
                    info = self.parse_code(info['args'])

                matches.extend(magic.get_completions(info))

            elif not info['magic']['code'] and not info['magic']['args']:
                matches = []
                for name in magics.keys():
                    if name.startswith(info['magic']['name']):
                        pre = info['magic']['prefix']
                        matches.append(pre + name)
                        info['start'] -= len(pre)
                        info['full_obj'] = pre + info['full_obj']
                        info['obj'] = pre + info['obj']

        else:
            matches.extend(self.get_completions(info))

        if info['full_obj'] and len(info['full_obj']) > len(info['obj']):
            new_list = [m for m in matches if m.startswith(info['full_obj'])]
            if new_list:
                content['cursor_end'] = (content['cursor_end'] +
                                         len(info['full_obj']) -
                                         len(info['obj']))
                matches = new_list

        content["matches"] = sorted(matches)

        return content

    def do_inspect(self, code, cursor_pos, detail_level=0):
        """Object introspection.

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#introspection
        """
        if cursor_pos > len(code):
            return

        content = {'status': 'aborted', 'data': {}, 'found': False}
        docstring = self.get_help_on(code, detail_level, none_on_fail=True,
             cursor_pos=cursor_pos)

        if docstring:
            content["status"] = "ok"
            content["found"] = True
            if isinstance(docstring, dict): ## {"text/plain": ..., mime-type: ...}
                content["data"] = docstring
                self.log.debug(str(docstring))
            else:
                content["data"] = {"text/plain": docstring}
                self.log.debug(docstring)

        return content


    def clear_output(self, wait=False):
        """Clear the output of the kernel."""
        self.send_response(self.iopub_socket, 'clear_output',
                           {'wait': wait})

    def Display(self, *objects, **kwargs):
        """Display one or more objects using rich display.

        Supports a `clear_output` keyword argument that clears the output before displaying.

        See https://ipython.readthedocs.io/en/stable/config/integrating.html?highlight=display#rich-display
        """
        if kwargs.get('clear_output'):
            self.clear_output(wait=True)

        for item in objects:
            if Widget and isinstance(item, Widget):
                self.log.debug('Display Widget')
                self._ipy_formatter(item)
            else:
                self.log.debug('Display Data')
                try:
                    data = _formatter(item, self.repr)
                except Exception as e:
                    self.Error(e)
                    return
                content = {
                    'data': data[0],
                    'metadata': data[1]
                }
                self.send_response(
                    self.iopub_socket,
                    'display_data',
                    content
                )

    def Print(self, *objects, **kwargs):
        """Print `objects` to the iopub stream, separated by `sep` and followed by `end`.

        Items can be strings or `Widget` instances.
        """
        for item in objects:
            if Widget and isinstance(item, Widget):
                self.Display(item)

        objects = [i for i in objects if not (Widget and isinstance(i, Widget))]
        message = format_message(*objects, **kwargs)

        stream_content = {
            'name': 'stdout', 'text': message}
        self.log.debug('Print: %s' % message.rstrip())
        if self.redirect_to_log:
            self.log.info(message.rstrip())
        else:
            self.send_response(self.iopub_socket, 'stream', stream_content)

    def Write(self, message):
        """Write message directly to the iopub stdout with no added end character."""
        stream_content = {
            'name': 'stdout', 'text': message}
        self.log.debug('Write: %s' % message)
        if self.redirect_to_log:
            self.log.info(message)
        else:
            self.send_response(self.iopub_socket, 'stream', stream_content)

    def Error(self, *objects, **kwargs):
        """Print `objects` to stdout, separated by `sep` and followed by `end`.

        Objects are cast to strings.
        """
        message = format_message(*objects, **kwargs)
        self.log.debug('Error: %s' % message.rstrip())
        stream_content = {
            'name': 'stderr',
            'text': RED + message + NORMAL
        }
        if self.redirect_to_log:
            self.log.info(message.rstrip())
        else:
            self.send_response(self.iopub_socket, 'stream', stream_content)

    ##############################
    # Private API and methods not likely to be overridden

    def reload_magics(self):
        """Reload all of the line and cell magics."""
        self.line_magics = {}
        self.cell_magics = {}

        # get base magic files and those relative to the current class
        # directory
        magic_files = []
        # Make a metakernel/magics if it doesn't exist:
        local_magics_dir = get_local_magics_dir()
        # Search all of the places there could be magics:
        try:
            paths = [os.path.join(os.path.dirname(
                os.path.abspath(inspect.getfile(self.__class__))), "magics")]
        except:
            paths = []
        paths += [local_magics_dir,
                  os.path.join(os.path.dirname(os.path.abspath(__file__)), "magics")]
        for magic_dir in paths:
            sys.path.append(magic_dir)
            magic_files.extend(glob.glob(os.path.join(magic_dir, "*.py")))

        for magic in magic_files:
            basename = os.path.basename(magic)
            if basename == "__init__.py":
                continue
            try:
                module = __import__(os.path.splitext(basename)[0])
                imp.reload(module)
                module.register_magics(self)
            except Exception as e:
                self.log.error("Can't load '%s': error: %s" % (magic, e))

    def register_magics(self, magic_klass):
        """Register magics for a given magic_klass."""
        magic = magic_klass(self)
        line_magics = magic.get_magics('line')
        cell_magics = magic.get_magics('cell')
        for name in line_magics:
            self.line_magics[name] = magic
        for name in cell_magics:
            self.cell_magics[name] = magic

    def send_response(self, *args, **kwargs):
        ### if we are running via %parallel, we might not have a
        ### session
        if self.session:
            super(MetaKernel, self).send_response(*args, **kwargs)

    def call_magic(self, line):
        """
        Given an line, such as "%download http://example.com/", parse
        and execute magic.
        """
        return self.get_magic(line)

    def get_magic(self, text):
        ## FIXME: Bad name, use call_magic instead.
        # if first line matches a magic,
        # call magic.call_magic() and return magic object
        info = self.parse_code(text)
        magic = self.line_magics['magic']
        return magic.get_magic(info)

    def get_magic_args(self, text):
        # if first line matches a magic,
        # call magic.call_magic() and return magic args
        info = self.parse_code(text)
        magic = self.line_magics['magic']
        return magic.get_magic(info, get_args=True)

    def get_help_on(self, expr, level=0, none_on_fail=False,
            cursor_pos=-1):
        """Get help for an expression using the help magic."""
        help_magic = self.line_magics['help']
        return help_magic.get_help_on(expr, level, none_on_fail, cursor_pos)

    def parse_code(self, code, cursor_start=0, cursor_end=-1):
        """Parse code using our parser."""
        return self.parser.parse_code(code, cursor_start, cursor_end)

    def _get_sticky_magics(self):
        retval = ""
        for key in self.sticky_magics:
            retval += (key + " " + self.sticky_magics[key] + "\n")
        return retval

    def _send_shell_response(self, socket, stream_type, content):
        publish_display_data({ 'text/plain': content['text'] })
Esempio n. 3
0
class MetaKernel(Kernel):

    identifier_regex = r'[^\d\W][\w\.]*'
    func_call_regex = r'([^\d\W][\w\.]*)\([^\)\()]*\Z'
    magic_prefixes = dict(magic='%', shell='!', help='?')
    help_suffix = '?'
    help_links = [
        {
            'text': "MetaKernel Magics",
            'url': "https://github.com/calysto/metakernel/blob/master/metakernel/magics/README.md",
        },
    ]
    language_info = {
        # 'mimetype': 'text/x-python',
        # 'name': 'python',
        # ------ If different from 'language':
        # 'codemirror_mode': {
        #    "version": 2,
        #    "name": "ipython"
        # }
        # 'pygments_lexer': 'language',
        # 'version'       : "x.y.z",
        # 'file_extension': '.py',
        'help_links': help_links,
    }
    meta_kernel = None

    def __init__(self, *args, **kwargs):
        super(MetaKernel, self).__init__(*args, **kwargs)
        if MetaKernel.meta_kernel is None:
            MetaKernel.meta_kernel = self
        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(".metakernel")
        else:
            # Write has already been set
            try:
                sys.stdout.write = self.Write
            except:
                pass  # Can't change stdout
        self.sticky_magics = {}
        self._i = None
        self._ii = None
        self._iii = None
        self._ = None
        self.__ = None
        self.___ = None
        self.max_hist_cache = 1000
        self.hist_cache = []
        self.comm_manager = CommManager(shell=None, parent=self,
                                        kernel=self)
        self.comm_manager.register_target('ipython.widget',
            lazy_import_handle_comm_opened)

        self.plot_settings = dict(backend='inline')
        self.hist_file = get_history_file(self)
        self.reload_magics()
        # provide a way to get the current instance
        self.set_variable("kernel", self)
        self.parser = Parser(self.identifier_regex, self.func_call_regex,
                             self.magic_prefixes, self.help_suffix)
        self.comm_manager = CommManager(shell=None, parent=self,
                                        kernel=self)
        self.comm_manager.register_target('ipython.widget',
                                          lazy_import_handle_comm_opened)
        comm_msg_types = ['comm_open', 'comm_msg', 'comm_close']
        for msg_type in comm_msg_types:
            self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
        self._ipy_formatter = IPythonDisplayFormatter()
        self.env = {}

    @classmethod
    def subkernel(cls, kernel):
        """
        Handle issues regarding making a kernel a subkernel to this
        one. Used in %parallel and %kernel.
        """
        pass

    def makeSubkernelTo(self, main_kernel, display_function):
        """
        Handle issues regarding making this kernel be a subkernel to
        another. Used in making metakernels be magics for IPython.
        """
        self.session = main_kernel.session
        self.Display = display_function

    def makeSubkernelToIPython(self):
        """
        Run this method in an IPython kernel to set
        this kernel's input/output settings.
        """
        from IPython import get_ipython
        from IPython.display import display
        ip = get_ipython()
        if ip: # we are running under an IPython kernel
            self.makeSubkernelTo(ip.parent, display)
        else:
            raise Exception("Need to run under an IPython kernel")

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

    def set_variable(self, name, value):
        """
        Set a variable to a Python-typed value.
        """
        pass

    def get_variable(self, name):
        """
        Lookup a variable name and return a Python-typed value.
        """
        pass

    def repr(self, item):
        return repr(item)

    def get_usage(self):
        return "This is a usage statement."

    def get_kernel_help_on(self, info, level=0, none_on_fail=False):
        if none_on_fail:
            return None
        else:
            return "Sorry, no help is available on '%s'." % info['code']

    def handle_plot_settings(self):
        """Handle the current plot settings"""
        pass

    def get_local_magics_dir(self):
        """
        Returns the path to local magics dir (eg ~/.ipython/metakernel/magics)
        """
        base = get_ipython_dir()
        return os.path.join(base, 'metakernel', 'magics')

    def get_completions(self, info):
        """
        Get completions from kernel based on info dict.
        """
        return []

    def do_execute_direct(self, code, silent=False):
        """
        Execute code in the kernel language.
        """
        pass

    def do_execute_file(self, filename):
        """
        Default code for running a file. Just opens the file, and sends
        the text to do_execute_direct.
        """
        code = "".join(open(filename).readlines())
        return self.do_execute_direct(code)

    def do_execute_meta(self, code):
        """
        Execute meta code in the kernel. This uses the execute infrastructure
        but allows JavaScript to talk directly to the kernel bypassing normal
        processing.

        When responding to the %%debug magic, the step and reset meta
        commands can answer with a string in the format:

        "highlight: [start_line, start_col, end_line, end_col]"

        for highlighting expressions in the frontend.
        """
        if code == "reset":
            raise Exception("This kernel does not implement this meta command")
        elif code == "stop":
            raise Exception("This kernel does not implement this meta command")
        elif code == "step":
            raise Exception("This kernel does not implement this meta command")
        elif code.startswith("inspect "):
            raise Exception("This kernel does not implement this meta command")
        else:
            raise Exception("Unknown meta command: '%s'" % code)

    def initialize_debug(self, code):
        """
        This function is used with the %%debug magic for highlighting
        lines of code, and for initializing debug functions.

        Return the empty string if highlighting is not supported.
        """
        #return "highlight: [%s, %s, %s, %s]" % (line1, col1, line2, col2)
        return ""

    def do_function_direct(self, function_name, arg):
        """
        Call a function in the kernel language with args (as a single item).
        """
        self.Error("This language does not support \"%pmap function args\".")

    def restart_kernel(self):
        """Restart the kernel"""
        pass

    ############################################
    # Implement base class methods

    def do_execute(self, code, silent=False, store_history=True, user_expressions=None,
                   allow_stdin=False):
        # Set the ability for the kernel to get standard-in:
        self._allow_stdin = allow_stdin
        # Create a default response:
        self.kernel_resp = {
            'status': 'ok',
            # The base class increments the execution count
            'execution_count': self.execution_count,
            'payload': [],
            'user_expressions': {},
        }

        # TODO: remove this when IPython fixes this
        # This happens at startup when the language is set to python
        if '_usage.page_guiref' in code:
            return self.kernel_resp

        if code and store_history:
            self.hist_cache.append(code.strip())

        if not code.strip():
            return self.kernel_resp

        info = self.parse_code(code)
        self.payload = []
        retval = None

        if info['magic'] and info['magic']['name'] == 'help':

            if info['magic']['type'] == 'line':
                level = 0
            else:
                level = 1
            text = self.get_help_on(code, level)
            self.log.debug(text)
            if text:
                self.payload = [{"data": {"text/plain": text},
                                 "start_line_number": 0,
                                 "source": "page"}]

        elif info['magic'] or self.sticky_magics:
            retval = None
            if self.sticky_magics:
                magics, code = _split_magics_code(code, self.magic_prefixes)
                code = magics + self._get_sticky_magics() + code
            stack = []
            # Handle magics:
            magic = None
            prefixes = ((self.magic_prefixes['shell'],
                         self.magic_prefixes['magic']))
            while code.startswith(prefixes):
                magic = self.get_magic(code)
                if magic is not None:
                    stack.append(magic)
                    code = magic.get_code()
                    # signal to exit, maybe error or no block
                    if not magic.evaluate:
                        break
                else:
                    break
            # Execute code, if any:
            if ((magic is None or magic.evaluate) and code.strip() != ""):
                if code.startswith("~~META~~:"):
                    retval = self.do_execute_meta(code[9:].strip())
                else:
                    retval = self.do_execute_direct(code)
            # Post-process magics:
            for magic in reversed(stack):
                retval = magic.post_process(retval)
        else:
            if code.startswith("~~META~~:"):
                retval = self.do_execute_meta(code[9:].strip())
            else:
                retval = self.do_execute_direct(code)

        self.post_execute(retval, code, silent)

        if 'payload' in self.kernel_resp:
            self.kernel_resp['payload'] = self.payload

        return self.kernel_resp

    def post_execute(self, retval, code, silent):
        # Handle in's
        self.set_variable("_iii", self._iii)
        self.set_variable("_ii", self._ii)
        self.set_variable("_i", code)
        self.set_variable("_i" + str(self.execution_count), code)
        self._iii = self._ii
        self._ii = code
        if (retval is not None):
            # --------------------------------------
            # Handle out's (only when non-null)
            self.set_variable("___", self.___)
            self.set_variable("__", self.__)
            self.set_variable("_", retval)
            self.set_variable("_" + str(self.execution_count), retval)
            self.___ = self.__
            self.__ = retval
            self.log.debug(retval)
            try:
                content = {'execution_count': self.execution_count,
                           'data': _formatter(retval, self.repr),
                           'metadata': dict()}
            except Exception as e:
                self.Error(e)
                return
            if not silent:
                if Widget and isinstance(retval, Widget):
                    self.Display(retval)
                    return
                self.send_response(self.iopub_socket, 'execute_result', content)

    def do_history(self, hist_access_type, output, raw, session=None,
                   start=None, stop=None, n=None, pattern=None, unique=False):
        """
        Access history at startup.
        """
        if not self.hist_file:
            return {'history': []}
        # else:
        if not os.path.exists(self.hist_file):
            with open(self.hist_file, 'wb') as fid:
                fid.write('')
        with open(self.hist_file, 'rb') as fid:
            history = fid.read().decode('utf-8', 'replace')
        history = history.splitlines()
        history = history[:self.max_hist_cache]
        self.hist_cache = history
        history = [(None, None, h) for h in history]
        return {'history': history}

    def do_shutdown(self, restart):
        """
        Shut down the app gracefully, saving history.
        """
        if self.hist_file:
            with open(self.hist_file, 'wb') as fid:
                data = '\n'.join(self.hist_cache[-self.max_hist_cache:])
                fid.write(data.encode('utf-8'))
        if restart:
            self.Print("Restarting kernel...")
            self.reload_magics()
            self.restart_kernel()
            self.Print("Done!")
        return {'status': 'ok', 'restart': restart}

    def do_is_complete(self, code):
        """
        Given code as string, returns dictionary with 'status' representing
        whether code is ready to evaluate. Possible values for status are:

           'complete'   - ready to evaluate
           'incomplete' - not yet ready
           'invalid'    - invalid code
           'unknown'    - unknown; the default unless overridden

        Optionally, if 'status' is 'incomplete', you may indicate
        an indentation string.

        Example:

            return {'status' : 'incomplete',
                    'indent': ' ' * 4}
        """
        return {'status' : 'unknown'}

    def do_complete(self, code, cursor_pos):
        info = self.parse_code(code, 0, cursor_pos)
        content = {
            'matches': [],
            'cursor_start': info['start'],
            'cursor_end': info['end'],
            'metadata': {},
            'status': 'ok'
        }

        matches = info['path_matches']

        if info['magic']:

            # if the last line contains another magic, use that
            line_info = self.parse_code(info['line'])
            if line_info['magic']:
                info = line_info

            if info['magic']['type'] == 'line':
                magics = self.line_magics
            else:
                magics = self.cell_magics

            if info['magic']['name'] in magics:
                magic = magics[info['magic']['name']]
                info = info['magic']
                if info['type'] == 'cell' and info['code']:
                    info = self.parse_code(info['code'])
                else:
                    info = self.parse_code(info['args'])

                matches.extend(magic.get_completions(info))

            elif not info['magic']['code'] and not info['magic']['args']:
                matches = []
                for name in magics.keys():
                    if name.startswith(info['magic']['name']):
                        pre = info['magic']['prefix']
                        matches.append(pre + name)
                        info['start'] -= len(pre)
                        info['full_obj'] = pre + info['full_obj']
                        info['obj'] = pre + info['obj']

        else:
            matches.extend(self.get_completions(info))

        if info['full_obj'] and len(info['full_obj']) > len(info['obj']):
            new_list = [m for m in matches if m.startswith(info['full_obj'])]
            if new_list:
                content['cursor_end'] = (content['cursor_end'] +
                                         len(info['full_obj']) -
                                         len(info['obj']))
                matches = new_list

        content["matches"] = sorted(matches)

        return content

    def do_inspect(self, code, cursor_pos, detail_level=0):
        # Object introspection
        if cursor_pos > len(code):
            return

        content = {'status': 'aborted', 'data': {}, 'found': False}
        docstring = self.get_help_on(code, detail_level, none_on_fail=True,
             cursor_pos=cursor_pos)

        if docstring:
            content["data"] = {"text/plain": docstring}
            content["status"] = "ok"
            content["found"] = True
            self.log.debug(docstring)

        return content

    ##############################
    # Private API and methods not likely to be overridden

    def reload_magics(self):
        self.line_magics = {}
        self.cell_magics = {}

        # get base magic files and those relative to the current class
        # directory
        magic_files = []
        # Make a metakernel/magics if it doesn't exist:
        local_magics_dir = get_local_magics_dir()
        # Search all of the places there could be magics:
        paths = [local_magics_dir,
                 os.path.join(os.path.dirname(os.path.abspath(__file__)), "magics")]
        try:
            paths += [os.path.join(os.path.dirname(
                os.path.abspath(inspect.getfile(self.__class__))), "magics")]
        except:
            pass
        for magic_dir in paths:
            sys.path.append(magic_dir)
            magic_files.extend(glob.glob(os.path.join(magic_dir, "*.py")))

        for magic in magic_files:
            basename = os.path.basename(magic)
            if basename == "__init__.py":
                continue
            try:
                module = __import__(os.path.splitext(basename)[0])
                imp.reload(module)
                module.register_magics(self)
            except Exception as e:
                self.log.error("Can't load '%s': error: %s" % (magic, e))

    def register_magics(self, magic_klass):
        magic = magic_klass(self)
        line_magics = magic.get_magics('line')
        cell_magics = magic.get_magics('cell')
        for name in line_magics:
            self.line_magics[name] = magic
        for name in cell_magics:
            self.cell_magics[name] = magic

    def clear_output(self, wait=False):
        self.send_response(self.iopub_socket, 'clear_output',
                           {'wait': wait})

    def Display(self, *args, **kwargs):
        clear_output = kwargs.get("clear_output", False)
        for message in args:
            if isinstance(message, HTML):
                if clear_output:
                    self.send_response(self.iopub_socket, 'clear_output',
                                       {'wait': True})
            if Widget and isinstance(message, Widget):
                self.log.debug('Display Widget')
                self._ipy_formatter(message)
            else:
                self.log.debug('Display Data')
                try:
                    data = _formatter(message, self.repr)
                except Exception as e:
                    self.Error(e)
                    return
                self.send_response(self.iopub_socket, 'display_data',
                                   {'data': data,
                                    'metadata': dict()})

    def Print(self, *args, **kwargs):
        end = kwargs["end"] if ("end" in kwargs) else "\n"
        message = ""
        for item in args:
            if Widget and isinstance(item, Widget):
                self.Display(item)
            else:
                if message:
                    message += " "
                if PY3:
                    message += str(item)
                else:
                    message += codecs.encode(item, "utf-8")
        message += end
        stream_content = {
            'name': 'stdout', 'text': message, 'metadata': dict()}
        self.log.debug('Print: %s' % message)
        self.send_response(self.iopub_socket, 'stream', stream_content)

    def Write(self, message):
        stream_content = {
            'name': 'stdout', 'text': message, 'metadata': dict()}
        self.log.debug('Write: %s' % message)
        self.send_response(self.iopub_socket, 'stream', stream_content)

    def Error(self, *args, **kwargs):
        message = format_message(*args, **kwargs)
        self.log.debug('Error: %s' % message)
        stream_content = {
            'name': 'stderr', 'text': message, 'metadata': dict()}
        self.send_response(self.iopub_socket, 'stream', stream_content)

    def call_magic(self, line):
        """
        Given an line, such as "%download http://example.com/", parse
        and execute magic.
        """
        return self.get_magic(line)

    def get_magic(self, text):
        ## FIXME: Bad name, use call_magic instead.
        # if first line matches a magic,
        # call magic.call_magic() and return magic object
        info = self.parse_code(text)
        magic = self.line_magics['magic']
        return magic.get_magic(info)

    def get_magic_args(self, text):
        # if first line matches a magic,
        # call magic.call_magic() and return magic args
        info = self.parse_code(text)
        magic = self.line_magics['magic']
        return magic.get_magic(info, get_args=True)

    def get_help_on(self, expr, level=0, none_on_fail=False,
            cursor_pos=-1):
        help_magic = self.line_magics['help']
        return help_magic.get_help_on(expr, level, none_on_fail, cursor_pos)

    def parse_code(self, code, cursor_start=0, cursor_end=-1):
        return self.parser.parse_code(code, cursor_start, cursor_end)

    def _get_sticky_magics(self):
        retval = ""
        for key in self.sticky_magics:
            retval += (key + " " +
                       " ".join(self.sticky_magics[key])).strip() + "\n"
        return retval
Esempio n. 4
0
class MetaKernel(Kernel):

    identifier_regex = r'[^\d\W][\w\.]*'
    func_call_regex = r'([^\d\W][\w\.]*)\([^\)\()]*\Z'
    magic_prefixes = dict(magic='%', shell='!', help='?')
    help_suffix = '?'
    help_links = [
        {
            'text':
            "MetaKernel Magics",
            'url':
            "https://github.com/calysto/metakernel/blob/master/metakernel/magics/README.md",
        },
    ]
    language_info = {
        # 'mimetype': 'text/x-python',
        # 'name': 'python',
        # ------ If different from 'language':
        # 'codemirror_mode': {
        #    "version": 2,
        #    "name": "ipython"
        # }
        # 'pygments_lexer': 'language',
        # 'version'       : "x.y.z",
        # 'file_extension': '.py',
        'help_links': help_links,
    }
    meta_kernel = None

    def __init__(self, *args, **kwargs):
        super(MetaKernel, self).__init__(*args, **kwargs)
        if MetaKernel.meta_kernel is None:
            MetaKernel.meta_kernel = self
        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(".metakernel")
        else:
            # Write has already been set
            try:
                sys.stdout.write = self.Write
            except:
                pass  # Can't change stdout
        self.sticky_magics = {}
        self._i = None
        self._ii = None
        self._iii = None
        self._ = None
        self.__ = None
        self.___ = None
        self.max_hist_cache = 1000
        self.hist_cache = []
        self.comm_manager = CommManager(shell=None, parent=self, kernel=self)
        self.comm_manager.register_target('ipython.widget',
                                          lazy_import_handle_comm_opened)

        self.plot_settings = dict(backend='inline')
        self.hist_file = get_history_file(self)
        self.reload_magics()
        # provide a way to get the current instance
        self.set_variable("kernel", self)
        self.parser = Parser(self.identifier_regex, self.func_call_regex,
                             self.magic_prefixes, self.help_suffix)
        self.comm_manager = CommManager(shell=None, parent=self, kernel=self)
        self.comm_manager.register_target('ipython.widget',
                                          lazy_import_handle_comm_opened)
        comm_msg_types = ['comm_open', 'comm_msg', 'comm_close']
        for msg_type in comm_msg_types:
            self.shell_handlers[msg_type] = getattr(self.comm_manager,
                                                    msg_type)
        self._ipy_formatter = IPythonDisplayFormatter()
        self.env = {}

    @classmethod
    def subkernel(cls, kernel):
        """
        Handle issues regarding making a kernel a subkernel to this
        one. Used in %parallel and %kernel.
        """
        pass

    def makeSubkernelTo(self, main_kernel, display_function):
        """
        Handle issues regarding making this kernel be a subkernel to
        another. Used in making metakernels be magics for IPython.
        """
        self.session = main_kernel.session
        self.Display = display_function

    def makeSubkernelToIPython(self):
        """
        Run this method in an IPython kernel to set
        this kernel's input/output settings.
        """
        from IPython import get_ipython
        from IPython.display import display
        ip = get_ipython()
        if ip:  # we are running under an IPython kernel
            self.makeSubkernelTo(ip.parent, display)
        else:
            raise Exception("Need to run under an IPython kernel")

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

    def set_variable(self, name, value):
        """
        Set a variable to a Python-typed value.
        """
        pass

    def get_variable(self, name):
        """
        Lookup a variable name and return a Python-typed value.
        """
        pass

    def repr(self, item):
        return repr(item)

    def get_usage(self):
        return "This is a usage statement."

    def get_kernel_help_on(self, info, level=0, none_on_fail=False):
        if none_on_fail:
            return None
        else:
            return "Sorry, no help is available on '%s'." % info['code']

    def handle_plot_settings(self):
        """Handle the current plot settings"""
        pass

    def get_local_magics_dir(self):
        """
        Returns the path to local magics dir (eg ~/.ipython/metakernel/magics)
        """
        base = get_ipython_dir()
        return os.path.join(base, 'metakernel', 'magics')

    def get_completions(self, info):
        """
        Get completions from kernel based on info dict.
        """
        return []

    def do_execute_direct(self, code, silent=False):
        """
        Execute code in the kernel language.
        """
        pass

    def do_execute_file(self, filename):
        """
        Default code for running a file. Just opens the file, and sends
        the text to do_execute_direct.
        """
        code = "".join(open(filename).readlines())
        return self.do_execute_direct(code)

    def do_execute_meta(self, code):
        """
        Execute meta code in the kernel. This uses the execute infrastructure
        but allows JavaScript to talk directly to the kernel bypassing normal
        processing.

        When responding to the %%debug magic, the step and reset meta
        commands can answer with a string in the format:

        "highlight: [start_line, start_col, end_line, end_col]"

        for highlighting expressions in the frontend.
        """
        if code == "reset":
            raise Exception("This kernel does not implement this meta command")
        elif code == "stop":
            raise Exception("This kernel does not implement this meta command")
        elif code == "step":
            raise Exception("This kernel does not implement this meta command")
        elif code.startswith("inspect "):
            raise Exception("This kernel does not implement this meta command")
        else:
            raise Exception("Unknown meta command: '%s'" % code)

    def initialize_debug(self, code):
        """
        This function is used with the %%debug magic for highlighting
        lines of code, and for initializing debug functions.

        Return the empty string if highlighting is not supported.
        """
        #return "highlight: [%s, %s, %s, %s]" % (line1, col1, line2, col2)
        return ""

    def do_function_direct(self, function_name, arg):
        """
        Call a function in the kernel language with args (as a single item).
        """
        self.Error("This language does not support \"%pmap function args\".")

    def restart_kernel(self):
        """Restart the kernel"""
        pass

    ############################################
    # Implement base class methods

    def do_execute(self,
                   code,
                   silent=False,
                   store_history=True,
                   user_expressions=None,
                   allow_stdin=False):
        # Set the ability for the kernel to get standard-in:
        self._allow_stdin = allow_stdin
        # Create a default response:
        self.kernel_resp = {
            'status': 'ok',
            # The base class increments the execution count
            'execution_count': self.execution_count,
            'payload': [],
            'user_expressions': {},
        }

        # TODO: remove this when IPython fixes this
        # This happens at startup when the language is set to python
        if '_usage.page_guiref' in code:
            return self.kernel_resp

        if code and store_history:
            self.hist_cache.append(code.strip())

        if not code.strip():
            return self.kernel_resp

        info = self.parse_code(code)
        self.payload = []
        retval = None

        if info['magic'] and info['magic']['name'] == 'help':

            if info['magic']['type'] == 'line':
                level = 0
            else:
                level = 1
            text = self.get_help_on(code, level)
            self.log.debug(text)
            if text:
                self.payload = [{
                    "data": {
                        "text/plain": text
                    },
                    "start_line_number": 0,
                    "source": "page"
                }]

        elif info['magic'] or self.sticky_magics:
            retval = None
            if self.sticky_magics:
                magics, code = _split_magics_code(code, self.magic_prefixes)
                code = magics + self._get_sticky_magics() + code
            stack = []
            # Handle magics:
            magic = None
            prefixes = ((self.magic_prefixes['shell'],
                         self.magic_prefixes['magic']))
            while code.startswith(prefixes):
                magic = self.get_magic(code)
                if magic is not None:
                    stack.append(magic)
                    code = magic.get_code()
                    # signal to exit, maybe error or no block
                    if not magic.evaluate:
                        break
                else:
                    break
            # Execute code, if any:
            if ((magic is None or magic.evaluate) and code.strip() != ""):
                if code.startswith("~~META~~:"):
                    retval = self.do_execute_meta(code[9:].strip())
                else:
                    retval = self.do_execute_direct(code)
            # Post-process magics:
            for magic in reversed(stack):
                retval = magic.post_process(retval)
        else:
            if code.startswith("~~META~~:"):
                retval = self.do_execute_meta(code[9:].strip())
            else:
                retval = self.do_execute_direct(code)

        self.post_execute(retval, code, silent)

        if 'payload' in self.kernel_resp:
            self.kernel_resp['payload'] = self.payload

        return self.kernel_resp

    def post_execute(self, retval, code, silent):
        # Handle in's
        self.set_variable("_iii", self._iii)
        self.set_variable("_ii", self._ii)
        self.set_variable("_i", code)
        self.set_variable("_i" + str(self.execution_count), code)
        self._iii = self._ii
        self._ii = code
        if (retval is not None):
            # --------------------------------------
            # Handle out's (only when non-null)
            self.set_variable("___", self.___)
            self.set_variable("__", self.__)
            self.set_variable("_", retval)
            self.set_variable("_" + str(self.execution_count), retval)
            self.___ = self.__
            self.__ = retval
            self.log.debug(retval)
            try:
                content = {
                    'execution_count': self.execution_count,
                    'data': _formatter(retval, self.repr),
                    'metadata': dict()
                }
            except Exception as e:
                self.Error(e)
                return
            if not silent:
                if Widget and isinstance(retval, Widget):
                    self.Display(retval)
                    return
                self.send_response(self.iopub_socket, 'execute_result',
                                   content)

    def do_history(self,
                   hist_access_type,
                   output,
                   raw,
                   session=None,
                   start=None,
                   stop=None,
                   n=None,
                   pattern=None,
                   unique=False):
        """
        Access history at startup.
        """
        if not self.hist_file:
            return {'history': []}
        # else:
        if not os.path.exists(self.hist_file):
            with open(self.hist_file, 'wb') as fid:
                fid.write('')
        with open(self.hist_file, 'rb') as fid:
            history = fid.read().decode('utf-8', 'replace')
        history = history.splitlines()
        history = history[:self.max_hist_cache]
        self.hist_cache = history
        history = [(None, None, h) for h in history]
        return {'history': history}

    def do_shutdown(self, restart):
        """
        Shut down the app gracefully, saving history.
        """
        if self.hist_file:
            with open(self.hist_file, 'wb') as fid:
                data = '\n'.join(self.hist_cache[-self.max_hist_cache:])
                fid.write(data.encode('utf-8'))
        if restart:
            self.Print("Restarting kernel...")
            self.reload_magics()
            self.restart_kernel()
            self.Print("Done!")
        return {'status': 'ok', 'restart': restart}

    def do_is_complete(self, code):
        """
        Given code as string, returns dictionary with 'status' representing
        whether code is ready to evaluate. Possible values for status are:

           'complete'   - ready to evaluate
           'incomplete' - not yet ready
           'invalid'    - invalid code
           'unknown'    - unknown; the default unless overridden

        Optionally, if 'status' is 'incomplete', you may indicate
        an indentation string.

        Example:

            return {'status' : 'incomplete',
                    'indent': ' ' * 4}
        """
        return {'status': 'unknown'}

    def do_complete(self, code, cursor_pos):
        info = self.parse_code(code, 0, cursor_pos)
        content = {
            'matches': [],
            'cursor_start': info['start'],
            'cursor_end': info['end'],
            'metadata': {},
            'status': 'ok'
        }

        matches = info['path_matches']

        if info['magic']:

            # if the last line contains another magic, use that
            line_info = self.parse_code(info['line'])
            if line_info['magic']:
                info = line_info

            if info['magic']['type'] == 'line':
                magics = self.line_magics
            else:
                magics = self.cell_magics

            if info['magic']['name'] in magics:
                magic = magics[info['magic']['name']]
                info = info['magic']
                if info['type'] == 'cell' and info['code']:
                    info = self.parse_code(info['code'])
                else:
                    info = self.parse_code(info['args'])

                matches.extend(magic.get_completions(info))

            elif not info['magic']['code'] and not info['magic']['args']:
                matches = []
                for name in magics.keys():
                    if name.startswith(info['magic']['name']):
                        pre = info['magic']['prefix']
                        matches.append(pre + name)
                        info['start'] -= len(pre)
                        info['full_obj'] = pre + info['full_obj']
                        info['obj'] = pre + info['obj']

        else:
            matches.extend(self.get_completions(info))

        if info['full_obj'] and len(info['full_obj']) > len(info['obj']):
            new_list = [m for m in matches if m.startswith(info['full_obj'])]
            if new_list:
                content['cursor_end'] = (content['cursor_end'] +
                                         len(info['full_obj']) -
                                         len(info['obj']))
                matches = new_list

        content["matches"] = sorted(matches)

        return content

    def do_inspect(self, code, cursor_pos, detail_level=0):
        # Object introspection
        if cursor_pos > len(code):
            return

        content = {'status': 'aborted', 'data': {}, 'found': False}
        docstring = self.get_help_on(code,
                                     detail_level,
                                     none_on_fail=True,
                                     cursor_pos=cursor_pos)

        if docstring:
            content["data"] = {"text/plain": docstring}
            content["status"] = "ok"
            content["found"] = True
            self.log.debug(docstring)

        return content

    ##############################
    # Private API and methods not likely to be overridden

    def reload_magics(self):
        self.line_magics = {}
        self.cell_magics = {}

        # get base magic files and those relative to the current class
        # directory
        magic_files = []
        # Make a metakernel/magics if it doesn't exist:
        local_magics_dir = get_local_magics_dir()
        # Search all of the places there could be magics:
        paths = [
            local_magics_dir,
            os.path.join(os.path.dirname(os.path.abspath(__file__)), "magics")
        ]
        try:
            paths += [
                os.path.join(
                    os.path.dirname(
                        os.path.abspath(inspect.getfile(self.__class__))),
                    "magics")
            ]
        except:
            pass
        for magic_dir in paths:
            sys.path.append(magic_dir)
            magic_files.extend(glob.glob(os.path.join(magic_dir, "*.py")))

        for magic in magic_files:
            basename = os.path.basename(magic)
            if basename == "__init__.py":
                continue
            try:
                module = __import__(os.path.splitext(basename)[0])
                imp.reload(module)
                module.register_magics(self)
            except Exception as e:
                self.log.error("Can't load '%s': error: %s" % (magic, e))

    def register_magics(self, magic_klass):
        magic = magic_klass(self)
        line_magics = magic.get_magics('line')
        cell_magics = magic.get_magics('cell')
        for name in line_magics:
            self.line_magics[name] = magic
        for name in cell_magics:
            self.cell_magics[name] = magic

    def clear_output(self, wait=False):
        self.send_response(self.iopub_socket, 'clear_output', {'wait': wait})

    def Display(self, *args, **kwargs):
        clear_output = kwargs.get("clear_output", False)
        for message in args:
            if isinstance(message, HTML):
                if clear_output:
                    self.send_response(self.iopub_socket, 'clear_output',
                                       {'wait': True})
            if Widget and isinstance(message, Widget):
                self.log.debug('Display Widget')
                self._ipy_formatter(message)
            else:
                self.log.debug('Display Data')
                try:
                    data = _formatter(message, self.repr)
                except Exception as e:
                    self.Error(e)
                    return
                self.send_response(self.iopub_socket, 'display_data', {
                    'data': data,
                    'metadata': dict()
                })

    def Print(self, *args, **kwargs):
        end = kwargs["end"] if ("end" in kwargs) else "\n"
        message = ""
        for item in args:
            if Widget and isinstance(item, Widget):
                self.Display(item)
            else:
                if message:
                    message += " "
                if PY3:
                    message += str(item)
                else:
                    message += codecs.encode(item, "utf-8")
        message += end
        stream_content = {
            'name': 'stdout',
            'text': message,
            'metadata': dict()
        }
        self.log.debug('Print: %s' % message)
        self.send_response(self.iopub_socket, 'stream', stream_content)

    def Write(self, message):
        stream_content = {
            'name': 'stdout',
            'text': message,
            'metadata': dict()
        }
        self.log.debug('Write: %s' % message)
        self.send_response(self.iopub_socket, 'stream', stream_content)

    def Error(self, *args, **kwargs):
        message = format_message(*args, **kwargs)
        self.log.debug('Error: %s' % message)
        stream_content = {
            'name': 'stderr',
            'text': message,
            'metadata': dict()
        }
        self.send_response(self.iopub_socket, 'stream', stream_content)

    def call_magic(self, line):
        """
        Given an line, such as "%download http://example.com/", parse
        and execute magic.
        """
        return self.get_magic(line)

    def get_magic(self, text):
        ## FIXME: Bad name, use call_magic instead.
        # if first line matches a magic,
        # call magic.call_magic() and return magic object
        info = self.parse_code(text)
        magic = self.line_magics['magic']
        return magic.get_magic(info)

    def get_magic_args(self, text):
        # if first line matches a magic,
        # call magic.call_magic() and return magic args
        info = self.parse_code(text)
        magic = self.line_magics['magic']
        return magic.get_magic(info, get_args=True)

    def get_help_on(self, expr, level=0, none_on_fail=False, cursor_pos=-1):
        help_magic = self.line_magics['help']
        return help_magic.get_help_on(expr, level, none_on_fail, cursor_pos)

    def parse_code(self, code, cursor_start=0, cursor_end=-1):
        return self.parser.parse_code(code, cursor_start, cursor_end)

    def _get_sticky_magics(self):
        retval = ""
        for key in self.sticky_magics:
            retval += (key + " " +
                       " ".join(self.sticky_magics[key])).strip() + "\n"
        return retval
Esempio n. 5
0
class polymakeKernel(Kernel):
    implementation = 'jupyter_polymake_wrapper'
    implementation_version = __version__

    help_links = [ { 'text': "Polymake website", 'url': "http://polymake.org/" },
                   { 'text': "Polymake documentation", 'url': "https://polymake.org/doku.php/documentation" },
                   { 'text': "Polymake tutorial", 'url': "https://polymake.org/doku.php/tutorial/start" },
                   { 'text': "Polymake reference", 'url': "https://polymake.org/release_docs/3.0/" } ]

    def _replace_get_ipython(self):
        new_kernel = own_ipython(self)
        global kernel_object_for_ipython
        kernel_object_for_ipython = new_kernel

    @property
    def language_version(self):
        m = version_pat.search(self.banner)
        return m.group(1)

    _banner = None

    @property
    def banner(self):
        if self._banner is None:
            self._banner = "Jupyter kernel for polymake"
        return self._banner

    language_info = {'name': 'polymake',
                     'codemirror_mode': 'perl', #
                     'mimetype': 'text/x-polymake', 
                     'file_extension': '.pl'} # FIXME: Is this even real?

    def __init__(self, **kwargs):
        Kernel.__init__(self, **kwargs)
        self._replace_get_ipython()
        self.comm_manager = CommManager(shell=None, parent=self,
                                        kernel=self)
        self.shell_handlers['comm_open'] = self.comm_manager.comm_open
        self.shell_handlers['comm_msg'] = self.comm_manager.comm_msg
        self.shell_handlers['comm_close'] = self.comm_manager.comm_close
        if ipywidgets_extension_loaded:
            self.comm_manager.register_target('ipython.widget', Widget.handle_comm_opened)
        self._start_polymake()

    def _start_polymake(self):
        JuPyMake.InitializePolymake()
        try:
            self._run_polymake_command( 'include "common::jupyter.rules";' )
        except PolymakeRunException:
            return
        return

    def _run_polymake_command( self, code ):
        try:
            output = JuPyMake.ExecuteCommand( code.strip()+"\n" )
        except Exception as exception:
            raise PolymakeRunException(exception.args[0])
        return output
    
    def _process_python( self, code ):
        if code.find( "@python" ) == -1 and code.find( "@widget" ) == -1:
            return False
        exec(code[7:],globals(),locals())
        return True

    def do_execute(self, code, silent, store_history=True,
                   user_expressions=None, allow_stdin=False):
        
        default_return = {'status': 'ok', 'execution_count': self.execution_count,
                          'payload': [], 'user_expressions': {}}
        
        if not code.strip():
            return default_return
        
        if self._process_python( code ):
            return default_return
        
        interrupted = False
        code = code.rstrip()
        
        try:
            output = self._run_polymake_command( code )
        except KeyboardInterrupt:
            self._run_polymake_command( '' )
            interrupted = True
        except PolymakeRunException as exception:
            output = exception.args[0]
            stream_content = {'execution_count': self.execution_count, 'data': { 'text/plain': "Error: Incomplete Statement:\n" + code } }
            self.send_response( self.iopub_socket, 'execute_result', stream_content )
            return {'status': 'error', 'execution_count': self.execution_count,
                    'ename': 'PolymakeRunException', 'evalue': output, 'traceback': []}
        if not silent:
            if output[0] == True:
                if output[1] != "":
                    output_stdout = output[1]
                    while output_stdout.find( '.@@HTML@@' ) != -1:
                        html_position = output_stdout.find( '.@@HTML@@' )
                        html_end_position = output_stdout.find( '.@@ENDHTML@@' )
                        if html_position > 0:
                            before_html = output_stdout[:html_position].rstrip()
                        else:
                            before_html = ''
                        output_html = output_stdout[html_position+9:html_end_position-1].strip().rstrip()
                        output_stdout = output_stdout[html_end_position+12:].strip()
                        if before_html != '':
                            stream_content = {'execution_count': self.execution_count, 'data': { 'text/plain': before_html } }
                            self.send_response( self.iopub_socket, 'execute_result', stream_content )
                        stream_content = {'execution_count': self.execution_count,
                                          'source' : "polymake",
                                          #'data': { 'text/html': "Sorry, threejs visualization is currently not available"},
                                          'data': { 'text/html': output_html},
                                          'metadata': dict() }
                        self.send_response( self.iopub_socket, 'display_data', stream_content )
                    if len(output_stdout) != 0:
                        stream_content = {'execution_count': self.execution_count, 'data': { 'text/plain': output_stdout } }
                        self.send_response( self.iopub_socket, 'execute_result', stream_content )
                if output[2] != "":
                    output_html = "<details><summary><pre style=\"display:inline\"><small>Click here for additional output</small></pre></summary>\n<pre>\n"+output[2]+"</pre>\n</details>\n"
                    stream_content = {'execution_count': self.execution_count,
                                      'source' : "polymake",
                                      'data': { 'text/html': output_html},
                                      'metadata': dict() }
                    self.send_response( self.iopub_socket, 'display_data', stream_content )
                if output[3] != "":
                    stream_content = {'execution_count': self.execution_count, 'data': { 'text/plain': output[3] } }
                    self.send_response( self.iopub_socket, 'execute_result', stream_content )
                    return {'status': 'error', 'execution_count': self.execution_count,
                            'ename': 'PolymakeRunException', 'evalue': output, 'traceback': []}
            elif output[0] == False:
                if output[3] == "":
                    stream_content = {'execution_count': self.execution_count, 'data': { 'text/plain': "Error: Incomplete Statement:\n" + code } }
                    self.send_response( self.iopub_socket, 'execute_result', stream_content )
                    return {'status': 'error', 'execution_count': self.execution_count,
                            'ename': 'IncompleteStatementError', 'evalue': output, 'traceback': []}
                else:
                    stream_content = {'execution_count': self.execution_count, 'data': { 'text/plain': output[3] } }
                    self.send_response( self.iopub_socket, 'execute_result', stream_content )
                    return {'status': 'error', 'execution_count': self.execution_count,
                            'ename': 'PolymakeRunException', 'evalue': output, 'traceback': []}
        if interrupted:
            return {'status': 'abort', 'execution_count': self.execution_count}

        return {'status': 'ok', 'execution_count': self.execution_count,
                'payload': [], 'user_expressions': {}}

    def do_shutdown(self, restart):
        if restart:
            self._start_polymake()
    
    ## Temporary method to determine offset of completion. Will be replaced soon.
    def get_completion_length( self, code, completion ):
      code_length = len(code)
      maximal_length = 0
      for i in range(1,code_length+1):
          if code[code_length-i:code_length] == completion[0:i]:
              maximal_length = i
      return maximal_length
    
    def do_complete(self, code, cursor_pos):
        try:
            completions = JuPyMake.GetCompletion(code[0:cursor_pos])
        except:
            completions = (0,"",[])
        completion_offset = completions[0]
        cur_start = cursor_pos - completion_offset
        return {'matches': completions[2], 'cursor_start': cur_start,
                'cursor_end': cursor_pos, 'metadata': dict(),
                'status': 'ok'}

    def do_is_complete( self, code ):
        new_code = 'if(0){ ' + code + ' }'
        try:
            output = self._run_polymake_command( new_code )
        except PolymakeRunException:
            return {'status' : 'incomplete', 'indent': '' }
        if output[0] == False:
            return {'status' : 'incomplete', 'indent': '' }
        return {'status' : 'complete' }

    def do_inspect( self, code, cursor_pos, detail_level=0 ):
        print(detail_level)
        ## ignore detail_level for now
        full = True
        try:
            output = JuPyMake.GetContextHelp( input=code, position=cursor_pos, full=full )
        except PolymakeRunException:
            output = []
        try:
            output_html = JuPyMake.GetContextHelp( input=code, position=cursor_pos, full=full, html=True )
        except PolymakeRunException:
            output_html = []
        output_data = { }
        if output != []:
            output_data['text/plain'] = "\n".join(output)
        if output_html != []:
            output_data['text/html'] = "\n".join(output_html)
        return {'status': 'ok', 'data': output_data, 'metadata': {}, 'found': True}
Esempio n. 6
0
class polymakeKernel(Kernel):
    implementation = 'jupyter_polymake_wrapper'
    implementation_version = __version__

    help_links = [ { 'text': "Polymake website", 'url': "http://polymake.org/" },
                   { 'text': "Polymake documentation", 'url': "https://polymake.org/doku.php/documentation" },
                   { 'text': "Polymake tutorial", 'url': "https://polymake.org/doku.php/tutorial/start" },
                   { 'text': "Polymake reference", 'url': "https://polymake.org/release_docs/3.0/" } ]

    def _replace_get_ipython(self):
        new_kernel = own_ipython(self)
        global kernel_object_for_ipython
        kernel_object_for_ipython = new_kernel

    @property
    def language_version(self):
        m = version_pat.search(self.banner)
        return m.group(1)

    _banner = None

    @property
    def banner(self):
        if self._banner is None:
            self._banner = "Polymake Jupyter kernel"
        return self._banner

    language_info = {'name': 'polymake',
                     'codemirror_mode': 'perl', #
                     'mimetype': 'text/x-polymake', 
                     'file_extension': '.pm'} # FIXME: Is this even real?

    def __init__(self, **kwargs):
        Kernel.__init__(self, **kwargs)
        self._replace_get_ipython()
        self.comm_manager = CommManager(shell=None, parent=self,
                                        kernel=self)
        self.shell_handlers['comm_open'] = self.comm_manager.comm_open
        self.shell_handlers['comm_msg'] = self.comm_manager.comm_msg
        self.shell_handlers['comm_close'] = self.comm_manager.comm_close
        if ipywidgets_extension_loaded:
            self.comm_manager.register_target('ipython.widget', Widget.handle_comm_opened)
        self._start_polymake()

    def _start_polymake(self):
        sig = signal.signal(signal.SIGINT, signal.SIG_DFL)
        try:
            polymake_run_command = pexpect.which( "polymake" )
            self.polymakewrapper = pexpect.spawnu( polymake_run_command + " -" )
            # set jupyter enviroment in polymake
            try:
                self._run_polymake_command( 'prefer "threejs";' )
                self._run_polymake_command( 'include "common::jupyter.rules";' )
                self._run_polymake_command( '$common::is_used_in_jupyter = 1;' )
            except PolymakeRunException:
                return False
        finally:
            signal.signal(signal.SIGINT, sig)
    
    def _run_polymake_command( self, code ):
        self.polymakewrapper.sendline( code.rstrip() + '; ' + 'print "===endofoutput===";' )
        self.polymakewrapper.expect( 'print "===endofoutput===";' )
        error_number = self.polymakewrapper.expect( [ "ERROR", "===endofoutput===" ] )
        output = self.polymakewrapper.before.strip().rstrip()
        if error_number == 0:
            self.polymakewrapper.sendline( 'print "===endofoutput===";' )
            self.polymakewrapper.expect( 'print "===endofoutput===";' )
            output = 'Error' + self.polymakewrapper.before
            self.polymakewrapper.expect( "===endofoutput===" )
            raise PolymakeRunException( output )
        return output
    
    def _process_python( self, code ):
        if code.find( "@python" ) == -1 and code.find( "@widget" ) == -1:
            return False
        exec(code[7:],globals(),locals())
        return True

    def do_execute(self, code, silent, store_history=True,
                   user_expressions=None, allow_stdin=False):
        
        default_return = {'status': 'ok', 'execution_count': self.execution_count,
                          'payload': [], 'user_expressions': {}}
        
        if not code.strip():
            return default_return
        
        if self._process_python( code ):
            return default_return
        
        interrupted = False
        code = code.rstrip()
        
        #stream_content = {'execution_count': self.execution_count, 'data': { 'text/plain': "Code:\n" + code_to_execute } }
        #self.send_response( self.iopub_socket, 'execute_result', stream_content )
        
        try:
            output = self._run_polymake_command( code )
        except KeyboardInterrupt:
            self.polymakewrapper.child.sendintr()
            self._run_polymake_command( '' )
            interrupted = True
        except pexpect.EOF:
            output = self.polymakewrapper.before + 'Restarting polymake'
            self._start_polymake()
        except PolymakeRunException as exception:
            output = exception.args[0]
            return {'status': 'error', 'execution_count': self.execution_count,
                    'ename': 'PolymakeRunException', 'evalue': output, 'traceback': []}
        if not silent:
            while output.find( '.@@HTML@@' ) != -1:
                html_position = output.find( '.@@HTML@@' )
                html_end_position = output.find( '.@@ENDHTML@@' )
                if html_position > 0:
                    before_html = output[:html_position-1].rstrip()
                else:
                    before_html = ''
                output_html = output[html_position+9:html_end_position-1].strip().rstrip()
                output = output[html_end_position+12:].strip()
                if before_html != '':
                    stream_content = {'execution_count': self.execution_count, 'data': { 'text/plain': before_html } }
                    self.send_response( self.iopub_socket, 'execute_result', stream_content )
                stream_content = {'execution_count': self.execution_count,
                                  'source' : "polymake",
                                  'data': { 'text/html': output_html},
                                  'metadata': dict() }
                self.send_response( self.iopub_socket, 'display_data', stream_content )
            if len(output) != 0:
                stream_content = {'execution_count': self.execution_count, 'data': { 'text/plain': output } }
                self.send_response( self.iopub_socket, 'execute_result', stream_content )
        
        if interrupted:
            return {'status': 'abort', 'execution_count': self.execution_count}

        return {'status': 'ok', 'execution_count': self.execution_count,
                'payload': [], 'user_expressions': {}}

    def do_shutdown(self, restart):
        
        self.polymakewrapper.terminate(force=True)
        if restart:
            self._start_polymake()


### basic code completion for polymake
### currently known shortcomings: intermediate completion, in particular for files, completion of variable names

    def code_completion (self,code):
        completion = []
        code = re.sub( "\)$", "", code)
        code = repr(code)
        code_line = 'print Jupyter::tab_completion(' + code + ');'
        try:
            output = self._run_polymake_command( code_line )
        except PolymakeRunException:
            return (0,[])
        completion = output.split("###")
        if ( len(completion) > 1 ) :
            completion_length = completion.pop(0)
        else :
            completion_length = 0
        return (completion_length,completion)

    
    def do_complete(self, code, cursor_pos):
        
        completion_length, completion = self.code_completion(code[0:cursor_pos])
        cur_start = cursor_pos - int(completion_length)
        
        return {'matches':  completion, 'cursor_start': cur_start,
                'cursor_end': cursor_pos, 'metadata': dict(),
                'status': 'ok'}

    def do_is_complete( self, code ):
        new_code = 'if(0){ ' + code + ' }'
        try:
            self._run_polymake_command( new_code )
        except PolymakeRunException:
            return {'status' : 'incomplete', 'indent': '' }
        return {'status' : 'complete' }

    def do_inspect( self, code, cursor_pos, detail_level=0 ):
        new_code = 'print Jupyter::context_help( q#' + code + '#, 1, "text" );'
        try:
            output = self._run_polymake_command( new_code )
        except PolymakeRunException:
            return {'status': 'ok', 'data': {}, 'metadata': {}, 'found': False}
        if output == '':
            return {'status': 'ok', 'data': {}, 'metadata': {}, 'found': False}
        else:
            return {'status': 'ok', 'data': { 'text/plain': output }, 'metadata': {}, 'found': True}
Esempio n. 7
0
class MetaKernel(Kernel):
    """The base MetaKernel class."""

    app_name = 'metakernel'
    identifier_regex = r'[^\d\W][\w\.]*'
    func_call_regex = r'([^\d\W][\w\.]*)\([^\)\()]*\Z'
    magic_prefixes = dict(magic='%', shell='!', help='?')
    help_suffix = '?'
    help_links = [
        {
            'text': "MetaKernel Magics",
            'url': "https://metakernel.readthedocs.io/en/latest/source/README.html",
        },
    ]
    language_info = {
        # 'mimetype': 'text/x-python',
        # 'name': 'python',
        # ------ If different from 'language':
        # 'codemirror_mode': {
        #    "version": 2,
        #    "name": "ipython"
        # }
        # 'pygments_lexer': 'language',
        # 'version'       : "x.y.z",
        # 'file_extension': '.py',
        'help_links': help_links,
    }
    plot_settings = Dict(dict(backend='inline')).tag(config=True)

    meta_kernel = None

    @classmethod
    def run_as_main(cls, *args, **kwargs):
        """Launch or install a metakernel.

        Modules implementing a metakernel subclass can use the following lines:

            if __name__ == '__main__':
                MetaKernelSubclass.run_as_main()
        """
        kwargs['app_name'] = cls.app_name
        MetaKernelApp.launch_instance(kernel_class=cls, *args, **kwargs)

    def __init__(self, *args, **kwargs):
        super(MetaKernel, self).__init__(*args, **kwargs)
        if MetaKernel.meta_kernel is None:
            MetaKernel.meta_kernel = self
        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(".metakernel")
        else:
            # Write has already been set
            try:
                sys.stdout.write = self.Write
            except:
                pass  # Can't change stdout
        self.redirect_to_log = False
        self.shell = None
        self.sticky_magics = OrderedDict()
        self._i = None
        self._ii = None
        self._iii = None
        self._ = None
        self.__ = None
        self.___ = None
        self.max_hist_cache = 1000
        self.hist_cache = []
        self.comm_manager = CommManager(shell=None, parent=self,
                                        kernel=self)
        self.comm_manager.register_target('ipython.widget',
            lazy_import_handle_comm_opened)

        self.hist_file = get_history_file(self)
        self.parser = Parser(self.identifier_regex, self.func_call_regex,
                             self.magic_prefixes, self.help_suffix)
        comm_msg_types = ['comm_open', 'comm_msg', 'comm_close']
        for msg_type in comm_msg_types:
            self.shell_handlers[msg_type] = getattr(self.comm_manager, msg_type)
        self._ipy_formatter = IPythonDisplayFormatter()
        self.env = {}
        self.reload_magics()
        # provide a way to get the current instance
        self.set_variable("kernel", self)
        # Run command line filenames, if given:
        if self.parent is not None and self.parent.extra_args:
            level = self.log.level
            self.log.setLevel("INFO")
            self.redirect_to_log = True
            self.Write("Executing files...")
            for filename in self.parent.extra_args:
                self.Write("    %s..." % filename)
                try:
                    self.do_execute_file(filename)
                except Exception as exc:
                    self.log.info("    %s" % (exc,))
            self.Write("Executing files: done!")
            self.log.setLevel(level)
            self.redirect_to_log = False

    def makeSubkernel(self, kernel):
        """
        Run this method in an IPython kernel to set
        this kernel's input/output settings.
        """
        from IPython import get_ipython
        from IPython.display import display
        shell = get_ipython()
        if shell: # we are running under an IPython kernel
            self.session = shell.kernel.session
            self.Display = display
            self.send_response = self._send_shell_response
        else:
            self.session = kernel.session
            self.send_response = kernel.send_response
            self.Display = kernel.Display

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

    def set_variable(self, name, value):
        """
        Set a variable to a Python-typed value.
        """
        pass

    def get_variable(self, name):
        """
        Lookup a variable name and return a Python-typed value.
        """
        pass

    def repr(self, item):
        """The repr of the kernel."""
        return repr(item)

    def get_usage(self):
        """Get the usage statement for the kernel."""
        return "This is a usage statement."

    def get_kernel_help_on(self, info, level=0, none_on_fail=False):
        """Get help on an object.  Called by the help magic."""
        if none_on_fail:
            return None
        else:
            return "Sorry, no help is available on '%s'." % info['code']

    def handle_plot_settings(self):
        """Handle the current plot settings"""
        pass

    def get_local_magics_dir(self):
        """
        Returns the path to local magics dir (eg ~/.ipython/metakernel/magics)
        """
        base = get_ipython_dir()
        return os.path.join(base, 'metakernel', 'magics')

    def get_completions(self, info):
        """
        Get completions from kernel based on info dict.
        """
        return []

    def do_execute_direct(self, code, silent=False):
        """
        Execute code in the kernel language.
        """
        pass

    def do_execute_file(self, filename):
        """
        Default code for running a file. Just opens the file, and sends
        the text to do_execute_direct.
        """
        code = "".join(open(filename).readlines())
        return self.do_execute_direct(code)

    def do_execute_meta(self, code):
        """
        Execute meta code in the kernel. This uses the execute infrastructure
        but allows JavaScript to talk directly to the kernel bypassing normal
        processing.

        When responding to the %%debug magic, the step and reset meta
        commands can answer with a string in the format:

        "highlight: [start_line, start_col, end_line, end_col]"

        for highlighting expressions in the frontend.
        """
        if code == "reset":
            raise Exception("This kernel does not implement this meta command")
        elif code == "stop":
            raise Exception("This kernel does not implement this meta command")
        elif code == "step":
            raise Exception("This kernel does not implement this meta command")
        elif code.startswith("inspect "):
            raise Exception("This kernel does not implement this meta command")
        else:
            raise Exception("Unknown meta command: '%s'" % code)

    def initialize_debug(self, code):
        """
        This function is used with the %%debug magic for highlighting
        lines of code, and for initializing debug functions.

        Return the empty string if highlighting is not supported.
        """
        #return "highlight: [%s, %s, %s, %s]" % (line1, col1, line2, col2)
        return ""

    def do_function_direct(self, function_name, arg):
        """
        Call a function in the kernel language with args (as a single item).
        """
        f = self.do_execute_direct(function_name)
        return f(arg)

    def restart_kernel(self):
        """Restart the kernel"""
        pass

    ############################################
    # Implement base class methods

    def do_execute(self, code, silent=False, store_history=True, user_expressions=None,
                   allow_stdin=False):
        """Handle code execution.

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#execute
        """
        # Set the ability for the kernel to get standard-in:
        self._allow_stdin = allow_stdin
        # Create a default response:
        self.kernel_resp = {
            'status': 'ok',
            # The base class increments the execution count
            'execution_count': self.execution_count,
            'payload': [],
            'user_expressions': {},
        }

        # TODO: remove this when IPython fixes this
        # This happens at startup when the language is set to python
        if '_usage.page_guiref' in code:
            return self.kernel_resp

        if code and store_history:
            self.hist_cache.append(code.strip())

        if not code.strip():
            return self.kernel_resp

        info = self.parse_code(code)
        self.payload = []
        retval = None

        if info['magic'] and info['magic']['name'] == 'help':

            if info['magic']['type'] == 'line':
                level = 0
            else:
                level = 1
            text = self.get_help_on(code, level)
            if text:
                content = {
                    "start_line_number": 0,
                    "source": "page",
                }
                if isinstance(text, dict):
                    content["data"] = text ## {mime-type: ..., mime-type:...}
                    self.log.debug(str(text))
                else:
                    content["data"] = {"text/plain": text}
                    self.log.debug(text)
                self.payload = [content]

        elif info['magic'] or self.sticky_magics:
            retval = None
            if self.sticky_magics:
                magics, code = _split_magics_code(code, self.magic_prefixes)
                code = magics + self._get_sticky_magics() + code
            stack = []
            # Handle magics:
            magic = None
            prefixes = ((self.magic_prefixes['shell'],
                         self.magic_prefixes['magic']))
            while code.startswith(prefixes):
                magic = self.get_magic(code)
                if magic is not None:
                    stack.append(magic)
                    code = magic.get_code()
                    # signal to exit, maybe error or no block
                    if not magic.evaluate:
                        break
                else:
                    break
            # Execute code, if any:
            if ((magic is None or magic.evaluate) and code.strip() != ""):
                if code.startswith("~~META~~:"):
                    retval = self.do_execute_meta(code[9:].strip())
                else:
                    retval = self.do_execute_direct(code)
            # Post-process magics:
            for magic in reversed(stack):
                retval = magic.post_process(retval)
        else:
            if code.startswith("~~META~~:"):
                retval = self.do_execute_meta(code[9:].strip())
            else:
                retval = self.do_execute_direct(code)

        self.post_execute(retval, code, silent)

        if 'payload' in self.kernel_resp:
            self.kernel_resp['payload'] = self.payload

        return self.kernel_resp

    def post_execute(self, retval, code, silent):
        """Post-execution actions

        Handle special kernel variables and display response if not silent.
        """
        # Handle in's
        self.set_variable("_iii", self._iii)
        self.set_variable("_ii", self._ii)
        self.set_variable("_i", code)
        self.set_variable("_i" + str(self.execution_count), code)
        self._iii = self._ii
        self._ii = code
        if (retval is not None):
            # --------------------------------------
            # Handle out's (only when non-null)
            self.set_variable("___", self.___)
            self.set_variable("__", self.__)
            self.set_variable("_", retval)
            self.set_variable("_" + str(self.execution_count), retval)
            self.___ = self.__
            self.__ = retval
            self.log.debug(retval)
            if isinstance(retval, ExceptionWrapper):
                self.kernel_resp['status'] = 'error'
                content = {
                    'traceback':  retval.traceback,
                    'evalue': retval.evalue,
                    'ename': retval.ename,
                }
                self.kernel_resp.update(content)
                if not silent:
                    self.send_response(self.iopub_socket, 'error', content)
            else:
                try:
                    data = _formatter(retval, self.repr)
                except Exception as e:
                    self.Error(e)
                    return
                content = {
                    'execution_count': self.execution_count,
                    'data': data[0],
                    'metadata': data[1],
                }
                if not silent:
                    if Widget and isinstance(retval, Widget):
                        self.Display(retval)
                        return
                    self.send_response(self.iopub_socket, 'execute_result', content)

    def do_history(self, hist_access_type, output, raw, session=None,
                   start=None, stop=None, n=None, pattern=None, unique=False):
        """
        Access history at startup.

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#history
        """
        with open(self.hist_file) as fid:
            self.hist_cache = json.loads(fid.read() or "[]")
        return {'history': [(None, None, h) for h in self.hist_cache]}

    def do_shutdown(self, restart):
        """
        Shut down the app gracefully, saving history.

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#kernel-shutdown
        """
        if self.hist_file:
            with open(self.hist_file, "w") as fid:
                json.dump(self.hist_cache[-self.max_hist_cache:], fid)
        if restart:
            self.Print("Restarting kernel...")
            self.restart_kernel()
            self.reload_magics()
            self.Print("Done!")
        return {'status': 'ok', 'restart': restart}

    def do_is_complete(self, code):
        """
        Given code as string, returns dictionary with 'status' representing
        whether code is ready to evaluate. Possible values for status are:

           'complete'   - ready to evaluate
           'incomplete' - not yet ready
           'invalid'    - invalid code
           'unknown'    - unknown; the default unless overridden

        Optionally, if 'status' is 'incomplete', you may indicate
        an indentation string.

        Example:

            return {'status' : 'incomplete',
                    'indent': ' ' * 4}

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#code-completeness
        """
        if code.startswith(self.magic_prefixes['magic']):
            ## force requirement to end with an empty line
            if code.endswith("\n"):
                return {'status' : 'complete'}
            else:
                return {'status' : 'incomplete'}
        # otherwise, how to know is complete?
        elif code.endswith("\n"):
            return {'status' : 'complete'}
        else:
            return {'status' : 'incomplete'}

    def do_complete(self, code, cursor_pos):
        """Handle code completion for the kernel.

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#completion
        """
        info = self.parse_code(code, 0, cursor_pos)
        content = {
            'matches': [],
            'cursor_start': info['start'],
            'cursor_end': info['end'],
            'status': 'ok'
        }

        matches = info['path_matches']

        if info['magic']:

            # if the last line contains another magic, use that
            line_info = self.parse_code(info['line'])
            if line_info['magic']:
                info = line_info

            if info['magic']['type'] == 'line':
                magics = self.line_magics
            else:
                magics = self.cell_magics

            if info['magic']['name'] in magics:
                magic = magics[info['magic']['name']]
                info = info['magic']
                if info['type'] == 'cell' and info['code']:
                    info = self.parse_code(info['code'])
                else:
                    info = self.parse_code(info['args'])

                matches.extend(magic.get_completions(info))

            elif not info['magic']['code'] and not info['magic']['args']:
                matches = []
                for name in magics.keys():
                    if name.startswith(info['magic']['name']):
                        pre = info['magic']['prefix']
                        matches.append(pre + name)
                        info['start'] -= len(pre)
                        info['full_obj'] = pre + info['full_obj']
                        info['obj'] = pre + info['obj']

        else:
            matches.extend(self.get_completions(info))

        if info['full_obj'] and len(info['full_obj']) > len(info['obj']):
            new_list = [m for m in matches if m.startswith(info['full_obj'])]
            if new_list:
                content['cursor_end'] = (content['cursor_end'] +
                                         len(info['full_obj']) -
                                         len(info['obj']))
                matches = new_list

        content["matches"] = sorted(matches)

        return content

    def do_inspect(self, code, cursor_pos, detail_level=0):
        """Object introspection.

        https://jupyter-client.readthedocs.io/en/stable/messaging.html#introspection
        """
        if cursor_pos > len(code):
            return

        content = {'status': 'aborted', 'data': {}, 'found': False}
        docstring = self.get_help_on(code, detail_level, none_on_fail=True,
             cursor_pos=cursor_pos)

        if docstring:
            content["status"] = "ok"
            content["found"] = True
            if isinstance(docstring, dict): ## {"text/plain": ..., mime-type: ...}
                content["data"] = docstring
                self.log.debug(str(docstring))
            else:
                content["data"] = {"text/plain": docstring}
                self.log.debug(docstring)

        return content


    def clear_output(self, wait=False):
        """Clear the output of the kernel."""
        self.send_response(self.iopub_socket, 'clear_output',
                           {'wait': wait})

    def Display(self, *objects, **kwargs):
        """Display one or more objects using rich display.

        Supports a `clear_output` keyword argument that clears the output before displaying.

        See https://ipython.readthedocs.io/en/stable/config/integrating.html?highlight=display#rich-display
        """
        if kwargs.get('clear_output'):
            self.clear_output(wait=True)

        for item in objects:
            if Widget and isinstance(item, Widget):
                self.log.debug('Display Widget')
                self._ipy_formatter(item)
            else:
                self.log.debug('Display Data')
                try:
                    data = _formatter(item, self.repr)
                except Exception as e:
                    self.Error(e)
                    return
                content = {
                    'data': data[0],
                    'metadata': data[1]
                }
                self.send_response(
                    self.iopub_socket,
                    'display_data',
                    content
                )

    def Print(self, *objects, **kwargs):
        """Print `objects` to the iopub stream, separated by `sep` and followed by `end`.

        Items can be strings or `Widget` instances.
        """
        for item in objects:
            if Widget and isinstance(item, Widget):
                self.Display(item)

        objects = [i for i in objects if not isinstance(i, Widget)]
        message = format_message(*objects, **kwargs)

        stream_content = {
            'name': 'stdout', 'text': message}
        self.log.debug('Print: %s' % message.rstrip())
        if self.redirect_to_log:
            self.log.info(message.rstrip())
        else:
            self.send_response(self.iopub_socket, 'stream', stream_content)

    def Write(self, message):
        """Write message directly to the iopub stdout with no added end character."""
        stream_content = {
            'name': 'stdout', 'text': message}
        self.log.debug('Write: %s' % message)
        if self.redirect_to_log:
            self.log.info(message)
        else:
            self.send_response(self.iopub_socket, 'stream', stream_content)

    def Error(self, *objects, **kwargs):
        """Print `objects` to stdout, separated by `sep` and followed by `end`.

        Objects are cast to strings.
        """
        message = format_message(*objects, **kwargs)
        self.log.debug('Error: %s' % message.rstrip())
        stream_content = {
            'name': 'stderr',
            'text': RED + message + NORMAL
        }
        if self.redirect_to_log:
            self.log.info(message.rstrip())
        else:
            self.send_response(self.iopub_socket, 'stream', stream_content)

    ##############################
    # Private API and methods not likely to be overridden

    def reload_magics(self):
        """Reload all of the line and cell magics."""
        self.line_magics = {}
        self.cell_magics = {}

        # get base magic files and those relative to the current class
        # directory
        magic_files = []
        # Make a metakernel/magics if it doesn't exist:
        local_magics_dir = get_local_magics_dir()
        # Search all of the places there could be magics:
        try:
            paths = [os.path.join(os.path.dirname(
                os.path.abspath(inspect.getfile(self.__class__))), "magics")]
        except:
            paths = []
        paths += [local_magics_dir,
                  os.path.join(os.path.dirname(os.path.abspath(__file__)), "magics")]
        for magic_dir in paths:
            sys.path.append(magic_dir)
            magic_files.extend(glob.glob(os.path.join(magic_dir, "*.py")))

        for magic in magic_files:
            basename = os.path.basename(magic)
            if basename == "__init__.py":
                continue
            try:
                module = __import__(os.path.splitext(basename)[0])
                imp.reload(module)
                module.register_magics(self)
            except Exception as e:
                self.log.error("Can't load '%s': error: %s" % (magic, e))

    def register_magics(self, magic_klass):
        """Register magics for a given magic_klass."""
        magic = magic_klass(self)
        line_magics = magic.get_magics('line')
        cell_magics = magic.get_magics('cell')
        for name in line_magics:
            self.line_magics[name] = magic
        for name in cell_magics:
            self.cell_magics[name] = magic

    def send_response(self, *args, **kwargs):
        ### if we are running via %parallel, we might not have a
        ### session
        if self.session:
            super(MetaKernel, self).send_response(*args, **kwargs)

    def call_magic(self, line):
        """
        Given an line, such as "%download http://example.com/", parse
        and execute magic.
        """
        return self.get_magic(line)

    def get_magic(self, text):
        ## FIXME: Bad name, use call_magic instead.
        # if first line matches a magic,
        # call magic.call_magic() and return magic object
        info = self.parse_code(text)
        magic = self.line_magics['magic']
        return magic.get_magic(info)

    def get_magic_args(self, text):
        # if first line matches a magic,
        # call magic.call_magic() and return magic args
        info = self.parse_code(text)
        magic = self.line_magics['magic']
        return magic.get_magic(info, get_args=True)

    def get_help_on(self, expr, level=0, none_on_fail=False,
            cursor_pos=-1):
        """Get help for an expression using the help magic."""
        help_magic = self.line_magics['help']
        return help_magic.get_help_on(expr, level, none_on_fail, cursor_pos)

    def parse_code(self, code, cursor_start=0, cursor_end=-1):
        """Parse code using our parser."""
        return self.parser.parse_code(code, cursor_start, cursor_end)

    def _get_sticky_magics(self):
        retval = ""
        for key in self.sticky_magics:
            retval += (key + " " + self.sticky_magics[key] + "\n")
        return retval

    def _send_shell_response(self, socket, stream_type, content):
        publish_display_data({ 'text/plain': content['text'] })