mirror of https://github.com/django/django.git
Fixed #9722 - used pyinotify as change detection system when available
Used pyinotify (when available) to replace the "pool-every-one-second" mechanism in `django.utils.autoreload`. Thanks Chris Lamb and Pascal Hartig for work on the patch.
This commit is contained in:
parent
e9cb333bc3
commit
15f82c7011
3
AUTHORS
3
AUTHORS
|
@ -286,6 +286,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Will Hardy <django@willhardy.com.au>
|
||||
Brian Harring <ferringb@gmail.com>
|
||||
Brant Harris
|
||||
Pascal Hartig <phartig@rdrei.net>
|
||||
Ronny Haryanto <http://ronny.haryan.to/>
|
||||
Axel Haustant <noirbizarre@gmail.com>
|
||||
Hawkeye
|
||||
|
@ -372,6 +373,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Vladimir Kuzma <vladimirkuzma.ch@gmail.com>
|
||||
Denis Kuzmichyov <kuzmichyov@gmail.com>
|
||||
Panos Laganakos <panos.laganakos@gmail.com>
|
||||
Chris Lamb <lamby@debian.org>
|
||||
Nick Lane <nick.lane.au@gmail.com>
|
||||
Łukasz Langa <lukasz@langa.pl>
|
||||
Stuart Langridge <http://www.kryogenix.org/>
|
||||
|
@ -666,6 +668,7 @@ answer newbie questions, and generally made Django that much better:
|
|||
Jesse Young <adunar@gmail.com>
|
||||
Marc Aymerich Gubern
|
||||
Wiktor Kołodziej <wiktor@pykonik.org>
|
||||
Unai Zalakain <unai@gisa-elkartea.org>
|
||||
Mykola Zamkovoi <nickzam@gmail.com>
|
||||
zegor
|
||||
Gasper Zejn <zejn@kiberpipa.org>
|
||||
|
|
|
@ -28,12 +28,14 @@
|
|||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from django.core.signals import request_finished
|
||||
try:
|
||||
from django.utils.six.moves import _thread as thread
|
||||
except ImportError:
|
||||
|
@ -51,6 +53,18 @@ try:
|
|||
except ImportError:
|
||||
termios = None
|
||||
|
||||
USE_INOTIFY = False
|
||||
try:
|
||||
# Test whether inotify is enabled and likely to work
|
||||
import pyinotify
|
||||
|
||||
fd = pyinotify.INotifyWrapper.create().inotify_init()
|
||||
if fd >= 0:
|
||||
USE_INOTIFY = True
|
||||
os.close(fd)
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
RUN_RELOADER = True
|
||||
|
||||
_mtimes = {}
|
||||
|
@ -58,14 +72,13 @@ _win = (sys.platform == "win32")
|
|||
|
||||
_error_files = []
|
||||
|
||||
def code_changed():
|
||||
global _mtimes, _win
|
||||
filenames = []
|
||||
for m in list(sys.modules.values()):
|
||||
try:
|
||||
filenames.append(m.__file__)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def gen_filenames():
|
||||
"""
|
||||
Yields a generator over filenames referenced in sys.modules.
|
||||
"""
|
||||
filenames = [filename.__file__ for filename in sys.modules.values()
|
||||
if hasattr(filename, '__file__')]
|
||||
for filename in filenames + _error_files:
|
||||
if not filename:
|
||||
continue
|
||||
|
@ -73,8 +86,42 @@ def code_changed():
|
|||
filename = filename[:-1]
|
||||
if filename.endswith("$py.class"):
|
||||
filename = filename[:-9] + ".py"
|
||||
if not os.path.exists(filename):
|
||||
continue # File might be in an egg, so it can't be reloaded.
|
||||
if os.path.exists(filename):
|
||||
yield filename
|
||||
|
||||
def inotify_code_changed():
|
||||
"""
|
||||
Checks for changed code using inotify. After being called
|
||||
it blocks until a change event has been fired.
|
||||
"""
|
||||
wm = pyinotify.WatchManager()
|
||||
notifier = pyinotify.Notifier(wm)
|
||||
|
||||
def update_watch(sender=None, **kwargs):
|
||||
mask = (
|
||||
pyinotify.IN_MODIFY |
|
||||
pyinotify.IN_DELETE |
|
||||
pyinotify.IN_ATTRIB |
|
||||
pyinotify.IN_MOVED_FROM |
|
||||
pyinotify.IN_MOVED_TO |
|
||||
pyinotify.IN_CREATE
|
||||
)
|
||||
for path in gen_filenames():
|
||||
wm.add_watch(path, mask)
|
||||
|
||||
request_finished.connect(update_watch)
|
||||
update_watch()
|
||||
|
||||
# Block forever
|
||||
notifier.check_events(timeout=None)
|
||||
notifier.stop()
|
||||
|
||||
# If we are here the code must have changed.
|
||||
return True
|
||||
|
||||
def code_changed():
|
||||
global _mtimes, _win
|
||||
for filename in gen_filenames():
|
||||
stat = os.stat(filename)
|
||||
mtime = stat.st_mtime
|
||||
if _win:
|
||||
|
@ -129,11 +176,16 @@ def ensure_echo_on():
|
|||
|
||||
def reloader_thread():
|
||||
ensure_echo_on()
|
||||
if USE_INOTIFY:
|
||||
fn = inotify_code_changed
|
||||
else:
|
||||
fn = code_changed
|
||||
while RUN_RELOADER:
|
||||
if code_changed():
|
||||
if fn():
|
||||
sys.exit(3) # force reload
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def restart_with_reloader():
|
||||
while True:
|
||||
args = [sys.executable] + ['-W%s' % o for o in sys.warnoptions] + sys.argv
|
||||
|
|
|
@ -794,6 +794,18 @@ needed. You don't need to restart the server for code changes to take effect.
|
|||
However, some actions like adding files or compiling translation files don't
|
||||
trigger a restart, so you'll have to restart the server in these cases.
|
||||
|
||||
If you are using Linux and install `pyinotify`_, kernel signals will be used to
|
||||
autoreload the server (rather than polling file modification timestamps each
|
||||
second). This offers better scaling to large projects, reduction in response
|
||||
time to code modification, more robust change detection, and battery usage
|
||||
reduction.
|
||||
|
||||
.. _pyinotify: https://pypi.python.org/pypi/pyinotify/
|
||||
|
||||
.. versionadded:: 1.7
|
||||
|
||||
``pyinotify`` support was added.
|
||||
|
||||
When you start the server, and each time you change Python code while the
|
||||
server is running, the server will validate all of your installed models. (See
|
||||
the ``validate`` command below.) If the validator finds errors, it will print
|
||||
|
|
|
@ -343,6 +343,9 @@ Management Commands
|
|||
Django takes this information from your settings file. If you have configured
|
||||
multiple caches or multiple databases, all cache tables are created.
|
||||
|
||||
* The :djadmin:`runserver` command now uses ``inotify`` Linux kernel signals
|
||||
for autoreloading if ``pyinotify`` is installed.
|
||||
|
||||
Models
|
||||
^^^^^^
|
||||
|
||||
|
|
Loading…
Reference in New Issue