Skip to content

Commit

Permalink
Add support for --reload-engine
Browse files Browse the repository at this point in the history
Currently, gunicorn automatically uses the preferred reloader (inotify
if present with fallback to polling). However, it would be useful in
some scenarios if users could force polling.

The solution for this is to add a new configuration option called
'reload_engine' which takes one of three options: ['auto', 'poll',
'inotify'].

Fixes benoitc#1459
  • Loading branch information
mark-adams committed Feb 14, 2017
1 parent 34a624f commit 9c70249
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 2 deletions.
28 changes: 28 additions & 0 deletions gunicorn/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from gunicorn import __version__
from gunicorn import _compat
from gunicorn.errors import ConfigError
from gunicorn.reloader import reloader_engines
from gunicorn import six
from gunicorn import util

Expand Down Expand Up @@ -496,6 +497,13 @@ def validate_hostport(val):
raise TypeError("Value must consist of: hostname:port")


def validate_reload_engine(val):
if val not in reloader_engines:
raise ConfigError("Invalid reload_engine: %r" % val)

return val


def get_default_config_file():
config_path = os.path.join(os.path.abspath(os.getcwd()),
'gunicorn.conf.py')
Expand Down Expand Up @@ -838,6 +846,26 @@ class Reload(Setting):
'''


class ReloadEngine(Setting):
name = "reload_engine"
section = "Debugging"
cli = ["--reload-engine"]
meta = "STRING"
validator = validate_reload_engine
default = "auto"
desc = """\
The implementation that should be used to power :ref:`reload`.
Valid engines are:
* 'auto'
* 'poll'
* 'inotify' (requires inotify)
.. versionadded:: 19.7
"""


class Spew(Setting):
name = "spew"
section = "Debugging"
Expand Down
6 changes: 6 additions & 0 deletions gunicorn/reloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,9 @@ def run(self):


preferred_reloader = InotifyReloader if has_inotify else Reloader

reloader_engines = {
'auto': preferred_reloader,
'poll': Reloader,
'inotify': InotifyReloader,
}
5 changes: 3 additions & 2 deletions gunicorn/workers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from gunicorn import six
from gunicorn import util
from gunicorn.workers.workertmp import WorkerTmp
from gunicorn.reloader import preferred_reloader
from gunicorn.reloader import reloader_engines
from gunicorn.http.errors import (
InvalidHeader, InvalidHeaderName, InvalidRequestLine, InvalidRequestMethod,
InvalidHTTPVersion, LimitRequestLine, LimitRequestHeaders,
Expand Down Expand Up @@ -119,7 +119,8 @@ def changed(fname):
time.sleep(0.1)
sys.exit(0)

self.reloader = preferred_reloader(callback=changed)
reloader_cls = reloader_engines[self.cfg.reload_engine]
self.reloader = reloader_cls(callback=changed)
self.reloader.start()

self.load_wsgi()
Expand Down
12 changes: 12 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from gunicorn import config
from gunicorn.app.base import Application
from gunicorn.errors import ConfigError
from gunicorn.workers.sync import SyncWorker
from gunicorn import glogging
from gunicorn.instrument import statsd
Expand Down Expand Up @@ -152,6 +153,17 @@ def func(a, b):
pytest.raises(TypeError, c.set, "pre_fork", lambda x: True)


def test_reload_engine_validation():
c = config.Config()

assert c.reload_engine == "auto"

c.set('reload_engine', 'poll')
assert c.reload_engine == 'poll'

pytest.raises(ConfigError, c.set, "reload_engine", "invalid")


def test_callable_validation_for_string():
from os.path import isdir as testfunc
assert config.validate_callable(-1)("os.path.isdir") == testfunc
Expand Down

0 comments on commit 9c70249

Please sign in to comment.