在Python3单元测试中警告未关闭的套接字

本教程将介绍在Python3单元测试中警告未关闭的套接字的处理方法,这篇教程是从别的地方看到的,然后加了一些国外程序员的疑问与解答,希望能对你有所帮助,好了,下面开始学习吧。

在Python3单元测试中警告未关闭的套接字 教程 第1张

问题描述

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

一项小小的研究发现,像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__()导致此类警告。

谢谢您的帮助。

推荐答案

__del__中使用tearDown逻辑可能会使您的程序不正确或更难推理,因为无法保证何时调用该方法,这可能会导致您收到警告。有几种方法可以解决这个问题:

1)公开关闭会话的方法,在测试中调用tearDown

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

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

2)使用上下文管理器

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

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

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和上下文管理器中的任何一个或组合,并消除该警告,并在代码中进行更显式的资源管理!

好了关于在Python3单元测试中警告未关闭的套接字的教程就到这里就结束了,希望趣模板源码网找到的这篇技术文章能帮助到大家,更多技术教程可以在站内搜索。