Сокет ResourceWarning с использованием urllib в Python 3

Я использую urllib.request.urlopen() для получения из веб-службы, которую я пытаюсь проверить.

Это возвращает объект HTTPResponse, который я затем читаю (), чтобы получить тело ответа.

Но я всегда вижу ResourceWarning о незамкнутом сокете из socket.py

Вот соответствующая функция:

from urllib.request import Request, urlopen

def get_from_webservice(url):
    """ GET from the webservice  """
    req = Request(url, method="GET", headers=HEADERS)
    with urlopen(req) as rsp:
        body = rsp.read().decode('utf-8')
        return json.loads(body)

Вот предупреждение, которое появляется в выводе программы:

$ ./test/test_webservices.py
/Library/Frameworks/Python.framework/Versions/3.3/lib/python3.3/socket.py:359: ResourceWarning: unclosed <socket.socket object, fd=5, family=30, type=1, proto=6>
self._sock = None
.s
----------------------------------------------------------------------
Ran 2 tests in 0.010s

OK (skipped=1)

Если я что-то могу сделать с HTTPResponse (или Request?), Чтобы он аккуратно закрыл свой сокет, я бы очень хотел знать, потому что этот код предназначен для моих модульных тестов; Я не люблю игнорировать предупреждения где угодно, но особенно не там.

1 ответ

Решение

Я не знаю, является ли это ответом, но это часть пути к ответу.

Если я добавлю заголовок "connection: close" к ответу от моих веб-сервисов, объект HTTPResponse, похоже, будет очищен должным образом без предупреждения.

И действительно, в спецификации HTTP ( http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html) сказано:

Приложения HTTP/1.1, которые не поддерживают постоянные соединения, ДОЛЖНЫ включать опцию "закрыть" в каждом сообщении.

Таким образом, проблема была на стороне сервера (т.е. моя вина!). Если у вас нет контроля над заголовками, поступающими с сервера, я не знаю, что вы можете сделать.

У меня была такая же проблема с urllib3 и я просто добавил контекстный менеджер для автоматического закрытия соединения:

import urllib3

def get(addr, headers):
    """ this function will close the connection after a http request. """
    with urllib3.PoolManager() as conn:
        res = conn.request('GET', addr, headers=headers)
        if r.status == 200:
            return res.data
        else:
            raise ConnectionError(res.reason)

Обратите внимание, что urllib3 предназначен для того, чтобы иметь пул соединений и поддерживать соединения для вас. Это может значительно ускорить ваше приложение, если ему необходимо выполнить серию запросов, например, несколько обращений к бэкэнд-API.

Пожалуйста, прочитайте urllib3 Документация по пулам соединений здесь: https://urllib3.readthedocs.io/en/1.5/pools.html

PS вы также можете использовать requests lib, которая не является частью стандартной библиотеки Python (на 2019 г.), но очень мощная и простая в использовании: http://docs.python-requests.org/en/master/

Другие вопросы по тегам