Flask-为什么会启动两次

发现启动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




版权声明:本文为u014102846原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。