import functools
import typing
+import warnings
from types import TracebackType
import httpcore
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:
[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)
"""
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._transport.__enter__()
for proxy in self._proxies.values():
if proxy is not None:
proxy.__enter__()
+ self._is_closed = False
return self
def __exit__(
exc_value: BaseException = None,
traceback: TracebackType = None,
) -> None:
- self._transport.__exit__(exc_type, exc_value, traceback)
- for proxy in self._proxies.values():
- if proxy is not None:
- proxy.__exit__(exc_type, exc_value, traceback)
+ if not self.is_closed:
+ self._is_closed = True
+
+ self._transport.__exit__(exc_type, exc_value, traceback)
+ for proxy in self._proxies.values():
+ if proxy is not None:
+ proxy.__exit__(exc_type, exc_value, traceback)
+
+ def __del__(self) -> None:
+ self.close()
class AsyncClient(BaseClient):
[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)
"""
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":
await self._transport.__aenter__()
for proxy in self._proxies.values():
if proxy is not None:
await proxy.__aenter__()
+ self._is_closed = False
return self
async def __aexit__(
exc_value: BaseException = None,
traceback: TracebackType = None,
) -> None:
- await self._transport.__aexit__(exc_type, exc_value, traceback)
- for proxy in self._proxies.values():
- if proxy is not None:
- await proxy.__aexit__(exc_type, exc_value, traceback)
+ if not self.is_closed:
+ self._is_closed = True
+ await self._transport.__aexit__(exc_type, exc_value, traceback)
+ for proxy in self._proxies.values():
+ if proxy is not None:
+ await proxy.__aexit__(exc_type, exc_value, traceback)
+
+ def __del__(self) -> None:
+ if not self.is_closed:
+ warnings.warn(
+ f"Unclosed {self!r}. "
+ "See https://www.python-httpx.org/async/#opening-and-closing-clients "
+ "for details."
+ )
class StreamContextManager:
"transport.aclose",
"transport.__aexit__",
]
+
+
+@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(UserWarning):
+ del async_client
"transport.close",
"transport.__exit__",
]
+
+
+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