1
0
mirror of https://github.com/django/django.git synced 2025-10-31 09:41:08 +00:00

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:
Unai Zalakain
2013-10-14 07:33:45 +02:00
committed by Tim Graham
parent e9cb333bc3
commit 15f82c7011
4 changed files with 81 additions and 11 deletions

View File

@@ -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