#!/usr/bin/env python3 import sys import argparse import cgi import tempfile import os import threading import time # Python 2 vs Python 3 compat try: from urllib.parse import unquote_plus except ImportError: from urllib import unquote_plus from wsgiref.util import setup_testing_defaults from wsgiref.simple_server import make_server import wsgiref.util try: import boxes.generators except ImportError: sys.path.append(os.path.dirname(__file__) + "/..") import boxes.generators class FileChecker(threading.Thread): def __init__(self, files=[], checkmodules=True): super(FileChecker, self).__init__() self.checkmodules = checkmodules self.timestamps = {} for path in files: self.timestamps[path] = os.stat(path).st_mtime if checkmodules: self._addModules() def _addModules(self): for name, module in sys.modules.items(): path = getattr(module, "__file__", None) if not path: continue if path not in self.timestamps: self.timestamps[path] = os.stat(path).st_mtime def filesOK(self): if self.checkmodules: self._addModules() for path, timestamp in self.timestamps.items(): try: if os.stat(path).st_mtime != timestamp: return False except FileNotFoundError: return False return True def run(self): while True: if not self.filesOK(): os.execv(__file__, sys.argv) time.sleep(1) class ArgumentParserError(Exception): pass class ThrowingArgumentParser(argparse.ArgumentParser): def error(self, message): raise ArgumentParserError(message) boxes.ArgumentParser = ThrowingArgumentParser # Evil hack class BServer: def __init__(self): self.boxes = {b.__name__ : b() for b in boxes.generators.getAllBoxGenerators().values() if b.webinterface} def arg2html(self, a): name = a.option_strings[0].replace("-", "") if isinstance(a, argparse._HelpAction): return "" row = """
""", box.__doc__ or "", """
""") return (s.encode("utf-8") for s in result) def menu(self): result = ["""A Python based generator for laser cut boxes and other things.
It features both finished parametrized generators as well as a Python API for writing your own scripts. It features finger and (flat) dovetail joints, flex cuts, holes and slots for screws and more high level functions.
These are the available generators:
""", str(e).encode(), b"""
""" ] def serve(self, environ, start_response): status = '200 OK' headers = [('Content-type', 'text/html; charset=utf-8')] d = cgi.parse_qs(environ['QUERY_STRING']) name = environ["PATH_INFO"][1:] box = self.boxes.get(name, None) if not box: start_response(status, headers) return self.menu() args = ["--"+arg for arg in environ['QUERY_STRING'].split("&")] if "--render=1" not in args: start_response(status, headers) return self.args2html(name, box) else: args = [a for a in args if a != "--render=1"] try: box.parseArgs(args) except (ArgumentParserError) as e: start_response(status, headers) return self.errorMessage(name, e) if name == "TrayLayout": start_response(status, headers) box.fillDefault(box.x, box.y) self.boxes["TrayLayout2"].argparser.set_defaults(layout=str(box)) return self.args2html( name, self.boxes["TrayLayout2"], action="TrayLayout2") if name == "TrayLayout2": try: box.parse(unquote_plus(box.layout).split("\n")) except Exception as e: raise start_response(status, headers) return self.errorMessage(name, e) start_response(status, box.formats.http_headers.get( box.format, [('Content-type', 'application/unknown; charset=utf-8')])) fd, box.output = tempfile.mkstemp() box.render() result = open(box.output).readlines() os.remove(box.output) os.close(fd) return (l.encode("utf-8") for l in result) if __name__=="__main__": fc = FileChecker() fc.start() boxserver = BServer() httpd = make_server('', 8000, boxserver.serve) print("Serving on port 8000...") httpd.serve_forever() else: application = BServer().serve