發現啓動flask之後,會自動的重啓
代碼如下:
$ ./run.py
Now starting to run....
* Running on http://hostname:port/ (Press CTRL+C to quit)
* Restarting with stat
Now starting to run....
* Debugger is active!
* Debugger pin code: 234-130-353
run.py
app=Flask(__name__)
if __name__=='__main__':
print 'Now starting to run....'
app.run(host="hostname",
port=1234,
debug=True)
原因:
當調用app.run()的時候,用到了Werkzeug庫,它會生成一個子進程,當代碼有變動的時候它會自動重啓
如果在run()里加入參數 use_reloader=False,就會取消這個功能,當然,在以後的代碼改動後也不會自動更新了。
可以查看WERKZEUG_RUN_MAIN環境變量,
默認情況下,調用子程序時,它會被設置爲True。
if
__name__=='__main__':
print os.environ.get('WERKZEUG_RUN_MAIN')
if os.environ.get('WERKZEUG_RUN_MAIN') == 'True':
print '################### Restarting @ {} ###################'.format(
datetime.utcnow())
app.run(host="hostname",
port=1234,
debug=True)
結果:
$./run.py
None
* Running on hostname:1234/ (Press CTRL+C to quit)
* Restarting with stat
true
* Debugger is active!
* Debugger pin code: 234-130-353
當user_reloader設爲False後,
$
./run.py
None
* Running on hostname:1234/ (Press CTRL+C to quit)
附錄:
import os | |
import sys | |
import time | |
import subprocess | |
import threading | |
from itertools import chain | |
from werkzeug._internal import _log | |
from werkzeug._compat import PY2, iteritems, text_type | |
def _iter_module_files(): | |
"""This iterates over all relevant Python files. It goes through all | |
loaded files from modules, all files in folders of already loaded modules | |
as well as all files reachable through a package. | |
""" | |
# The list call is necessary on Python 3 in case the module | |
# dictionary modifies during iteration. | |
for module in list(sys.modules.values()): | |
if module is None: | |
continue | |
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 | |
def _find_observable_paths(extra_files=None): | |
"""Finds all paths that should be observed.""" | |
rv = set(os.path.abspath(x) for xin sys.path) | |
for filename in extra_files or (): | |
rv.add(os.path.dirname(os.path.abspath(filename))) | |
for module in list(sys.modules.values()): | |
fn = getattr(module, '__file__',None) | |
if fn is None: | |
continue | |
fn = os.path.abspath(fn) | |
rv.add(os.path.dirname(fn)) | |
return _find_common_roots(rv) | |
def _find_common_roots(paths): | |
"""Out of some paths it finds the common roots that need monitoring.""" | |
paths = [x.split(os.path.sep)for x in paths] | |
root = {} | |
for chunks in sorted(paths, key=len,reverse=True): | |
node = root | |
for chunk in chunks: | |
node = node.setdefault(chunk, {}) | |
node.clear() | |
rv = set() | |
def _walk(node, path): | |
for prefix, child in iteritems(node): | |
_walk(child, path + (prefix,)) | |
if not node: | |
rv.add('/'.join(path)) | |
_walk(root, ()) | |
return rv | |
class ReloaderLoop(object): | |
name = None | |
# monkeypatched by testsuite. wrapping with `staticmethod` is required in | |
# case time.sleep has been replaced by a non-c function (e.g. by | |
# `eventlet.monkey_patch`) before we get here | |
_sleep = staticmethod(time.sleep) | |
def __init__(self, extra_files=None,interval=1): | |
self.extra_files = set(os.path.abspath(x) | |
for x in extra_files or ()) | |
self.interval = interval | |
def run(self): | |
pass | |
def restart_with_reloader(self): | |
"""Spawn a new Python interpreter with the same arguments as this one, | |
but running the reloader thread. | |
""" | |
while 1: | |
_log('info',' * Restarting with%s'% self.name) | |
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'and PY2: | |
for key, value in iteritems(new_environ): | |
if isinstance(value, text_type): | |
new_environ[key] = value.encode('iso-8859-1') | |
exit_code = subprocess.call(args,env=new_environ) | |
if exit_code != 3: | |
return exit_code | |
def trigger_reload(self,filename): | |
self.log_reload(filename) | |
sys.exit(3) | |
def log_reload(self, filename): | |
filename = os.path.abspath(filename) | |
_log('info',' * Detected change in%r, reloading'% filename) | |
class StatReloaderLoop(ReloaderLoop): | |
name = 'stat' | |
def run(self): | |
mtimes = {} | |
while 1: | |
for filename in chain(_iter_module_files(), | |
self.extra_files): | |
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: | |
self.trigger_reload(filename) | |
self._sleep(self.interval) | |
class WatchdogReloaderLoop(ReloaderLoop): | |
def __init__(self, *args, **kwargs): | |
ReloaderLoop.__init__(self,*args, **kwargs) | |
from watchdog.observers import Observer | |
from watchdog.events import FileSystemEventHandler | |
self.observable_paths = set() | |
def _check_modification(filename): | |
if filename in self.extra_files: | |
self.trigger_reload(filename) | |
dirname = os.path.dirname(filename) | |
if dirname.startswith(tuple(self.observable_paths)): | |
if filename.endswith(('.pyc','.pyo')): | |
self.trigger_reload(filename[:-1]) | |
elif filename.endswith('.py'): | |
self.trigger_reload(filename) | |
class _CustomHandler(FileSystemEventHandler): | |
def on_created(self, event): | |
_check_modification(event.src_path) | |
def on_modified(self, event): | |
_check_modification(event.src_path) | |
def on_moved(self, event): | |
_check_modification(event.src_path) | |
_check_modification(event.dest_path) | |
def on_deleted(self, event): | |
_check_modification(event.src_path) | |
reloader_name = Observer.__name__.lower() | |
if reloader_name.endswith('observer'): | |
reloader_name = reloader_name[:-8] | |
reloader_name += ' reloader' | |
self.name = reloader_name | |
self.observer_class = Observer | |
self.event_handler = _CustomHandler() | |
self.should_reload = False | |
def trigger_reload(self,filename): | |
# This is called inside an event handler, which means throwing | |
# SystemExit has no effect. | |
# https://github.com/gorakhargosh/watchdog/issues/294 | |
self.should_reload = True | |
self.log_reload(filename) | |
def run(self): | |
watches = {} | |
observer = self.observer_class() | |
observer.start() | |
while not self.should_reload: | |
to_delete = set(watches) | |
paths = _find_observable_paths(self.extra_files) | |
for path in paths: | |
if path not in watches: | |
try: | |
watches[path] = observer.schedule( | |
self.event_handler, path,recursive=True) | |
except OSError as e: | |
message = str(e) | |
if message != "Path is not a directory": | |
# Log the exception | |
_log('error', message) | |
# Clear this path from list of watches We don't want | |
# the same error message showing again in the next | |
# iteration. | |
watches[path] = None | |
to_delete.discard(path) | |
for path in to_delete: | |
watch = watches.pop(path, None) | |
if watch is not None: | |
observer.unschedule(watch) | |
self.observable_paths = paths | |
self._sleep(self.interval) | |
sys.exit(3) | |
reloader_loops = { | |
'stat': StatReloaderLoop, | |
'watchdog': WatchdogReloaderLoop, | |
} | |
try: | |
__import__('watchdog.observers') | |
except ImportError: | |
reloader_loops['auto']= reloader_loops['stat'] | |
else: | |
reloader_loops['auto']= reloader_loops['watchdog'] | |
def run_with_reloader(main_func,extra_files=None,interval=1, | |
reloader_type='auto'): | |
"""Run the given function in an independent python interpreter.""" | |
import signal | |
reloader = reloader_loops[reloader_type](extra_files, interval) | |
signal.signal(signal.SIGTERM,lambda *args: sys.exit(0)) | |
try: | |
if os.environ.get('WERKZEUG_RUN_MAIN')== 'true': | |
t = threading.Thread(target=main_func,args=()) | |
t.setDaemon(True) | |
t.start() | |
reloader.run() | |
else: | |
sys.exit(reloader.restart_with_reloader()) | |
except KeyboardInterrupt: | |
pass |