]> git.ipfire.org Git - thirdparty/httpx.git/commitdiff
Version 0.15.0 (#1301)
authorTom Christie <tom@tomchristie.com>
Tue, 22 Sep 2020 10:44:28 +0000 (11:44 +0100)
committerGitHub <noreply@github.com>
Tue, 22 Sep 2020 10:44:28 +0000 (11:44 +0100)
* Version 0.15.0

* Update CHANGELOG.md

Co-authored-by: Jamie Hewland <jamie.hewland@hpe.com>
* Escalate deprecations into removals.

* Deprecate overly verbose timeout parameter names

* Fully deprecate max_keepalive in favour of explicit max_keepalive_connections

* Fully deprecate PoolLimits in favour of Limits

* Deprecate instantiating 'Timeout' without fully explicit values

* Include deprecation notes in changelog

* Use httpcore 0.11.x

Co-authored-by: Jamie Hewland <jamie.hewland@hpe.com>
18 files changed:
CHANGELOG.md
httpx/__init__.py
httpx/__version__.py
httpx/_client.py
httpx/_config.py
httpx/_models.py
httpx/_transports/asgi.py
httpx/_transports/wsgi.py
setup.py
tests/client/test_auth.py
tests/client/test_client.py
tests/client/test_event_hooks.py
tests/client/test_redirects.py
tests/models/test_headers.py
tests/models/test_queryparams.py
tests/test_config.py
tests/test_timeouts.py
tests/utils.py

index 8bc3579a7526c2e70d843c56f81177e0b84f4bed..c889b4407f9825912cb8520ecbf3ce18f0688e15 100644 (file)
@@ -4,6 +4,48 @@ All notable changes to this project will be documented in this file.
 
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
 
+## 0.15.0
+
+### Added
+
+* Added support for event hooks. (Pull #1246)
+* Added support for authentication flows which require either sync or async I/O. (Pull #1217)
+* Added support for monitoring download progress with `response.num_bytes_downloaded`. (Pull #1268)
+* Added `Request(content=...)` for byte content, instead of overloading `Request(data=...)` (Pull #1266)
+* Added support for all URL components as parameter names when using `url.copy_with(...)`. (Pull #1285)
+* Neater split between automatically populated headers on `Request` instances, vs default `client.headers`. (Pull #1248)
+* Unclosed `AsyncClient` instances will now raise warnings if garbage collected. (Pull #1197)
+* Support `Response(content=..., text=..., html=..., json=...)` for creating usable response instances in code. (Pull #1265, #1297)
+* Support instantiating requests from the low-level transport API. (Pull #1293)
+* Raise errors on invalid URL types. (Pull #1259)
+
+### Changed
+
+* Cleaned up expected behaviour for URL escaping. `url.path` is now URL escaped. (Pull #1285)
+* Cleaned up expected behaviour for bytes vs str in URL components. `url.userinfo` and `url.query` are not URL escaped, and so return bytes. (Pull #1285)
+* Drop `url.authority` property in favour of `url.netloc`, since "authority" was semantically incorrect. (Pull #1285)
+* Drop `url.full_path` property in favour of `url.raw_path`, for better consistency with other parts of the API. (Pull #1285)
+* No longer use the `chardet` library for auto-detecting charsets, instead defaulting to a simpler approach when no charset is specified. (#1269)
+
+### Fixed
+
+* Swapped ordering of redirects and authentication flow. (Pull #1267)
+* `.netrc` lookups should use host, not host+port. (Pull #1298)
+
+### Removed
+
+* The `URLLib3Transport` class no longer exists. We've published it instead as an example of [a custom transport class](https://gist.github.com/florimondmanca/d56764d78d748eb9f73165da388e546e). (Pull #1182)
+* Drop `request.timer` attribute, which was being used internally to set `response.elapsed`. (Pull #1249)
+* Drop `response.decoder` attribute, which was being used internally. (Pull #1276)
+* `Request.prepare()` is now a private method. (Pull #1284)
+* The `Headers.getlist()` method had previously been deprecated in favour of `Headers.get_list()`. It is now fully removed.
+* The `QueryParams.getlist()` method had previously been deprecated in favour of `QueryParams.get_list()`. It is now fully removed.
+* The `URL.is_ssl` property had previously been deprecated in favour of `URL.scheme == "https"`. It is now fully removed.
+* The `httpx.PoolLimits` class had previously been deprecated in favour of `httpx.Limits`. It is now fully removed.
+* The `max_keepalive` setting had previously been deprecated in favour of the more explicit `max_keepalive_connections`. It is now fully removed.
+* The verbose `httpx.Timeout(5.0, connect_timeout=60.0)` style had previously been deprecated in favour of `httpx.Timeout(5.0, connect=60.0)`. It is now fully removed.
+* Support for instantiating a timeout config missing some defaults, such as `httpx.Timeout(connect=60.0)`, had previously been deprecated in favour of enforcing a more explicit style, such as `httpx.Timeout(5.0, connect=60.0)`. This is now strictly enforced.
+
 ## 0.14.3 (September 2nd, 2020)
 
 ### Added
index 842532142cd2a9f3ae5f902882aa08f855424899..3e1fdc4520efa59fa26ca2492e36ea0cc191598e 100644 (file)
@@ -2,7 +2,7 @@ from .__version__ import __description__, __title__, __version__
 from ._api import delete, get, head, options, patch, post, put, request, stream
 from ._auth import Auth, BasicAuth, DigestAuth
 from ._client import AsyncClient, Client
-from ._config import Limits, PoolLimits, Proxy, Timeout, create_ssl_context
+from ._config import Limits, Proxy, Timeout, create_ssl_context
 from ._exceptions import (
     CloseError,
     ConnectError,
@@ -70,7 +70,6 @@ __all__ = [
     "NotRedirectResponse",
     "options",
     "patch",
-    "PoolLimits",
     "PoolTimeout",
     "post",
     "ProtocolError",
index aff7043ec17ca6d206d28bb940e828ad1987e314..117eed369ec1783c32792a2808941d19b8dd22fa 100644 (file)
@@ -1,3 +1,3 @@
 __title__ = "httpx"
 __description__ = "A next generation HTTP client, for Python 3."
-__version__ = "0.14.3"
+__version__ = "0.15.0"
index f797a2e33cd1f68783bdd0850f8619017509b6cc..6208535f0bd7b736e5bc462f75fc22dbe0256fc9 100644 (file)
@@ -850,18 +850,12 @@ class Client(BaseClient):
         timer.sync_start()
 
         with map_exceptions(HTTPCORE_EXC_MAP, request=request):
-            (
-                http_version,
-                status_code,
-                reason_phrase,
-                headers,
-                stream,
-            ) = transport.request(
+            (status_code, headers, stream, ext) = transport.request(
                 request.method.encode(),
                 request.url.raw,
                 headers=request.headers.raw,
                 stream=request.stream,  # type: ignore
-                timeout=timeout.as_dict(),
+                ext={"timeout": timeout.as_dict()},
             )
 
         def on_close(response: Response) -> None:
@@ -871,9 +865,9 @@ class Client(BaseClient):
 
         response = Response(
             status_code,
-            http_version=http_version.decode("ascii"),
             headers=headers,
             stream=stream,  # type: ignore
+            ext=ext,
             request=request,
             on_close=on_close,
         )
@@ -1501,18 +1495,12 @@ class AsyncClient(BaseClient):
         await timer.async_start()
 
         with map_exceptions(HTTPCORE_EXC_MAP, request=request):
-            (
-                http_version,
-                status_code,
-                reason_phrase,
-                headers,
-                stream,
-            ) = await transport.request(
+            (status_code, headers, stream, ext,) = await transport.arequest(
                 request.method.encode(),
                 request.url.raw,
                 headers=request.headers.raw,
                 stream=request.stream,  # type: ignore
-                timeout=timeout.as_dict(),
+                ext={"timeout": timeout.as_dict()},
             )
 
         async def on_close(response: Response) -> None:
@@ -1522,9 +1510,9 @@ class AsyncClient(BaseClient):
 
         response = Response(
             status_code,
-            http_version=http_version.decode("ascii"),
             headers=headers,
             stream=stream,  # type: ignore
+            ext=ext,
             request=request,
             on_close=on_close,
         )
index 8d589eadecaebe6235cf64cf37ee4abc791be3a8..623392f47e09a88654d503625ca4e2b4ae9d5f53 100644 (file)
@@ -1,7 +1,6 @@
 import os
 import ssl
 import typing
-import warnings
 from base64 import b64encode
 from pathlib import Path
 
@@ -9,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, warn_deprecated
+from ._utils import get_ca_bundle_from_env, get_logger
 
 DEFAULT_CIPHERS = ":".join(
     [
@@ -212,44 +211,7 @@ class Timeout:
         read: typing.Union[None, float, UnsetType] = UNSET,
         write: typing.Union[None, float, UnsetType] = UNSET,
         pool: typing.Union[None, float, UnsetType] = UNSET,
-        # Deprecated aliases.
-        connect_timeout: typing.Union[None, float, UnsetType] = UNSET,
-        read_timeout: typing.Union[None, float, UnsetType] = UNSET,
-        write_timeout: typing.Union[None, float, UnsetType] = UNSET,
-        pool_timeout: typing.Union[None, float, UnsetType] = UNSET,
     ):
-        if not isinstance(connect_timeout, UnsetType):
-            warn_deprecated(
-                "httpx.Timeout(..., connect_timeout=...) is deprecated and will "
-                "raise errors in a future version. "
-                "Use httpx.Timeout(..., connect=...) instead."
-            )
-            connect = connect_timeout
-
-        if not isinstance(read_timeout, UnsetType):
-            warn_deprecated(
-                "httpx.Timeout(..., read_timeout=...) is deprecated and will "
-                "raise errors in a future version. "
-                "Use httpx.Timeout(..., write=...) instead."
-            )
-            read = read_timeout
-
-        if not isinstance(write_timeout, UnsetType):
-            warn_deprecated(
-                "httpx.Timeout(..., write_timeout=...) is deprecated and will "
-                "raise errors in a future version. "
-                "Use httpx.Timeout(..., write=...) instead."
-            )
-            write = write_timeout
-
-        if not isinstance(pool_timeout, UnsetType):
-            warn_deprecated(
-                "httpx.Timeout(..., pool_timeout=...) is deprecated and will "
-                "raise errors in a future version. "
-                "Use httpx.Timeout(..., pool=...) instead."
-            )
-            pool = pool_timeout
-
         if isinstance(timeout, Timeout):
             # Passed as a single explicit Timeout.
             assert connect is UNSET
@@ -278,13 +240,10 @@ class Timeout:
             self.pool = pool
         else:
             if isinstance(timeout, UnsetType):
-                warnings.warn(
+                raise ValueError(
                     "httpx.Timeout must either include a default, or set all "
-                    "four parameters explicitly. Omitting the default argument "
-                    "is deprecated and will raise errors in a future version.",
-                    DeprecationWarning,
+                    "four parameters explicitly."
                 )
-                timeout = None
             self.connect = timeout if isinstance(connect, UnsetType) else connect
             self.read = timeout if isinstance(read, UnsetType) else read
             self.write = timeout if isinstance(write, UnsetType) else write
@@ -335,16 +294,7 @@ class Limits:
         *,
         max_connections: int = None,
         max_keepalive_connections: int = None,
-        # Deprecated parameter naming, in favour of more explicit version:
-        max_keepalive: int = None,
     ):
-        if max_keepalive is not None:
-            warnings.warn(
-                "'max_keepalive' is deprecated. Use 'max_keepalive_connections'.",
-                DeprecationWarning,
-            )
-            max_keepalive_connections = max_keepalive
-
         self.max_connections = max_connections
         self.max_keepalive_connections = max_keepalive_connections
 
@@ -363,15 +313,6 @@ class Limits:
         )
 
 
-class PoolLimits(Limits):
-    def __init__(self, **kwargs: typing.Any) -> None:
-        warn_deprecated(
-            "httpx.PoolLimits(...) is deprecated and will raise errors in the future. "
-            "Use httpx.Limits(...) instead."
-        )
-        super().__init__(**kwargs)
-
-
 class Proxy:
     def __init__(
         self, url: URLTypes, *, headers: HeaderTypes = None, mode: str = "DEFAULT"
index 83e644c2fa14662dc85f34894c8ca6cdd1ea282b..03635f3e38e22e9878619037773b3129048ffd78 100644 (file)
@@ -5,7 +5,6 @@ import email.message
 import json as jsonlib
 import typing
 import urllib.request
-import warnings
 from collections.abc import MutableMapping
 from http.cookiejar import Cookie, CookieJar
 from urllib.parse import parse_qsl, quote, unquote, urlencode
@@ -272,12 +271,6 @@ class URL:
             self.raw_path,
         )
 
-    @property
-    def is_ssl(self) -> bool:
-        message = 'URL.is_ssl() is pending deprecation. Use url.scheme == "https"'
-        warnings.warn(message, DeprecationWarning)
-        return self.scheme == "https"
-
     @property
     def is_absolute_url(self) -> bool:
         """
@@ -525,13 +518,6 @@ class QueryParams(typing.Mapping[str, str]):
         query_string = str(self)
         return f"{class_name}({query_string!r})"
 
-    def getlist(self, key: typing.Any) -> typing.List[str]:
-        message = (
-            "QueryParams.getlist() is pending deprecation. Use QueryParams.get_list()"
-        )
-        warnings.warn(message, DeprecationWarning)
-        return self.get_list(key)
-
 
 class Headers(typing.MutableMapping[str, str]):
     """
@@ -757,11 +743,6 @@ class Headers(typing.MutableMapping[str, str]):
             return f"{class_name}({as_dict!r}{encoding_str})"
         return f"{class_name}({as_list!r}{encoding_str})"
 
-    def getlist(self, key: str, split_commas: bool = False) -> typing.List[str]:
-        message = "Headers.getlist() is pending deprecation. Use Headers.get_list()"
-        warnings.warn(message, DeprecationWarning)
-        return self.get_list(key, split_commas=split_commas)
-
 
 class Request:
     def __init__(
@@ -883,19 +864,19 @@ class Response:
         html: str = None,
         json: typing.Any = None,
         stream: ByteStream = None,
-        http_version: str = None,
         request: Request = None,
+        ext: dict = None,
         history: typing.List["Response"] = None,
         on_close: typing.Callable = None,
     ):
         self.status_code = status_code
-        self.http_version = http_version
         self.headers = Headers(headers)
 
         self._request: typing.Optional[Request] = request
 
         self.call_next: typing.Optional[typing.Callable] = None
 
+        self.ext = {} if ext is None else ext
         self.history = [] if history is None else list(history)
         self._on_close = on_close
 
@@ -964,9 +945,13 @@ class Response:
     def request(self, value: Request) -> None:
         self._request = value
 
+    @property
+    def http_version(self) -> str:
+        return self.ext.get("http_version", "HTTP/1.1")
+
     @property
     def reason_phrase(self) -> str:
-        return codes.get_reason_phrase(self.status_code)
+        return self.ext.get("reason", codes.get_reason_phrase(self.status_code))
 
     @property
     def url(self) -> typing.Optional[URL]:
index a58e10a6d68a8a712e318ad71ffdec99b8e11955..94976f3aad0dd42612cc99cd022a5f68f5f0e1b5 100644 (file)
@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING, Callable, List, Mapping, Optional, Tuple, Union
+from typing import TYPE_CHECKING, Callable, List, Optional, Tuple, Union
 
 import httpcore
 import sniffio
@@ -67,14 +67,14 @@ class ASGITransport(httpcore.AsyncHTTPTransport):
         self.root_path = root_path
         self.client = client
 
-    async def request(
+    async def arequest(
         self,
         method: bytes,
         url: Tuple[bytes, bytes, Optional[int], bytes],
         headers: List[Tuple[bytes, bytes]] = None,
         stream: httpcore.AsyncByteStream = None,
-        timeout: Mapping[str, Optional[float]] = None,
-    ) -> Tuple[bytes, int, bytes, List[Tuple[bytes, bytes]], httpcore.AsyncByteStream]:
+        ext: dict = None,
+    ) -> Tuple[int, List[Tuple[bytes, bytes]], httpcore.AsyncByteStream, dict]:
         headers = [] if headers is None else headers
         stream = httpcore.PlainByteStream(content=b"") if stream is None else stream
 
@@ -154,5 +154,6 @@ class ASGITransport(httpcore.AsyncHTTPTransport):
         assert response_headers is not None
 
         stream = httpcore.PlainByteStream(content=b"".join(body_parts))
+        ext = {}
 
-        return (b"HTTP/1.1", status_code, b"", response_headers, stream)
+        return (status_code, response_headers, stream, ext)
index 0573c9cf4ccfe4c8add89c83e7a841bd3d3837a0..5169d92e70a0cc33128a727b7eca2d4bc06efa4a 100644 (file)
@@ -64,13 +64,9 @@ class WSGITransport(httpcore.SyncHTTPTransport):
         url: typing.Tuple[bytes, bytes, typing.Optional[int], bytes],
         headers: typing.List[typing.Tuple[bytes, bytes]] = None,
         stream: httpcore.SyncByteStream = None,
-        timeout: typing.Mapping[str, typing.Optional[float]] = None,
+        ext: dict = None,
     ) -> typing.Tuple[
-        bytes,
-        int,
-        bytes,
-        typing.List[typing.Tuple[bytes, bytes]],
-        httpcore.SyncByteStream,
+        int, typing.List[typing.Tuple[bytes, bytes]], httpcore.SyncByteStream, dict
     ]:
         headers = [] if headers is None else headers
         stream = httpcore.PlainByteStream(content=b"") if stream is None else stream
@@ -127,5 +123,6 @@ class WSGITransport(httpcore.SyncHTTPTransport):
             for key, value in seen_response_headers
         ]
         stream = httpcore.IteratorByteStream(iterator=result)
+        ext = {}
 
-        return (b"HTTP/1.1", status_code, b"", headers, stream)
+        return (status_code, headers, stream, ext)
index d7006dd805027c0030817bd2f569e4a734fe2430..482c27208e7ca0aa3177075e83e9f19d2895ce17 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -58,7 +58,7 @@ setup(
         "certifi",
         "sniffio",
         "rfc3986[idna2008]>=1.3,<2",
-        "httpcore==0.10.*",
+        "httpcore==0.11.*",
     ],
     extras_require={
         "http2": "h2==3.*",
index 59777b8c696ffc9a931cdcf660a3a66adfcb1734..e3d328c6fa9cc32b5267f78dcb6efa0adc2adfcc 100644 (file)
@@ -13,7 +13,7 @@ import pytest
 
 import httpx
 from httpx import URL, Auth, BasicAuth, DigestAuth, ProtocolError, Request, Response
-from tests.utils import AsyncMockTransport, MockTransport
+from tests.utils import MockTransport
 
 from ..common import FIXTURES_DIR
 
@@ -155,7 +155,7 @@ async def test_basic_auth() -> None:
     auth = ("tomchristie", "password123")
     app = App()
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -167,7 +167,7 @@ async def test_basic_auth_in_url() -> None:
     url = "https://tomchristie:password123@example.org/"
     app = App()
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url)
 
     assert response.status_code == 200
@@ -180,9 +180,7 @@ async def test_basic_auth_on_session() -> None:
     auth = ("tomchristie", "password123")
     app = App()
 
-    async with httpx.AsyncClient(
-        transport=AsyncMockTransport(app), auth=auth
-    ) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app), auth=auth) as client:
         response = await client.get(url)
 
     assert response.status_code == 200
@@ -198,7 +196,7 @@ async def test_custom_auth() -> None:
         request.headers["Authorization"] = "Token 123"
         return request
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -211,7 +209,7 @@ async def test_netrc_auth() -> None:
     url = "http://netrcexample.org"
     app = App()
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url)
 
     assert response.status_code == 200
@@ -226,7 +224,7 @@ async def test_auth_header_has_priority_over_netrc() -> None:
     url = "http://netrcexample.org"
     app = App()
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, headers={"Authorization": "Override"})
 
     assert response.status_code == 200
@@ -240,7 +238,7 @@ async def test_trust_env_auth() -> None:
     app = App()
 
     async with httpx.AsyncClient(
-        transport=AsyncMockTransport(app), trust_env=False
+        transport=MockTransport(app), trust_env=False
     ) as client:
         response = await client.get(url)
 
@@ -248,7 +246,7 @@ async def test_trust_env_auth() -> None:
     assert response.json() == {"auth": None}
 
     async with httpx.AsyncClient(
-        transport=AsyncMockTransport(app), trust_env=True
+        transport=MockTransport(app), trust_env=True
     ) as client:
         response = await client.get(url)
 
@@ -264,9 +262,7 @@ async def test_auth_disable_per_request() -> None:
     auth = ("tomchristie", "password123")
     app = App()
 
-    async with httpx.AsyncClient(
-        transport=AsyncMockTransport(app), auth=auth
-    ) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app), auth=auth) as client:
         response = await client.get(url, auth=None)
 
     assert response.status_code == 200
@@ -286,7 +282,7 @@ async def test_auth_hidden_header() -> None:
     auth = ("example-username", "example-password")
     app = App()
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert "'authorization': '[secure]'" in str(response.request.headers)
@@ -296,7 +292,7 @@ async def test_auth_hidden_header() -> None:
 async def test_auth_property() -> None:
     app = App()
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         assert client.auth is None
 
         client.auth = ("tomchristie", "password123")  # type: ignore
@@ -314,11 +310,11 @@ async def test_auth_invalid_type() -> None:
 
     with pytest.raises(TypeError):
         client = httpx.AsyncClient(
-            transport=AsyncMockTransport(app),
+            transport=MockTransport(app),
             auth="not a tuple, not a callable",  # type: ignore
         )
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         with pytest.raises(TypeError):
             await client.get(auth="not a tuple, not a callable")  # type: ignore
 
@@ -332,7 +328,7 @@ async def test_digest_auth_returns_no_auth_if_no_digest_header_in_response() ->
     auth = DigestAuth(username="tomchristie", password="password123")
     app = App()
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -361,7 +357,7 @@ async def test_digest_auth_200_response_including_digest_auth_header() -> None:
     auth_header = 'Digest realm="realm@host.com",qop="auth",nonce="abc",opaque="xyz"'
     app = App(auth_header=auth_header, status_code=200)
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -375,7 +371,7 @@ async def test_digest_auth_401_response_without_digest_auth_header() -> None:
     auth = DigestAuth(username="tomchristie", password="password123")
     app = App(auth_header="", status_code=401)
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 401
@@ -404,7 +400,7 @@ async def test_digest_auth(
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(algorithm=algorithm)
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -435,7 +431,7 @@ async def test_digest_auth_no_specified_qop() -> None:
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(qop="")
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -467,7 +463,7 @@ async def test_digest_auth_qop_including_spaces_and_auth_returns_auth(qop: str)
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(qop=qop)
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -480,7 +476,7 @@ async def test_digest_auth_qop_auth_int_not_implemented() -> None:
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(qop="auth-int")
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         with pytest.raises(NotImplementedError):
             await client.get(url, auth=auth)
 
@@ -491,7 +487,7 @@ async def test_digest_auth_qop_must_be_auth_or_auth_int() -> None:
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(qop="not-auth")
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         with pytest.raises(ProtocolError):
             await client.get(url, auth=auth)
 
@@ -502,7 +498,7 @@ async def test_digest_auth_incorrect_credentials() -> None:
     auth = DigestAuth(username="tomchristie", password="password123")
     app = DigestApp(send_response_after_attempt=2)
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 401
@@ -524,7 +520,7 @@ async def test_async_digest_auth_raises_protocol_error_on_malformed_header(
     auth = DigestAuth(username="tomchristie", password="password123")
     app = App(auth_header=auth_header, status_code=401)
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         with pytest.raises(ProtocolError):
             await client.get(url, auth=auth)
 
@@ -558,7 +554,7 @@ async def test_async_auth_history() -> None:
     auth = RepeatAuth(repeat=2)
     app = App(auth_header="abc")
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -610,7 +606,7 @@ async def test_digest_auth_unavailable_streaming_body():
     async def streaming_body():
         yield b"Example request body"  # pragma: nocover
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         with pytest.raises(httpx.StreamConsumed):
             await client.post(url, data=streaming_body(), auth=auth)
 
@@ -625,7 +621,7 @@ async def test_async_auth_reads_response_body() -> None:
     auth = ResponseBodyAuth("xyz")
     app = App()
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
@@ -659,7 +655,7 @@ async def test_async_auth() -> None:
     auth = SyncOrAsyncAuth()
     app = App()
 
-    async with httpx.AsyncClient(transport=AsyncMockTransport(app)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(app)) as client:
         response = await client.get(url, auth=auth)
 
     assert response.status_code == 200
index 67c7faae508bdef699ea15f88b60b636330c67db..54c67a366bf44a8c44420ef8a16c4526b5ef644a 100644 (file)
@@ -179,8 +179,6 @@ def test_merge_absolute_url():
     client = httpx.Client(base_url="https://www.example.com/")
     request = client.build_request("GET", "http://www.example.com/")
     assert request.url == "http://www.example.com/"
-    with pytest.warns(DeprecationWarning):
-        assert not request.url.is_ssl
 
 
 def test_merge_relative_url():
index a2cfa936a4b6a6d76fe70b72f78ced580ab9c6d1..a81f31e1e55e7b172e1a3a610bab5a997282e51a 100644 (file)
@@ -1,7 +1,7 @@
 import pytest
 
 import httpx
-from tests.utils import AsyncMockTransport, MockTransport
+from tests.utils import MockTransport
 
 
 def app(request: httpx.Request) -> httpx.Response:
@@ -73,7 +73,7 @@ async def test_async_event_hooks():
     event_hooks = {"request": [on_request], "response": [on_response]}
 
     async with httpx.AsyncClient(
-        event_hooks=event_hooks, transport=AsyncMockTransport(app)
+        event_hooks=event_hooks, transport=MockTransport(app)
     ) as http:
         await http.get("http://127.0.0.1:8000/", auth=("username", "password"))
 
@@ -104,7 +104,7 @@ async def test_async_event_hooks_raising_exception():
     event_hooks = {"response": [raise_on_4xx_5xx]}
 
     async with httpx.AsyncClient(
-        event_hooks=event_hooks, transport=AsyncMockTransport(app)
+        event_hooks=event_hooks, transport=MockTransport(app)
     ) as http:
         try:
             await http.get("http://127.0.0.1:8000/status/400")
@@ -166,7 +166,7 @@ async def test_async_event_hooks_with_redirect():
     event_hooks = {"request": [on_request], "response": [on_response]}
 
     async with httpx.AsyncClient(
-        event_hooks=event_hooks, transport=AsyncMockTransport(app)
+        event_hooks=event_hooks, transport=MockTransport(app)
     ) as http:
         await http.get("http://127.0.0.1:8000/redirect", auth=("username", "password"))
 
index f32512bbf9a7f81ee6f6c6e17214653ee25b89c6..1c7911bc3521c7192654a22df4f62eb849c20240 100644 (file)
@@ -2,7 +2,7 @@ import httpcore
 import pytest
 
 import httpx
-from tests.utils import AsyncMockTransport, MockTransport
+from tests.utils import MockTransport
 
 
 def redirects(request: httpx.Request) -> httpx.Response:
@@ -232,14 +232,14 @@ def test_multiple_redirects():
 
 @pytest.mark.usefixtures("async_environment")
 async def test_async_too_many_redirects():
-    async with httpx.AsyncClient(transport=AsyncMockTransport(redirects)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(redirects)) as client:
         with pytest.raises(httpx.TooManyRedirects):
             await client.get("https://example.org/multiple_redirects?count=21")
 
 
 @pytest.mark.usefixtures("async_environment")
 async def test_async_too_many_redirects_calling_next():
-    async with httpx.AsyncClient(transport=AsyncMockTransport(redirects)) as client:
+    async with httpx.AsyncClient(transport=MockTransport(redirects)) as client:
         url = "https://example.org/multiple_redirects?count=21"
         response = await client.get(url, allow_redirects=False)
         with pytest.raises(httpx.TooManyRedirects):
index 8c1042105d0228e1e5668da3ee8c99d75d014f59..263db1192082bfbedf03c68e9347f401f6a42826 100644 (file)
@@ -15,9 +15,6 @@ def test_headers():
     assert h.get("nope", default=None) is None
     assert h.get_list("a") == ["123", "456"]
 
-    with pytest.warns(DeprecationWarning):
-        assert h.getlist("a") == ["123", "456"]
-
     assert list(h.keys()) == ["a", "b"]
     assert list(h.values()) == ["123, 456", "789"]
     assert list(h.items()) == [("a", "123, 456"), ("b", "789")]
index b5ae86e9d0152419bef01bc67855355ece437928..d591eded8c33aa5cda24f1870bff4df882679afe 100644 (file)
@@ -21,9 +21,6 @@ def test_queryparams(source):
     assert q.get("nope", default=None) is None
     assert q.get_list("a") == ["123", "456"]
 
-    with pytest.warns(DeprecationWarning):
-        assert q.getlist("a") == ["123", "456"]
-
     assert list(q.keys()) == ["a", "b"]
     assert list(q.values()) == ["456", "789"]
     assert list(q.items()) == [("a", "456"), ("b", "789")]
index 5c68badba3bac6e76b426799d6568c851eae5b7a..23f29e00c6678269950a2097539f25068f04f307 100644 (file)
@@ -110,16 +110,6 @@ def test_limits_eq():
     assert limits == httpx.Limits(max_connections=100)
 
 
-def test_pool_limits_deprecated():
-    with pytest.warns(DeprecationWarning):
-        httpx.PoolLimits()
-
-
-def test_max_keepalive_deprecated():
-    with pytest.warns(DeprecationWarning):
-        httpx.Limits(max_keepalive=50)
-
-
 def test_timeout_eq():
     timeout = httpx.Timeout(timeout=5.0)
     assert timeout == httpx.Timeout(timeout=5.0)
@@ -159,9 +149,8 @@ def test_timeout_from_one_value_and_default():
 
 
 def test_timeout_missing_default():
-    with pytest.warns(DeprecationWarning):
-        timeout = httpx.Timeout(pool=60.0)
-        assert timeout == httpx.Timeout(timeout=(None, None, None, 60.0))
+    with pytest.raises(ValueError):
+        httpx.Timeout(pool=60.0)
 
 
 def test_timeout_from_tuple():
index 034b42f3146c7d1d718de91c8fc1254328fc3c69..46a8bee8fceb0344a55a150a3c16e314cda28deb 100644 (file)
@@ -41,17 +41,3 @@ async def test_pool_timeout(server):
         async with client.stream("GET", server.url):
             with pytest.raises(httpx.PoolTimeout):
                 await client.get("http://localhost:8000/")
-
-
-def test_deprecated_verbose_timeout_params():
-    with pytest.warns(DeprecationWarning):
-        httpx.Timeout(None, read_timeout=1.0)
-
-    with pytest.warns(DeprecationWarning):
-        httpx.Timeout(None, write_timeout=1.0)
-
-    with pytest.warns(DeprecationWarning):
-        httpx.Timeout(None, connect_timeout=1.0)
-
-    with pytest.warns(DeprecationWarning):
-        httpx.Timeout(None, pool_timeout=1.0)
index ba8a188e78b9d662e55bd167509425af477bb834..75157dee7ef4816718ddf42039bf563b09550c56 100644 (file)
@@ -1,7 +1,7 @@
 import contextlib
 import logging
 import os
-from typing import Callable, List, Mapping, Optional, Tuple
+from typing import Callable, List, Optional, Tuple
 
 import httpcore
 
@@ -24,7 +24,7 @@ def override_log_level(log_level: str):
         logging.getLogger("httpx").handlers = []
 
 
-class MockTransport(httpcore.SyncHTTPTransport):
+class MockTransport(httpcore.SyncHTTPTransport, httpcore.AsyncHTTPTransport):
     def __init__(self, handler: Callable) -> None:
         self.handler = handler
 
@@ -34,8 +34,8 @@ class MockTransport(httpcore.SyncHTTPTransport):
         url: Tuple[bytes, bytes, Optional[int], bytes],
         headers: List[Tuple[bytes, bytes]] = None,
         stream: httpcore.SyncByteStream = None,
-        timeout: Mapping[str, Optional[float]] = None,
-    ) -> Tuple[bytes, int, bytes, List[Tuple[bytes, bytes]], httpcore.SyncByteStream]:
+        ext: dict = None,
+    ) -> Tuple[int, List[Tuple[bytes, bytes]], httpcore.SyncByteStream, dict]:
         request = httpx.Request(
             method=method,
             url=url,
@@ -45,26 +45,20 @@ class MockTransport(httpcore.SyncHTTPTransport):
         request.read()
         response = self.handler(request)
         return (
-            (response.http_version or "HTTP/1.1").encode("ascii"),
             response.status_code,
-            response.reason_phrase.encode("ascii"),
             response.headers.raw,
             response.stream,
+            response.ext,
         )
 
-
-class AsyncMockTransport(httpcore.AsyncHTTPTransport):
-    def __init__(self, handler: Callable) -> None:
-        self.handler = handler
-
-    async def request(
+    async def arequest(
         self,
         method: bytes,
         url: Tuple[bytes, bytes, Optional[int], bytes],
         headers: List[Tuple[bytes, bytes]] = None,
         stream: httpcore.AsyncByteStream = None,
-        timeout: Mapping[str, Optional[float]] = None,
-    ) -> Tuple[bytes, int, bytes, List[Tuple[bytes, bytes]], httpcore.AsyncByteStream]:
+        ext: dict = None,
+    ) -> Tuple[int, List[Tuple[bytes, bytes]], httpcore.AsyncByteStream, dict]:
         request = httpx.Request(
             method=method,
             url=url,
@@ -74,9 +68,8 @@ class AsyncMockTransport(httpcore.AsyncHTTPTransport):
         await request.aread()
         response = self.handler(request)
         return (
-            (response.http_version or "HTTP/1.1").encode("ascii"),
             response.status_code,
-            response.reason_phrase.encode("ascii"),
             response.headers.raw,
             response.stream,
+            response.ext,
         )