ちょっとニッチな状況でハマったのでメモ
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