ちょっとニッチな状況でハマったのでメモ
runでファイルコピーするだけのタスクを作った時、ファイルコピーを以下のようにやってしまった。
import shutil from luigi import Task
class UserTask(Task):
def run(self): shutil.copy('<source path>', self.output().path)
def output(self): return LocalTarget('<output path>')
|
ディレクトリを自動で作ってくれない
shutil.copyでコピーしているので当然と言えば当然だが、outputの親ディレクトリを自動で作成はしてくれない。
self.output().makedirs()を明示的に実行する必要がある。
def run(self): self.output().makedirs() shutil.copy('<source path>', self.output().path)
|
self.output().makedirs()はself.output().open('w')で書き込む時は内部的に自動で呼んでくれる。なのでshutil.copyではなく直接書き込めばmakedirs()を明示的に呼ぶ必要はない。
def run(self): with self.output.oepn('w') as output, open('<source path>', 'r') as input: output.write(input.read())
|
ユニットテスト時に MockTarget でパッチできない
パッチできないわけではないけど、意図した動きにならなかった。
import unittest from unittest import mock from luigi import Task from luigi import build, LocalTarget from luigi.mock import MockTarget
class UserTask(Task):
def run(self): self.output().makedirs() shutil.copy('<source path>', self.output().path)
def output(self): return LocalTarget('<output path>')
def mocked_output(*args, **kwargs): m = MockTarget('mock_target') m.makedirs = lambda: True return m
class TestUserTask(unittest.TestCase):
@mock.patch('__main__.UserTask.output', side_effect=mocked_output) def test_method(self, m_mock): """ test file copy """ result = build([UserTask()], local_scheduler=True) self.assertTrue(result)
if __name__ == "__main__": unittest.main()
|
LocalTargetをMockTargetに置き換えるようoutputをモックでパッチしたかった。
しかしoutputファイルの書き出しはself.output().openでやっているわけではないので、MockTargetにしたにも関わらずファイルの実体が書き出されてしまう。
ここもshutil.copyではなくself.output().open('w')でやれば問題なかった。
パッチはするけど LocalTarget で
モックでパッチするけど、MockTargetではなくLocalTargetでis_tmpする形にしている。
def mocked_output(*args, **kwargs): m = LocalTarget(is_tmp=True) m.makedirs = lambda: True return m
|
また、makedirsが呼ばれるとディレクトリを作ったしまうので、適当なメソッド(上記ではラムダ)で上書きしておく
まとめ
outputのターゲットは、ちゃんとself.output().open('w')で書かないと意図しない動作することがあるよ!
実行環境
- Windows 10
- Python 3.6.3
- luigi 2.7.2