From: Tom Christie Date: Fri, 6 Dec 2019 15:20:01 +0000 (+0000) Subject: Tighten up top-level API to only expose public API (#608) X-Git-Tag: 0.9.0~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d15dc0b1f8e775f3debc09ff0228f5ca56eab322;p=thirdparty%2Fhttpx.git Tighten up top-level API to only expose public API (#608) * Tighten up top-level API to only expose public API * Leave HTTPProxyMode for backwards compat, raising warnings. * Add missing import --- diff --git a/docs/advanced.md b/docs/advanced.md index 27edac86..8845348d 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -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. diff --git a/httpx/__init__.py b/httpx/__init__.py index b6cd6df2..ce92f885 100644 --- a/httpx/__init__.py +++ b/httpx/__init__.py @@ -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", ] diff --git a/httpx/dispatch/proxy_http.py b/httpx/dispatch/proxy_http.py index a6e49736..7da35f5b 100644 --- a/httpx/dispatch/proxy_http.py +++ b/httpx/dispatch/proxy_http.py @@ -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, diff --git a/tests/client/test_auth.py b/tests/client/test_auth.py index d81ddb11..16cf86d2 100644 --- a/tests/client/test_auth.py +++ b/tests/client/test_auth.py @@ -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): diff --git a/tests/client/test_cookies.py b/tests/client/test_cookies.py index b20631df..2cdf42bc 100644 --- a/tests/client/test_cookies.py +++ b/tests/client/test_cookies.py @@ -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): diff --git a/tests/client/test_headers.py b/tests/client/test_headers.py index a7921c05..37a5683d 100755 --- a/tests/client/test_headers.py +++ b/tests/client/test_headers.py @@ -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"] diff --git a/tests/client/test_proxies.py b/tests/client/test_proxies.py index 89af4d61..2e05b864 100644 --- a/tests/client/test_proxies.py +++ b/tests/client/test_proxies.py @@ -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) diff --git a/tests/client/test_queryparams.py b/tests/client/test_queryparams.py index 8fcba512..9417aab1 100644 --- a/tests/client/test_queryparams.py +++ b/tests/client/test_queryparams.py @@ -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): diff --git a/tests/client/test_redirects.py b/tests/client/test_redirects.py index 4dbf2b85..5e763b00 100644 --- a/tests/client/test_redirects.py +++ b/tests/client/test_redirects.py @@ -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): diff --git a/tests/concurrency.py b/tests/concurrency.py index 0d7d1350..74c3ab05 100644 --- a/tests/concurrency.py +++ b/tests/concurrency.py @@ -9,7 +9,7 @@ import typing import trio -from httpx import AsyncioBackend +from httpx.concurrency.asyncio import AsyncioBackend from httpx.concurrency.trio import TrioBackend diff --git a/tests/conftest.py b/tests/conftest.py index b803bd1d..a3037cb2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -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 = { diff --git a/tests/dispatch/test_connection_pools.py b/tests/dispatch/test_connection_pools.py index 6f4361dd..4311655c 100644 --- a/tests/dispatch/test_connection_pools.py +++ b/tests/dispatch/test_connection_pools.py @@ -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) diff --git a/tests/dispatch/test_connections.py b/tests/dispatch/test_connections.py index f39ac4fb..54fb2ba7 100644 --- a/tests/dispatch/test_connections.py +++ b/tests/dispatch/test_connections.py @@ -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") diff --git a/tests/dispatch/test_proxy_http.py b/tests/dispatch/test_proxy_http.py index 4776a8d2..4234b984 100644 --- a/tests/dispatch/test_proxy_http.py +++ b/tests/dispatch/test_proxy_http.py @@ -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=)" + "proxy_mode='DEFAULT')" ) diff --git a/tests/dispatch/utils.py b/tests/dispatch/utils.py index deb9a8fa..2f26fda3 100644 --- a/tests/dispatch/utils.py +++ b/tests/dispatch/utils.py @@ -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 diff --git a/tests/models/test_url.py b/tests/models/test_url.py index c4a74a8c..f6c7ab4d 100644 --- a/tests/models/test_url.py +++ b/tests/models/test_url.py @@ -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( diff --git a/tests/test_concurrency.py b/tests/test_concurrency.py index 0898dd4b..a3b1df71 100644 --- a/tests/test_concurrency.py +++ b/tests/test_concurrency.py @@ -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 diff --git a/tests/test_config.py b/tests/test_config.py index ad7a626c..f97680c4 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -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 diff --git a/tests/test_decoders.py b/tests/test_decoders.py index a599ce02..76eeca0e 100644 --- a/tests/test_decoders.py +++ b/tests/test_decoders.py @@ -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 diff --git a/tests/test_multipart.py b/tests/test_multipart.py index b5e2290a..718fb456 100644 --- a/tests/test_multipart.py +++ b/tests/test_multipart.py @@ -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"")} 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"")} 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"' diff --git a/tests/test_timeouts.py b/tests/test_timeouts.py index e64c4b97..80fefd33 100644 --- a/tests/test_timeouts.py +++ b/tests/test_timeouts.py @@ -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/")