diff --git a/gunicorn/config.py b/gunicorn/config.py index 02489a90..ff6c40ec 100644 --- a/gunicorn/config.py +++ b/gunicorn/config.py @@ -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" diff --git a/gunicorn/reloader.py b/gunicorn/reloader.py index b5e7679f..86f8f88f 100644 --- a/gunicorn/reloader.py +++ b/gunicorn/reloader.py @@ -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 diff --git a/gunicorn/workers/base.py b/gunicorn/workers/base.py index 887ad8d6..885ff6d4 100644 --- a/gunicorn/workers/base.py +++ b/gunicorn/workers/base.py @@ -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()