PK]iHJ5AAbash_kernel/__init__.py"""A bash kernel for Jupyter""" from .kernel import __version__ PKeHbash_kernel/__main__.pyfrom ipykernel.kernelapp import IPKernelApp from .kernel import BashKernel IPKernelApp.launch_instance(kernel_class=BashKernel) PKeH3Iaabash_kernel/images.pyimport base64 import imghdr import os #from IPython. _TEXT_SAVED_IMAGE = "bash_kernel: saved image data to:" image_setup_cmd = """ display () { TMPFILE=$(mktemp ${TMPDIR-/tmp}/bash_kernel.XXXXXXXXXX) cat > $TMPFILE echo "%s $TMPFILE" >&2 } """ % _TEXT_SAVED_IMAGE def display_data_for_image(filename): with open(filename, 'rb') as f: image = f.read() os.unlink(filename) image_type = imghdr.what(None, image) if image_type is None: raise ValueError("Not a valid image: %s" % image) image_data = base64.b64encode(image).decode('ascii') content = { 'data': { 'image/' + image_type: image_data }, 'metadata': {} } return content def extract_image_filenames(output): output_lines = [] image_filenames = [] for line in output.split("\n"): if line.startswith(_TEXT_SAVED_IMAGE): filename = line.rstrip().split(": ")[-1] image_filenames.append(filename) else: output_lines.append(line) output = "\n".join(output_lines) return image_filenames, output PKUKKbash_kernel/install.pyimport json import os import sys import argparse from jupyter_client.kernelspec import KernelSpecManager from IPython.utils.tempdir import TemporaryDirectory kernel_json = {"argv":[sys.executable,"-m","bash_kernel", "-f", "{connection_file}"], "display_name":"Bash", "language":"bash", "codemirror_mode":"shell", "env":{"PS1": "$"} } def install_my_kernel_spec(user=True, prefix=None): with TemporaryDirectory() as td: os.chmod(td, 0o755) # Starts off as 700, not user readable with open(os.path.join(td, 'kernel.json'), 'w') as f: json.dump(kernel_json, f, sort_keys=True) # TODO: Copy resources once they're specified print('Installing IPython kernel spec') KernelSpecManager().install_kernel_spec(td, 'bash', user=user, replace=True, prefix=prefix) def _is_root(): try: return os.geteuid() == 0 except AttributeError: return False # assume not an admin on non-Unix platforms def main(argv=[]): parser = argparse.ArgumentParser( description='Install KernelSpec for Bash Kernel' ) prefix_locations = parser.add_mutually_exclusive_group() prefix_locations.add_argument( '--user', help='Install KernelSpec in user homedirectory', action='store_false' if _is_root() else 'store_true' ) prefix_locations.add_argument( '--sys-prefix', help='Install KernelSpec in sys.prefix. Useful in conda / virtualenv', action='store_true', dest='sys_prefix' ) prefix_locations.add_argument( '--prefix', help='Install KernelSpec in this prefix', default=None ) args = parser.parse_args() if args.sys_prefix: prefix = sys.prefix user = None elif args.user: prefix = None user = True else: prefix = args.prefix user = None install_my_kernel_spec(user=user, prefix=prefix) if __name__ == '__main__': main(argv=sys.argv) PKUKxha bash_kernel/kernel.pyfrom ipykernel.kernelbase import Kernel from pexpect import replwrap, EOF import pexpect from subprocess import check_output import os.path import re import signal __version__ = '0.7' version_pat = re.compile(r'version (\d+(\.\d+)+)') from .images import ( extract_image_filenames, display_data_for_image, image_setup_cmd ) class IREPLWrapper(replwrap.REPLWrapper): """A subclass of REPLWrapper that gives incremental output specifically for bash_kernel. The parameters are the same as for REPLWrapper, except for one extra parameter: :param line_output_callback: a callback method to receive each batch of incremental output. It takes one string parameter. """ def __init__(self, cmd_or_spawn, orig_prompt, prompt_change, extra_init_cmd=None, line_output_callback=None): self.line_output_callback = line_output_callback replwrap.REPLWrapper.__init__(self, cmd_or_spawn, orig_prompt, prompt_change, extra_init_cmd=extra_init_cmd) def _expect_prompt(self, timeout=-1): if timeout == None: # "None" means we are executing code from a Jupyter cell by way of the run_command # in the do_execute() code below, so do incremental output. while True: pos = self.child.expect_exact([self.prompt, self.continuation_prompt, u'\r\n'], timeout=None) if pos == 2: # End of line received self.line_output_callback(self.child.before + '\n') else: if len(self.child.before) != 0: # prompt received, but partial line precedes it self.line_output_callback(self.child.before) break else: # Otherwise, use existing non-incremental code pos = replwrap.REPLWrapper._expect_prompt(self, timeout=timeout) # Prompt received, so return normally return pos class BashKernel(Kernel): implementation = 'bash_kernel' implementation_version = __version__ @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 = check_output(['bash', '--version']).decode('utf-8') return self._banner language_info = {'name': 'bash', 'codemirror_mode': 'shell', 'mimetype': 'text/x-sh', 'file_extension': '.sh'} def __init__(self, **kwargs): Kernel.__init__(self, **kwargs) self._start_bash() def _start_bash(self): # Signal handlers are inherited by forked processes, and we can't easily # reset it from the subprocess. Since kernelapp ignores SIGINT except in # message handlers, we need to temporarily reset the SIGINT handler here # so that bash and its children are interruptible. sig = signal.signal(signal.SIGINT, signal.SIG_DFL) try: # Note: the next few lines mirror functionality in the # bash() function of pexpect/replwrap.py. Look at the # source code there for comments and context for # understanding the code here. bashrc = os.path.join(os.path.dirname(pexpect.__file__), 'bashrc.sh') child = pexpect.spawn("bash", ['--rcfile', bashrc], echo=False, encoding='utf-8', codec_errors='replace') ps1 = replwrap.PEXPECT_PROMPT[:5] + u'\[\]' + replwrap.PEXPECT_PROMPT[5:] ps2 = replwrap.PEXPECT_CONTINUATION_PROMPT[:5] + u'\[\]' + replwrap.PEXPECT_CONTINUATION_PROMPT[5:] prompt_change = u"PS1='{0}' PS2='{1}' PROMPT_COMMAND=''".format(ps1, ps2) # Using IREPLWrapper to get incremental output self.bashwrapper = IREPLWrapper(child, u'\$', prompt_change, extra_init_cmd="export PAGER=cat", line_output_callback=self.process_output) finally: signal.signal(signal.SIGINT, sig) # Register Bash function to write image data to temporary file self.bashwrapper.run_command(image_setup_cmd) def process_output(self, output): if not self.silent: image_filenames, output = extract_image_filenames(output) # Send standard output stream_content = {'name': 'stdout', 'text': output} self.send_response(self.iopub_socket, 'stream', stream_content) # Send images, if any for filename in image_filenames: try: data = display_data_for_image(filename) except ValueError as e: message = {'name': 'stdout', 'text': str(e)} self.send_response(self.iopub_socket, 'stream', message) else: self.send_response(self.iopub_socket, 'display_data', data) def do_execute(self, code, silent, store_history=True, user_expressions=None, allow_stdin=False): self.silent = silent if not code.strip(): return {'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {}} interrupted = False try: # Note: timeout=None tells IREPLWrapper to do incremental # output. Also note that the return value from # run_command is not needed, because the output was # already sent by IREPLWrapper. self.bashwrapper.run_command(code.rstrip(), timeout=None) except KeyboardInterrupt: self.bashwrapper.child.sendintr() interrupted = True self.bashwrapper._expect_prompt() output = self.bashwrapper.child.before self.process_output(output) except EOF: output = self.bashwrapper.child.before + 'Restarting Bash' self._start_bash() self.process_output(output) if interrupted: return {'status': 'abort', 'execution_count': self.execution_count} try: exitcode = int(self.bashwrapper.run_command('echo $?').rstrip()) except Exception: exitcode = 1 if exitcode: error_content = {'execution_count': self.execution_count, 'ename': '', 'evalue': str(exitcode), 'traceback': []} self.send_response(self.iopub_socket, 'error', error_content) error_content['status'] = 'error' return error_content else: return {'status': 'ok', 'execution_count': self.execution_count, 'payload': [], 'user_expressions': {}} def do_complete(self, code, cursor_pos): code = code[:cursor_pos] default = {'matches': [], 'cursor_start': 0, 'cursor_end': cursor_pos, 'metadata': dict(), 'status': 'ok'} if not code or code[-1] == ' ': return default tokens = code.replace(';', ' ').split() if not tokens: return default matches = [] token = tokens[-1] start = cursor_pos - len(token) if token[0] == '$': # complete variables cmd = 'compgen -A arrayvar -A export -A variable %s' % token[1:] # strip leading $ output = self.bashwrapper.run_command(cmd).rstrip() completions = set(output.split()) # append matches including leading $ matches.extend(['$'+c for c in completions]) else: # complete functions and builtins cmd = 'compgen -cdfa %s' % token output = self.bashwrapper.run_command(cmd).rstrip() matches.extend(output.split()) if not matches: return default matches = [m for m in matches if m.startswith(token)] return {'matches': sorted(matches), 'cursor_start': start, 'cursor_end': cursor_pos, 'metadata': dict(), 'status': 'ok'} PKeHM:!bash_kernel-0.7.dist-info/LICENSECopyright (c) 2015, Thomas Kluyver and contributors All rights reserved. BSD 3-clause license: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. PK!H١Wdbash_kernel-0.7.dist-info/WHEEL HM K-*ϳR03rOK-J,/RH,Q0343 /, (-JLR()*M ILR(4KM̫#DPK!H^V "bash_kernel-0.7.dist-info/METADATA}Rn1}WC@z EBZAEJUڦ 8qmoC{/Q"!9y *Dd x߱XZiΠb 0!{WҘY2uynּ$S]~q/K4>2~ffl҄\˚pqrZHU@h{A7;E Fbdߺ͞C$3@ڏ)LuUgml^m7MN=}]deʋۯ;>7ҡnXc {~i)5Ɩz\!TQ0gJg:bO;GbMLG1DYΡ *>5pMk`j;G(q UZ:ƑnG4gϡ$I!dYG)vcKP>a4Cg`wc}LJ%PTh6G+ЇZ] Lk/ͰrhUQx?f|`VPK!H4v bash_kernel-0.7.dist-info/RECORD}ι@@|q P[@YjMYfn@OgIՍQkȩE-H>lyFL(g9sV`#-#I>Hk"=-^ظvptndkǬĊ#.5>0b]*iI@=ۙ\md/BTenxj!I~pJc 矶k{- _lZnX,$8CSR{nwLom^Ӹ(<6fCqʎ4NIT5qk l[>y4}9+3c2S+*.n[k毎VξcڝB6dG3=)ٮHƦVŋ ؕ+e4i o]rA⋬n.$,PK]iHJ5AAbash_kernel/__init__.pyPKeHvbash_kernel/__main__.pyPKeH3Iaa+bash_kernel/images.pyPKUKKbash_kernel/install.pyPKUKxha  bash_kernel/kernel.pyPKeHM:!l.bash_kernel-0.7.dist-info/LICENSEPK!H١Wd4bash_kernel-0.7.dist-info/WHEELPK!H^V "55bash_kernel-0.7.dist-info/METADATAPK!H4v 7bash_kernel-0.7.dist-info/RECORDPK l9