watchdog、おまいだったのか。いつも、ファイルを監視してくれていたのは…【Python】
前書き
この記事はJSL(日本システム技研) Advent Calendar 2021のカレンダーの12/6(月)の記事です。
みなさん、watchdogというライブラリをご存知でしょうか。
watchdogは「番犬」「監視人」という意味があるようです。指定したフォルダ配下を監視し、ファイルの作成/変更/削除/移動イベントを感知してくれます。
他のライブラリからも利用されるため、お手元の環境でpip freeze | grep watchdog
を実行すると出てくるかもしれません。
この記事では以下の観点からwatchdogを紹介したいと思います。
- 【watchdog】が使われているライブラリの紹介
- watchdogを自分でも使ってみよう!(サンプル)
参考リンク
Watchdog公式
- GitHub - gorakhargosh/watchdog: Python library and shell utilities to monitor filesystem events.
- Watchdog — watchdog 0.8.2 documentation
Flask, Werkzeug公式
pytest-watch公式
参考にさせていただいた記事
環境
バージョン | |
---|---|
MacOS Big Sur | 11.1 |
Python3 | 3.9.1 |
watchdog | 2.1.6 |
pytest | 6.2.5 |
pytest-watch | 4.2.0 |
watchdogが使われているライブラリ
まずはwatchdogを利用しているライブラリを紹介します。
普段は意識しませんが、「勝手にリロードしてくれる」系の動作はwatchdogを使っていることがあります。(もちろん、自前で実装しているライブラリも多いですが).
Flask
Welcome to Flask — Flask Documentation (2.0.x)
Webアプリケーションフレームワークとして有名なFlaskです。
アイコンの画像が何物かわからないので、知っている人がいたら教えて欲しいです。(少なくともフラスコには見えないので、僕は勝手にシシトウだと思っています。)
Flaskには開発用サーバがついており、アプリケーションのファイルを編集すると、自動でサーバーがrestartして変更を反映するという機能を持っています。
こちらの公式ドキュメントにありますが、せっかくなので実際にやってみましょう。
- プロジェクト立ち上げ〜仮想環境にFlaskをインストール.
$ mkdir myproject $ cd myproject $ python3.9 -m venv env $ source env/bin/activate $ pip install --upgrade pip $ pip install Flask
sample.py
を作成し、以下のコードを記述します。
from flask import Flask app = Flask(__name__) @app.route('/') def gongitsune(): return 'ごん、おまいだったのか....'
- 環境変数を設定します。
FLASK_ENV=development
がリロードを有効にする設定です。
$ export FLASK_APP=sample.py # 先程作成した.pyファイル. $ export FLASK_ENV=development # debugモード有効化
- 開発用サーバーを立ち上げます。
$ python -m flask run
さて、するとwatchdogがリロードをしてくれるはずですね!
sample.py
ファイルを変更するとメッセージが出てきます。
.... * Restarting with stat ....
あれ?watchdogは...???
Flask公式ドキュメントのこの辺りを見ると、watchdogは自分でpip installする必要があると書かれています。
statはデフォルトでonになっているリローダです。watchdogの方がより高速で効率が良いとdocには書いてあります。
Watchdog provides a faster, more efficient reloader for the development server.
余談ですが、正確に言うとFlaskに使われているWerkzeugでstag/watchdogが採用されています。
Werkzeugは, WSGI準拠のアプリケーション作成を助けてくれるライブラリです。
Serving WSGI Applications — Werkzeug Documentation (2.0.x)
上記リンクによると、watchdogはstatよりリロードが速く、また効率についてはバッテリーの消費が少ないということらしいです。
前置きが長くなりましたが、リローダをstat→watchdogに切り替えてみましょう!
ドキュメントによると、pipでインストールすれば、自動でstatからwatchdogに切り替わります。
$ pip install watchdog
$ python -m flask run
.... * Restarting with watchdog (fsevents) ...
やりました。
小さなプロジェクトだとあまり変化は感じられませんが、top
コマンドなどでCPU使用率などみてるとwatchdogの方が確かに低くなっていました(半分くらいになる)。
ちなみに同じく有名なWebフレームワークのDjangoにも自動リロード機能つきの開発サーバがついていますが、こちらは自前で実装しているようです。
以下のリンクがリロード用のモジュールのようですね(間違ってたらご指摘お願いします)。 django/autoreload.py at main · django/django · GitHub
pytest-watch
少し古いですが、ファイルを変更するたびにpytestを自動実行してくれる強力なライブラリです。
watchdogを使ったお手本のような例です。.py
で終わるファイルを検知してくれます。
以下のようなプロダクトコード/テストコードを書きます。
def heiju(text): return f"兵十「{text}」" def test_heiju(): text = "おまいだったのか" actual = heiju(text) assert actual == "兵十「{おまいだったのか}」"
以下のgifでは
- まず普通に
pytest
でテストを実行しています。 - 次に、
ptw
でテストを実行しています。コードを編集すると、自動で再度テストが走っています。
watchdogがファイルの変更を検知し、その度にpytestが走るというシンプルな仕組みですがかなり強力だと思います。
自分は小さなスクリプトを作る時や、ちょっと競プロに手を出していた時はこちらを使っていました。コードがめっちゃ速く書けます。
watchdogを自分でも使ってみる。
前置き: tmuxと一緒に使うのがオススメ.
画面分割→ 1画面でコード編集、1画面をwatchdogで監視...この形が一番捗ります。
自分はtmuxが好きなのでこちらをお勧めしますが、iTerm2を使ってる人も周りには多いですね。
こちらの記事がわかりやすいので、紹介させていただきます。 - tmuxを必要最低限で入門して使う - Qiita
例1: watchmedoでコマンドを登録してみる
まずはwatchmedoを使ってみましょう!
こちらwatchdogの公式製ツールで、以下のように追加インストールできます。
$ pip install "watchdog[watchmedo]"
パッと思いつくようなことは、大体コイツで可能です。
例えば以下のことをやってみましょう。
- jsonファイルが変更されたら、文法チェックをおこなう。
$ watchmedo shell-command \ -c 'python -m json.tool ${watch_src_path} > /dev/null' -p '*.json' --drop
まずはGIFをどうぞ。
若干わかりづらいですが、:w
というコマンドが画面一番下に出た瞬間、保存しています。
jsonをチェックしているコマンドはPythonの標準ライブラリで、jsonのフォーマッタです。標準出力を/dev/null
に捨てることでエラーチェックに使っています。
手前味噌ですがこちらの記事でも紹介しました。 シェル上でjsonをフォーマットする、各種ツール導入手順と使い方の個人的なまとめ
-c
オプションで、ファイル変更イベント時に実行するコマンドを渡しています。${watch_src_path}
は変更されたファイルのパスをフルパスで取得できます。
-p
オプションで、変更を検知するファイル名のパターンを記述しています(今回はjson)--drop
はイベントが複数回走らないようにしています。- これはvimのファイル変更方法が特殊なため、指定しています。他のエディタなら不要かと思います(未検証)
これらは以下のようにhelpを見ると、全てのオプションが説明されています。
$ watchmedo shell-command -h
ちなみに、コマンドを登録するshell-command
サブコマンドの他にも単純なlogを出したり、yamlを読ませて実行するサブコマンドもあります。
以下のようにして確認できます。
$ watchmedo -h
例2: ファイルの種類によって異なるコマンドを実行する!
このようなケースでは、yamlファイルに動作を定義して簡単に実行するサブコマンドが用意されています.
tricks-from というサブコマンドで、読み込むyamlファイルはtricsと呼ばれています。
また、以下の記事を参考にさせていただきました。
Python Watchdogのtricksを試す - Qiita
ではyamlを書いてみます。
tricks: - watchdog.tricks.ShellCommandTrick: patterns: - "*.py" shell_command: 'python ${watch_src_path}' drop_during_process: true - watchdog.tricks.ShellCommandTrick: patterns: - "*.json" shell_command: 'python -m json.tool ${watch_src_path} > /dev/null' drop_during_process: true
では、上記のtrics.yaml
を指定してwatchmedoを実行してみます。
$ watchmedo tricks-from trics.yaml
pythonファイル編集→実行、jsonファイル編集→チェック、の順番でテストしています。
例3: 監視用スクリプトを実行する!
一番自由度が高い方法です。
Quickstart — watchdog 0.8.2 documentation
公式ドキュメントにもサンプルがあり、いろんな記事で紹介されているので、(機能的にはこちらメインですが)省略させていただきます。
以下、watchdogの利用例の記事を紹介させていただきます。
Pythonで変更のあったモジュールを動的インポート/リロードする - Qiita Pythonのwatchdogを使ってFTP受信ファイルを安全に処理する - Qiita Pythonのwatchdogを使ってFTP受信ファイルを安全に処理する - Qiita
まとめ
- watchdogを使うと、ファイル変更を監視することができる。
- watchmedoにより、ファイル変更-> コマンド実行やログ出しなどのよくあるパターンを簡単に実行できる。
- watchdogは他の便利なライブラリを支える、縁の下の力持ちとしても活躍している。