From 27d1e9887a09b0bf51dd62cb59e5d64c2ad89cde Mon Sep 17 00:00:00 2001 From: Stanis Trendelenburg Date: Sun, 2 Feb 2020 22:57:14 +0100 Subject: [PATCH] Fix issues #2133 and #2244 Start reloader after loading the WSGI app. --- gunicorn/workers/base.py | 5 +-- tests/test_reload.py | 68 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 tests/test_reload.py diff --git a/gunicorn/workers/base.py b/gunicorn/workers/base.py index 8e0129cf..a6d84bd2 100644 --- a/gunicorn/workers/base.py +++ b/gunicorn/workers/base.py @@ -117,8 +117,6 @@ class Worker(object): self.init_signals() - self.load_wsgi() - # start the reloader if self.cfg.reload: def changed(fname): @@ -132,6 +130,9 @@ class Worker(object): reloader_cls = reloader_engines[self.cfg.reload_engine] self.reloader = reloader_cls(extra_files=self.cfg.reload_extra_files, callback=changed) + + self.load_wsgi() + if self.reloader: self.reloader.start() self.cfg.post_worker_init(self) diff --git a/tests/test_reload.py b/tests/test_reload.py new file mode 100644 index 00000000..f5b18258 --- /dev/null +++ b/tests/test_reload.py @@ -0,0 +1,68 @@ +import unittest.mock as mock + +from gunicorn.app.base import Application +from gunicorn.workers.base import Worker +from gunicorn.reloader import reloader_engines + + +class ReloadApp(Application): + def __init__(self): + super().__init__("no usage", prog="gunicorn_test") + + def do_load_config(self): + self.load_default_config() + self.cfg.set('reload', True) + self.cfg.set('reload_engine', 'poll') + + +class SyntaxErrorApp(ReloadApp): + def wsgi(self): + error = SyntaxError('invalid syntax') + error.filename = 'syntax_error_filename' + raise error + + +class MyWorker(Worker): + def run(self): + pass + + +def test_reload_on_syntax_error(): + """ + Test that reloading works if the application has a syntax error. + """ + reloader = mock.Mock() + reloader_engines['poll'] = lambda *args, **kw: reloader + + app = SyntaxErrorApp() + cfg = app.cfg + log = mock.Mock() + worker = MyWorker(age=0, ppid=0, sockets=[], app=app, timeout=0, cfg=cfg, log=log) + + worker.init_process() + reloader.start.assert_called_with() + reloader.add_extra_file.assert_called_with('syntax_error_filename') + + +def test_start_reloader_after_load_wsgi(): + """ + Check that the reloader is started after the wsgi app has been loaded. + """ + reloader = mock.Mock() + reloader_engines['poll'] = lambda *args, **kw: reloader + + app = ReloadApp() + cfg = app.cfg + log = mock.Mock() + worker = MyWorker(age=0, ppid=0, sockets=[], app=app, timeout=0, cfg=cfg, log=log) + + worker.load_wsgi = mock.Mock() + mock_parent = mock.Mock() + mock_parent.attach_mock(worker.load_wsgi, 'load_wsgi') + mock_parent.attach_mock(reloader.start, 'reloader_start') + + worker.init_process() + mock_parent.assert_has_calls([ + mock.call.load_wsgi(), + mock.call.reloader_start(), + ])