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



發佈了47 篇原創文章 · 獲贊 32 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章