以下为个人学习笔记整理

# 基于 watchdog 实现自动化更新

# 背景:

每次修改 Python 后为了不重启服务器进行调试,需要手动执行一个指令来指定需要热更的文件,有时候会比较麻烦。为此写了一个自动识别文件修改的工具,可以实时监听文件的修改并自动执行热更操作。

# 原理:

  1. 基于 watchdog 监听某个文件下的文件
  2. 发现文件修改后触发相应的 Event。
  3. 根据 Event 内容生成热更指令,写入热更文件。
  4. 热更程序定时读取文件,执行热更指令,并清空文件。

# 核心代码:

# -*- coding: utf8 -*-
# DATE: 2020/10/16 Fri
import sys
import time
import logging
from watchdog.observers import Observer
from watchdog.events import LoggingEventHandler
class EventHandler(LoggingEventHandler):
    def on_modified(self, event):
        print(f"{event.src_path}")
    ...
        
if __name__ == "__main__":
    event_handler = EventHandler()
    observer = Observer()
    observer.schedule(event_handler, path="./", recursive=True)
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

# 代码实现:

# -*- coding: utf8 -*-
# DATE: 2020/10/16 Fri
import os
import time
import enum
import json
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
class EventType(enum.IntEnum):
	UPDATE = 1
class FilterPath:
	def __init__(self):
		self._paths = WatchDog.get_json_info("listen_path")
	def __call__(self, path: str):
		for _path in self._paths:
			if path.startswith(_path):
				return True
		return False
class FilterFile:
	def __init__(self):
		self._files = WatchDog.get_json_info("listen_file_ext")
	def __call__(self, path: str):
		_, ext = os.path.splitext(path)
		if ext not in self._files:
			return False
		return True
class CmdConverter:
	def __init__(self):
		self.root_path = WatchDog.get_json_info("converter_root")
		self._replaces = WatchDog.get_json_info("replace_char")
		self._cmd_format = WatchDog.get_json_info("cmd_format")
	def __call__(self, event_type: EventType, path: str):
		if not path.startswith(self.root_path):
			return ""
		path = path[len(self.root_path):]
		for _replace in self._replaces:
			path = path.replace(_replace, ".")
		if event_type == EventType.UPDATE:
			return self._cmd_format.format("update", path)
		return ""
class FileWriter:
	def __init__(self):
		self.write_path = WatchDog.get_json_info("update_path")
	def write_to_file(self, content):
		try:
			with open(self.write_path, "a") as f:
				f.writelines(content)
		except Exception as e:
			print(e)
class AutoUpdateHandler(FileSystemEventHandler):
	def __init__(self):
		self._filters = [FilterPath(), FilterFile()]
		self._converter = CmdConverter()
		self._writer = FileWriter()
	def on_modified(self, event):
		for _filter in self._filters:
			if not _filter(event.src_path):
				return
		cmd = self._converter(EventType.UPDATE, event.src_path)
		self._writer.write_to_file(cmd)
		print(f'cmd:{cmd}', end='')
class WatchDog:
	json_info = {}
	valid_init = False
	@classmethod
	def init(cls):
		if cls.valid_init:
			return
		try:
			with open("./config.json", "r") as f:
				cls.json_info = json.load(f)
			cls.valid_init = True
		except Exception:
			pass
	@classmethod
	def get_json_info(cls, key):
		if not cls.valid_init:
			cls.init()
		return cls.json_info.get(key)
	@classmethod
	def listen_change(cls):
		observer = Observer()
		observer.schedule(AutoUpdateHandler(), path=cls.get_json_info("root_path"), recursive=True)
		observer.start()
		try:
			while True:
				time.sleep(1)
		except KeyboardInterrupt:
			observer.stop()
		observer.join()
if __name__ == "__main__":
	WatchDog.init()
	WatchDog.listen_change()

部分路径参数抽离到了 config.json 文件内:

{
    "root_path":"/root",
    "listen_path":[
        "/root/xxxx"
        // 需要监听的文件目录,有些文件可以忽略修改,在这里进行定制
    ],
    "listen_file_ext":[".py"], // 监听文件后缀
    "update_path":"需要写入的 update 文件,该文件会被热更程序定时读取",
    "converter_root":"用于转换更新指令的路径 例如监听路径是 /root/ 修改文件是 /root/code/Python/test.py 而热更指令可能是 update Python.test.py, 所以这里应该填 /root/code/ 用于进行路径转换",
    "cmd_format":"{0} {1}\n", // 热更指令,例如:update Python.test.py
    "replace_char":[
        "/",
        "\\"
    ]
}

参考内容:

  • watchdog github 源码
更新于 阅读次数

请我[恰饭]~( ̄▽ ̄)~*

鑫酱(●'◡'●) 微信支付

微信支付

鑫酱(●'◡'●) 支付宝

支付宝