[Python] luigiのTask#outputをopenで書き込まない場合
ちょっとニッチな状況でハマったのでメモ
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でパッチできない
パッチできないわけではないけど、意図した動きにならなかった。
# !/usr/bin/env python3
# coding: utf-8
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