Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

config: permit --reload-extra-files without --reload #3271

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion docs/source/settings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,10 @@ The default behavior is to attempt inotify with a fallback to file
system polling. Generally, inotify should be preferred if available
because it consumes less system resources.

.. note::
If the application fails to load while this option is used,
the (potentially sensitive!) traceback will be shared in
the response to subsequent HTTP requests.
.. note::
In order to use the inotify reloader, you must have the ``inotify``
package installed.
Expand Down Expand Up @@ -114,10 +118,13 @@ Valid engines are:

**Default:** ``[]``

Extends :ref:`reload` option to also watch and reload on additional files
Alternative or extension to :ref:`reload` option to (also) watch
and reload on additional files
(e.g., templates, configurations, specifications, etc.).

.. versionadded:: 19.8
.. versionchanged:: 23.FIXME
Option no longer silently ignored if used without :ref:`reload`.

.. _spew:

Expand Down
9 changes: 8 additions & 1 deletion gunicorn/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,6 +921,10 @@ class Reload(Setting):
system polling. Generally, inotify should be preferred if available
because it consumes less system resources.

.. note::
If the application fails to load while this option is used,
the (potentially sensitive!) traceback will be shared in
the response to subsequent HTTP requests.
.. note::
In order to use the inotify reloader, you must have the ``inotify``
package installed.
Expand Down Expand Up @@ -956,10 +960,13 @@ class ReloadExtraFiles(Setting):
validator = validate_list_of_existing_files
default = []
desc = """\
Extends :ref:`reload` option to also watch and reload on additional files
Alternative or extension to :ref:`reload` option to (also) watch
and reload on additional files
(e.g., templates, configurations, specifications, etc.).

.. versionadded:: 19.8
.. versionchanged:: 23.FIXME
Option no longer silently ignored if used without :ref:`reload`.
"""


Expand Down
15 changes: 12 additions & 3 deletions gunicorn/reloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,21 @@


class Reloader(threading.Thread):
def __init__(self, extra_files=None, interval=1, callback=None):
def __init__(self, extra_files=None, interval=1, callback=None, auto_detect=False):
super().__init__()
self.daemon = True
self._extra_files = set(extra_files or ())
self._interval = interval
self._callback = callback
self._auto_detect = auto_detect

def add_extra_file(self, filename):
self._extra_files.add(filename)

def get_files(self):
if not self._auto_detect:
return self._extra_files

fnames = [
COMPILED_EXT_RE.sub('py', module.__file__)
for module in tuple(sys.modules.values())
Expand Down Expand Up @@ -71,12 +75,13 @@ class InotifyReloader(threading.Thread):
| inotify.constants.IN_MOVE_SELF | inotify.constants.IN_MOVED_FROM
| inotify.constants.IN_MOVED_TO)

def __init__(self, extra_files=None, callback=None):
def __init__(self, extra_files=None, callback=None, auto_detect=False):
super().__init__()
self.daemon = True
self._callback = callback
self._dirs = set()
self._watcher = Inotify()
self._auto_detect = auto_detect

for extra_file in extra_files:
self.add_extra_file(extra_file)
Expand All @@ -91,6 +96,9 @@ def add_extra_file(self, filename):
self._dirs.add(dirname)

def get_dirs(self):
if not self._auto_detect:
return set()

fnames = [
os.path.dirname(os.path.abspath(COMPILED_EXT_RE.sub('py', module.__file__)))
for module in tuple(sys.modules.values())
Expand All @@ -100,6 +108,7 @@ def get_dirs(self):
return set(fnames)

def run(self):
# FIXME: _watchers/_dirs inconsistent - latter gets reset
self._dirs = self.get_dirs()

for dirname in self._dirs:
Expand All @@ -117,7 +126,7 @@ def run(self):
else:

class InotifyReloader:
def __init__(self, extra_files=None, callback=None):
def __init__(self, extra_files=None, callback=None, auto_detect=False):
raise ImportError('You must have the inotify module installed to '
'use the inotify reloader')

Expand Down
4 changes: 2 additions & 2 deletions gunicorn/workers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def init_process(self):
self.init_signals()

# start the reloader
if self.cfg.reload:
if self.cfg.reload or self.cfg.reload_extra_files:
def changed(fname):
self.log.info("Worker reloading: %s modified", fname)
self.alive = False
Expand All @@ -130,7 +130,7 @@ def changed(fname):

reloader_cls = reloader_engines[self.cfg.reload_engine]
self.reloader = reloader_cls(extra_files=self.cfg.reload_extra_files,
callback=changed)
callback=changed, auto_detect=self.cfg.reload)

self.load_wsgi()
if self.reloader:
Expand Down
Loading