app/django/utils/autoreload.py
changeset 54 03e267d67478
child 323 ff1a9aa48cfd
equal deleted inserted replaced
53:57b4279d8c4e 54:03e267d67478
       
     1 # Autoreloading launcher.
       
     2 # Borrowed from Peter Hunt and the CherryPy project (http://www.cherrypy.org).
       
     3 # Some taken from Ian Bicking's Paste (http://pythonpaste.org/).
       
     4 #
       
     5 # Portions copyright (c) 2004, CherryPy Team (team@cherrypy.org)
       
     6 # All rights reserved.
       
     7 #
       
     8 # Redistribution and use in source and binary forms, with or without modification,
       
     9 # are permitted provided that the following conditions are met:
       
    10 #
       
    11 #     * Redistributions of source code must retain the above copyright notice,
       
    12 #       this list of conditions and the following disclaimer.
       
    13 #     * Redistributions in binary form must reproduce the above copyright notice,
       
    14 #       this list of conditions and the following disclaimer in the documentation
       
    15 #       and/or other materials provided with the distribution.
       
    16 #     * Neither the name of the CherryPy Team nor the names of its contributors
       
    17 #       may be used to endorse or promote products derived from this software
       
    18 #       without specific prior written permission.
       
    19 #
       
    20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
       
    21 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
       
    22 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
       
    23 # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
       
    24 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
       
    25 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
       
    26 # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
       
    27 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
       
    28 # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    29 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    30 
       
    31 import os, sys, time
       
    32 
       
    33 try:
       
    34     import thread
       
    35 except ImportError:
       
    36     import dummy_thread as thread
       
    37 
       
    38 # This import does nothing, but it's necessary to avoid some race conditions
       
    39 # in the threading module. See http://code.djangoproject.com/ticket/2330 .
       
    40 try:
       
    41     import threading
       
    42 except ImportError:
       
    43     pass
       
    44 
       
    45 
       
    46 RUN_RELOADER = True
       
    47 
       
    48 def reloader_thread():
       
    49     mtimes = {}
       
    50     win = (sys.platform == "win32")
       
    51     while RUN_RELOADER:
       
    52         for filename in filter(lambda v: v, map(lambda m: getattr(m, "__file__", None), sys.modules.values())):
       
    53             if filename.endswith(".pyc") or filename.endswith("*.pyo"):
       
    54                 filename = filename[:-1]
       
    55             if not os.path.exists(filename):
       
    56                 continue # File might be in an egg, so it can't be reloaded.
       
    57             stat = os.stat(filename)
       
    58             mtime = stat.st_mtime
       
    59             if win:
       
    60                 mtime -= stat.st_ctime
       
    61             if filename not in mtimes:
       
    62                 mtimes[filename] = mtime
       
    63                 continue
       
    64             if mtime != mtimes[filename]:
       
    65                 sys.exit(3) # force reload
       
    66         time.sleep(1)
       
    67 
       
    68 def restart_with_reloader():
       
    69     while True:
       
    70         args = [sys.executable] + sys.argv
       
    71         if sys.platform == "win32":
       
    72             args = ['"%s"' % arg for arg in args]
       
    73         new_environ = os.environ.copy()
       
    74         new_environ["RUN_MAIN"] = 'true'
       
    75         exit_code = os.spawnve(os.P_WAIT, sys.executable, args, new_environ)
       
    76         if exit_code != 3:
       
    77             return exit_code
       
    78 
       
    79 def main(main_func, args=None, kwargs=None):
       
    80     if os.environ.get("RUN_MAIN") == "true":
       
    81         if args is None:
       
    82             args = ()
       
    83         if kwargs is None:
       
    84             kwargs = {}
       
    85         thread.start_new_thread(main_func, args, kwargs)
       
    86         try:
       
    87             reloader_thread()
       
    88         except KeyboardInterrupt:
       
    89             pass
       
    90     else:
       
    91         try:
       
    92             sys.exit(restart_with_reloader())
       
    93         except KeyboardInterrupt:
       
    94             pass