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:
		
				
					committed by
					
						 Tim Graham
						Tim Graham
					
				
			
			
				
	
			
			
			
						parent
						
							e9cb333bc3
						
					
				
				
					commit
					15f82c7011
				
			| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user