Skip to content

Commit

Permalink
Made Client and AsyncClient checking for being closed in __del__ (enc…
Browse files Browse the repository at this point in the history
  • Loading branch information
cdeler committed Aug 21, 2020
1 parent 15c1e42 commit 4d03ac1
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 12 deletions.
52 changes: 44 additions & 8 deletions httpx/_client.py
@@ -1,5 +1,6 @@
import functools
import typing
import warnings
from types import TracebackType

import httpcore
Expand Down Expand Up @@ -79,6 +80,14 @@ def __init__(
self.max_redirects = max_redirects
self._trust_env = trust_env
self._netrc = NetRCInfo()
self._is_closed = True

@property
def is_closed(self) -> bool:
"""
Check if the client being closed
"""
return self._is_closed

@property
def trust_env(self) -> bool:
Expand Down Expand Up @@ -696,6 +705,8 @@ def send(
[0]: /advanced/#request-instances
"""
self._is_closed = False

timeout = self.timeout if isinstance(timeout, UnsetType) else Timeout(timeout)

auth = self._build_request_auth(request, auth)
Expand Down Expand Up @@ -1029,12 +1040,16 @@ def close(self) -> None:
"""
Close transport and proxies.
"""
self._transport.close()
for proxy in self._proxies.values():
if proxy is not None:
proxy.close()
if not self.is_closed:
self._is_closed = True

self._transport.close()
for proxy in self._proxies.values():
if proxy is not None:
proxy.close()

def __enter__(self) -> "Client":
self._is_closed = False
return self

def __exit__(
Expand All @@ -1045,6 +1060,9 @@ def __exit__(
) -> None:
self.close()

def __del__(self) -> None:
self.close()


class AsyncClient(BaseClient):
"""
Expand Down Expand Up @@ -1298,6 +1316,8 @@ async def send(
[0]: /advanced/#request-instances
"""
self._is_closed = False

timeout = self.timeout if isinstance(timeout, UnsetType) else Timeout(timeout)

auth = self._build_request_auth(request, auth)
Expand Down Expand Up @@ -1633,12 +1653,16 @@ async def aclose(self) -> None:
"""
Close transport and proxies.
"""
await self._transport.aclose()
for proxy in self._proxies.values():
if proxy is not None:
await proxy.aclose()
if not self.is_closed:
self._is_closed = True

await self._transport.aclose()
for proxy in self._proxies.values():
if proxy is not None:
await proxy.aclose()

async def __aenter__(self) -> "AsyncClient":
self._is_closed = False
return self

async def __aexit__(
Expand All @@ -1649,6 +1673,18 @@ async def __aexit__(
) -> None:
await self.aclose()

def __del__(self) -> None:
if not self.is_closed:
warnings.warn(
f"Unclosed {self!r}. "
"Please call\n"
"\t>>> await async_client.aclose()\n"
"or use it as a context manager\n"
"\t>>> async with httpx.AsyncClient() as client:\n"
"\t>>> ...",
ResourceWarning,
)


class StreamContextManager:
def __init__(
Expand Down
42 changes: 42 additions & 0 deletions tests/client/test_async_client.py
Expand Up @@ -166,3 +166,45 @@ async def test_100_continue(server):

assert response.status_code == 200
assert response.content == data


@pytest.mark.usefixtures("async_environment")
async def test_that_async_client_is_closed_by_default():
client = httpx.AsyncClient()

assert client.is_closed


@pytest.mark.usefixtures("async_environment")
async def test_that_send_cause_async_client_to_be_not_closed():
client = httpx.AsyncClient()

await client.get("http://example.com")

assert not client.is_closed

await client.aclose()


@pytest.mark.usefixtures("async_environment")
async def test_that_async_client_is_not_closed_in_with_block():
async with httpx.AsyncClient() as client:
assert not client.is_closed


@pytest.mark.usefixtures("async_environment")
async def test_that_async_client_is_closed_after_with_block():
async with httpx.AsyncClient() as client:
pass

assert client.is_closed


@pytest.mark.usefixtures("async_environment")
async def test_that_async_client_caused_warning_when_being_deleted():
async_client = httpx.AsyncClient()

await async_client.get("http://example.com")

with pytest.warns(ResourceWarning):
del async_client
26 changes: 26 additions & 0 deletions tests/client/test_client.py
Expand Up @@ -208,3 +208,29 @@ def test_pool_limits_deprecated():

with pytest.warns(DeprecationWarning):
httpx.AsyncClient(pool_limits=limits)


def test_that_client_is_closed_by_default():
client = httpx.Client()

assert client.is_closed


def test_that_send_cause_client_to_be_not_closed():
client = httpx.Client()

client.get("http://example.com")

assert not client.is_closed


def test_that_client_is_not_closed_in_with_block():
with httpx.Client() as client:
assert not client.is_closed


def test_that_client_is_closed_after_with_block():
with httpx.Client() as client:
pass

assert client.is_closed
8 changes: 4 additions & 4 deletions tests/client/test_proxies.py
Expand Up @@ -122,13 +122,13 @@ def test_transport_for_request(url, proxies, expected):

@pytest.mark.asyncio
async def test_async_proxy_close():
client = httpx.AsyncClient(proxies={"all://": PROXY_URL})
await client.aclose()
async with httpx.AsyncClient(proxies={"all://": PROXY_URL}):
pass


def test_sync_proxy_close():
client = httpx.Client(proxies={"all://": PROXY_URL})
client.close()
with httpx.Client(proxies={"all://": PROXY_URL}):
pass


def test_unsupported_proxy_scheme():
Expand Down

0 comments on commit 4d03ac1

Please sign in to comment.