Flask использует базовую функцию Werkzug run_with_reloader
(найденную в serving.py
) ..., которая сама использует функции restart_with_reloader
и reloader_loop
, созданные ранее в том же файле.
run_with_reloader
порождает другой процесс python (снова запускает Werkzug со всеми теми же аргументами, которые вы передали первому), и этот новый процесс использует модуль thread
для порождения нового потока или подпроцесса, который запускает вашсерверная функция.Затем он запускает reloader_loop
и ждет.
reloader_loop
просто проходит по всем импортированным модулям и получает даты их последнего изменения.Затем с заданными интервалами (по умолчанию 1 с) он снова проверяет все файлы, чтобы увидеть, были ли они изменены.Если они есть, текущий запущенный ( slave ) процесс Werkzug завершается (завершается) с кодом выхода 3. Как только он выходит, поток или подпроцесс, который он запустил (который фактически выполняет работу), завершается какЧто ж.Процесс master проверяет, был ли код выхода равен 3. Если это так, он запускает новый подпроцесс slave , как и раньше.В противном случае он завершается с тем же кодом выхода.
Вот код для справки:
def reloader_loop(extra_files=None, interval=1):
"""When this function is run from the main thread, it will force other
threads to exit when any modules currently loaded change.
Copyright notice. This function is based on the autoreload.py from
the CherryPy trac which originated from WSGIKit which is now dead.
:param extra_files: a list of additional files it should watch.
"""
def iter_module_files():
for module in sys.modules.values():
filename = getattr(module, '__file__', None)
if filename:
old = None
while not os.path.isfile(filename):
old = filename
filename = os.path.dirname(filename)
if filename == old:
break
else:
if filename[-4:] in ('.pyc', '.pyo'):
filename = filename[:-1]
yield filename
mtimes = {}
while 1:
for filename in chain(iter_module_files(), extra_files or ()):
try:
mtime = os.stat(filename).st_mtime
except OSError:
continue
old_time = mtimes.get(filename)
if old_time is None:
mtimes[filename] = mtime
continue
elif mtime > old_time:
_log('info', ' * Detected change in %r, reloading' % filename)
sys.exit(3)
time.sleep(interval)
def restart_with_reloader():
"""Spawn a new Python interpreter with the same arguments as this one,
but running the reloader thread.
"""
while 1:
_log('info', ' * Restarting with reloader...')
args = [sys.executable] + sys.argv
new_environ = os.environ.copy()
new_environ['WERKZEUG_RUN_MAIN'] = 'true'
# a weird bug on windows. sometimes unicode strings end up in the
# environment and subprocess.call does not like this, encode them
# to latin1 and continue.
if os.name == 'nt':
for key, value in new_environ.iteritems():
if isinstance(value, unicode):
new_environ[key] = value.encode('iso-8859-1')
exit_code = subprocess.call(args, env=new_environ)
if exit_code != 3:
return exit_code
def run_with_reloader(main_func, extra_files=None, interval=1):
"""Run the given function in an independent python interpreter."""
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
thread.start_new_thread(main_func, ())
try:
reloader_loop(extra_files, interval)
except KeyboardInterrupt:
return
try:
sys.exit(restart_with_reloader())
except KeyboardInterrupt:
pass