mirror of
https://github.com/frappe/gunicorn.git
synced 2026-01-14 11:09:11 +08:00
Merge pull request #682 from tilgovi/feature/526
Add --reload option for code reloading
This commit is contained in:
commit
d4f2481384
28
NOTICE
28
NOTICE
@ -54,6 +54,34 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
gunicorn.reloader
|
||||||
|
-----------------
|
||||||
|
|
||||||
|
Based on greins.reloader module under MIT license:
|
||||||
|
|
||||||
|
2010 (c) Meebo, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person
|
||||||
|
obtaining a copy of this software and associated documentation
|
||||||
|
files (the "Software"), to deal in the Software without
|
||||||
|
restriction, including without limitation the rights to use,
|
||||||
|
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
doc/sitemap_gen.py
|
doc/sitemap_gen.py
|
||||||
------------------
|
------------------
|
||||||
Under BSD License :
|
Under BSD License :
|
||||||
|
|||||||
@ -1,35 +0,0 @@
|
|||||||
import logging
|
|
||||||
import os
|
|
||||||
import signal
|
|
||||||
import sys
|
|
||||||
|
|
||||||
def on_starting(server):
|
|
||||||
# use server hook to patch socket to allow worker reloading
|
|
||||||
from gevent import monkey
|
|
||||||
monkey.patch_socket()
|
|
||||||
|
|
||||||
def when_ready(server):
|
|
||||||
def monitor():
|
|
||||||
modify_times = {}
|
|
||||||
while True:
|
|
||||||
for module in sys.modules.values():
|
|
||||||
path = getattr(module, "__file__", None)
|
|
||||||
if not path: continue
|
|
||||||
if path.endswith(".pyc") or path.endswith(".pyo"):
|
|
||||||
path = path[:-1]
|
|
||||||
try:
|
|
||||||
modified = os.stat(path).st_mtime
|
|
||||||
except:
|
|
||||||
continue
|
|
||||||
if path not in modify_times:
|
|
||||||
modify_times[path] = modified
|
|
||||||
continue
|
|
||||||
if modify_times[path] != modified:
|
|
||||||
logging.info("%s modified; restarting server", path)
|
|
||||||
os.kill(os.getpid(), signal.SIGHUP)
|
|
||||||
modify_times = {}
|
|
||||||
break
|
|
||||||
gevent.sleep(1)
|
|
||||||
|
|
||||||
import gevent
|
|
||||||
gevent.spawn(monitor)
|
|
||||||
@ -706,6 +706,25 @@ class Debug(Setting):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class Reload(Setting):
|
||||||
|
name = "reload"
|
||||||
|
section = 'Debugging'
|
||||||
|
cli = ['--reload']
|
||||||
|
validator = validate_bool
|
||||||
|
action = 'store_true'
|
||||||
|
default = False
|
||||||
|
desc = '''\
|
||||||
|
Restart workers when code changes.
|
||||||
|
|
||||||
|
This setting is intended for development. It will cause workers to be
|
||||||
|
restarted whenever application code changes.
|
||||||
|
|
||||||
|
The reloader is incompatible with application preloading. When using a
|
||||||
|
paste configuration be sure that the server block does not import any
|
||||||
|
application code or the reload will not work as designed.
|
||||||
|
'''
|
||||||
|
|
||||||
|
|
||||||
class Spew(Setting):
|
class Spew(Setting):
|
||||||
name = "spew"
|
name = "spew"
|
||||||
section = "Debugging"
|
section = "Debugging"
|
||||||
|
|||||||
54
gunicorn/reloader.py
Normal file
54
gunicorn/reloader.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# -*- coding: utf-8 -
|
||||||
|
#
|
||||||
|
# This file is part of gunicorn released under the MIT license.
|
||||||
|
# See the NOTICE for more information.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
|
||||||
|
|
||||||
|
class Reloader(threading.Thread):
|
||||||
|
def __init__(self, extra_files=None, interval=1, callback=None):
|
||||||
|
super(Reloader, self).__init__()
|
||||||
|
self.setDaemon(True)
|
||||||
|
self._extra_files = set(extra_files or ())
|
||||||
|
self._extra_files_lock = threading.RLock()
|
||||||
|
self._interval = interval
|
||||||
|
self._callback = callback
|
||||||
|
|
||||||
|
def add_extra_file(self, filename):
|
||||||
|
with self._extra_files_lock:
|
||||||
|
self._extra_files.add(filename)
|
||||||
|
|
||||||
|
def get_files(self):
|
||||||
|
fnames = [
|
||||||
|
re.sub('py[co]$', 'py', module.__file__)
|
||||||
|
for module in sys.modules.values()
|
||||||
|
if hasattr(module, '__file__')
|
||||||
|
]
|
||||||
|
|
||||||
|
with self._extra_files_lock:
|
||||||
|
fnames.extend(self._extra_files)
|
||||||
|
|
||||||
|
return fnames
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
mtimes = {}
|
||||||
|
while True:
|
||||||
|
for filename in self.get_files():
|
||||||
|
try:
|
||||||
|
mtime = os.stat(filename).st_mtime
|
||||||
|
except OSError:
|
||||||
|
continue
|
||||||
|
old_time = mtimes.get(filename)
|
||||||
|
if old_time is None:
|
||||||
|
mtimes[filename] = mtime
|
||||||
|
continue
|
||||||
|
elif mtime > old_time:
|
||||||
|
if self._callback:
|
||||||
|
self._callback(filename)
|
||||||
|
time.sleep(self._interval)
|
||||||
@ -12,6 +12,7 @@ import traceback
|
|||||||
|
|
||||||
from gunicorn import util
|
from gunicorn import util
|
||||||
from gunicorn.workers.workertmp import WorkerTmp
|
from gunicorn.workers.workertmp import WorkerTmp
|
||||||
|
from gunicorn.reloader import Reloader
|
||||||
from gunicorn.http.errors import InvalidHeader, InvalidHeaderName, \
|
from gunicorn.http.errors import InvalidHeader, InvalidHeaderName, \
|
||||||
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, \
|
InvalidRequestLine, InvalidRequestMethod, InvalidHTTPVersion, \
|
||||||
LimitRequestLine, LimitRequestHeaders
|
LimitRequestLine, LimitRequestHeaders
|
||||||
@ -79,6 +80,14 @@ class Worker(object):
|
|||||||
loop is initiated.
|
loop is initiated.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
# start the reloader
|
||||||
|
if self.cfg.reload:
|
||||||
|
def changed(fname):
|
||||||
|
self.log.info("Worker reloading: %s modified", fname)
|
||||||
|
os.kill(self.pid, signal.SIGTERM)
|
||||||
|
raise SystemExit()
|
||||||
|
Reloader(callback=changed).start()
|
||||||
|
|
||||||
# set enviroment' variables
|
# set enviroment' variables
|
||||||
if self.cfg.env:
|
if self.cfg.env:
|
||||||
for k, v in self.cfg.env.items():
|
for k, v in self.cfg.env.items():
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user