]> git.ipfire.org Git - thirdparty/httpx.git/commitdiff
Tighten up top-level API to only expose public API (#608)
authorTom Christie <tom@tomchristie.com>
Fri, 6 Dec 2019 15:20:01 +0000 (15:20 +0000)
committerGitHub <noreply@github.com>
Fri, 6 Dec 2019 15:20:01 +0000 (15:20 +0000)
* Tighten up top-level API to only expose public API

* Leave HTTPProxyMode for backwards compat, raising warnings.

* Add missing import

21 files changed:
docs/advanced.md
httpx/__init__.py
httpx/dispatch/proxy_http.py
tests/client/test_auth.py
tests/client/test_cookies.py
tests/client/test_headers.py
tests/client/test_proxies.py
tests/client/test_queryparams.py
tests/client/test_redirects.py
tests/concurrency.py
tests/conftest.py
tests/dispatch/test_connection_pools.py
tests/dispatch/test_connections.py
tests/dispatch/test_proxy_http.py
tests/dispatch/utils.py
tests/models/test_url.py
tests/test_concurrency.py
tests/test_config.py
tests/test_decoders.py
tests/test_multipart.py
tests/test_timeouts.py

index 27edac8650dab8a9cdb145f25ef7b1caec8abee4..8845348d79ad6871476e4574191da8f0560bbc6a 100644 (file)
@@ -243,7 +243,7 @@ Proxies can be configured to have different behavior such as forwarding or tunne
 ```python
 proxy = httpx.HTTPProxy(
     proxy_url="https://127.0.0.1",
-    proxy_mode=httpx.HTTPProxyMode.TUNNEL_ONLY
+    proxy_mode="TUNNEL_ONLY"  # May be "TUNNEL_ONLY" or "FORWARD_ONLY". Defaults to "DEFAULT".
 )
 async with httpx.Client(proxies=proxy) as client:
     # This request will be tunneled instead of forwarded.
index b6cd6df2b73b41c04694fe78330db8c22d20a3d6..ce92f88593c41cef7e33f195d046bc0b55f80a2d 100644 (file)
@@ -2,23 +2,11 @@ from .__version__ import __description__, __title__, __version__
 from .api import delete, get, head, options, patch, post, put, request, stream
 from .auth import BasicAuth, DigestAuth
 from .client import Client
-from .concurrency.asyncio import AsyncioBackend
-from .concurrency.base import BasePoolSemaphore, BaseSocketStream, ConcurrencyBackend
-from .config import (
-    USER_AGENT,
-    CertTypes,
-    PoolLimits,
-    SSLConfig,
-    Timeout,
-    TimeoutConfig,
-    TimeoutTypes,
-    VerifyTypes,
-)
-from .dispatch.base import Dispatcher
-from .dispatch.connection import HTTPConnection
-from .dispatch.connection_pool import ConnectionPool
+from .config import TimeoutConfig  # For 0.8 backwards compat.
+from .config import PoolLimits, Timeout
 from .dispatch.proxy_http import HTTPProxy, HTTPProxyMode
 from .exceptions import (
+    ConnectionClosed,
     ConnectTimeout,
     CookieConflict,
     DecodingError,
@@ -38,23 +26,7 @@ from .exceptions import (
     TooManyRedirects,
     WriteTimeout,
 )
-from .models import (
-    URL,
-    AuthTypes,
-    Cookies,
-    CookieTypes,
-    Headers,
-    HeaderTypes,
-    Origin,
-    QueryParams,
-    QueryParamTypes,
-    Request,
-    RequestData,
-    RequestFiles,
-    Response,
-    ResponseContent,
-    URLTypes,
-)
+from .models import URL, Cookies, Headers, Origin, QueryParams, Request, Response
 from .status_codes import StatusCode, codes
 
 __all__ = [
@@ -71,24 +43,18 @@ __all__ = [
     "put",
     "request",
     "stream",
+    "codes",
     "BasicAuth",
     "Client",
     "DigestAuth",
-    "AsyncioBackend",
-    "USER_AGENT",
-    "CertTypes",
     "PoolLimits",
-    "SSLConfig",
     "Timeout",
-    "TimeoutConfig",
-    "VerifyTypes",
-    "HTTPConnection",
-    "BasePoolSemaphore",
-    "ConnectionPool",
+    "TimeoutConfig",  # For 0.8 backwards compat.
     "HTTPProxy",
-    "HTTPProxyMode",
+    "HTTPProxyMode",  # For 0.8 backwards compat.
     "ConnectTimeout",
     "CookieConflict",
+    "ConnectionClosed",
     "DecodingError",
     "HTTPError",
     "InvalidURL",
@@ -106,25 +72,14 @@ __all__ = [
     "WriteTimeout",
     "BaseSocketStream",
     "ConcurrencyBackend",
-    "Dispatcher",
     "URL",
-    "URLTypes",
     "StatusCode",
-    "codes",
-    "TimeoutTypes",
-    "AuthTypes",
     "Cookies",
-    "CookieTypes",
     "Headers",
-    "HeaderTypes",
     "Origin",
     "QueryParams",
-    "QueryParamTypes",
     "Request",
-    "RequestData",
     "TimeoutException",
     "Response",
-    "ResponseContent",
-    "RequestFiles",
     "DigestAuth",
 ]
index a6e4973661d3502ec8f047c12d8402f8f1dc62a0..7da35f5b71382dc1a5509e6245c740e821cd63c2 100644 (file)
@@ -1,5 +1,6 @@
 import enum
 import typing
+import warnings
 from base64 import b64encode
 
 from ..concurrency.base import ConcurrencyBackend
@@ -14,11 +15,18 @@ logger = get_logger(__name__)
 
 
 class HTTPProxyMode(enum.Enum):
+    # This enum is pending deprecation in order to reduce API surface area,
+    # but is currently still around for 0.8 backwards compat.
     DEFAULT = "DEFAULT"
     FORWARD_ONLY = "FORWARD_ONLY"
     TUNNEL_ONLY = "TUNNEL_ONLY"
 
 
+DEFAULT_MODE = "DEFAULT"
+FORWARD_ONLY = "FORWARD_ONLY"
+TUNNEL_ONLY = "TUNNEL_ONLY"
+
+
 class HTTPProxy(ConnectionPool):
     """A proxy that sends requests to the recipient server
     on behalf of the connecting client.
@@ -29,7 +37,7 @@ class HTTPProxy(ConnectionPool):
         proxy_url: URLTypes,
         *,
         proxy_headers: HeaderTypes = None,
-        proxy_mode: HTTPProxyMode = HTTPProxyMode.DEFAULT,
+        proxy_mode: str = "DEFAULT",
         verify: VerifyTypes = True,
         cert: CertTypes = None,
         trust_env: bool = None,
@@ -38,6 +46,15 @@ class HTTPProxy(ConnectionPool):
         backend: typing.Union[str, ConcurrencyBackend] = "auto",
     ):
 
+        if isinstance(proxy_mode, HTTPProxyMode):
+            warnings.warn(
+                "The 'HTTPProxyMode' enum is pending deprecation. "
+                "Use a plain string instead. proxy_mode='FORWARD_ONLY', or "
+                "proxy_mode='TUNNEL_ONLY'."
+            )
+            proxy_mode = proxy_mode.value
+        assert proxy_mode in ("DEFAULT", "FORWARD_ONLY", "TUNNEL_ONLY")
+
         super(HTTPProxy, self).__init__(
             verify=verify,
             cert=cert,
@@ -162,8 +179,8 @@ class HTTPProxy(ConnectionPool):
         tunnel all 'HTTPS' requests.
         """
         return (
-            self.proxy_mode == HTTPProxyMode.DEFAULT and not origin.is_ssl
-        ) or self.proxy_mode == HTTPProxyMode.FORWARD_ONLY
+            self.proxy_mode == DEFAULT_MODE and not origin.is_ssl
+        ) or self.proxy_mode == FORWARD_ONLY
 
     async def send(
         self,
index d81ddb11a046db9b9fd3192186b3ea5c55cc7fda..16cf86d27024e7714afe9b0cad2ba5d6794f6de3 100644 (file)
@@ -4,18 +4,9 @@ import os
 
 import pytest
 
-from httpx import (
-    URL,
-    CertTypes,
-    Client,
-    DigestAuth,
-    Dispatcher,
-    ProtocolError,
-    Request,
-    Response,
-    TimeoutTypes,
-    VerifyTypes,
-)
+from httpx import URL, Client, DigestAuth, ProtocolError, Request, Response
+from httpx.config import CertTypes, TimeoutTypes, VerifyTypes
+from httpx.dispatch.base import Dispatcher
 
 
 class MockDispatch(Dispatcher):
index b20631dfbb0d788a173331fb9f1e52844ce7994f..2cdf42bc5f3d5cc45adddbacbd0a6780a4633306 100644 (file)
@@ -3,16 +3,9 @@ from http.cookiejar import Cookie, CookieJar
 
 import pytest
 
-from httpx import (
-    CertTypes,
-    Client,
-    Cookies,
-    Dispatcher,
-    Request,
-    Response,
-    TimeoutTypes,
-    VerifyTypes,
-)
+from httpx import Client, Cookies, Request, Response
+from httpx.config import CertTypes, TimeoutTypes, VerifyTypes
+from httpx.dispatch.base import Dispatcher
 
 
 class MockDispatch(Dispatcher):
index a7921c0579d1c1194f8e5487745d123da980ec1c..37a5683d7f61e8d4cd52b9193de4343d0b451d39 100755 (executable)
@@ -4,17 +4,9 @@ import json
 
 import pytest
 
-from httpx import (
-    CertTypes,
-    Client,
-    Dispatcher,
-    Request,
-    Response,
-    TimeoutTypes,
-    VerifyTypes,
-    __version__,
-    models,
-)
+from httpx import Client, Headers, Request, Response, __version__
+from httpx.config import CertTypes, TimeoutTypes, VerifyTypes
+from httpx.dispatch.base import Dispatcher
 
 
 class MockDispatch(Dispatcher):
@@ -132,7 +124,7 @@ async def test_header_update():
 
 
 def test_header_does_not_exist():
-    headers = models.Headers({"foo": "bar"})
+    headers = Headers({"foo": "bar"})
     with pytest.raises(KeyError):
         del headers["baz"]
 
index 89af4d61eaae9839989296746598003fcb3efb7c..2e05b8644c6b9125349efbed85c908bf2f40ef3a 100644 (file)
@@ -40,7 +40,6 @@ def test_proxies_has_same_properties_as_dispatch():
     pool = client.dispatch
     proxy = client.proxies["all"]
 
-    assert isinstance(pool, httpx.ConnectionPool)
     assert isinstance(proxy, httpx.HTTPProxy)
 
     for prop in [
@@ -101,7 +100,6 @@ def test_dispatcher_for_request(url, proxies, expected):
     dispatcher = client.dispatcher_for_url(httpx.URL(url))
 
     if expected is None:
-        assert isinstance(dispatcher, httpx.ConnectionPool)
         assert dispatcher is client.dispatch
     else:
         assert isinstance(dispatcher, httpx.HTTPProxy)
index 8fcba512a5d5c7703de9e3579dd97744273c8a02..9417aab156b363df53d3e3ab3b4109a4d926baba 100644 (file)
@@ -2,17 +2,9 @@ import json
 
 import pytest
 
-from httpx import (
-    CertTypes,
-    Client,
-    Dispatcher,
-    QueryParams,
-    Request,
-    Response,
-    TimeoutTypes,
-    VerifyTypes,
-)
-from httpx.models import URL
+from httpx import URL, Client, QueryParams, Request, Response
+from httpx.config import CertTypes, TimeoutTypes, VerifyTypes
+from httpx.dispatch.base import Dispatcher
 
 
 class MockDispatch(Dispatcher):
index 4dbf2b85aa7a974bb955d025191e02a8c890ad05..5e763b002d53e3db5d7db1062deb0dcb16f9e2c6 100644 (file)
@@ -5,19 +5,17 @@ import pytest
 
 from httpx import (
     URL,
-    CertTypes,
     Client,
-    Dispatcher,
     NotRedirectResponse,
     RedirectBodyUnavailable,
     RedirectLoop,
     Request,
     Response,
-    TimeoutTypes,
     TooManyRedirects,
-    VerifyTypes,
     codes,
 )
+from httpx.config import CertTypes, TimeoutTypes, VerifyTypes
+from httpx.dispatch.base import Dispatcher
 
 
 class MockDispatch(Dispatcher):
index 0d7d1350831a8f4e153454c158a4a7cb2d7ab5c1..74c3ab05bfddaf3ac5688e10db426c4c885bc4f3 100644 (file)
@@ -9,7 +9,7 @@ import typing
 
 import trio
 
-from httpx import AsyncioBackend
+from httpx.concurrency.asyncio import AsyncioBackend
 from httpx.concurrency.trio import TrioBackend
 
 
index b803bd1d504ad6641486afbe631b11aecc4c87c2..a3037cb2b0614716999cb275d4b5a258e2a8932f 100644 (file)
@@ -17,7 +17,8 @@ from cryptography.hazmat.primitives.serialization import (
 from uvicorn.config import Config
 from uvicorn.main import Server
 
-from httpx import URL, AsyncioBackend
+from httpx import URL
+from httpx.concurrency.asyncio import AsyncioBackend
 from httpx.concurrency.trio import TrioBackend
 
 ENVIRONMENT_VARIABLES = {
index 6f4361dd115310694e4824de84b74b4f65690b07..4311655c59e126372a1ba56833540dba60d2e983 100644 (file)
@@ -1,11 +1,12 @@
 import httpx
+from httpx.dispatch.connection_pool import ConnectionPool
 
 
 async def test_keepalive_connections(server, backend):
     """
     Connections should default to staying in a keep-alive state.
     """
-    async with httpx.ConnectionPool(backend=backend) as http:
+    async with ConnectionPool(backend=backend) as http:
         response = await http.request("GET", server.url)
         await response.read()
         assert len(http.active_connections) == 0
@@ -21,7 +22,7 @@ async def test_differing_connection_keys(server, backend):
     """
     Connections to differing connection keys should result in multiple connections.
     """
-    async with httpx.ConnectionPool(backend=backend) as http:
+    async with ConnectionPool(backend=backend) as http:
         response = await http.request("GET", server.url)
         await response.read()
         assert len(http.active_connections) == 0
@@ -39,7 +40,7 @@ async def test_soft_limit(server, backend):
     """
     pool_limits = httpx.PoolLimits(soft_limit=1)
 
-    async with httpx.ConnectionPool(pool_limits=pool_limits, backend=backend) as http:
+    async with ConnectionPool(pool_limits=pool_limits, backend=backend) as http:
         response = await http.request("GET", server.url)
         await response.read()
         assert len(http.active_connections) == 0
@@ -55,7 +56,7 @@ async def test_streaming_response_holds_connection(server, backend):
     """
     A streaming request should hold the connection open until the response is read.
     """
-    async with httpx.ConnectionPool(backend=backend) as http:
+    async with ConnectionPool(backend=backend) as http:
         response = await http.request("GET", server.url)
         assert len(http.active_connections) == 1
         assert len(http.keepalive_connections) == 0
@@ -70,7 +71,7 @@ async def test_multiple_concurrent_connections(server, backend):
     """
     Multiple conncurrent requests should open multiple conncurrent connections.
     """
-    async with httpx.ConnectionPool(backend=backend) as http:
+    async with ConnectionPool(backend=backend) as http:
         response_a = await http.request("GET", server.url)
         assert len(http.active_connections) == 1
         assert len(http.keepalive_connections) == 0
@@ -93,7 +94,7 @@ async def test_close_connections(server, backend):
     Using a `Connection: close` header should close the connection.
     """
     headers = [(b"connection", b"close")]
-    async with httpx.ConnectionPool(backend=backend) as http:
+    async with ConnectionPool(backend=backend) as http:
         response = await http.request("GET", server.url, headers=headers)
         await response.read()
         assert len(http.active_connections) == 0
@@ -104,7 +105,7 @@ async def test_standard_response_close(server, backend):
     """
     A standard close should keep the connection open.
     """
-    async with httpx.ConnectionPool(backend=backend) as http:
+    async with ConnectionPool(backend=backend) as http:
         response = await http.request("GET", server.url)
         await response.read()
         await response.close()
@@ -116,7 +117,7 @@ async def test_premature_response_close(server, backend):
     """
     A premature close should close the connection.
     """
-    async with httpx.ConnectionPool(backend=backend) as http:
+    async with ConnectionPool(backend=backend) as http:
         response = await http.request("GET", server.url)
         await response.close()
         assert len(http.active_connections) == 0
@@ -130,7 +131,7 @@ async def test_keepalive_connection_closed_by_server_is_reestablished(
     Upon keep-alive connection closed by remote a new connection
     should be reestablished.
     """
-    async with httpx.ConnectionPool(backend=backend) as http:
+    async with ConnectionPool(backend=backend) as http:
         response = await http.request("GET", server.url)
         await response.read()
 
@@ -150,7 +151,7 @@ async def test_keepalive_http2_connection_closed_by_server_is_reestablished(
     Upon keep-alive connection closed by remote a new connection
     should be reestablished.
     """
-    async with httpx.ConnectionPool(backend=backend) as http:
+    async with ConnectionPool(backend=backend) as http:
         response = await http.request("GET", server.url)
         await response.read()
 
@@ -168,7 +169,7 @@ async def test_connection_closed_free_semaphore_on_acquire(server, restart, back
     Verify that max_connections semaphore is released
     properly on a disconnected connection.
     """
-    async with httpx.ConnectionPool(
+    async with ConnectionPool(
         pool_limits=httpx.PoolLimits(hard_limit=1), backend=backend
     ) as http:
         response = await http.request("GET", server.url)
index f39ac4fb6d7328c8166884fb12a6d5aa43a8e898..54fb2ba7addd222efcfaf4026c76ab24f22e0db2 100644 (file)
@@ -1,6 +1,7 @@
 import pytest
 
-from httpx import HTTPConnection, exceptions
+import httpx
+from httpx.dispatch.connection import HTTPConnection
 
 
 async def test_get(server, backend):
@@ -18,7 +19,7 @@ async def test_post(server, backend):
 
 
 async def test_premature_close(server, backend):
-    with pytest.raises(exceptions.ConnectionClosed):
+    with pytest.raises(httpx.ConnectionClosed):
         async with HTTPConnection(origin=server.url, backend=backend) as conn:
             response = await conn.request(
                 "GET", server.url.copy_with(path="/premature_close")
index 4776a8d2cf3e4fb6cc617ab5e6732a2f16772bd2..4234b9847f7374e0dfda1e7a25e0be3cb859ccae 100644 (file)
@@ -22,9 +22,7 @@ async def test_proxy_tunnel_success(backend):
         backend=backend,
     )
     async with httpx.HTTPProxy(
-        proxy_url="http://127.0.0.1:8000",
-        backend=raw_io,
-        proxy_mode=httpx.HTTPProxyMode.TUNNEL_ONLY,
+        proxy_url="http://127.0.0.1:8000", backend=raw_io, proxy_mode="TUNNEL_ONLY",
     ) as proxy:
         response = await proxy.request("GET", "http://example.com")
 
@@ -60,9 +58,7 @@ async def test_proxy_tunnel_non_2xx_response(backend, status_code):
 
     with pytest.raises(httpx.ProxyError) as e:
         async with httpx.HTTPProxy(
-            proxy_url="http://127.0.0.1:8000",
-            backend=raw_io,
-            proxy_mode=httpx.HTTPProxyMode.TUNNEL_ONLY,
+            proxy_url="http://127.0.0.1:8000", backend=raw_io, proxy_mode="TUNNEL_ONLY",
         ) as proxy:
             await proxy.request("GET", "http://example.com")
 
@@ -112,9 +108,7 @@ async def test_proxy_tunnel_start_tls(backend):
         backend=backend,
     )
     async with httpx.HTTPProxy(
-        proxy_url="http://127.0.0.1:8000",
-        backend=raw_io,
-        proxy_mode=httpx.HTTPProxyMode.TUNNEL_ONLY,
+        proxy_url="http://127.0.0.1:8000", backend=raw_io, proxy_mode="TUNNEL_ONLY",
     ) as proxy:
         resp = await proxy.request("GET", "https://example.com")
 
@@ -150,9 +144,7 @@ async def test_proxy_tunnel_start_tls(backend):
     assert recv[4].startswith(b"GET /target HTTP/1.1\r\nhost: example.com\r\n")
 
 
-@pytest.mark.parametrize(
-    "proxy_mode", [httpx.HTTPProxyMode.FORWARD_ONLY, httpx.HTTPProxyMode.DEFAULT]
-)
+@pytest.mark.parametrize("proxy_mode", ["FORWARD_ONLY", "DEFAULT"])
 async def test_proxy_forwarding(backend, proxy_mode):
     raw_io = MockRawSocketBackend(
         data_to_send=(
@@ -204,11 +196,11 @@ def test_proxy_repr():
     proxy = httpx.HTTPProxy(
         "http://127.0.0.1:1080",
         proxy_headers={"Custom": "Header"},
-        proxy_mode=httpx.HTTPProxyMode.DEFAULT,
+        proxy_mode="DEFAULT",
     )
 
     assert repr(proxy) == (
         "HTTPProxy(proxy_url=URL('http://127.0.0.1:1080') "
         "proxy_headers=Headers({'custom': 'Header'}) "
-        "proxy_mode=<HTTPProxyMode.DEFAULT: 'DEFAULT'>)"
+        "proxy_mode='DEFAULT')"
     )
index deb9a8fa4c45370ceabb7f8c2571ae52a794f651..2f26fda3b3fd47ca2d97c20e798d701e8e7df1ce 100644 (file)
@@ -5,7 +5,9 @@ import h2.config
 import h2.connection
 import h2.events
 
-from httpx import AsyncioBackend, BaseSocketStream, Request, Timeout
+from httpx import Request, Timeout
+from httpx.concurrency.asyncio import AsyncioBackend
+from httpx.concurrency.base import BaseSocketStream
 from tests.concurrency import sleep
 
 
index c4a74a8c210dcb0e8fe57be7fa4f29860a0ab0e6..f6c7ab4de7f36e1b6bcca305fb57f78b1fac1a9f 100644 (file)
@@ -1,7 +1,6 @@
 import pytest
 
-from httpx import URL, Origin
-from httpx.exceptions import InvalidURL
+from httpx import URL, InvalidURL, Origin
 
 
 @pytest.mark.parametrize(
index 0898dd4bb7370c3cc1b1982d5c29f33fe559f9f8..a3b1df7106c2abd1d88119a39674bef76209bb4b 100644 (file)
@@ -1,8 +1,10 @@
 import pytest
 import trio
 
-from httpx import AsyncioBackend, SSLConfig, Timeout
+from httpx import Timeout
+from httpx.concurrency.asyncio import AsyncioBackend
 from httpx.concurrency.trio import TrioBackend
+from httpx.config import SSLConfig
 from tests.concurrency import run_concurrently, sleep
 
 
index ad7a626caec3d802dc8dc0433dbfdde17d68acba..f97680c452200df68e58ae677c9632b64e1d1237 100644 (file)
@@ -7,23 +7,24 @@ from pathlib import Path
 import pytest
 
 import httpx
+from httpx.config import SSLConfig
 
 
 def test_load_ssl_config():
-    ssl_config = httpx.SSLConfig()
+    ssl_config = SSLConfig()
     context = ssl_config.load_ssl_context()
     assert context.verify_mode == ssl.VerifyMode.CERT_REQUIRED
     assert context.check_hostname is True
 
 
 def test_load_ssl_config_verify_non_existing_path():
-    ssl_config = httpx.SSLConfig(verify="/path/to/nowhere")
+    ssl_config = SSLConfig(verify="/path/to/nowhere")
     with pytest.raises(IOError):
         ssl_config.load_ssl_context()
 
 
 def test_load_ssl_config_verify_existing_file():
-    ssl_config = httpx.SSLConfig(verify=httpx.config.DEFAULT_CA_BUNDLE_PATH)
+    ssl_config = SSLConfig(verify=httpx.config.DEFAULT_CA_BUNDLE_PATH)
     context = ssl_config.load_ssl_context()
     assert context.verify_mode == ssl.VerifyMode.CERT_REQUIRED
     assert context.check_hostname is True
@@ -36,7 +37,7 @@ def test_load_ssl_config_verify_env_file(https_server, ca_cert_pem_file, config)
         if config.endswith("_FILE")
         else str(Path(ca_cert_pem_file).parent)
     )
-    ssl_config = httpx.SSLConfig(trust_env=True)
+    ssl_config = SSLConfig(trust_env=True)
     context = ssl_config.load_ssl_context()
     assert context.verify_mode == ssl.VerifyMode.CERT_REQUIRED
     assert context.check_hostname is True
@@ -55,14 +56,14 @@ def test_load_ssl_config_verify_env_file(https_server, ca_cert_pem_file, config)
 
 def test_load_ssl_config_verify_directory():
     path = httpx.config.DEFAULT_CA_BUNDLE_PATH.parent
-    ssl_config = httpx.SSLConfig(verify=path)
+    ssl_config = SSLConfig(verify=path)
     context = ssl_config.load_ssl_context()
     assert context.verify_mode == ssl.VerifyMode.CERT_REQUIRED
     assert context.check_hostname is True
 
 
 def test_load_ssl_config_cert_and_key(cert_pem_file, cert_private_key_file):
-    ssl_config = httpx.SSLConfig(cert=(cert_pem_file, cert_private_key_file))
+    ssl_config = SSLConfig(cert=(cert_pem_file, cert_private_key_file))
     context = ssl_config.load_ssl_context()
     assert context.verify_mode == ssl.VerifyMode.CERT_REQUIRED
     assert context.check_hostname is True
@@ -72,7 +73,7 @@ def test_load_ssl_config_cert_and_key(cert_pem_file, cert_private_key_file):
 def test_load_ssl_config_cert_and_encrypted_key(
     cert_pem_file, cert_encrypted_private_key_file, password
 ):
-    ssl_config = httpx.SSLConfig(
+    ssl_config = SSLConfig(
         cert=(cert_pem_file, cert_encrypted_private_key_file, password)
     )
     context = ssl_config.load_ssl_context()
@@ -83,7 +84,7 @@ def test_load_ssl_config_cert_and_encrypted_key(
 def test_load_ssl_config_cert_and_key_invalid_password(
     cert_pem_file, cert_encrypted_private_key_file
 ):
-    ssl_config = httpx.SSLConfig(
+    ssl_config = SSLConfig(
         cert=(cert_pem_file, cert_encrypted_private_key_file, "password1")
     )
 
@@ -92,13 +93,13 @@ def test_load_ssl_config_cert_and_key_invalid_password(
 
 
 def test_load_ssl_config_cert_without_key_raises(cert_pem_file):
-    ssl_config = httpx.SSLConfig(cert=cert_pem_file)
+    ssl_config = SSLConfig(cert=cert_pem_file)
     with pytest.raises(ssl.SSLError):
         ssl_config.load_ssl_context()
 
 
 def test_load_ssl_config_no_verify():
-    ssl_config = httpx.SSLConfig(verify=False)
+    ssl_config = SSLConfig(verify=False)
     context = ssl_config.load_ssl_context()
     assert context.verify_mode == ssl.VerifyMode.CERT_NONE
     assert context.check_hostname is False
@@ -106,7 +107,7 @@ def test_load_ssl_config_no_verify():
 
 def test_load_ssl_context():
     ssl_context = ssl.create_default_context()
-    ssl_config = httpx.SSLConfig(verify=ssl_context)
+    ssl_config = SSLConfig(verify=ssl_context)
 
     assert ssl_config.verify is True
     assert ssl_config.ssl_context is ssl_context
@@ -114,20 +115,20 @@ def test_load_ssl_context():
 
 
 def test_ssl_repr():
-    ssl = httpx.SSLConfig(verify=False)
+    ssl = SSLConfig(verify=False)
     assert repr(ssl) == "SSLConfig(cert=None, verify=False)"
 
 
+def test_ssl_eq():
+    ssl = SSLConfig(verify=False)
+    assert ssl == SSLConfig(verify=False)
+
+
 def test_limits_repr():
     limits = httpx.PoolLimits(hard_limit=100)
     assert repr(limits) == "PoolLimits(soft_limit=None, hard_limit=100)"
 
 
-def test_ssl_eq():
-    ssl = httpx.SSLConfig(verify=False)
-    assert ssl == httpx.SSLConfig(verify=False)
-
-
 def test_limits_eq():
     limits = httpx.PoolLimits(hard_limit=100)
     assert limits == httpx.PoolLimits(hard_limit=100)
@@ -196,7 +197,7 @@ def test_ssl_config_support_for_keylog_file(tmpdir, monkeypatch):  # pragma: noc
     with monkeypatch.context() as m:
         m.delenv("SSLKEYLOGFILE", raising=False)
 
-        ssl_config = httpx.SSLConfig(trust_env=True)
+        ssl_config = SSLConfig(trust_env=True)
         ssl_config.load_ssl_context()
 
         assert ssl_config.ssl_context.keylog_filename is None
@@ -206,12 +207,12 @@ def test_ssl_config_support_for_keylog_file(tmpdir, monkeypatch):  # pragma: noc
     with monkeypatch.context() as m:
         m.setenv("SSLKEYLOGFILE", filename)
 
-        ssl_config = httpx.SSLConfig(trust_env=True)
+        ssl_config = SSLConfig(trust_env=True)
         ssl_config.load_ssl_context()
 
         assert ssl_config.ssl_context.keylog_filename == filename
 
-        ssl_config = httpx.SSLConfig(trust_env=False)
+        ssl_config = SSLConfig(trust_env=False)
         ssl_config.load_ssl_context()
 
         assert ssl_config.ssl_context.keylog_filename is None
index a599ce026bfbb4062d77aaa537b225de1be372b8..76eeca0e320316d343b0412a9910051010db8948 100644 (file)
@@ -108,7 +108,7 @@ def test_decoding_errors(header_value):
     headers = [(b"Content-Encoding", header_value)]
     body = b"test 123"
     compressed_body = brotli.compress(body)[3:]
-    with pytest.raises(httpx.exceptions.DecodingError):
+    with pytest.raises(httpx.DecodingError):
         response = httpx.Response(200, headers=headers, content=compressed_body)
         response.content
 
index b5e2290a7f788f3b65673f938bda6c829116043a..718fb4565b53fd9b2600b28dd89c129b0573b664 100644 (file)
@@ -6,34 +6,28 @@ from unittest import mock
 
 import pytest
 
-from httpx import (
-    CertTypes,
-    Client,
-    Dispatcher,
-    Request,
-    Response,
-    TimeoutTypes,
-    VerifyTypes,
-    multipart,
-)
+import httpx
+from httpx.config import CertTypes, TimeoutTypes, VerifyTypes
+from httpx.dispatch.base import Dispatcher
+from httpx.multipart import _format_param, multipart_encode
 
 
 class MockDispatch(Dispatcher):
     async def send(
         self,
-        request: Request,
+        request: httpx.Request,
         verify: VerifyTypes = None,
         cert: CertTypes = None,
         timeout: TimeoutTypes = None,
-    ) -> Response:
+    ) -> httpx.Response:
         content = await request.read()
-        return Response(200, content=content)
+        return httpx.Response(200, content=content)
 
 
 @pytest.mark.parametrize(("value,output"), (("abc", b"abc"), (b"abc", b"abc")))
 @pytest.mark.asyncio
 async def test_multipart(value, output):
-    client = Client(dispatch=MockDispatch())
+    client = httpx.Client(dispatch=MockDispatch())
 
     # Test with a single-value 'data' argument, and a plain file 'files' argument.
     data = {"text": value}
@@ -57,7 +51,7 @@ async def test_multipart(value, output):
 @pytest.mark.parametrize(("key"), (b"abc", 1, 2.3, None))
 @pytest.mark.asyncio
 async def test_multipart_invalid_key(key):
-    client = Client(dispatch=MockDispatch())
+    client = httpx.Client(dispatch=MockDispatch())
     data = {key: "abc"}
     files = {"file": io.BytesIO(b"<file content>")}
     with pytest.raises(TypeError) as e:
@@ -68,7 +62,7 @@ async def test_multipart_invalid_key(key):
 @pytest.mark.parametrize(("value"), (1, 2.3, None, [None, "abc"], {None: "abc"}))
 @pytest.mark.asyncio
 async def test_multipart_invalid_value(value):
-    client = Client(dispatch=MockDispatch())
+    client = httpx.Client(dispatch=MockDispatch())
     data = {"text": value}
     files = {"file": io.BytesIO(b"<file content>")}
     with pytest.raises(TypeError) as e:
@@ -78,7 +72,7 @@ async def test_multipart_invalid_value(value):
 
 @pytest.mark.asyncio
 async def test_multipart_file_tuple():
-    client = Client(dispatch=MockDispatch())
+    client = httpx.Client(dispatch=MockDispatch())
 
     # Test with a list of values 'data' argument, and a tuple style 'files' argument.
     data = {"text": ["abc"]}
@@ -111,7 +105,7 @@ def test_multipart_encode():
 
     with mock.patch("os.urandom", return_value=os.urandom(16)):
         boundary = binascii.hexlify(os.urandom(16)).decode("ascii")
-        body, content_type = multipart.multipart_encode(data=data, files=files)
+        body, content_type = multipart_encode(data=data, files=files)
         assert content_type == f"multipart/form-data; boundary={boundary}"
         assert body == (
             '--{0}\r\nContent-Disposition: form-data; name="a"\r\n\r\n1\r\n'
@@ -135,7 +129,7 @@ def test_multipart_encode_files_allows_filenames_as_none():
     with mock.patch("os.urandom", return_value=os.urandom(16)):
         boundary = binascii.hexlify(os.urandom(16)).decode("ascii")
 
-        body, content_type = multipart.multipart_encode(data={}, files=files)
+        body, content_type = multipart_encode(data={}, files=files)
 
         assert content_type == f"multipart/form-data; boundary={boundary}"
         assert body == (
@@ -160,7 +154,7 @@ def test_multipart_encode_files_guesses_correct_content_type(
     with mock.patch("os.urandom", return_value=os.urandom(16)):
         boundary = binascii.hexlify(os.urandom(16)).decode("ascii")
 
-        body, content_type = multipart.multipart_encode(data={}, files=files)
+        body, content_type = multipart_encode(data={}, files=files)
 
         assert content_type == f"multipart/form-data; boundary={boundary}"
         assert body == (
@@ -176,7 +170,7 @@ def test_multipart_encode_files_allows_str_content():
     with mock.patch("os.urandom", return_value=os.urandom(16)):
         boundary = binascii.hexlify(os.urandom(16)).decode("ascii")
 
-        body, content_type = multipart.multipart_encode(data={}, files=files)
+        body, content_type = multipart_encode(data={}, files=files)
 
         assert content_type == f"multipart/form-data; boundary={boundary}"
         assert body == (
@@ -190,17 +184,17 @@ def test_multipart_encode_files_allows_str_content():
 
 class TestHeaderParamHTML5Formatting:
     def test_unicode(self):
-        param = multipart._format_param("filename", "n\u00e4me")
+        param = _format_param("filename", "n\u00e4me")
         assert param == b'filename="n\xc3\xa4me"'
 
     def test_ascii(self):
-        param = multipart._format_param("filename", b"name")
+        param = _format_param("filename", b"name")
         assert param == b'filename="name"'
 
     def test_unicode_escape(self):
-        param = multipart._format_param("filename", "hello\\world\u0022")
+        param = _format_param("filename", "hello\\world\u0022")
         assert param == b'filename="hello\\\\world%22"'
 
     def test_unicode_with_control_character(self):
-        param = multipart._format_param("filename", "hello\x1A\x1B\x1C")
+        param = _format_param("filename", "hello\x1A\x1B\x1C")
         assert param == b'filename="hello%1A\x1B%1C"'
index e64c4b97ad51a3a4352d0a8af200cd4a310006ab..80fefd33341363d80e671b6b19b936fcbfda8254 100644 (file)
@@ -1,49 +1,41 @@
 import pytest
 
-from httpx import (
-    Client,
-    ConnectTimeout,
-    PoolLimits,
-    PoolTimeout,
-    ReadTimeout,
-    Timeout,
-    WriteTimeout,
-)
+import httpx
 
 
 async def test_read_timeout(server, backend):
-    timeout = Timeout(read_timeout=1e-6)
+    timeout = httpx.Timeout(read_timeout=1e-6)
 
-    async with Client(timeout=timeout, backend=backend) as client:
-        with pytest.raises(ReadTimeout):
+    async with httpx.Client(timeout=timeout, backend=backend) as client:
+        with pytest.raises(httpx.ReadTimeout):
             await client.get(server.url.copy_with(path="/slow_response"))
 
 
 async def test_write_timeout(server, backend):
-    timeout = Timeout(write_timeout=1e-6)
+    timeout = httpx.Timeout(write_timeout=1e-6)
 
-    async with Client(timeout=timeout, backend=backend) as client:
-        with pytest.raises(WriteTimeout):
+    async with httpx.Client(timeout=timeout, backend=backend) as client:
+        with pytest.raises(httpx.WriteTimeout):
             data = b"*" * 1024 * 1024 * 100
             await client.put(server.url.copy_with(path="/slow_response"), data=data)
 
 
 async def test_connect_timeout(server, backend):
-    timeout = Timeout(connect_timeout=1e-6)
+    timeout = httpx.Timeout(connect_timeout=1e-6)
 
-    async with Client(timeout=timeout, backend=backend) as client:
-        with pytest.raises(ConnectTimeout):
+    async with httpx.Client(timeout=timeout, backend=backend) as client:
+        with pytest.raises(httpx.ConnectTimeout):
             # See https://stackoverflow.com/questions/100841/
             await client.get("http://10.255.255.1/")
 
 
 async def test_pool_timeout(server, backend):
-    pool_limits = PoolLimits(hard_limit=1)
-    timeout = Timeout(pool_timeout=1e-4)
+    pool_limits = httpx.PoolLimits(hard_limit=1)
+    timeout = httpx.Timeout(pool_timeout=1e-4)
 
-    async with Client(
+    async with httpx.Client(
         pool_limits=pool_limits, timeout=timeout, backend=backend
     ) as client:
         async with client.stream("GET", server.url):
-            with pytest.raises(PoolTimeout):
+            with pytest.raises(httpx.PoolTimeout):
                 await client.get("http://localhost:8000/")