Commit c66bc839 authored by Robin Schoonover's avatar Robin Schoonover
Browse files

Pull in reloading server.

parents
import os
import re
from setuptools import setup, find_packages
def read(filename):
with open(os.path.join(os.path.dirname(__file__), filename)) as f:
return f.read()
def read_version(filename):
return re.search(r"__version__ = '(.*?)'", read(filename)).group(1)
setup(
name = 'wsgidev',
version = read_version('wsgidev/__init__.py'),
description = 'Simple reloading wrapper',
author = 'Robin Schoonover',
author_email = 'robin@cornhooves.org',
install_requires = [
'waitress'
],
packages = find_packages('.'),
entry_points={
'console_scripts': [
'wsgidev=wsgidev.__main__:main',
],
},
)
__version__ = '0.1'
from .server import Server
import argparse
from .server import Server
def main():
parser = argparse.ArgumentParser('Reloading WSGI Development Server')
parser.add_argument('--host', default='localhost')
parser.add_argument('--port', default=8080, type=int)
parser.add_argument('wsgiapp')
args = parser.parse_args()
server = Server(args.wsgiapp, host=args.host, port=args.port)
server.run_forever()
if __name__=='__main__':
main()
\ No newline at end of file
import logging
import os
import signal
import subprocess
import sys
import threading
import time
log = logging.getLogger()
class FailedApp(object):
def __init__(self, msg):
self.msg = msg
def __call__(self, environ, start_response):
start_response('500 Internal Server Error', [('Content-Type', 'text/plain')])
return [b"Failed: ", self.msg.encode('utf-8'), b"\n"]
class Server(object):
def __init__(self, app, host="0.0.0.0", port=80):
self.app = app
self.host = host
self.port = port
self.last_start = 0
def run_forever(self):
while True:
try:
proc = self.spawn_process()
self.process = proc
proc.wait()
except KeyboardInterrupt:
return self.quitting()
def quitting(self):
if self.process:
self.process.terminate()
self.process.wait()
def spawn_process(self):
now = time.time()
if now - self.last_start < 1:
# restarting too fast, delay
time.sleep(1)
self.last_start = now
args = [sys.executable, "-m", __name__, self.app, str(self.host), str(self.port)]
return subprocess.Popen(args, stdin=subprocess.DEVNULL)
class ServerChild(object):
def __init__(self, app, host, port):
self.app = app
self.host = host
self.port = port
self.module_times = {}
def run_forever(self):
self.reload_thread = threading.Thread(target=self.reload_main)
self.server_init()
self.reload_thread.start()
log.info("Server started.")
try:
self.server.run()
finally:
self.server = None
def load_app(self):
module_name, func_name = self.app.split(':', 1)
try:
__import__(module_name)
except ImportError as exc:
log.exception("Failed to import module %s", module_name)
return FailedApp("Import failed (%s)"%(exc, ))
module = sys.modules[module_name]
try:
return getattr(module, func_name)
except AttributeError as exc:
log.exception("Failed to load attr %s from module", func_name)
return FailedApp("Import failed (%s)"%(exc, ))
def server_init(self):
from waitress import create_server
app = self.load_app()
self.server = create_server(app, host=self.host, port=self.port)
def reload_main(self):
while True:
if self.server is None:
return
needreload = False
for name in list(sys.modules):
module = sys.modules[name]
if hasattr(module, "__file__"):
try:
mtime = os.path.getmtime(module.__file__)
except OSError:
log.warn("Error getting mtime (is this an egg?): %s", module.__file__)
continue
if not name in self.module_times:
self.module_times[name] = mtime
elif self.module_times[name] < mtime:
log.info("Detected change: %s (%d -> %d)",
name,
self.module_times[name],
mtime)
self.module_times[name] = mtime
needreload = True
if needreload:
log.info("Sending kill signal.")
os.kill(os.getpid(), signal.SIGINT)
time.sleep(1)
if __name__=='__main__':
# actual server starts here
ServerChild(sys.argv[1], sys.argv[2], int(sys.argv[3])).run_forever()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment