Modify 'reload' config to be more consistent with existing API

--reload = Runs the reloader with inotify if available and falls back on
           FS polling.
--reload=inotify = Forces the reloader to run with inotify
--reload=poll = Forces the reloader to use FS polling
This commit is contained in:
Mark Adams 2016-10-24 09:08:02 -05:00 committed by Mark Adams
parent 64b26ef766
commit 92d48256e4
3 changed files with 43 additions and 30 deletions

View File

@ -489,6 +489,19 @@ def validate_hostport(val):
else:
raise TypeError("Value must consist of: hostname:port")
def validate_reloader(val):
if val is None:
val = 'default'
choices = ['poll', 'inotify', 'default']
if val not in choices:
raise ConfigError(
'Invalid reloader type. Must be one of: %s' % choices
)
def get_default_config_file():
config_path = os.path.join(os.path.abspath(os.getcwd()),
'gunicorn.conf.py')
@ -806,9 +819,11 @@ class Reload(Setting):
name = "reload"
section = 'Debugging'
cli = ['--reload']
validator = validate_bool
action = 'store_true'
default = False
validator = validate_reloader
nargs = '?'
const = 'default'
default = None
desc = '''\
Restart workers when code changes.
@ -818,24 +833,14 @@ class Reload(Setting):
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.
When using this option, you can optionally specify whether you would
like to use file system polling or the kernel's inotify API to watch
for changes. Generally, inotify should be preferred if available
because it consumes less system resources. If no preference is given,
inotify will attempted with a fallback to FS polling.'
'''
class ReloadInotify(Setting):
name = 'inotify'
section = 'Debugging'
cli = ['--use-inotify']
validator = validate_bool
action = "store_true"
default = False
desc = '''\
When using the 'reload' option, use the kernel's inotify APIs to watch
files instead of polling the filesystem. On many systems this could result
in a performance improvement when using 'reload'.
This setting must be used in conjunction with 'reload' and requires the
'inotify' package be installed from PyPI.
'''
class Spew(Setting):
name = "spew"

View File

@ -60,18 +60,20 @@ try:
except ImportError:
has_inotify = False
class InotifyReloader():
def __init__(self, callback=None):
raise ImportError('You must have the inotify module installed to use the INotify reloader')
raise ImportError('You must have the inotify module installed to use '
'the inotify reloader')
if has_inotify:
class InotifyReloader(threading.Thread):
event_mask = (inotify.constants.IN_CREATE | inotify.constants.IN_DELETE
| inotify.constants.IN_DELETE_SELF | inotify.constants.IN_MODIFY
| inotify.constants.IN_MOVE_SELF | inotify.constants.IN_MOVED_FROM
event_mask = (inotify.constants.IN_CREATE | inotify.constants.IN_DELETE
| inotify.constants.IN_DELETE_SELF | inotify.constants.IN_MODIFY
| inotify.constants.IN_MOVE_SELF | inotify.constants.IN_MOVED_FROM
| inotify.constants.IN_MOVED_TO)
def __init__(self, extra_files=None, callback=None):
super(InotifyReloader, self).__init__()
self.setDaemon(True)
@ -102,14 +104,14 @@ if has_inotify:
for dirname in self._dirs:
self._watcher.add_watch(dirname, mask=self.event_mask)
for event in self._watcher.event_gen():
if event is None:
continue
types = event[1]
filename = event[3]
self._callback(filename)
preferred_reloader = InotifyReloader if has_inotify else Reloader

View File

@ -14,7 +14,7 @@ import traceback
from gunicorn import util
from gunicorn.workers.workertmp import WorkerTmp
from gunicorn.reloader import Reloader, InotifyReloader, has_inotify
from gunicorn.reloader import preferred_reloader, Reloader, InotifyReloader
from gunicorn.http.errors import (
InvalidHeader, InvalidHeaderName, InvalidRequestLine, InvalidRequestMethod,
InvalidHTTPVersion, LimitRequestLine, LimitRequestHeaders,
@ -122,8 +122,14 @@ class Worker(object):
self.cfg.worker_int(self)
time.sleep(0.1)
sys.exit(0)
reloader_cls = Reloader if not self.cfg.inotify else InotifyReloader
if self.cfg.reload == 'poll':
reloader_cls = Reloader
elif self.cfg.reload == 'inotify':
reloader_cls = InotifyReloader
else:
reloader_cls = preferred_reloader
self.reloader = reloader_cls(callback=changed)
self.reloader.start()