質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Q&A

解決済

2回答

4777閲覧

UnittestでWarningが出てしまう。

Tera0724

総合スコア18

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

0グッド

0クリップ

投稿2020/06/30 11:53

編集2020/07/01 03:18

現在、unittestの学習をしています。テストを行うプログラムは以下の通りです。

class Test(unittest.TestCase): # Test1を使ったテストケース def TestCase1(self): sys.argv.append("入力データのpath") expected = main() with open("正解データのpath") as f: answerData = json.load(f) self.assertEqual(expected, answerData) del sys.argv[1] # Test2を使ったテストケース def TestCase2(self): sys.argv.append("入力データのpath") expected = main() with open("正解データのpath") as f: answerData = json.load(f) self.assertEqual(expected, answerData) del sys.argv[1] # Test3を使ったテストケース def TestCase3(self): sys.argv.append("入力データのpath") expected = main() with open("正解データのpath") as f: answerData = json.load(f) self.assertEqual(expected, answerData) del sys.argv[1] if __name__ == "__main__": unittest.main()

上記プログラムを実行したところ、以下のようなwarningが出てしまいます。

/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pyocr/tesseract.py:364: ResourceWarning: unclosed file <_io.BufferedReader name=4>
configs=builder.tesseract_configs)
ResourceWarning: Enable tracemalloc to get the object allocation traceback

unittest.main(warnings='ignore')
とすれば出力されなくなるのですが、根本の解決にはなってないと感じました。そこで色々しれべてみたのですが、私はテストコードを書くのが初めてで理由を見つけることができませんでした。
上記warningについての対処方法について教えていただきたいです。
よろしくお願いします。

追記
yymmt様の回答を参考にソースコードをみたところ、closeされていない箇所があったため以下の画像のようにコードを付け足しました。
tesseract.py のrun_tesseract関数
イメージ説明

tesseract.pyのget_version関数
イメージ説明

ただ、開発をする上でパッケージのソースコードを勝手に書き換えるとコンフリクトを起こすとあったのですが、ソースコード外でクローズすることは可能でしょうか?
わかりづらい文章となってしまい申し訳ありません。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

pyocrと言うパッケージのソースコード tesseract.pyを確認しました。tesseractと言うコマンドをsubprocess.Popen()で呼び出していますが、stdoutを開けっ放しのままにしてcloseしていないことが原因です。pyocrの開発者に連絡を取らない限り、根本的な解決にはならないでしょう。

以下、同様のエラーを出力するソースコードです。

python

1import unittest 2import subprocess 3 4 5class TestSubprocess(unittest.TestCase): 6 def test_ls(self): 7 proc = subprocess.Popen( 8 ["ls", "-1"], 9 stdout=subprocess.PIPE, 10 stderr=subprocess.STDOUT, 11 ) 12 proc.wait() 13 # proc.stdout.close() # pyocrはこれを忘れている 14 15 16if __name__ == "__main__": 17 unittest.main()

[追記]
ソースコード外でなんとかしたいと言うことなのでpyocrの問題箇所にモンキーパッチを充てる方法を追記します。ただしパッケージが更新した際に問題が発生すると言うことは解消されません。

python

1import pyocr 2... 3def run_tesseract_monkey_patch(input_filename, output_filename_base, cwd=None, lang=None, flags=None, configs=None): 4 # オリジナルのソースコードをコピペ 5 pyocr.tesseract._set_environment() 6 command = [ 7 pyocr.tesseract.TESSERACT_CMD, 8 input_filename, 9 output_filename_base, 10 ] 11 if lang is not None: 12 command += ["-l", lang] 13 if flags is not None: 14 command += flags 15 if configs is not None: 16 command += configs 17 proc = subprocess.Popen( 18 command, 19 cwd=cwd, 20 startupinfo=pyocr.tesseract.g_subprocess_startup_info, 21 creationflags=pyocr.tesseract.g_creation_flags, 22 stdout=subprocess.PIPE, 23 stderr=subprocess.STDOUT, 24 ) 25 errors = proc.stdout.read() 26 status = proc.wait() 27 proc.stdout.close() 28 return (status, errors) 29 30 31def main(): 32 pyocr.tesseract.run_tesseract = run_tesseract_monkey_patch 33 # get_version()も同じように上書きする 34 # 以降普通に処理

投稿2020/06/30 15:14

編集2020/07/01 04:05
yymmt

総合スコア1615

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Tera0724

2020/07/01 03:25

回答ありがとうございます。yymmt様の助言を参考にtesseract.pyを見たところ、追記で示した箇所のprocをcloseすることでwarningは解消されました。ただ、開発する上でパッケージのソースコードを勝手にいじった場合コンフリクト等が発生するとありました。 ソースコード外でcloseすることは可能でしょうは? コードを追った感じでは最後の出力にはなっていないため厳しいのではと思うのですが。。。 パッケージでのエラー等が出たのが初めてであるため対処法がわからない状態です。 わかりずらい質問となってしまい申し訳ありませんが、答えていただけると幸いです。 よろしくお願いします。
yymmt

2020/07/01 04:14 編集

ソースコード外で対処するのは困難です。私も警告は1行たりとも残したくない派ですのでお気持ちは理解できますが、パッケージに手を入れる事は最終手段になりますので、ユニットテストでしか発生しない警告なら放置するか、作者に修正依頼を行うのが良いでしょう。 それとは別に技術的に不可能かと言われると、tesseractと言うプログラムを見つけてシグナルを送り強制終了と言うことは不可能ではないですが、そこまでやるくらいならsubprocessを含む関数部分を全て上書きする方が現実的です。
yymmt

2020/07/01 03:57

後者のsubprocess部分を上書きする方法を追記しました。get_versionも同じことをする必要があります。この方法はモンキーパッチと呼ばれます。
Tera0724

2020/07/01 05:26

回答ありがとうございます。yymmt様のサンプルコードを参考にget_versionの処理も作成したところ、warningを消すことができました。 モンキーパッチについては知らなかったため学習していきます。 ありがとうございました。
guest

0

追記

main()pyocr というパッケージが呼び出され、
そのパッケージが警告を発しているようです

pyocr · PyPI

原因が main() 内であるかどうかの切り分け

ためしに、テストコード中の main() の呼び出し箇所を
一時的に次のように編集します:

python

1# expected = main() 2expected = {}

unittest を実行すると、
テストは失敗するかもしれませんが、警告は表示されなくなるのではないでしょうか

main() 内で pyocr 呼び出した覚えがない場合

main() 内でなんらかのパッケージが
間接的に pyocr を呼び出している可能性があります
その場合は、
次のコマンドの結果を質問欄に追記していただけるともう少し助言できるかもしれません:

pip freeze

元の回答

その書き方だと、途中でテストが失敗したとき、
ファイルが開いたままでリソースが開放されない可能性があります

answerFile = open("正解データのpath", 'r') answerData = json.load(answerFile) # ここで失敗すると answerFile.close() # この行が実行されません

次のようにすると、ファイルを開いて閉じるという面倒がなくなります

from pathlib import Path # ~ answerFile = Path("正解データのpath").read_text() answerData = json.load(answerFile)

投稿2020/06/30 12:03

編集2020/06/30 17:15
y_shinoda

総合スコア3272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Tera0724

2020/06/30 12:56

回答ありがとうございます。調べたのですが、read_textの使い方がようくわからなかったため、withを使って書き直しました。 しかし、warningは消えませんでした。。。 出力はOKと出てくれるのですが。。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問