簡單實現熱加載python交互模式(利用文件修改時間 和 python模塊重載)

最近寫公司 項目的時候碰到一個問題,公司使用flask,在自測單個模塊下的函數會用到flask自帶的shell調試,但是這個shell有一個問題就是無發熱加載,每次修改了文件,就要退出重新進入,然後在輸入from ..... import .... 然後開始執行函數,效率極其低下,還讓人有點惱火,所以我就想寫一個可以熱加載的shell,提高一下效率,因爲時間比較短,所以比較簡陋,不過目前能滿足我的需求

import sys, os, importlib, readline, traceback
"""
importlib.reload 重載模塊
readline 這個模塊必須有,因爲接收輸入並保存在內存
"""

def iteration_module_files():
    # 獲取環境
    # 篩選內置模塊和一些項目外的模塊
    for module in sys.modules.values():
        filename = getattr(module, '__file__', None)
        if filename:
            # 減少判斷只檢測本目錄下的模塊
            if not os.getcwd() in filename:
                continue
            if filename[-4:] in ('.pyo', '.pyc'):
                filename = filename[:-1]
            # 將模塊也帶過去,因爲後面需要重載被修改文件的模塊
            yield (filename, module)

def is_files_modify(mtimes, module_list):
    # 檢測模塊文件是否修改
    for filename, module in iteration_module_files():
        try:
            # 文件修改時間
            mtime = os.stat(filename).st_mtime
        except IOError:
            continue
        # 從dict中根據文件名取出上一次文件修改時間
        old_time = mtimes.get(filename, None)
        # 如果沒有上一次的修改時間,加入dict
        if old_time is None:
            mtimes[filename] = mtime
        # 對比修改時間
        elif mtime > old_time:
            # 將修改了時間的模塊加入列表
            module_list.add(module)
            # 如果不把新的時間賦值給文件,那麼會一直進入重載
            mtimes[filename] = mtime
    return module_list

def file_detector():
    # 添加文件修改時間
    mtimes = {}
    # 添加模塊
    module_list = set()
    # 輸出信息
    cprt = 'Type "help", "copyright", "credits" or "license" for more information.'
    sys.stderr.write("* Python * %s on %s\n%s\n" %
               (sys.version, sys.platform, cprt))
    # 死循環檢測文件是否修改
    while 1:
        is_files_modify(mtimes, module_list)
        # 如果有文件修改,就重載修改過的模塊
        if module_list:
            for module in module_list:
                importlib.reload(module)
                # 記得清空模塊列表
                module_list = set()

        # 執行語句
        try:
            exec(input('=>'))
        except KeyboardInterrupt as k:
            print('  Ctrl+C')
            return
        except Exception as e:
            print(traceback.print_exc())

def runs():
    file_detector()

if __name__ == '__main__':
    # 導入項目的入口模塊
    __import__('test')
    # print(sys.modules.keys())
    runs()

"""
思路:不停檢測模塊文件的修改時間,如果文件修改就重新加載該模塊

用法:
文件 test.py
這個程序中導入test
__import__('test')

終端中:
import test
test.demo()
結果:
 1111

 修改文件,終端:
 import test
test.demo()
結果:
 1111222

退出:
 輸入 exit()
 或者
 Ctrl+C

 缺點:
 只支持單行python語句,需要print打印信息

 這裏還有一個缺點就是,如果通過from... import ...的形式調用摸一個函數或者類,
 修改了該文件以後必須用import...形式導入修改過的文件模塊,重新加載,
 因爲直接用from... import ...形式沒有重載這個模塊文件的話調用的是緩存中的模塊數據,
 不過因爲這裏有輸入緩存區,所以可以每次開始先用import...再用from...import...,
 每次直接上下鍵選擇即可,不需要太多額外輸入

目前在公司項目中使用還可以,但是有時候會碰到一些服務項目中用到os.path.abspath(".")當路徑,
所以要這個程序與入口程序在同級目錄下
"""

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章