Django 的 runserver 如何實作 autoreload

做完上篇的勘查後, 好奇之下順便看一下 runserver 怎麼實作 autoreload。

看 core/management/commands/runserver.py 會看到 autoreload.main(inner_run)。然後看 utils/autoreload.py, 稍微看原始碼, 配合 pdb觀察行為, 還滿簡單的。

inner_run 是真正處理 web request 的函式, autoreload 的行為如下:

  1. 在執行 inner_run 前, 先 fork 出 child process
  2. 讓 child process 開另一個 thread
  3. parent process 等待 fork 出來的 child process 結束
  4. child process 的一個 thread 每秒掃一次所有載入的 module, 看看原始碼是否有更新。有的話, 結束這個 process, 傳回 exit code 3
  5. child process 的另一個 thread 執行 inner_run
  6. parent process 發覺 child process 的 exit code 為 3 時, 重 fork 一次, 然後回到步驟 2

要注意的是, 要用 parent process 重 fork, 而不是 child process 自己再 fork 新 process。這樣才能確保由原本乾淨的狀態重讀全部 modules (parent process fork 完就沒做事了, 確保狀態沒有一絲改變)。

偵測程式更動的程式是 code_changed(), 關鍵部份是如何找出所有相關程式:

filter(lambda v: v, map(lambda m: getattr(m, "__file__", None), sys.modules.values()))

然後將 .pyc, .pyo 改為 .py, 接著記錄所有 py 檔的上次更新時間, 留待下次呼叫此函式時比對。從這段程式可看出, 包含 site-package 或 virtualenv 裡的程式也在偵測範圍內, 還挺方便的。

留言

這個網誌中的熱門文章

(C/C++ ) 如何在 Linux 上使用自行編譯的第三方函式庫

virtualbox 使用 USB 裝置

熟悉系統工具好處多多