Repository: encode/httpx
Pull request #819 (2020-02-20 10:57:09)

Drop RedirectLoop exception
Simply drop RedirectLoop exception and instead rely on max number of redirects (and retries on future) like requests, urllib3 and many others do.
More info in previous closed pr https://github.com/encode/httpx/pull/801


Patch:
diff --git a/.gitignore b/.gitignore
index 22e1fbf..49e14cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,5 @@ site/
 *.egg-info/
 venv*/
 .python-version
+build/
+dist/
diff --git a/httpx/__init__.py b/httpx/__init__.py
index 2c4cb72..d02c72e 100644
--- a/httpx/__init__.py
+++ b/httpx/__init__.py
@@ -18,7 +18,6 @@ from ._exceptions import (
     ProtocolError,
     ProxyError,
     ReadTimeout,
-    RedirectLoop,
     RequestBodyUnavailable,
     RequestNotRead,
     ResponseClosed,
@@ -66,7 +65,6 @@ __all__ = [
     "PoolTimeout",
     "ProtocolError",
     "ReadTimeout",
-    "RedirectLoop",
     "RequestBodyUnavailable",
     "ResponseClosed",
     "ResponseNotRead",
diff --git a/httpx/_client.py b/httpx/_client.py
index 6c87858..472d455 100644
--- a/httpx/_client.py
+++ b/httpx/_client.py
@@ -27,13 +27,7 @@ from ._dispatch.connection_pool import ConnectionPool
 from ._dispatch.proxy_http import HTTPProxy
 from ._dispatch.urllib3 import URLLib3Dispatcher
 from ._dispatch.wsgi import WSGIDispatch
-from ._exceptions import (
-    HTTPError,
-    InvalidURL,
-    RedirectLoop,
-    RequestBodyUnavailable,
-    TooManyRedirects,
-)
+from ._exceptions import HTTPError, InvalidURL, RequestBodyUnavailable, TooManyRedirects
 from ._models import (
     URL,
     Cookies,
@@ -615,9 +609,6 @@ class Client(BaseClient):
         while True:
             if len(history) > self.max_redirects:
                 raise TooManyRedirects()
-            urls = ((resp.request.method, resp.url) for resp in history)
-            if (request.method, request.url) in urls:
-                raise RedirectLoop()
 
             response = self.send_handling_auth(
                 request, auth=auth, timeout=timeout, history=history
@@ -1142,9 +1133,6 @@ class AsyncClient(BaseClient):
         while True:
             if len(history) > self.max_redirects:
                 raise TooManyRedirects()
-            urls = ((resp.request.method, resp.url) for resp in history)
-            if (request.method, request.url) in urls:
-                raise RedirectLoop()
 
             response = await self.send_handling_auth(
                 request, auth=auth, timeout=timeout, history=history
diff --git a/httpx/_exceptions.py b/httpx/_exceptions.py
index 562c2ed..0f0ea06 100644
--- a/httpx/_exceptions.py
+++ b/httpx/_exceptions.py
@@ -101,12 +101,6 @@ class TooManyRedirects(RedirectError):
     """
 
 
-class RedirectLoop(RedirectError):
-    """
-    Infinite redirect loop.
-    """
-
-
 class NotRedirectResponse(RedirectError):
     """
     Response was not a redirect response.


Test patch:
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/client/test_redirects.py b/tests/client/test_redirects.py
index ad138e5..190ff09 100644
--- a/tests/client/test_redirects.py
+++ b/tests/client/test_redirects.py
@@ -7,7 +7,6 @@ from httpx import (
     URL,
     AsyncClient,
     NotRedirectResponse,
-    RedirectLoop,
     Request,
     RequestBodyUnavailable,
     Response,
@@ -245,20 +244,10 @@ async def test_too_many_redirects_calling_next():
 @pytest.mark.usefixtures("async_environment")
 async def test_redirect_loop():
     client = AsyncClient(dispatch=MockDispatch())
-    with pytest.raises(RedirectLoop):
+    with pytest.raises(TooManyRedirects):
         await client.get("https://example.org/redirect_loop")
 
 
-@pytest.mark.usefixtures("async_environment")
-async def test_redirect_loop_calling_next():
-    client = AsyncClient(dispatch=MockDispatch())
-    url = "https://example.org/redirect_loop"
-    response = await client.get(url, allow_redirects=False)
-    with pytest.raises(RedirectLoop):
-        while response.is_redirect:
-            response = await response.anext()
-
-
 @pytest.mark.usefixtures("async_environment")
 async def test_cross_domain_redirect():
     client = AsyncClient(dispatch=MockDispatch())