Tornado「debug=True」動的コンパイル


日曜日は珍しく暇で、ちょうどこの間先生が配置したホームページの宿題の中でdebugの動的自動コンパイルを使って、ずっとどのように仕事をしているのか分からなかったので、googleはtornadoの英語の公式サイトを利用して、動的自動コンパイルに関するソースコードを見ました.ソースの注釈が多くて、多分原理が分かったので、今私の見解を分かち合いましょう.間違いがあったら教えてください.
まずアプリケーションオブジェクトの構造関数を見てみましょう
def __init__(self, handlers=None, default_host="", transforms=None,**settings):

ここで*settingsではdebug=Trueに転送して動的コンパイルを有効にすることができます.以下のようにします.
    __app__ = tornado.web.Application(
        handlers=[(r'/', MainHandler)]
        debug=True
    )
が送信されると、アプリケーションコンストラクション関数で次のコードが実行されます.
 if self.settings.get('debug'):
     self.settings.setdefault('autoreload', True)
     self.settings.setdefault('compiled_template_cache', False)
     self.settings.setdefault('static_hash_cache', False)
     self.settings.setdefault('serve_traceback', True)

 # Automatically reload modified modules
 if self.settings.get('autoreload'):
     from tornado import autoreload
     autoreload.start()
では、debugがTrueに設定された後、autoreload、complied_template_cache、static_hash_cache、serve_tracebackはそれぞれ値を割り当てられる.そのうちcomplied_template_Cacheのデフォルト値はTrueで、Falseに変更すると、テンプレートは各リクエストを受け入れると再コンパイルされます.static_hash_Cacheのデフォルト値はTrueで、Falseに変更されるとurlは各リクエストを受信すると再ロードされます.serve_tracebackは異常追跡と関係があるはずです.
autoreloadがTrueに設定された後、次のif文が実行されます.ここで、autoreload関数start()が呼び出されます.注意autoreloadオブジェクトは生成されません.autoreloadはclassではなく、機能関数セットのヘッダドメインのように見えます.では、この情報を追跡して、start()の中で神馬のことをしたのに動的にコンパイルできるのかを見てみましょう.
それで見つけたAutoreloadのソースコード、start()関数を見てみましょう.
_watched_files = set()
_reload_hooks = []
_reload_attempted = False
_io_loops = weakref.WeakKeyDictionary()

def start(io_loop=None, check_time=500):
    """Begins watching source files for changes using the given `.IOLoop`. """
    io_loop = io_loop or ioloop.IOLoop.current()
    if io_loop in _io_loops:
        return
    _io_loops[io_loop] = True
    if len(_io_loops) > 1:
        gen_log.warning("tornado.autoreload started more than once in the same process")
    add_reload_hook(functools.partial(io_loop.close, all_fds=True))
    modify_times = {}
    callback = functools.partial(_reload_on_update, modify_times)
    scheduler = ioloop.PeriodicCallback(callback, check_time, io_loop=io_loop)
    scheduler.start()
ここでstartには2つのデフォルトパラメータがあるので、上は直接autoreload.start()はこのように呼び出されます.io_loopは現在のio_を割り当てますloopオブジェクト(io_loopオブジェクトはtornadoが高性能と高同時性を実現するためにsocket読み書きイベントを処理する)である.前の文は同じioが複数あるかどうかを判断するために使われているはずです.loopが有効になっています(これはよくわかりませんが、理解にはあまり影響しません).次にadd_を呼び出したreload_hook関数は、そのパラメータとしてバイアス関数を渡します.
ほほほ、偏関数とは何かを聞くに違いない.簡単に言えば、パラメータを事前に関数に渡し、呼び出し可能な関数を返すことです.例:
from operator import add
import functools
print add(1,2) #3
add1 = functools.partial(add,1)
print add1(10) #11
画面からadd_へジャンプreload_hook関数:
def add_reload_hook(fn):
    """Add a function to be called before reloading the process.
    """
    _reload_hooks.append(fn)
私*は、意外にも1つの追加文しかありません.私も酔っ払っています.でもコメントが肝心!説明関数は_に保存されます.reload_hooksリストには、後で取り出すために呼び出されます.
ではstart()関数に戻ります.次はmodifyを初期化しますtimes辞書は、名前を見るとドキュメントの修正時間を記録するために使われていると推測し、その後、確かにそうであることを証明した.次に、もう1つのバイアス関数callbackが定義され、呼び出しcallback()は呼び出し_に相当する.reload_on_update(modify_times).すると画面はまた_reload_on_update関数:
def _reload_on_update(modify_times):
    if _reload_attempted:
        # We already tried to reload and it didn't work, so don't try again.
        return
    if process.task_id() is not None:
        # We're in a child process created by fork_processes.  If child
        # processes restarted themselves, they'd all restart and then
        # all call fork_processes again.
        return
    for module in sys.modules.values():
        # Some modules play games with sys.modules (e.g. email/__init__.py
        # in the standard library), and occasionally this can cause strange
        # failures in getattr.  Just ignore anything that's not an ordinary
        # module.
        if not isinstance(module, types.ModuleType):
            continue
        path = getattr(module, "__file__", None)
        if not path:
            continue
        if path.endswith(".pyc") or path.endswith(".pyo"):
            path = path[:-1]
        _check_file(modify_times, path)
    for path in _watched_files:
        _check_file(modify_times, path)

例によれば、前のいくつかの判断は、いくつかの重複や誤った操作を防止するためである.sys.module.values()は、すべてのシステムがインポートしたモジュールを返し、pathは現在のモジュールのパスを取得します.通常はcontinueジャンプループに遭遇しないため、_が呼び出されます.check_file関数.画面が再び転送ドアを開きます.
def _check_file(modify_times, path):
    try:
        modified = os.stat(path).st_mtime
    except Exception:
        return
    if path not in modify_times:
        modify_times[path] = modified
        return
    if modify_times[path] != modified:
        gen_log.info("%s modified; restarting server", path)
        _reload()
ハハ!modifiedが現在のモジュールパスの変更時間を取得しているのを見ました.次の2つの判断は重要です!1つ目の判断は、現在のモジュールがmodify_にあるか否かを判断することである.times辞書では、モジュールが新しい場合は、辞書に追加して変更時間を割り当てます.2つ目の判断は,既存のモジュールが再修正されたか否かを判断することである.例えば初めて修正しました.pyファイルは3:00:00に最初の判断をトリガーし、2回目の修正が3:00:10になると2番目の判断がトリガーされます.変更時間が変更されたため、「○○モジュールパスが変更され、サーバーを再起動しています」という情報が画面に印刷され、実行されます.reload().画面遷移:
def _reload():
    global _reload_attempted
    _reload_attempted = True
    for fn in _reload_hooks:
        fn()
    if hasattr(signal, "setitimer"):
        # Clear the alarm signal set by
        # ioloop.set_blocking_log_threshold so it doesn't fire
        # after the exec.
        signal.setitimer(signal.ITIMER_REAL, 0, 0)
    # sys.path fixes: see comments at top of file.  If sys.path[0] is an empty
    # string, we were (probably) invoked with -m and the effective path
    # is about to change on re-exec.  Add the current directory to $PYTHONPATH
    # to ensure that the new process sees the same path we did.
    path_prefix = '.' + os.pathsep
    if (sys.path[0] == '' and
            not os.environ.get("PYTHONPATH", "").startswith(path_prefix)):
        os.environ["PYTHONPATH"] = (path_prefix +
                                    os.environ.get("PYTHONPATH", ""))
    if sys.platform == 'win32':
        # os.execv is broken on Windows and can't properly parse command line
        # arguments and executable name if they contain whitespaces. subprocess
        # fixes that behavior.
        subprocess.Popen([sys.executable] + sys.argv)
        sys.exit(0)
    else:
        try:
            os.execv(sys.executable, [sys.executable] + sys.argv)
        except OSError:
            # Mac OS X versions prior to 10.6 do not support execv in
            # a process that contains multiple threads.  Instead of
            # re-executing in the current process, start a new one
            # and cause the current process to exit.  This isn't
            # ideal since the new process is detached from the parent
            # terminal and thus cannot easily be killed with ctrl-C,
            # but it's better than not being able to autoreload at
            # all.
            # Unfortunately the errno returned in this case does not
            # appear to be consistent, so we can't easily check for
            # this error specifically.
            os.spawnv(os.P_NOWAIT, sys.executable,
                      [sys.executable] + sys.argv)
            sys.exit(0)
長さを嫌ってはいけません.ほとんどが注釈です.ここでまず_reload_attemptedはTrueのために割り当てられており、再コンパイルを試みたことをお伝えします.以前は保存されていましたreload_hooksのバイアス関数
functools.partial(io_loop.close, all_fds=True)
 被取出来调用了,关闭了之前的io_loop。然后的一串代码我无法解释,不过我猜测应该是保证编译路径的正确性,因为在这过程中用户很可能改变了文件的位置甚至是编译环境。最后就是判断操作平台,然后执行编译命令,代码于是被重新编译。 
  
 

咳咳,你以为这样就完了?其实这只是start()里一条偏函数引出来的一串函数而已,我们还得再回到start()函数。接下来有这么两条语句:

scheduler = ioloop.PeriodicCallback(callback, check_time, io_loop=io_loop)
scheduler.start()
これは非常によく理解されており、scheduleは時刻表の意味であり、PeriodicCallbackはその名の通り周期的な呼び出しの意味である.つまりshedulerですstart()以降、プログラムはcheck_ごとにtime時間はcallback()関数を呼び出し、さっきの一連の関数を実行し、関連するファイルが変更されると、すべての動作を停止し、コードを再コンパイルします.そこで、ダイナミックな自動コンパイル機能が実現しました!
もちろん、詳しく知りたい子供靴は、この転送ドアからtornado公式サイトにアクセスして、100%オリジナルのソースコードを表示することができます.
http://www.tornadoweb.org/en/stable/_modules/tornado/web.html#Application http://www.tornadoweb.org/en/stable/_modules/tornado/autoreload.html#add_reload_hook
もう一度説明しますが、この文章は個人的な見解にすぎません.もし不適切なところや間違ったところがあれば、皆さんが積極的に指摘してください.
よし、もうすぐ0時だから、洗濯して寝るよ~