watchdogを使ったファイル監視とコマンド実行
最近はTypeScriptを書くことが多く、パッケージ管理はnpmを使っています。
そのnpmですが、 — watchオプション1つでファイル監視して差分ビルドしてくれるって凄くありがたい機能があります。
pythonでユニットテストを書く場合にもこういった機能があればいいなーと思い、あれこれと考えたところ、ファイル監視できればいいんじゃないかという結論に至りました。
そういったライブラリがないかと探したら見つかったので、今日はそのライブラリを使って、テストコードが作成、変更、ファイル移動した時にテストランナーを実行するサンプルをご紹介したいと思います。
ディレクトリ構成は以下を想定しています。
|-- main.py
|-- tests/
|-- __init__.py
|-- test_a.py
|-- test_b.py
動作要件は、テストコードを作成、更新、移動のいずれか操作をした場合にテストランナーを実行することを想定しています。
今回使用するパッケージの公式ドキュメントはこちら
まずはパッケージインストール
pip install watchdog
pip freeze | grep watchdog
サンプルコードはこちら
https://gist.github.com/KentaYamada/44391c8f65c0103c4c3286a41b2a8ad5
ファイル監視には主に2つのオブジェクトを使用します。
「イベントハンドラ」
監視対象のファイルが作成、更新、削除、移動のいずれかを行った時に実行してほしい処理を書きます。
watchdogでは、on_created、on_modified、on_moved、on_deletedといったコールバック関数がイベントハンドラクラスに定義されているので、ファイル操作に応じて実行するプログラムを実装することができます。
また、on_any_eventメソッドは、監視対象のファイルが何らかの操作をした場合にコールされるので、ファイル操作共通の処理を実装したい場合に利用するといいでしょう。
イベントハンドラは、ファイルの正規表現であったり、ログ監視であったりと用途に合わせていくつかのクラスが用意されています。
要件に合わせてイベントハンドラクラスを使ってください。
「オブサーバー」
実際に対象のファイルを監視するオブジェクトになります。
インスタンス化する際に、監視対象のディレクトリ、上述したイベントハンドラなどを登録して、監視します。
ファイル監視自体はwatchdogのおかげで簡単にすることはできました。
ですが、ファイル操作イベントをキャッチしたあとのプログラム実行に落とし穴がありました…
「ファイル操作時のコールバック関数は、サブプロセスで実行しないといけない」
プロセス周りの仕組みをあまり理解できてないからとは思っていますが…
テストコードのファイル更新時にテストランナーを走らせたいと考えていて、以下のコードを関数化し、on_modifiedメソッドでコールするように実装しました。
from unittest import TestLoader, TextTestRunnerdef run_tests():
loader = TestLoader()
tests = loader.discover('./tests')
runner = TextTestRunner()
runner.run(tests)...on_modified(self, event):
run_tests()
これでメインプログラムを実行し、テストコードにテストケースを追加したところ、前回実行したテストコードで実行されてしまう(謎)
以下のソースだと、ソース反映後も正しく動きました。
on_modified(self, event):
subprocess.call('python3 -m unittest discover ./tests', shell=True)
この書き方だと、シェルスクリプトに書いてるのと変わらないからので、できれば関数化したプログラムをコールして動かせるようにしたいです(^^;)
原因を詳しく調べて、解決できたらまたお知らせしようと思います。
というわけで、watchdogを使ってテストコードの変更検知して自動実行するといったことをご紹介しました。
実装してみて感じたことは、npmのwatchオプションは凄いかった(笑)
これと同じようなことをVimとquickrun、vimprocというプラグインを使って実現することができます。
こちらも追々紹介できたらなと思います。
それでは(・ω・)丿