#!/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 = "
".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()