mirror of
https://github.com/dathere/100.dathere.com.git
synced 2025-12-19 00:29:25 +00:00
feat: initial commit for 100.dathere.com and first exercise
This commit is contained in:
commit
86f90af434
35 changed files with 860 additions and 0 deletions
5
bash_kernel/.gitignore
vendored
Normal file
5
bash_kernel/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
__pycache__
|
||||
*.pyc
|
||||
build/
|
||||
dist/
|
||||
MANIFEST
|
||||
14
bash_kernel/LICENSE
Normal file
14
bash_kernel/LICENSE
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
Copyright (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.
|
||||
22
bash_kernel/README.rst
Normal file
22
bash_kernel/README.rst
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
A simple IPython kernel for bash
|
||||
|
||||
This requires IPython 3.
|
||||
|
||||
To install::
|
||||
|
||||
pip install bash_kernel
|
||||
python -m bash_kernel.install
|
||||
|
||||
To use it, run one of:
|
||||
|
||||
.. code:: shell
|
||||
|
||||
ipython notebook
|
||||
# In the notebook interface, select Bash from the 'New' menu
|
||||
ipython qtconsole --kernel bash
|
||||
ipython console --kernel bash
|
||||
|
||||
For details of how this works, see the Jupyter docs on `wrapper kernels
|
||||
<http://jupyter-client.readthedocs.org/en/latest/wrapperkernels.html>`_, and
|
||||
Pexpect's docs on the `replwrap module
|
||||
<http://pexpect.readthedocs.org/en/latest/api/replwrap.html>`_
|
||||
3
bash_kernel/bash_kernel/__init__.py
Normal file
3
bash_kernel/bash_kernel/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
"""A bash kernel for Jupyter"""
|
||||
|
||||
__version__ = '0.4.1'
|
||||
3
bash_kernel/bash_kernel/__main__.py
Normal file
3
bash_kernel/bash_kernel/__main__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
from ipykernel.kernelapp import IPKernelApp
|
||||
from .kernel import BashKernel
|
||||
IPKernelApp.launch_instance(kernel_class=BashKernel)
|
||||
48
bash_kernel/bash_kernel/images.py
Normal file
48
bash_kernel/bash_kernel/images.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import 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
|
||||
47
bash_kernel/bash_kernel/install.py
Normal file
47
bash_kernel/bash_kernel/install.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import json
|
||||
import os
|
||||
import sys
|
||||
import getopt
|
||||
|
||||
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=[]):
|
||||
prefix = None
|
||||
user = not _is_root()
|
||||
|
||||
opts, _ = getopt.getopt(argv[1:], '', ['user', 'prefix='])
|
||||
for k, v in opts:
|
||||
if k == '--user':
|
||||
user = True
|
||||
elif k == '--prefix':
|
||||
prefix = v
|
||||
user = False
|
||||
|
||||
install_my_kernel_spec(user=user, prefix=prefix)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(argv=sys.argv)
|
||||
154
bash_kernel/bash_kernel/kernel.py
Normal file
154
bash_kernel/bash_kernel/kernel.py
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
from ipykernel.kernelbase import Kernel
|
||||
from pexpect import replwrap, EOF
|
||||
|
||||
from subprocess import check_output
|
||||
from os import unlink
|
||||
|
||||
import base64
|
||||
import imghdr
|
||||
import re
|
||||
import signal
|
||||
import urllib
|
||||
|
||||
__version__ = '0.2'
|
||||
|
||||
version_pat = re.compile(r'version (\d+(\.\d+)+)')
|
||||
|
||||
from .images import (
|
||||
extract_image_filenames, display_data_for_image, image_setup_cmd
|
||||
)
|
||||
|
||||
|
||||
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:
|
||||
self.bashwrapper = replwrap.bash()
|
||||
finally:
|
||||
signal.signal(signal.SIGINT, sig)
|
||||
|
||||
# Register Bash function to write image data to temporary file
|
||||
self.bashwrapper.run_command(image_setup_cmd)
|
||||
|
||||
def do_execute(self, code, silent, store_history=True,
|
||||
user_expressions=None, allow_stdin=False):
|
||||
if not code.strip():
|
||||
return {'status': 'ok', 'execution_count': self.execution_count,
|
||||
'payload': [], 'user_expressions': {}}
|
||||
|
||||
interrupted = False
|
||||
try:
|
||||
output = 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
|
||||
except EOF:
|
||||
output = self.bashwrapper.child.before + 'Restarting Bash'
|
||||
self._start_bash()
|
||||
|
||||
if not 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)
|
||||
|
||||
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 and not (code.rstrip().endswith("-h") or code.rstrip().endswith("--help")):
|
||||
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'}
|
||||
|
||||
|
||||
11
bash_kernel/flit.ini
Normal file
11
bash_kernel/flit.ini
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
[metadata]
|
||||
module = bash_kernel
|
||||
author = Thomas Kluyver
|
||||
author-email = thomas@kluyver.me.uk
|
||||
home-page = https://github.com/takluyver/bash_kernel
|
||||
requires = pexpect (>=3.3)
|
||||
description-file = README.rst
|
||||
classifiers = Framework :: IPython
|
||||
License :: OSI Approved :: BSD License
|
||||
Programming Language :: Python :: 3
|
||||
Topic :: System :: Shells
|
||||
21
bash_kernel/pyproject.toml
Normal file
21
bash_kernel/pyproject.toml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
[build-system]
|
||||
requires = ["flit_core >=3.2,<4"]
|
||||
build-backend = "flit_core.buildapi"
|
||||
|
||||
[project]
|
||||
name = "bash_kernel"
|
||||
authors = [
|
||||
{name = "Thomas Kluyver", email = "thomas@kluyver.me.uk"},
|
||||
]
|
||||
readme = "README.rst"
|
||||
dependencies = ["pexpect (>=4.0)", "ipykernel"]
|
||||
classifiers = [
|
||||
"Framework :: Jupyter",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: System :: Shells",
|
||||
]
|
||||
dynamic = ["version", "description"]
|
||||
|
||||
[project.urls]
|
||||
Source = "https://github.com/takluyver/bash_kernel"
|
||||
Loading…
Add table
Add a link
Reference in a new issue