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": " "}
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'] })
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
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
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}
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}
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'] })