138 lines
3.2 KiB
Python
Executable File
138 lines
3.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
DIR = "./scripts/"
|
|
HOST = "localhost"
|
|
PORT = 9090
|
|
|
|
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description="pier - Python Interpreter Executing Remotely",
|
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
)
|
|
|
|
parser.add_argument("-d", "--dir", default=DIR, help="directory containing python scripts")
|
|
parser.add_argument("-H", "--host", default=HOST, help="host name")
|
|
parser.add_argument("-P", "--port", default=PORT, help="port number", type=int)
|
|
|
|
clargs = parser.parse_args()
|
|
|
|
|
|
from contextlib import redirect_stdout, redirect_stderr
|
|
from functools import lru_cache
|
|
from glob import glob
|
|
from io import StringIO
|
|
|
|
import os
|
|
import sys
|
|
import shlex
|
|
import traceback
|
|
import http.server
|
|
import socketserver
|
|
|
|
|
|
URL_SPACE = "%20"
|
|
|
|
exec_globals = {}
|
|
exec_locals = {}
|
|
|
|
|
|
class ScriptServer(http.server.SimpleHTTPRequestHandler):
|
|
|
|
def do_GET(self):
|
|
args = self.path.lstrip("/").replace(URL_SPACE, " ")
|
|
args = shlex.split(args)
|
|
|
|
if not args:
|
|
super().do_GET()
|
|
return
|
|
|
|
scr = args[0]
|
|
|
|
if not scr.endswith(".py"):
|
|
scr += ".py"
|
|
|
|
scripts = sorted(glob("*.py"))
|
|
if scr not in scripts:
|
|
printable_scripts = "\n".join(scripts)
|
|
self.send_result(f"{scr} does not exist. choose from:\n", printable_scripts)
|
|
return
|
|
|
|
res = run_script(scr, args)
|
|
|
|
msg = f"{scr} output:\n"
|
|
msg += "_" * 80
|
|
msg += f"\n{res}"
|
|
self.send_result(msg)
|
|
|
|
|
|
def send_result(self, *msg):
|
|
self.send_response(200)
|
|
self.send_header("Content-type", "text/html")
|
|
self.end_headers()
|
|
msg = encode_html(*msg)
|
|
self.wfile.write(msg)
|
|
print(msg)
|
|
|
|
|
|
|
|
def run_script(fn, args):
|
|
sio = StringIO()
|
|
with redirect_stdout(sio), redirect_stderr(sio):
|
|
code = open_script(fn)
|
|
old_sys_argv, sys.argv = sys.argv, args
|
|
try:
|
|
exec(code, exec_globals, exec_locals)
|
|
except BaseException as e: # need to catch SystemExit etc. as well
|
|
print("_" * 80)
|
|
tn = type(e).__name__
|
|
msg = f"{tn}: {e}"
|
|
print(msg, file=sys.stderr)
|
|
traceback.print_exc()
|
|
sys.argv = old_sys_argv
|
|
return sio.getvalue()
|
|
|
|
@lru_cache()
|
|
def open_script(fn):
|
|
print("reading", fn)
|
|
print("_" * 80)
|
|
with open(fn) as f:
|
|
src = f.read()
|
|
code = compile(src, fn, "exec")
|
|
return code
|
|
|
|
def encode_html(*msg):
|
|
msg = (str(i) for i in msg)
|
|
msg = " ".join(msg)
|
|
msg = msg.split("\n")
|
|
msg = "<br>".join(msg)
|
|
msg = msg.encode()
|
|
return msg
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
os.chdir(clargs.dir)
|
|
except Exception as e:
|
|
tn = type(e).__name__
|
|
msg = f"{tn}: {e}"
|
|
raise SystemExit(msg)
|
|
|
|
sys.path.insert(0, os.getcwd()) # add the script folder to the path
|
|
|
|
host = clargs.host
|
|
port = clargs.port
|
|
|
|
socketserver.TCPServer.allow_reuse_address = True
|
|
with socketserver.ThreadingTCPServer((host, port), ScriptServer) as httpd:
|
|
print(f"serving at {host}:{port}")
|
|
try:
|
|
httpd.serve_forever()
|
|
except KeyboardInterrupt:
|
|
print()
|
|
|
|
|
|
|