class XonshKernel: """Xonsh xernal for Jupyter""" implementation = "Xonsh " + version implementation_version = version language = "xonsh" language_version = version.split(".")[:3] banner = "Xonsh - Python-powered, cross-platform shell" language_info = { "name": "xonsh", "version": version, "pygments_lexer": "xonsh", "codemirror_mode": "shell", "mimetype": "text/x-sh", "file_extension": ".xsh", } signature_schemes = {"hmac-sha256": hashlib.sha256} def __init__(self, debug_level=0, session_id=None, config=None, **kwargs): """ Parameters ---------- debug_level : int, optional Integer from 0 (no debugging) to 3 (all debugging), default: 0. session_id : str or None, optional Unique string id representing the kernel session. If None, this will be replaced with a random UUID. config : dict or None, optional Configuration dictionary to start server with. BY default will search the command line for options (if given) or use default configuration. """ self.debug_level = debug_level self.session_id = str( uuid.uuid4()) if session_id is None else session_id self._parser = None self.config = self.make_default_config() if config is None else config self.exiting = False self.execution_count = 1 self.completer = Completer() @property def parser(self): if self._parser is None: p = ArgumentParser("jupyter_kerenel") p.add_argument("-f", dest="config_file", default=None) self._parser = p return self._parser def make_default_config(self): """Provides default configuration""" ns, unknown = self.parser.parse_known_args(sys.argv) if ns.config_file is None: self.dprint(1, "Starting xonsh kernel with default args...") config = { "control_port": 0, "hb_port": 0, "iopub_port": 0, "ip": "127.0.0.1", "key": str(uuid.uuid4()), "shell_port": 0, "signature_scheme": "hmac-sha256", "stdin_port": 0, "transport": "tcp", } else: self.dprint(1, "Loading simple_kernel with args:", sys.argv) self.dprint(1, "Reading config file {!r}...".format(ns.config_file)) with open(ns.config_file) as f: config = json.load(f) return config def iopub_handler(self, message): """Handles iopub requests.""" self.dprint(2, "iopub received:", message) def control_handler(self, wire_message): """Handles control requests""" self.dprint(1, "control received:", wire_message) identities, msg = self.deserialize_wire_message(wire_message) if msg["header"]["msg_type"] == "shutdown_request": self.shutdown() def stdin_handler(self, message): self.dprint(2, "stdin received:", message) def start(self): """Starts the server""" ioloop.install() connection = self.config["transport"] + "://" + self.config["ip"] secure_key = self.config["key"].encode() digestmod = self.signature_schemes[self.config["signature_scheme"]] self.auth = hmac.HMAC(secure_key, digestmod=digestmod) # Heartbeat ctx = zmq.Context() self.heartbeat_socket = ctx.socket(zmq.REP) self.config["hb_port"] = bind(self.heartbeat_socket, connection, self.config["hb_port"]) # IOPub/Sub, aslo called SubSocketChannel in IPython sources self.iopub_socket = ctx.socket(zmq.PUB) self.config["iopub_port"] = bind(self.iopub_socket, connection, self.config["iopub_port"]) self.iopub_stream = zmqstream.ZMQStream(self.iopub_socket) self.iopub_stream.on_recv(self.iopub_handler) # Control self.control_socket = ctx.socket(zmq.ROUTER) self.config["control_port"] = bind(self.control_socket, connection, self.config["control_port"]) self.control_stream = zmqstream.ZMQStream(self.control_socket) self.control_stream.on_recv(self.control_handler) # Stdin: self.stdin_socket = ctx.socket(zmq.ROUTER) self.config["stdin_port"] = bind(self.stdin_socket, connection, self.config["stdin_port"]) self.stdin_stream = zmqstream.ZMQStream(self.stdin_socket) self.stdin_stream.on_recv(self.stdin_handler) # Shell self.shell_socket = ctx.socket(zmq.ROUTER) self.config["shell_port"] = bind(self.shell_socket, connection, self.config["shell_port"]) self.shell_stream = zmqstream.ZMQStream(self.shell_socket) self.shell_stream.on_recv(self.shell_handler) # start up configurtation self.dprint(2, "Config:", json.dumps(self.config)) self.dprint(1, "Starting loops...") self.hb_thread = threading.Thread(target=self.heartbeat_loop) self.hb_thread.daemon = True self.hb_thread.start() self.dprint(1, "Ready! Listening...") ioloop.IOLoop.instance().start() def shutdown(self): """Shutsdown the kernel""" self.exiting = True ioloop.IOLoop.instance().stop() def dprint(self, level, *args, **kwargs): """Print but with debug information.""" if level <= self.debug_level: print("DEBUG" + str(level) + ":", file=sys.__stdout__, *args, **kwargs) sys.__stdout__.flush() def sign(self, messages): """Sign a message list with a secure signature.""" h = self.auth.copy() for m in messages: h.update(m) return h.hexdigest().encode("ascii") def new_header(self, message_type): """Make a new header""" return { "date": datetime.datetime.now().isoformat(), "msg_id": str(uuid.uuid4()), "username": "******", "session": self.session_id, "msg_type": message_type, "version": "5.0", } def send( self, stream, message_type, content=None, parent_header=None, metadata=None, identities=None, ): """Send data to the client via a stream""" header = self.new_header(message_type) if content is None: content = {} if parent_header is None: parent_header = {} if metadata is None: metadata = {} messages = list( map(dump_bytes, [header, parent_header, metadata, content])) signature = self.sign(messages) parts = [DELIM, signature] + messages if identities: parts = identities + parts self.dprint(3, "send parts:", parts) stream.send_multipart(parts) if isinstance(stream, zmqstream.ZMQStream): stream.flush() def deserialize_wire_message(self, wire_message): """Split the routing prefix and message frames from a message on the wire""" delim_idx = wire_message.index(DELIM) identities = wire_message[:delim_idx] m_signature = wire_message[delim_idx + 1] msg_frames = wire_message[delim_idx + 2:] keys = ("header", "parent_header", "metadata", "content") m = {k: load_bytes(v) for k, v in zip(keys, msg_frames)} check_sig = self.sign(msg_frames) if check_sig != m_signature: raise ValueError("Signatures do not match") return identities, m def run_thread(self, loop, name): """Run main thread""" self.dprint(2, "Starting loop for {name!r}...".format(name=name)) while not self.exiting: self.dprint(2, "{} Loop!".format(name)) try: loop.start() except ZMQError as e: self.dprint(1, "{} ZMQError!\n {}".format(name, e)) if e.errno == errno.EINTR: continue else: raise except Exception: self.dprint(2, "{} Exception!".format(name)) if self.exiting: break else: raise else: self.dprint(2, "{} Break!".format(name)) break def heartbeat_loop(self): """Run heartbeat""" self.dprint(2, "Starting heartbeat loop...") while not self.exiting: self.dprint(3, ".", end="") try: zmq.device(zmq.FORWARDER, self.heartbeat_socket, self.heartbeat_socket) except zmq.ZMQError as e: if e.errno == errno.EINTR: continue else: raise else: break def shell_handler(self, message): """Dispatch shell messages to their handlers""" self.dprint(1, "received:", message) identities, msg = self.deserialize_wire_message(message) handler = getattr(self, "handle_" + msg["header"]["msg_type"], None) if handler is None: self.dprint(0, "unknown message type:", msg["header"]["msg_type"]) return handler(msg, identities) def handle_execute_request(self, message, identities): """Handle execute request messages.""" self.dprint(2, "Xonsh Kernel Executing:", pformat(message["content"]["code"])) # Start by sending busy signal content = {"execution_state": "busy"} self.send(self.iopub_stream, "status", content, parent_header=message["header"]) # confirm the input that we are executing content = { "execution_count": self.execution_count, "code": message["content"]["code"], } self.send(self.iopub_stream, "execute_input", content, parent_header=message["header"]) # execute the code metadata = { "dependencies_met": True, "engine": self.session_id, "status": "ok", "started": datetime.datetime.now().isoformat(), } content = self.do_execute(parent_header=message["header"], **message["content"]) self.send( self.shell_stream, "execute_reply", content, metadata=metadata, parent_header=message["header"], identities=identities, ) self.execution_count += 1 # once we are done, send a signal that we are idle content = {"execution_state": "idle"} self.send(self.iopub_stream, "status", content, parent_header=message["header"]) def do_execute(self, code="", silent=False, store_history=True, user_expressions=None, allow_stdin=False, parent_header=None, **kwargs): """Execute user code.""" if len(code.strip()) == 0: return { "status": "ok", "execution_count": self.execution_count, "payload": [], "user_expressions": {}, } shell = builtins.__xonsh_shell__ hist = builtins.__xonsh_history__ try: shell.default(code, self, parent_header) interrupted = False except KeyboardInterrupt: interrupted = True if interrupted: return {"status": "abort", "execution_count": self.execution_count} rtn = 0 if (hist is None or len(hist) == 0) else hist.rtns[-1] if 0 < rtn: message = { "status": "error", "execution_count": self.execution_count, "ename": "", "evalue": str(rtn), "traceback": [], } else: message = { "status": "ok", "execution_count": self.execution_count, "payload": [], "user_expressions": {}, } return message def _respond_in_chunks(self, name, s, chunksize=1024, parent_header=None): if s is None: return n = len(s) if n == 0: return lower = range(0, n, chunksize) upper = range(chunksize, n + chunksize, chunksize) for l, u in zip(lower, upper): response = {"name": name, "text": s[l:u]} self.send(self.iopub_socket, "stream", response, parent_header=parent_header) def handle_complete_request(self, message, identities): """Handles kernel info requests.""" content = self.do_complete(message["content"]["code"], message["content"]["cursor_pos"]) self.send( self.shell_stream, "complete_reply", content, parent_header=message["header"], identities=identities, ) def do_complete(self, code, pos): """Get completions.""" shell = builtins.__xonsh_shell__ line = code.split("\n")[-1] line = builtins.aliases.expand_alias(line) prefix = line.split(" ")[-1] endidx = pos begidx = pos - len(prefix) rtn, _ = self.completer.complete(prefix, line, begidx, endidx, shell.ctx) if isinstance(rtn, Set): rtn = list(rtn) message = { "matches": rtn, "cursor_start": begidx, "cursor_end": endidx, "metadata": {}, "status": "ok", } return message def handle_kernel_info_request(self, message, identities): """Handles kernel info requests.""" content = { "protocol_version": "5.0", "ipython_version": [1, 1, 0, ""], "language": self.language, "language_version": self.language_version, "implementation": self.implementation, "implementation_version": self.implementation_version, "language_info": self.language_info, "banner": self.banner, } self.send( self.shell_stream, "kernel_info_reply", content, parent_header=message["header"], identities=identities, )
class XonshKernel(MetaKernel): implementation = 'Calysto Xonsh Kernel' implementation_version = __version__ language = 'xonsh' language_version = version banner = 'Xonsh - the Python-ish, BASHwards-looking shell' language_info = {'name': 'xonsh', 'pygments_lexer': 'xonsh', 'codemirror_mode': 'shell', 'mimetype': 'text/x-sh', 'file_extension': '.xsh', 'version': __version__ } def __init__(self, **kwargs): self.completer = Completer() super().__init__(**kwargs) def do_execute_direct(self, code, silent=False): out, err, interrupted = self._do_execute_direct(code) hist = builtins.__xonsh_history__ if not silent: # stdout response if out: self._respond_in_chunks('stdout', out.strip()) if err: self._respond_in_chunks('stderr', err.strip()) if hasattr(builtins, '_') and builtins._ is not None: # rely on sys.displayhook functionality self._respond_in_chunks('stdout', pformat(builtins._)) builtins._ = None if len(hist) > 0 and not out and not err: return hist.outs[-1] def _do_execute_direct(self, code): shell = builtins.__xonsh_shell__ env = builtins.__xonsh_env__ out = io.StringIO() err = io.StringIO() enc = env.get('XONSH_ENCODING') out = SpooledTemporaryFile(max_size=MAX_SIZE, mode='w+t', encoding=enc, newline='\n') err = SpooledTemporaryFile(max_size=MAX_SIZE, mode='w+t', encoding=enc, newline='\n') try: with redirect_stdout(out), redirect_stderr(err), \ swap(builtins, '__xonsh_stdout_uncaptured__', out), \ swap(builtins, '__xonsh_stderr_uncaptured__', err), \ env.swap({'XONSH_STORE_STDOUT': False}): shell.default(code) interrupted = False except KeyboardInterrupt: interrupted = True output, error = '', '' if out.tell() > 0: out.seek(0) output = out.read() if err.tell() > 0: err.seek(0) error = err.read() out.close() err.close() return output, error, interrupted def _respond_in_chunks(self, name, s, chunksize=1024): if s is None: return n = len(s) if n == 0: return lower = range(0, n, chunksize) upper = range(chunksize, n+chunksize, chunksize) for l, u in zip(lower, upper): if name == 'stderr': self.Error(s[l:u]) else: self.Print(s[l:u]) def do_complete(self, code, pos): """Get completions.""" shell = builtins.__xonsh_shell__ line = code.split('\n')[-1] prefix = line.split(' ')[-1] endidx = pos begidx = pos - len(prefix) rtn, _ = self.completer.complete(prefix, line, begidx, endidx, shell.ctx) message = {'matches': rtn, 'cursor_start': begidx, 'cursor_end': endidx, 'metadata': {}, 'status': 'ok'} return message def get_kernel_help_on(self, info, level=0, none_on_fail=False): obj = info.get('help_obj', '') if not obj or len(obj.split()) > 1: if none_on_fail: return None else: return "" output = '' if ON_POSIX: output, _, _ = self._do_execute_direct('man %s' % obj) if output.startswith('No manual entry for'): output = '' else: output, _, _, = self._do_execute_direct('help %s' % obj) if output.startswith('This command is not supported'): output = '' if not output: output, _, _ = self._do_execute_direct('help(%s)' % obj) return output
class XonshKernel(Kernel): """Xonsh xernal for Jupyter""" implementation = 'Xonsh ' + version implementation_version = version language = 'xonsh' language_version = version banner = 'Xonsh - the Python-ish, BASHwards-looking shell' language_info = {'name': 'xonsh', 'pygments_lexer': 'xonsh', 'codemirror_mode': 'shell', 'mimetype': 'text/x-sh', 'file_extension': '.xsh', } def __init__(self, **kwargs): self.completer = Completer() super().__init__(**kwargs) def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False): """Execute user code.""" if len(code.strip()) == 0: return {'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {}} env = builtins.__xonsh_env__ shell = builtins.__xonsh_shell__ hist = builtins.__xonsh_history__ enc = env.get('XONSH_ENCODING') out = SpooledTemporaryFile(max_size=MAX_SIZE, mode='w+t', encoding=enc, newline='\n') err = SpooledTemporaryFile(max_size=MAX_SIZE, mode='w+t', encoding=enc, newline='\n') try: with redirect_stdout(out), redirect_stderr(err), \ swap(builtins, '__xonsh_stdout_uncaptured__', out), \ swap(builtins, '__xonsh_stderr_uncaptured__', err), \ env.swap({'XONSH_STORE_STDOUT': False}): shell.default(code) interrupted = False except KeyboardInterrupt: interrupted = True if not silent: # stdout response if out.tell() > 0: out.seek(0) self._respond_in_chunks('stdout', out.read()) if err.tell() > 0: err.seek(0) self._respond_in_chunks('stderr', err.read()) if hasattr(builtins, '_') and builtins._ is not None: # rely on sys.displayhook functionality self._respond_in_chunks('stdout', pformat(builtins._)) builtins._ = None if hist is not None and len(hist) > 0 and out.tell() == 0 and err.tell() == 0: self._respond_in_chunks('stdout', hist.outs[-1]) out.close() err.close() if interrupted: return {'status': 'abort', 'execution_count': self.execution_count} rtn = 0 if (hist is None or len(hist) == 0) else hist.rtns[-1] if 0 < rtn: message = {'status': 'error', 'execution_count': self.execution_count, 'ename': '', 'evalue': str(rtn), 'traceback': []} else: message = {'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {}} return message def _respond_in_chunks(self, name, s, chunksize=1024): if s is None: return n = len(s) if n == 0: return lower = range(0, n, chunksize) upper = range(chunksize, n+chunksize, chunksize) for l, u in zip(lower, upper): response = {'name': name, 'text': s[l:u]} self.send_response(self.iopub_socket, 'stream', response) def do_complete(self, code, pos): """Get completions.""" shell = builtins.__xonsh_shell__ line = code.split('\n')[-1] line = builtins.aliases.expand_alias(line) prefix = line.split(' ')[-1] endidx = pos begidx = pos - len(prefix) rtn, _ = self.completer.complete(prefix, line, begidx, endidx, shell.ctx) message = {'matches': rtn, 'cursor_start': begidx, 'cursor_end': endidx, 'metadata': {}, 'status': 'ok'} return message
class XonshKernel: """Xonsh xernal for Jupyter""" implementation = "Xonsh " + version implementation_version = version language = "xonsh" language_version = version.split(".")[:3] banner = "Xonsh - Python-powered, cross-platform shell" language_info = { "name": "xonsh", "version": version, "pygments_lexer": "xonsh", "codemirror_mode": "shell", "mimetype": "text/x-sh", "file_extension": ".xsh", } signature_schemes = {"hmac-sha256": hashlib.sha256} def __init__(self, debug_level=0, session_id=None, config=None, **kwargs): """ Parameters ---------- debug_level : int, optional Integer from 0 (no debugging) to 3 (all debugging), default: 0. session_id : str or None, optional Unique string id representing the kernel session. If None, this will be replaced with a random UUID. config : dict or None, optional Configuration dictionary to start server with. BY default will search the command line for options (if given) or use default configuration. """ self.debug_level = debug_level self.session_id = str(uuid.uuid4()) if session_id is None else session_id self._parser = None self.config = self.make_default_config() if config is None else config self.exiting = False self.execution_count = 1 self.completer = Completer() @property def parser(self): if self._parser is None: p = ArgumentParser("jupyter_kerenel") p.add_argument("-f", dest="config_file", default=None) self._parser = p return self._parser def make_default_config(self): """Provides default configuration""" ns, unknown = self.parser.parse_known_args(sys.argv) if ns.config_file is None: self.dprint(1, "Starting xonsh kernel with default args...") config = { "control_port": 0, "hb_port": 0, "iopub_port": 0, "ip": "127.0.0.1", "key": str(uuid.uuid4()), "shell_port": 0, "signature_scheme": "hmac-sha256", "stdin_port": 0, "transport": "tcp", } else: self.dprint(1, "Loading simple_kernel with args:", sys.argv) self.dprint(1, "Reading config file {!r}...".format(ns.config_file)) with open(ns.config_file) as f: config = json.load(f) return config def iopub_handler(self, message): """Handles iopub requests.""" self.dprint(2, "iopub received:", message) def control_handler(self, wire_message): """Handles control requests""" self.dprint(1, "control received:", wire_message) identities, msg = self.deserialize_wire_message(wire_message) if msg["header"]["msg_type"] == "shutdown_request": self.shutdown() def stdin_handler(self, message): self.dprint(2, "stdin received:", message) def start(self): """Starts the server""" ioloop.install() connection = self.config["transport"] + "://" + self.config["ip"] secure_key = self.config["key"].encode() digestmod = self.signature_schemes[self.config["signature_scheme"]] self.auth = hmac.HMAC(secure_key, digestmod=digestmod) # Heartbeat ctx = zmq.Context() self.heartbeat_socket = ctx.socket(zmq.REP) self.config["hb_port"] = bind( self.heartbeat_socket, connection, self.config["hb_port"] ) # IOPub/Sub, aslo called SubSocketChannel in IPython sources self.iopub_socket = ctx.socket(zmq.PUB) self.config["iopub_port"] = bind( self.iopub_socket, connection, self.config["iopub_port"] ) self.iopub_stream = zmqstream.ZMQStream(self.iopub_socket) self.iopub_stream.on_recv(self.iopub_handler) # Control self.control_socket = ctx.socket(zmq.ROUTER) self.config["control_port"] = bind( self.control_socket, connection, self.config["control_port"] ) self.control_stream = zmqstream.ZMQStream(self.control_socket) self.control_stream.on_recv(self.control_handler) # Stdin: self.stdin_socket = ctx.socket(zmq.ROUTER) self.config["stdin_port"] = bind( self.stdin_socket, connection, self.config["stdin_port"] ) self.stdin_stream = zmqstream.ZMQStream(self.stdin_socket) self.stdin_stream.on_recv(self.stdin_handler) # Shell self.shell_socket = ctx.socket(zmq.ROUTER) self.config["shell_port"] = bind( self.shell_socket, connection, self.config["shell_port"] ) self.shell_stream = zmqstream.ZMQStream(self.shell_socket) self.shell_stream.on_recv(self.shell_handler) # start up configurtation self.dprint(2, "Config:", json.dumps(self.config)) self.dprint(1, "Starting loops...") self.hb_thread = threading.Thread(target=self.heartbeat_loop) self.hb_thread.daemon = True self.hb_thread.start() self.dprint(1, "Ready! Listening...") ioloop.IOLoop.instance().start() def shutdown(self): """Shutsdown the kernel""" self.exiting = True ioloop.IOLoop.instance().stop() def dprint(self, level, *args, **kwargs): """Print but with debug information.""" if level <= self.debug_level: print("DEBUG" + str(level) + ":", file=sys.__stdout__, *args, **kwargs) sys.__stdout__.flush() def sign(self, messages): """Sign a message list with a secure signature.""" h = self.auth.copy() for m in messages: h.update(m) return h.hexdigest().encode("ascii") def new_header(self, message_type): """Make a new header""" return { "date": datetime.datetime.now().isoformat(), "msg_id": str(uuid.uuid4()), "username": "******", "session": self.session_id, "msg_type": message_type, "version": "5.0", } def send( self, stream, message_type, content=None, parent_header=None, metadata=None, identities=None, ): """Send data to the client via a stream""" header = self.new_header(message_type) if content is None: content = {} if parent_header is None: parent_header = {} if metadata is None: metadata = {} messages = list(map(dump_bytes, [header, parent_header, metadata, content])) signature = self.sign(messages) parts = [DELIM, signature] + messages if identities: parts = identities + parts self.dprint(3, "send parts:", parts) stream.send_multipart(parts) if isinstance(stream, zmqstream.ZMQStream): stream.flush() def deserialize_wire_message(self, wire_message): """Split the routing prefix and message frames from a message on the wire""" delim_idx = wire_message.index(DELIM) identities = wire_message[:delim_idx] m_signature = wire_message[delim_idx + 1] msg_frames = wire_message[delim_idx + 2 :] keys = ("header", "parent_header", "metadata", "content") m = {k: load_bytes(v) for k, v in zip(keys, msg_frames)} check_sig = self.sign(msg_frames) if check_sig != m_signature: raise ValueError("Signatures do not match") return identities, m def run_thread(self, loop, name): """Run main thread""" self.dprint(2, "Starting loop for {name!r}...".format(name=name)) while not self.exiting: self.dprint(2, "{} Loop!".format(name)) try: loop.start() except ZMQError as e: self.dprint(1, "{} ZMQError!\n {}".format(name, e)) if e.errno == errno.EINTR: continue else: raise except Exception: self.dprint(2, "{} Exception!".format(name)) if self.exiting: break else: raise else: self.dprint(2, "{} Break!".format(name)) break def heartbeat_loop(self): """Run heartbeat""" self.dprint(2, "Starting heartbeat loop...") while not self.exiting: self.dprint(3, ".", end="") try: zmq.device(zmq.FORWARDER, self.heartbeat_socket, self.heartbeat_socket) except zmq.ZMQError as e: if e.errno == errno.EINTR: continue else: raise else: break def shell_handler(self, message): """Dispatch shell messages to their handlers""" self.dprint(1, "received:", message) identities, msg = self.deserialize_wire_message(message) handler = getattr(self, "handle_" + msg["header"]["msg_type"], None) if handler is None: self.dprint(0, "unknown message type:", msg["header"]["msg_type"]) return handler(msg, identities) def handle_execute_request(self, message, identities): """Handle execute request messages.""" self.dprint(2, "Xonsh Kernel Executing:", pformat(message["content"]["code"])) # Start by sending busy signal content = {"execution_state": "busy"} self.send(self.iopub_stream, "status", content, parent_header=message["header"]) # confirm the input that we are executing content = { "execution_count": self.execution_count, "code": message["content"]["code"], } self.send( self.iopub_stream, "execute_input", content, parent_header=message["header"] ) # execute the code metadata = { "dependencies_met": True, "engine": self.session_id, "status": "ok", "started": datetime.datetime.now().isoformat(), } content = self.do_execute(parent_header=message["header"], **message["content"]) self.send( self.shell_stream, "execute_reply", content, metadata=metadata, parent_header=message["header"], identities=identities, ) self.execution_count += 1 # once we are done, send a signal that we are idle content = {"execution_state": "idle"} self.send(self.iopub_stream, "status", content, parent_header=message["header"]) def do_execute( self, code="", silent=False, store_history=True, user_expressions=None, allow_stdin=False, parent_header=None, **kwargs ): """Execute user code.""" if len(code.strip()) == 0: return { "status": "ok", "execution_count": self.execution_count, "payload": [], "user_expressions": {}, } shell = builtins.__xonsh__.shell hist = builtins.__xonsh__.history try: shell.default(code, self, parent_header) interrupted = False except KeyboardInterrupt: interrupted = True if interrupted: return {"status": "abort", "execution_count": self.execution_count} rtn = 0 if (hist is None or len(hist) == 0) else hist.rtns[-1] if 0 < rtn: message = { "status": "error", "execution_count": self.execution_count, "ename": "", "evalue": str(rtn), "traceback": [], } else: message = { "status": "ok", "execution_count": self.execution_count, "payload": [], "user_expressions": {}, } return message def _respond_in_chunks(self, name, s, chunksize=1024, parent_header=None): if s is None: return n = len(s) if n == 0: return lower = range(0, n, chunksize) upper = range(chunksize, n + chunksize, chunksize) for l, u in zip(lower, upper): response = {"name": name, "text": s[l:u]} self.send( self.iopub_socket, "stream", response, parent_header=parent_header ) def handle_complete_request(self, message, identities): """Handles kernel info requests.""" content = self.do_complete( message["content"]["code"], message["content"]["cursor_pos"] ) self.send( self.shell_stream, "complete_reply", content, parent_header=message["header"], identities=identities, ) def do_complete(self, code, pos): """Get completions.""" shell = builtins.__xonsh__.shell line = code.split("\n")[-1] line = builtins.aliases.expand_alias(line) prefix = line.split(" ")[-1] endidx = pos begidx = pos - len(prefix) rtn, _ = self.completer.complete(prefix, line, begidx, endidx, shell.ctx) if isinstance(rtn, Set): rtn = list(rtn) message = { "matches": rtn, "cursor_start": begidx, "cursor_end": endidx, "metadata": {}, "status": "ok", } return message def handle_kernel_info_request(self, message, identities): """Handles kernel info requests.""" content = { "protocol_version": "5.0", "ipython_version": [1, 1, 0, ""], "language": self.language, "language_version": self.language_version, "implementation": self.implementation, "implementation_version": self.implementation_version, "language_info": self.language_info, "banner": self.banner, } self.send( self.shell_stream, "kernel_info_reply", content, parent_header=message["header"], identities=identities, )
class Shell(Cmd): """The xonsh shell.""" def __init__(self, completekey='tab', stdin=None, stdout=None, ctx=None): super(Shell, self).__init__(completekey=completekey, stdin=stdin, stdout=stdout) self.execer = Execer() env = builtins.__xonsh_env__ if ctx is not None: self.ctx = ctx else: rc = env.get('XONSHRC', None) self.ctx = xonshrc_context(rcfile=rc, execer=self.execer) builtins.__xonsh_ctx__ = self.ctx self.ctx['__name__'] = '__main__' self.completer = Completer() self.buffer = [] self.stdout = StringIO() self.stderr = StringIO() self.last = "" self.need_more_lines = False self.mlprompt = None setup_readline() def __del__(self): teardown_readline() def emptyline(self): """Called when an empty line has been entered.""" self.need_more_lines = False self.default('') def parseline(self, line): """Overridden to no-op.""" return '', line, line def precmd(self, line): return line if self.need_more_lines else line.lstrip() def default(self, line): """Implements code execution.""" line = line if line.endswith('\n') else line + '\n' code = self.push(line) if code is None: return try: # Temporarily redirect stdout and stderr to save results in # history. with redirect_stdout(self.stdout), redirect_stderr(self.stderr): self.execer.exec(code, mode='single', glbs=self.ctx) # no locals self.stdout.seek(0) self.stderr.seek(0) sys.stdout.write(self.stdout.read()) sys.stderr.write(self.stderr.read()) cmd = {} cmd['cmd'] = self.last self.stdout.seek(0) cmd['stdout'] = self.stdout.read() self.stderr.seek(0) cmd['stderr'] = self.stderr.read() self.stdout.seek(0) self.stdout.truncate() self.stderr.seek(0) self.stderr.truncate() builtins.__history__.add(cmd) except XonshError as e: print(e.args[0], file=sys.stderr, end='') except: traceback.print_exc() if builtins.__xonsh_exit__: return True def push(self, line): """Pushes a line onto the buffer and compiles the code in a way that enables multiline input. """ code = None self.buffer.append(line) if self.need_more_lines: return code src = ''.join(self.buffer) try: code = self.execer.compile(src, mode='single', glbs=None, locs=self.ctx) self.last = ''.join(filter(lambda x: x != '\n', self.buffer)) self.reset_buffer() except SyntaxError: if line == '\n': self.reset_buffer() traceback.print_exc() return None self.need_more_lines = True return code def reset_buffer(self): """Resets the line buffer.""" self.buffer.clear() self.need_more_lines = False self.mlprompt = None def completedefault(self, text, line, begidx, endidx): """Implements tab-completion for text.""" rl_completion_suppress_append() # this needs to be called each time return self.completer.complete(text, line, begidx, endidx, ctx=self.ctx) # tab complete on first index too completenames = completedefault def cmdloop(self, intro=None): while not builtins.__xonsh_exit__: try: super(Shell, self).cmdloop(intro=intro) except KeyboardInterrupt: print() # Gives a newline self.reset_buffer() intro = None builtins.__history__.close_history() def settitle(self): env = builtins.__xonsh_env__ term = env.get('TERM', None) if term is None or term == 'linux': return if 'TITLE' in env: t = env['TITLE'] else: return t = format_prompt(t) sys.stdout.write("\x1b]2;{0}\x07".format(t)) @property def prompt(self): """Obtains the current prompt string.""" global RL_LIB, RL_CAN_RESIZE if RL_CAN_RESIZE: # This is needed to support some system where line-wrapping doesn't # work. This is a bug in upstream Python, or possibly readline. RL_LIB.rl_reset_screen_size() if self.need_more_lines: if self.mlprompt is None: self.mlprompt = multiline_prompt() return self.mlprompt env = builtins.__xonsh_env__ if 'PROMPT' in env: p = env['PROMPT'] p = format_prompt(p) else: p = "set '$PROMPT = ...' $ " self.settitle() return p
class Shell(Cmd): """The xonsh shell.""" def __init__(self, completekey='tab', stdin=None, stdout=None, ctx=None): super(Shell, self).__init__(completekey=completekey, stdin=stdin, stdout=stdout) self.execer = Execer() env = builtins.__xonsh_env__ self.ctx = ctx if ctx is not None else \ xonshrc_context(rcfile=env.get('XONSHRC', None), execer=self.execer) self.completer = Completer() self.buffer = [] self.need_more_lines = False self.mlprompt = None setup_readline() def __del__(self): teardown_readline() def emptyline(self): """Called when an empty line has been entered.""" self.need_more_lines = False self.default('') def parseline(self, line): """Overridden to no-op.""" return '', line, line def precmd(self, line): return line if self.need_more_lines else line.lstrip() def default(self, line): """Implements code execution.""" line = line if line.endswith('\n') else line + '\n' code = self.push(line) if code is None: return try: self.execer.exec(code, mode='single', glbs=self.ctx) # no locals except: traceback.print_exc() if builtins.__xonsh_exit__: return True def push(self, line): """Pushes a line onto the buffer and compiles the code in a way that enables multiline input. """ code = None self.buffer.append(line) if self.need_more_lines: return code src = ''.join(self.buffer) try: code = self.execer.compile(src, mode='single', glbs=None, locs=self.ctx) self.reset_buffer() except SyntaxError: if line == '\n': self.reset_buffer() traceback.print_exc() return None self.need_more_lines = True return code def reset_buffer(self): """Resets the line buffer.""" self.buffer.clear() self.need_more_lines = False self.mlprompt = None def completedefault(self, text, line, begidx, endidx): """Implements tab-completion for text.""" rl_completion_suppress_append() # this needs to be called each time return self.completer.complete(text, line, begidx, endidx, ctx=self.ctx) # tab complete on first index too completenames = completedefault def cmdloop(self, intro=None): while not builtins.__xonsh_exit__: try: super(Shell, self).cmdloop(intro=intro) except KeyboardInterrupt: print() # Gives a newline self.reset_buffer() intro = None def settitle(self): env = builtins.__xonsh_env__ term = env.get('TERM', None) if term is None or term == 'linux': return if 'TITLE' in env: t = env['TITLE'] if callable(t): t = t() else: return t = format_prompt(t) sys.stdout.write("\x1b]2;{0}\x07".format(t)) @property def prompt(self): """Obtains the current prompt string.""" global RL_LIB, RL_CAN_RESIZE if RL_CAN_RESIZE: # This is needed to support some system where line-wrapping doesn't # work. This is a bug in upstream Python, or possibly readline. RL_LIB.rl_reset_screen_size() if self.need_more_lines: if self.mlprompt is None: self.mlprompt = multiline_prompt() return self.mlprompt env = builtins.__xonsh_env__ if 'PROMPT' in env: p = env['PROMPT'] if callable(p): p = p() p = format_prompt(p) else: p = "set '$PROMPT = ...' $ " self.settitle() return p
class XonshKernel(Kernel): """Xonsh xernal for Jupyter""" implementation = 'Xonsh ' + version implementation_version = version language = 'xonsh' language_version = version banner = 'Xonsh - Python-powered, cross-platform shell' language_info = {'name': 'xonsh', 'version': version, 'pygments_lexer': 'xonsh', 'codemirror_mode': 'shell', 'mimetype': 'text/x-sh', 'file_extension': '.xsh', } def __init__(self, **kwargs): self.completer = Completer() super().__init__(**kwargs) def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False): """Execute user code.""" if len(code.strip()) == 0: return {'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {}} shell = builtins.__xonsh_shell__ hist = builtins.__xonsh_history__ try: shell.default(code) interrupted = False except KeyboardInterrupt: interrupted = True if not silent: # stdout response if hasattr(builtins, '_') and builtins._ is not None: # rely on sys.displayhook functionality self._respond_in_chunks('stdout', pformat(builtins._)) builtins._ = None if hist is not None and len(hist) > 0: self._respond_in_chunks('stdout', hist.outs[-1]) if interrupted: return {'status': 'abort', 'execution_count': self.execution_count} rtn = 0 if (hist is None or len(hist) == 0) else hist.rtns[-1] if 0 < rtn: message = {'status': 'error', 'execution_count': self.execution_count, 'ename': '', 'evalue': str(rtn), 'traceback': []} else: message = {'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {}} return message def _respond_in_chunks(self, name, s, chunksize=1024): if s is None: return n = len(s) if n == 0: return lower = range(0, n, chunksize) upper = range(chunksize, n+chunksize, chunksize) for l, u in zip(lower, upper): response = {'name': name, 'text': s[l:u], } self.send_response(self.iopub_socket, 'stream', response) def do_complete(self, code, pos): """Get completions.""" shell = builtins.__xonsh_shell__ line = code.split('\n')[-1] line = builtins.aliases.expand_alias(line) prefix = line.split(' ')[-1] endidx = pos begidx = pos - len(prefix) rtn, _ = self.completer.complete(prefix, line, begidx, endidx, shell.ctx) message = {'matches': rtn, 'cursor_start': begidx, 'cursor_end': endidx, 'metadata': {}, 'status': 'ok'} return message
class XonshKernel(MetaKernel): implementation = 'Calysto Xonsh Kernel' implementation_version = __version__ language = 'xonsh' language_version = version banner = 'Xonsh - the Python-ish, BASHwards-looking shell' language_info = { 'name': 'xonsh', 'pygments_lexer': 'xonsh', 'codemirror_mode': 'shell', 'mimetype': 'text/x-sh', 'file_extension': '.xsh', 'version': __version__ } def __init__(self, **kwargs): self.completer = Completer() super().__init__(**kwargs) def do_execute_direct(self, code, silent=False): out, err, interrupted = self._do_execute_direct(code) hist = builtins.__xonsh_history__ if not silent: # stdout response if out: self._respond_in_chunks('stdout', out.strip()) if err: self._respond_in_chunks('stderr', err.strip()) if hasattr(builtins, '_') and builtins._ is not None: # rely on sys.displayhook functionality self._respond_in_chunks('stdout', pformat(builtins._)) builtins._ = None if len(hist) > 0 and not out and not err: return hist.outs[-1] def _do_execute_direct(self, code): shell = builtins.__xonsh_shell__ env = builtins.__xonsh_env__ out = io.StringIO() err = io.StringIO() enc = env.get('XONSH_ENCODING') out = SpooledTemporaryFile(max_size=MAX_SIZE, mode='w+t', encoding=enc, newline='\n') err = SpooledTemporaryFile(max_size=MAX_SIZE, mode='w+t', encoding=enc, newline='\n') try: with redirect_stdout(out), redirect_stderr(err), \ swap(builtins, '__xonsh_stdout_uncaptured__', out), \ swap(builtins, '__xonsh_stderr_uncaptured__', err), \ env.swap({'XONSH_STORE_STDOUT': False}): shell.default(code) interrupted = False except KeyboardInterrupt: interrupted = True output, error = '', '' if out.tell() > 0: out.seek(0) output = out.read() if err.tell() > 0: err.seek(0) error = err.read() out.close() err.close() return output, error, interrupted def _respond_in_chunks(self, name, s, chunksize=1024): if s is None: return n = len(s) if n == 0: return lower = range(0, n, chunksize) upper = range(chunksize, n + chunksize, chunksize) for l, u in zip(lower, upper): if name == 'stderr': self.Error(s[l:u]) else: self.Print(s[l:u]) def do_complete(self, code, pos): """Get completions.""" shell = builtins.__xonsh_shell__ line = code.split('\n')[-1] prefix = line.split(' ')[-1] endidx = pos begidx = pos - len(prefix) rtn, _ = self.completer.complete(prefix, line, begidx, endidx, shell.ctx) message = { 'matches': rtn, 'cursor_start': begidx, 'cursor_end': endidx, 'metadata': {}, 'status': 'ok' } return message def get_kernel_help_on(self, info, level=0, none_on_fail=False): obj = info.get('help_obj', '') if not obj or len(obj.split()) > 1: if none_on_fail: return None else: return "" output = '' if ON_POSIX: output, _, _ = self._do_execute_direct('man %s' % obj) if output.startswith('No manual entry for'): output = '' else: output, _, _, = self._do_execute_direct('help %s' % obj) if output.startswith('This command is not supported'): output = '' if not output: output, _, _ = self._do_execute_direct('help(%s)' % obj) return output
class XonshKernel(Kernel): """Xonsh xernal for Jupyter""" implementation = 'Xonsh ' + version implementation_version = version language = 'xonsh' language_version = version banner = 'Xonsh - Python-powered, cross-platform shell' language_info = { 'name': 'xonsh', 'version': version, 'pygments_lexer': 'xonsh', 'codemirror_mode': 'shell', 'mimetype': 'text/x-sh', 'file_extension': '.xsh', } def __init__(self, **kwargs): self.completer = Completer() super().__init__(**kwargs) def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False): """Execute user code.""" if len(code.strip()) == 0: return { 'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {} } shell = builtins.__xonsh_shell__ hist = builtins.__xonsh_history__ try: shell.default(code) interrupted = False except KeyboardInterrupt: interrupted = True if not silent: # stdout response if hasattr(builtins, '_') and builtins._ is not None: # rely on sys.displayhook functionality self._respond_in_chunks('stdout', pformat(builtins._)) builtins._ = None if hist is not None and len(hist) > 0: self._respond_in_chunks('stdout', hist.outs[-1]) if interrupted: return {'status': 'abort', 'execution_count': self.execution_count} rtn = 0 if (hist is None or len(hist) == 0) else hist.rtns[-1] if 0 < rtn: message = { 'status': 'error', 'execution_count': self.execution_count, 'ename': '', 'evalue': str(rtn), 'traceback': [] } else: message = { 'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {} } return message def _respond_in_chunks(self, name, s, chunksize=1024): if s is None: return n = len(s) if n == 0: return lower = range(0, n, chunksize) upper = range(chunksize, n + chunksize, chunksize) for l, u in zip(lower, upper): response = { 'name': name, 'text': s[l:u], } self.send_response(self.iopub_socket, 'stream', response) def do_complete(self, code, pos): """Get completions.""" shell = builtins.__xonsh_shell__ line = code.split('\n')[-1] line = builtins.aliases.expand_alias(line) prefix = line.split(' ')[-1] endidx = pos begidx = pos - len(prefix) rtn, _ = self.completer.complete(prefix, line, begidx, endidx, shell.ctx) message = { 'matches': rtn, 'cursor_start': begidx, 'cursor_end': endidx, 'metadata': {}, 'status': 'ok' } return message