_Python 2
_と_Python 3
_の間で互換性があるようにいくつかのコードを変更していますが、ユニットテストの出力に警告があります。
_/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/unittest/case.py:601:
ResourceWarning: unclosed socket.socket fd=4,
family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6,
laddr=('1.1.2.3', 65087), raddr=('5.8.13.21', 8080)
_
requests や boto のような人気のあるライブラリーからもこれが起こっていると判断された小さな研究。
警告または filter it を完全に無視できます。私のサービスであれば、応答に_connection: close
_ヘッダーを設定できます( link )。
_Python 3.6.1
_の警告を示す例は次のとおりです。
app.py
_import requests
class Service(object):
def __init__(self):
self.session = requests.Session()
def get_info(self):
uri = 'http://api.stackexchange.com/2.2/info?site=stackoverflow'
response = self.session.get(uri)
if response.status_code == 200:
return response.json()
else:
response.raise_for_status()
def __del__(self):
self.session.close()
if __name__ == '__main__':
service = Service()
print(service.get_info())
_
test.py
_import unittest
class TestService(unittest.TestCase):
def test_growing(self):
import app
service = app.Service()
res = service.get_info()
self.assertTrue(res['items'][0]['new_active_users'] > 1)
if __name__ == '__main__':
unittest.main()
_
セッションが明示的に閉じられ、__del__()
に依存せずにこの種の警告が発生しないように、セッションを管理するより良い/正しい方法はありますか。
助けてくれてありがとう。
__del__
にティアダウンロジックがあると、プログラムが不正確になったり、推論が難しくなる可能性があります。そのメソッドがいつ呼び出されるかは保証されないため、警告が表示される可能性があります。これに対処するには、いくつかの方法があります。
tearDown
unittest
の- tearDown
メソッドを使用すると、各テストの後に実行されるコードを定義できます。このフックを使用してセッションを閉じると、テストが失敗した場合や例外が発生した場合でも機能します。
app.py
import requests
class Service(object):
def __init__(self):
self.session = requests.Session()
def get_info(self):
uri = 'http://api.stackexchange.com/2.2/info?site=stackoverflow'
response = self.session.get(uri)
if response.status_code == 200:
return response.json()
else:
response.raise_for_status()
def close(self):
self.session.close()
if __name__ == '__main__':
service = Service()
print(service.get_info())
service.close()
test.py
import unittest
import app
class TestService(unittest.TestCase):
def setUp(self):
self.service = app.Service()
super().setUp()
def tearDown(self):
self.service.close()
def test_growing(self):
res = self.service.get_info()
self.assertTrue(res['items'][0]['new_active_users'] > 1)
if __name__ == '__main__':
unittest.main()
コンテキストマネージャ は、何かのスコープを明示的に定義する非常に便利な方法でもあります。前の例では、すべての呼び出しサイトで.close()
が正しく呼び出されるようにする必要があります。そうしないと、リソースがリークします。コンテキストマネージャを使用すると、コンテキストマネージャのスコープ内に例外がある場合でも、これは自動的に処理されます。
ソリューション1)の上に構築して、追加のマジックメソッド(__enter__
および__exit__
)を定義して、クラスがwith
ステートメントで動作するようにすることができます。
注:ここでの素晴らしい点は、このコードがソリューション1)の使用もサポートしていることです。明示的な.close()
があり、何らかの理由でコンテキストマネージャーが不便だった場合に役立ちます。
app.py
import requests
class Service(object):
def __init__(self):
self.session = requests.Session()
def __enter__(self):
return self
def get_info(self):
uri = 'http://api.stackexchange.com/2.2/info?site=stackoverflow'
response = self.session.get(uri)
if response.status_code == 200:
return response.json()
else:
response.raise_for_status()
def close(self):
self.session.close()
def __exit__(self, exc_type, exc_value, traceback):
self.close()
if __name__ == '__main__':
with Service() as service:
print(service.get_info())
test.py
import unittest
import app
class TestService(unittest.TestCase):
def test_growing(self):
with app.Service() as service:
res = service.get_info()
self.assertTrue(res['items'][0]['new_active_users'] > 1)
if __name__ == '__main__':
unittest.main()
必要なものに応じて、setUp
/tearDown
とコンテキストマネージャーのいずれか、または組み合わせを使用して、その警告を取り除き、さらにコードでより明示的なリソース管理を行うことができます。
これは、警告をあまり気にしない場合に最適なソリューションです
warningsをインポートし、ドライバーが開始する場所にこの行を追加するだけです-
warnings.filterwarnings(action="ignore", message="unclosed", category=ResourceWarning)