Ответ 1
Наличие логики разрыва в __del__
может сделать вашу программу неправильной или трудной для рассуждения, потому что нет гарантии того, когда этот метод будет вызван, что может привести к полученному вами предупреждению. Есть несколько способов решить эту проблему:
1) Выставьте метод, чтобы закрыть сеанс, и вызовите его в тесте 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()
2) Используйте контекстный менеджер
Диспетчер контекста также является очень полезным способом явно определить область действия чего-либо. В предыдущем примере вы должны убедиться, что .close()
вызывается правильно на каждом сайте вызовов, иначе ваши ресурсы будут просачиваться. С помощью диспетчера контекста это обрабатывается автоматически, даже если есть исключение в рамках диспетчера контекста.
Основываясь на решении 1), вы можете определить дополнительные магические методы (__enter__
и __exit__
), чтобы ваш класс работал with
оператором 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
и менеджера контекста, либо избавиться от этого предупреждения, а также иметь более четкое управление ресурсами в своем коде!