繁体   English   中英

Python 3单元测试中的ResourceWarning未关闭套接字

[英]ResourceWarning unclosed socket in Python 3 Unit Test

我正在修改一些代码以在Python 2Python 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)

一些研究确定这也发生在流行的库中,比如requestsboto3

我可以忽略警告或完全过滤它 如果是我的服务,我可以在响应中设置connection: close标头( 链接)。

这是一个在Python 3.6.1中显示警告的示例:

应用程序

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())

测试文件

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__使用拆卸逻辑会使您的程序不正确或难以推理,因为无法保证何时调用该方法,可能会导致您收到警告。 有几种方法可以解决这个问题:

1)公开一个关闭session的方法,在测试tearDown

unittesttearDown方法允许您定义一些将在每次测试后运行的代码。 即使测试失败或有异常,使用这个钩子关闭会话也能工作,这很好。

应用程序

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()

测试文件

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()

2)使用上下文管理器

上下文管理器也是一种非常有用的方式来明确定义某事物的范围。 在前面的示例中,您必须确保在每个调用站点正确调用.close() ,否则您的资源将泄漏。 使用上下文管理器,即使在上下文管理器的范围内存在异常,也会自动处理。

在解决方案 1) 的基础上,您可以定义额外的魔术方法( __enter____exit__ ),以便您的类使用with语句。

注意:这里的好处是此代码还支持解决方案 1) 中的用法,带有显式.close() ,如果上下文管理器由于某种原因不方便,这可能很有用。

应用程序

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())

测试文件

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和上下文管理器,并摆脱该警告,并在您的代码中进行更明确的资源管理!

如果您不太关心警告,这是最好的解决方案

只需导入警告并在您的驱动程序启动的地方添加这一行 -

import warnings

warnings.filterwarnings(action="ignore", message="unclosed", category=ResourceWarning)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM