Repository: encode/httpx

Issue:
Drop `backend=...` argument.
Now that we have async autodetection, I think we ought to consider dropping the `backend` argument from `AsyncClient.__init__(...)`, since it's not really required.

If anything it'd be better to find out if/when there are any cases that `sniffio` isn't able to correctly detect, and fix them, rather than having users be able to flick an override switch.

(It'd also potentially make it easier to have a clear split if we wanted to factor the dispatch out into an entirely independant package at some point, since we'd want the backend implementations to reside in the core networking package, rather than in `httpx`.)


Discussion:

Resolving patch:
diff --git a/httpx/_client.py b/httpx/_client.py
--- a/httpx/_client.py
+++ b/httpx/_client.py
@@ -5,7 +5,6 @@
 import hstspreload
 
 from ._auth import Auth, AuthTypes, BasicAuth, FunctionAuth
-from ._backends.base import ConcurrencyBackend
 from ._config import (
     DEFAULT_MAX_REDIRECTS,
     DEFAULT_POOL_LIMITS,
@@ -920,9 +919,6 @@ class AsyncClient(BaseClient):
     over the network.
     * **app** - *(optional)* An ASGI application to send requests to,
     rather than sending actual network requests.
-    * **backend** - *(optional)* A concurrency backend to use when issuing
-    async requests. Either 'auto', 'asyncio', 'trio', or a `ConcurrencyBackend`
-    instance. Defaults to 'auto', for autodetection.
     * **trust_env** - *(optional)* Enables or disables usage of environment
     variables for configuration.
     * **uds** - *(optional)* A path to a Unix domain socket to connect through.
@@ -945,7 +941,6 @@ def __init__(
         base_url: URLTypes = None,
         dispatch: AsyncDispatcher = None,
         app: typing.Callable = None,
-        backend: typing.Union[str, ConcurrencyBackend] = "auto",
         trust_env: bool = True,
         uds: str = None,
     ):
@@ -969,7 +964,6 @@ def __init__(
             pool_limits=pool_limits,
             dispatch=dispatch,
             app=app,
-            backend=backend,
             trust_env=trust_env,
             uds=uds,
         )
@@ -980,7 +974,6 @@ def __init__(
                 cert=cert,
                 http2=http2,
                 pool_limits=pool_limits,
-                backend=backend,
                 trust_env=trust_env,
             )
             for key, proxy in proxy_map.items()
@@ -994,7 +987,6 @@ def init_dispatch(
         pool_limits: PoolLimits = DEFAULT_POOL_LIMITS,
         dispatch: AsyncDispatcher = None,
         app: typing.Callable = None,
-        backend: typing.Union[str, ConcurrencyBackend] = "auto",
         trust_env: bool = True,
         uds: str = None,
     ) -> AsyncDispatcher:
@@ -1009,7 +1001,6 @@ def init_dispatch(
             cert=cert,
             http2=http2,
             pool_limits=pool_limits,
-            backend=backend,
             trust_env=trust_env,
             uds=uds,
         )
@@ -1021,7 +1012,6 @@ def init_proxy_dispatch(
         cert: CertTypes = None,
         http2: bool = False,
         pool_limits: PoolLimits = DEFAULT_POOL_LIMITS,
-        backend: typing.Union[str, ConcurrencyBackend] = "auto",
         trust_env: bool = True,
     ) -> AsyncDispatcher:
         return HTTPProxy(
@@ -1032,7 +1022,6 @@ def init_proxy_dispatch(
             cert=cert,
             http2=http2,
             pool_limits=pool_limits,
-            backend=backend,
             trust_env=trust_env,
         )
 


Test patch:
diff --git a/tests/client/test_async_client.py b/tests/client/test_async_client.py
--- a/tests/client/test_async_client.py
+++ b/tests/client/test_async_client.py
@@ -160,10 +160,3 @@ async def test_uds(uds_server):
     assert response.status_code == 200
     assert response.text == "Hello, world!"
     assert response.encoding == "iso-8859-1"
-
-
-async def test_explicit_backend(server, async_environment):
-    async with httpx.AsyncClient(backend=async_environment) as client:
-        response = await client.get(server.url)
-    assert response.status_code == 200
-    assert response.text == "Hello, world!"
diff --git a/tests/dispatch/test_http2.py b/tests/dispatch/test_http2.py
--- a/tests/dispatch/test_http2.py
+++ b/tests/dispatch/test_http2.py
@@ -7,6 +7,7 @@
 from h2.settings import SettingCodes
 
 from httpx import AsyncClient, Response, TimeoutException
+from httpx._dispatch.connection_pool import ConnectionPool
 
 from .utils import MockHTTP2Backend
 
@@ -25,8 +26,9 @@ async def app(request):
 @pytest.mark.asyncio
 async def test_http2_get_request():
     backend = MockHTTP2Backend(app=app)
+    dispatch = ConnectionPool(backend=backend, http2=True)
 
-    async with AsyncClient(backend=backend, http2=True) as client:
+    async with AsyncClient(dispatch=dispatch) as client:
         response = await client.get("http://example.org")
 
     assert response.status_code == 200
@@ -36,8 +38,9 @@ async def test_http2_get_request():
 @pytest.mark.asyncio
 async def test_http2_post_request():
     backend = MockHTTP2Backend(app=app)
+    dispatch = ConnectionPool(backend=backend, http2=True)
 
-    async with AsyncClient(backend=backend, http2=True) as client:
+    async with AsyncClient(dispatch=dispatch) as client:
         response = await client.post("http://example.org", data=b"<data>")
 
     assert response.status_code == 200
@@ -51,9 +54,10 @@ async def test_http2_post_request():
 @pytest.mark.asyncio
 async def test_http2_large_post_request():
     backend = MockHTTP2Backend(app=app)
+    dispatch = ConnectionPool(backend=backend, http2=True)
 
     data = b"a" * 100000
-    async with AsyncClient(backend=backend, http2=True) as client:
+    async with AsyncClient(dispatch=dispatch) as client:
         response = await client.post("http://example.org", data=data)
     assert response.status_code == 200
     assert json.loads(response.content) == {
@@ -66,8 +70,9 @@ async def test_http2_large_post_request():
 @pytest.mark.asyncio
 async def test_http2_multiple_requests():
     backend = MockHTTP2Backend(app=app)
+    dispatch = ConnectionPool(backend=backend, http2=True)
 
-    async with AsyncClient(backend=backend, http2=True) as client:
+    async with AsyncClient(dispatch=dispatch) as client:
         response_1 = await client.get("http://example.org/1")
         response_2 = await client.get("http://example.org/2")
         response_3 = await client.get("http://example.org/3")
@@ -89,8 +94,9 @@ async def test_http2_reconnect():
     be seemlessly reconnected.
     """
     backend = MockHTTP2Backend(app=app)
+    dispatch = ConnectionPool(backend=backend, http2=True)
 
-    async with AsyncClient(backend=backend, http2=True) as client:
+    async with AsyncClient(dispatch=dispatch) as client:
         response_1 = await client.get("http://example.org/1")
         backend.server.close_connection = True
         response_2 = await client.get("http://example.org/2")
@@ -105,8 +111,9 @@ async def test_http2_reconnect():
 @pytest.mark.asyncio
 async def test_http2_settings_in_handshake():
     backend = MockHTTP2Backend(app=app)
+    dispatch = ConnectionPool(backend=backend, http2=True)
 
-    async with AsyncClient(backend=backend, http2=True) as client:
+    async with AsyncClient(dispatch=dispatch) as client:
         await client.get("http://example.org")
 
     h2_conn = backend.server.conn
diff --git a/tests/dispatch/utils.py b/tests/dispatch/utils.py
--- a/tests/dispatch/utils.py
+++ b/tests/dispatch/utils.py
@@ -10,9 +10,9 @@
 
 
 class MockHTTP2Backend:
-    def __init__(self, app, backend="auto"):
+    def __init__(self, app):
         self.app = app
-        self.backend = lookup_backend(backend)
+        self.backend = lookup_backend()
         self.server = None
 
     async def open_tcp_stream(
@@ -31,7 +31,7 @@ def __getattr__(self, name: str) -> typing.Any:
 
 
 class MockHTTP2Server(BaseSocketStream):
-    def __init__(self, app, backend):
+    def __init__(self, app, backend: MockHTTP2Backend):
         config = h2.config.H2Configuration(client_side=False)
         self.conn = h2.connection.H2Connection(config=config)
         self.app = app
@@ -151,8 +151,8 @@ def send_return_data(self, stream_id):
 
 
 class MockRawSocketBackend:
-    def __init__(self, data_to_send=b"", backend="auto"):
-        self.backend = lookup_backend(backend)
+    def __init__(self, data_to_send=b""):
+        self.backend = lookup_backend()
         self.data_to_send = data_to_send
         self.received_data = []
         self.stream = MockRawSocketStream(self)