我正在修改某些代码以便在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和boto3等热门图书馆。
我可以完全忽略警告或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__()
来产生这种警告。
感谢您的帮助。
答案 0 :(得分:1)
在__del__
中包含拆解逻辑可能会使您的程序不正确或难以推理,因为无法保证何时调用该方法,有可能导致您得到警告。有两种解决方法:
tearDown
unittest
的{{3}}方法可让您定义一些将在每次测试后运行的代码。即使测试失败或出现异常,使用此钩子来关闭会话也可以。
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()
tearDown
也是明确定义事物范围的一种非常有用的方法。在上一个示例中,您必须确保在每个调用站点上都正确调用.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
和上下文管理器中的任一个,也可以结合使用它们,从而摆脱该警告,并在代码中进行更明确的资源管理!
答案 1 :(得分:1)
如果您不太担心警告,这是最好的解决方案
只需导入警告并将此行添加到驱动程序启动的位置-
warnings.filterwarnings(action="ignore", message="unclosed",
category=ResourceWarning)