Repository: encode/httpx

Issue:
Deprecate ProxyLimits naming in favour of max_connections and max_keepalive
Prompted by https://github.com/encode/httpx/pull/942#discussion_r424341967

The `PoolLimits` class uses `soft_limit` and `hard_limit` whereas in httpcore we use `max_connections` and `max_keepalive` which are clearer.

Discussion:
tomchristie: The URLLib3 changes here will become redundant after #966, but I'm going to ignore that here to keep this PR tightly scoped.
tomchristie: Merged #967 into this as we're dependant on it.

Patch:
diff --git a/docs/advanced.md b/docs/advanced.md
index 4d1f1d8..2c1f1a6 100644
--- a/docs/advanced.md
+++ b/docs/advanced.md
@@ -404,14 +404,14 @@ response = client.get('http://example.com/')
 You can control the connection pool size using the `pool_limits` keyword
 argument on the client. It takes instances of `httpx.PoolLimits` which define:
 
-- `soft_limit`, number of allowable keep-alive connections, or `None` to always
+- `max_keepalive`, number of allowable keep-alive connections, or `None` to always
 allow. (Defaults 10)
-- `hard_limit`, maximum number of allowable connections, or` None` for no limits.
+- `max_connections`, maximum number of allowable connections, or` None` for no limits.
 (Default 100)
 
 
 ```python
-limits = httpx.PoolLimits(soft_limit=5, hard_limit=10)
+limits = httpx.PoolLimits(max_keepalive=5, max_connections=10)
 client = httpx.Client(pool_limits=limits)
 ```
 
diff --git a/httpx/_client.py b/httpx/_client.py
index 8c3e47f..a526a0c 100644
--- a/httpx/_client.py
+++ b/httpx/_client.py
@@ -504,13 +504,11 @@ class Client(BaseClient):
         ssl_context = SSLConfig(
             verify=verify, cert=cert, trust_env=trust_env
         ).ssl_context
-        max_keepalive = pool_limits.soft_limit
-        max_connections = pool_limits.hard_limit
 
         return httpcore.SyncConnectionPool(
             ssl_context=ssl_context,
-            max_keepalive=max_keepalive,
-            max_connections=max_connections,
+            max_keepalive=pool_limits.max_keepalive,
+            max_connections=pool_limits.max_connections,
         )
 
     def init_proxy_transport(
@@ -524,16 +522,14 @@ class Client(BaseClient):
         ssl_context = SSLConfig(
             verify=verify, cert=cert, trust_env=trust_env
         ).ssl_context
-        max_keepalive = pool_limits.soft_limit
-        max_connections = pool_limits.hard_limit
 
         return httpcore.SyncHTTPProxy(
             proxy_url=proxy.url.raw,
             proxy_headers=proxy.headers.raw,
             proxy_mode=proxy.mode,
             ssl_context=ssl_context,
-            max_keepalive=max_keepalive,
-            max_connections=max_connections,
+            max_keepalive=pool_limits.max_keepalive,
+            max_connections=pool_limits.max_connections,
         )
 
     def transport_for_url(self, url: URL) -> httpcore.SyncHTTPTransport:
@@ -1050,13 +1046,11 @@ class AsyncClient(BaseClient):
         ssl_context = SSLConfig(
             verify=verify, cert=cert, trust_env=trust_env
         ).ssl_context
-        max_keepalive = pool_limits.soft_limit
-        max_connections = pool_limits.hard_limit
 
         return httpcore.AsyncConnectionPool(
             ssl_context=ssl_context,
-            max_keepalive=max_keepalive,
-            max_connections=max_connections,
+            max_keepalive=pool_limits.max_keepalive,
+            max_connections=pool_limits.max_connections,
             http2=http2,
         )
 
@@ -1072,16 +1066,14 @@ class AsyncClient(BaseClient):
         ssl_context = SSLConfig(
             verify=verify, cert=cert, trust_env=trust_env
         ).ssl_context
-        max_keepalive = pool_limits.soft_limit
-        max_connections = pool_limits.hard_limit
 
         return httpcore.AsyncHTTPProxy(
             proxy_url=proxy.url.raw,
             proxy_headers=proxy.headers.raw,
             proxy_mode=proxy.mode,
             ssl_context=ssl_context,
-            max_keepalive=max_keepalive,
-            max_connections=max_connections,
+            max_keepalive=pool_limits.max_keepalive,
+            max_connections=pool_limits.max_connections,
             http2=http2,
         )
 
diff --git a/httpx/_config.py b/httpx/_config.py
index 1247882..0dc7a46 100644
--- a/httpx/_config.py
+++ b/httpx/_config.py
@@ -8,7 +8,7 @@ import certifi
 
 from ._models import URL, Headers
 from ._types import CertTypes, HeaderTypes, TimeoutTypes, URLTypes, VerifyTypes
-from ._utils import get_ca_bundle_from_env, get_logger
+from ._utils import get_ca_bundle_from_env, get_logger, warn_deprecated
 
 DEFAULT_CIPHERS = ":".join(
     [
@@ -288,29 +288,43 @@ class PoolLimits:
 
     **Parameters:**
 
-    * **soft_limit** - Allow the connection pool to maintain keep-alive connections
+    * **max_keepalive** - Allow the connection pool to maintain keep-alive connections
                        below this point.
-    * **hard_limit** - The maximum number of concurrent connections that may be
+    * **max_connections** - The maximum number of concurrent connections that may be
                        established.
     """
 
     def __init__(
-        self, *, soft_limit: int = None, hard_limit: int = None,
+        self,
+        *,
+        max_keepalive: int = None,
+        max_connections: int = None,
+        soft_limit: int = None,
+        hard_limit: int = None,
     ):
-        self.soft_limit = soft_limit
-        self.hard_limit = hard_limit
+        self.max_keepalive = max_keepalive
+        self.max_connections = max_connections
+        if soft_limit is not None:  # pragma: nocover
+            self.max_keepalive = soft_limit
+            warn_deprecated("'soft_limit' is deprecated. Use 'max_keepalive' instead.",)
+        if hard_limit is not None:  # pragma: nocover
+            self.max_connections = hard_limit
+            warn_deprecated(
+                "'hard_limit' is deprecated. Use 'max_connections' instead.",
+            )
 
     def __eq__(self, other: typing.Any) -> bool:
         return (
             isinstance(other, self.__class__)
-            and self.soft_limit == other.soft_limit
-            and self.hard_limit == other.hard_limit
+            and self.max_keepalive == other.max_keepalive
+            and self.max_connections == other.max_connections
         )
 
     def __repr__(self) -> str:
         class_name = self.__class__.__name__
         return (
-            f"{class_name}(soft_limit={self.soft_limit}, hard_limit={self.hard_limit})"
+            f"{class_name}(max_keepalive={self.max_keepalive}, "
+            f"max_connections={self.max_connections})"
         )
 
 
diff --git a/httpx/_transports/urllib3.py b/httpx/_transports/urllib3.py
index 71882e1..714c925 100644
--- a/httpx/_transports/urllib3.py
+++ b/httpx/_transports/urllib3.py
@@ -34,23 +34,23 @@ class URLLib3Transport(httpcore.SyncHTTPTransport):
         ssl_config = SSLConfig(
             verify=verify, cert=cert, trust_env=trust_env, http2=False
         )
-        hard_limit = pool_limits.hard_limit
-        soft_limit = pool_limits.soft_limit
+        max_connections = pool_limits.max_connections
+        max_keepalive = pool_limits.max_keepalive
 
         # Our connection pool configuration doesn't quite match up with urllib3's
         # controls, but we can put sensible mappings in place:
-        if hard_limit is None:
+        if max_connections is None:
             block = False
-            if soft_limit is None:
+            if max_keepalive is None:
                 num_pools = 1000
                 maxsize = 1000
             else:
-                num_pools = int(math.sqrt(soft_limit))
-                maxsize = int(math.sqrt(soft_limit))
+                num_pools = int(math.sqrt(max_keepalive))
+                maxsize = int(math.sqrt(max_keepalive))
         else:
             block = True
-            num_pools = int(math.sqrt(hard_limit))
-            maxsize = int(math.sqrt(hard_limit))
+            num_pools = int(math.sqrt(max_connections))
+            maxsize = int(math.sqrt(max_connections))
 
         self.pool = self.init_pool_manager(
             proxy=proxy,


Test patch:
diff --git a/tests/test_config.py b/tests/test_config.py
index f0ecf31..41d8191 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -118,13 +118,13 @@ def test_ssl_eq():
 
 
 def test_limits_repr():
-    limits = httpx.PoolLimits(hard_limit=100)
-    assert repr(limits) == "PoolLimits(soft_limit=None, hard_limit=100)"
+    limits = httpx.PoolLimits(max_connections=100)
+    assert repr(limits) == "PoolLimits(max_keepalive=None, max_connections=100)"
 
 
 def test_limits_eq():
-    limits = httpx.PoolLimits(hard_limit=100)
-    assert limits == httpx.PoolLimits(hard_limit=100)
+    limits = httpx.PoolLimits(max_connections=100)
+    assert limits == httpx.PoolLimits(max_connections=100)
 
 
 def test_timeout_eq():