#!/usr/bin/env python3 import sys import argparse import cgi import tempfile import os.path import threading import time csspath = os.path.join(os.path.dirname(__file__), 'self.css') css = (open(csspath).read()) # 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} self.groups = boxes.generators.ui_groups self.groups_by_name = boxes.generators.ui_groups_by_name for name, box in self.boxes.items(): self.groups_by_name.get(box.ui_group, self.groups_by_name["Misc"]).add(box) def arg2html(self, a, prefix): name = a.option_strings[0].replace("-", "") if isinstance(a, argparse._HelpAction): return "" viewname = name if prefix and name.startswith(prefix + '_'): viewname = name[len(prefix)+1:] row = """
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.
""", 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 = ["--"+unquote_plus(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(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(files=[csspath]) fc.start() boxserver = BServer() httpd = make_server('', 8000, boxserver.serve) print("Serving on port 8000...") httpd.serve_forever() else: application = BServer().serve