Skip to content

Commit 80960fa

Browse files
authored
Version 0.28.0. (#3419)
1 parent a33c878 commit 80960fa

File tree

6 files changed

+98
-41
lines changed

6 files changed

+98
-41
lines changed

CHANGELOG.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,26 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

7-
## 0.28.0 (...)
7+
## 0.28.0 (28th November, 2024)
88

9-
The 0.28 release includes a limited set of backwards incompatible changes.
9+
The 0.28 release includes a limited set of deprecations.
1010

11-
**Backwards incompatible changes**:
11+
**Deprecations**:
1212

13-
SSL configuration has been significantly simplified.
13+
We are working towards a simplified SSL configuration API.
1414

15-
* The `verify` argument no longer accepts string arguments.
16-
* The `cert` argument has now been removed.
17-
* The `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables are no longer automatically used.
15+
*For users of the standard `verify=True` or `verify=False` cases, or `verify=<ssl_context>` case this should require no changes. The following cases have been deprecated...*
1816

19-
For users of the standard `verify=True` or `verify=False` cases this should require no changes.
17+
* The `verify` argument as a string argument is now deprecated and will raise warnings.
18+
* The `cert` argument is now deprecated and will raise warnings.
2019

21-
For information on configuring more complex SSL cases, please see the [SSL documentation](docs/advanced/ssl.md).
20+
Our revised [SSL documentation](docs/advanced/ssl.md) covers how to implement the same behaviour with a more constrained API.
2221

2322
**The following changes are also included**:
2423

25-
* The undocumented `URL.raw` property has now been deprecated, and will raise warnings.
2624
* The deprecated `proxies` argument has now been removed.
2725
* The deprecated `app` argument has now been removed.
28-
* Ensure JSON request bodies are compact. (#3363)
26+
* JSON request bodies use a compact representation. (#3363)
2927
* Review URL percent escape sets, based on WHATWG spec. (#3371, #3373)
3028
* Ensure `certifi` and `httpcore` are only imported if required. (#3377)
3129
* Treat `socks5h` as a valid proxy scheme. (#3178)

httpx/_api.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def request(
5151
proxy: ProxyTypes | None = None,
5252
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
5353
follow_redirects: bool = False,
54-
verify: ssl.SSLContext | bool = True,
54+
verify: ssl.SSLContext | str | bool = True,
5555
trust_env: bool = True,
5656
) -> Response:
5757
"""
@@ -136,7 +136,7 @@ def stream(
136136
proxy: ProxyTypes | None = None,
137137
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
138138
follow_redirects: bool = False,
139-
verify: ssl.SSLContext | bool = True,
139+
verify: ssl.SSLContext | str | bool = True,
140140
trust_env: bool = True,
141141
) -> typing.Iterator[Response]:
142142
"""
@@ -180,7 +180,7 @@ def get(
180180
auth: AuthTypes | None = None,
181181
proxy: ProxyTypes | None = None,
182182
follow_redirects: bool = False,
183-
verify: ssl.SSLContext | bool = True,
183+
verify: ssl.SSLContext | str | bool = True,
184184
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
185185
trust_env: bool = True,
186186
) -> Response:
@@ -216,7 +216,7 @@ def options(
216216
auth: AuthTypes | None = None,
217217
proxy: ProxyTypes | None = None,
218218
follow_redirects: bool = False,
219-
verify: ssl.SSLContext | bool = True,
219+
verify: ssl.SSLContext | str | bool = True,
220220
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
221221
trust_env: bool = True,
222222
) -> Response:
@@ -252,7 +252,7 @@ def head(
252252
auth: AuthTypes | None = None,
253253
proxy: ProxyTypes | None = None,
254254
follow_redirects: bool = False,
255-
verify: ssl.SSLContext | bool = True,
255+
verify: ssl.SSLContext | str | bool = True,
256256
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
257257
trust_env: bool = True,
258258
) -> Response:
@@ -292,7 +292,7 @@ def post(
292292
auth: AuthTypes | None = None,
293293
proxy: ProxyTypes | None = None,
294294
follow_redirects: bool = False,
295-
verify: ssl.SSLContext | bool = True,
295+
verify: ssl.SSLContext | str | bool = True,
296296
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
297297
trust_env: bool = True,
298298
) -> Response:
@@ -333,7 +333,7 @@ def put(
333333
auth: AuthTypes | None = None,
334334
proxy: ProxyTypes | None = None,
335335
follow_redirects: bool = False,
336-
verify: ssl.SSLContext | bool = True,
336+
verify: ssl.SSLContext | str | bool = True,
337337
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
338338
trust_env: bool = True,
339339
) -> Response:
@@ -374,7 +374,7 @@ def patch(
374374
auth: AuthTypes | None = None,
375375
proxy: ProxyTypes | None = None,
376376
follow_redirects: bool = False,
377-
verify: ssl.SSLContext | bool = True,
377+
verify: ssl.SSLContext | str | bool = True,
378378
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
379379
trust_env: bool = True,
380380
) -> Response:
@@ -412,7 +412,7 @@ def delete(
412412
proxy: ProxyTypes | None = None,
413413
follow_redirects: bool = False,
414414
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
415-
verify: ssl.SSLContext | bool = True,
415+
verify: ssl.SSLContext | str | bool = True,
416416
trust_env: bool = True,
417417
) -> Response:
418418
"""

httpx/_client.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from ._types import (
3434
AsyncByteStream,
3535
AuthTypes,
36+
CertTypes,
3637
CookieTypes,
3738
HeaderTypes,
3839
ProxyTypes,
@@ -644,7 +645,9 @@ def __init__(
644645
params: QueryParamTypes | None = None,
645646
headers: HeaderTypes | None = None,
646647
cookies: CookieTypes | None = None,
647-
verify: ssl.SSLContext | bool = True,
648+
verify: ssl.SSLContext | str | bool = True,
649+
cert: CertTypes | None = None,
650+
trust_env: bool = True,
648651
http1: bool = True,
649652
http2: bool = False,
650653
proxy: ProxyTypes | None = None,
@@ -656,7 +659,6 @@ def __init__(
656659
event_hooks: None | (typing.Mapping[str, list[EventHook]]) = None,
657660
base_url: URL | str = "",
658661
transport: BaseTransport | None = None,
659-
trust_env: bool = True,
660662
default_encoding: str | typing.Callable[[bytes], str] = "utf-8",
661663
) -> None:
662664
super().__init__(
@@ -687,6 +689,8 @@ def __init__(
687689

688690
self._transport = self._init_transport(
689691
verify=verify,
692+
cert=cert,
693+
trust_env=trust_env,
690694
http1=http1,
691695
http2=http2,
692696
limits=limits,
@@ -698,6 +702,8 @@ def __init__(
698702
else self._init_proxy_transport(
699703
proxy,
700704
verify=verify,
705+
cert=cert,
706+
trust_env=trust_env,
701707
http1=http1,
702708
http2=http2,
703709
limits=limits,
@@ -713,7 +719,9 @@ def __init__(
713719

714720
def _init_transport(
715721
self,
716-
verify: ssl.SSLContext | bool = True,
722+
verify: ssl.SSLContext | str | bool = True,
723+
cert: CertTypes | None = None,
724+
trust_env: bool = True,
717725
http1: bool = True,
718726
http2: bool = False,
719727
limits: Limits = DEFAULT_LIMITS,
@@ -724,6 +732,8 @@ def _init_transport(
724732

725733
return HTTPTransport(
726734
verify=verify,
735+
cert=cert,
736+
trust_env=trust_env,
727737
http1=http1,
728738
http2=http2,
729739
limits=limits,
@@ -732,13 +742,17 @@ def _init_transport(
732742
def _init_proxy_transport(
733743
self,
734744
proxy: Proxy,
735-
verify: ssl.SSLContext | bool = True,
745+
verify: ssl.SSLContext | str | bool = True,
746+
cert: CertTypes | None = None,
747+
trust_env: bool = True,
736748
http1: bool = True,
737749
http2: bool = False,
738750
limits: Limits = DEFAULT_LIMITS,
739751
) -> BaseTransport:
740752
return HTTPTransport(
741753
verify=verify,
754+
cert=cert,
755+
trust_env=trust_env,
742756
http1=http1,
743757
http2=http2,
744758
limits=limits,
@@ -1345,7 +1359,8 @@ def __init__(
13451359
params: QueryParamTypes | None = None,
13461360
headers: HeaderTypes | None = None,
13471361
cookies: CookieTypes | None = None,
1348-
verify: ssl.SSLContext | bool = True,
1362+
verify: ssl.SSLContext | str | bool = True,
1363+
cert: CertTypes | None = None,
13491364
http1: bool = True,
13501365
http2: bool = False,
13511366
proxy: ProxyTypes | None = None,
@@ -1388,6 +1403,8 @@ def __init__(
13881403

13891404
self._transport = self._init_transport(
13901405
verify=verify,
1406+
cert=cert,
1407+
trust_env=trust_env,
13911408
http1=http1,
13921409
http2=http2,
13931410
limits=limits,
@@ -1400,6 +1417,8 @@ def __init__(
14001417
else self._init_proxy_transport(
14011418
proxy,
14021419
verify=verify,
1420+
cert=cert,
1421+
trust_env=trust_env,
14031422
http1=http1,
14041423
http2=http2,
14051424
limits=limits,
@@ -1414,7 +1433,9 @@ def __init__(
14141433

14151434
def _init_transport(
14161435
self,
1417-
verify: ssl.SSLContext | bool = True,
1436+
verify: ssl.SSLContext | str | bool = True,
1437+
cert: CertTypes | None = None,
1438+
trust_env: bool = True,
14181439
http1: bool = True,
14191440
http2: bool = False,
14201441
limits: Limits = DEFAULT_LIMITS,
@@ -1425,6 +1446,8 @@ def _init_transport(
14251446

14261447
return AsyncHTTPTransport(
14271448
verify=verify,
1449+
cert=cert,
1450+
trust_env=trust_env,
14281451
http1=http1,
14291452
http2=http2,
14301453
limits=limits,
@@ -1433,13 +1456,17 @@ def _init_transport(
14331456
def _init_proxy_transport(
14341457
self,
14351458
proxy: Proxy,
1436-
verify: ssl.SSLContext | bool = True,
1459+
verify: ssl.SSLContext | str | bool = True,
1460+
cert: CertTypes | None = None,
1461+
trust_env: bool = True,
14371462
http1: bool = True,
14381463
http2: bool = False,
14391464
limits: Limits = DEFAULT_LIMITS,
14401465
) -> AsyncBaseTransport:
14411466
return AsyncHTTPTransport(
14421467
verify=verify,
1468+
cert=cert,
1469+
trust_env=trust_env,
14431470
http1=http1,
14441471
http2=http2,
14451472
limits=limits,

httpx/_config.py

Lines changed: 36 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
from __future__ import annotations
22

3+
import os
34
import typing
45

56
from ._models import Headers
6-
from ._types import HeaderTypes, TimeoutTypes
7+
from ._types import CertTypes, HeaderTypes, TimeoutTypes
78
from ._urls import URL
89

910
if typing.TYPE_CHECKING:
@@ -19,28 +20,54 @@ class UnsetType:
1920
UNSET = UnsetType()
2021

2122

22-
def create_ssl_context(verify: ssl.SSLContext | bool = True) -> ssl.SSLContext:
23+
def create_ssl_context(
24+
verify: ssl.SSLContext | str | bool = True,
25+
cert: CertTypes | None = None,
26+
trust_env: bool = True,
27+
) -> ssl.SSLContext:
2328
import ssl
29+
import warnings
2430

2531
import certifi
2632

2733
if verify is True:
28-
return ssl.create_default_context(cafile=certifi.where())
34+
if trust_env and os.environ.get("SSL_CERT_FILE"): # pragma: nocover
35+
ctx = ssl.create_default_context(cafile=os.environ["SSL_CERT_FILE"])
36+
elif trust_env and os.environ.get("SSL_CERT_DIR"): # pragma: nocover
37+
ctx = ssl.create_default_context(capath=os.environ["SSL_CERT_DIR"])
38+
else:
39+
# Default case...
40+
ctx = ssl.create_default_context(cafile=certifi.where())
2941
elif verify is False:
3042
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
3143
ssl_context.check_hostname = False
3244
ssl_context.verify_mode = ssl.CERT_NONE
3345
return ssl_context
3446
elif isinstance(verify, str): # pragma: nocover
35-
# Explicitly handle this deprecated usage pattern.
36-
msg = (
37-
"verify should be a boolean or SSLContext, since version 0.28. "
47+
message = (
48+
"`verify=<str>` is deprecated. "
3849
"Use `verify=ssl.create_default_context(cafile=...)` "
39-
"or `verify=ssl.create_default_context(capath=...)`."
50+
"or `verify=ssl.create_default_context(capath=...)` instead."
51+
)
52+
warnings.warn(message, DeprecationWarning)
53+
if os.path.isdir(verify):
54+
return ssl.create_default_context(capath=verify)
55+
return ssl.create_default_context(cafile=verify)
56+
else:
57+
ctx = verify
58+
59+
if cert: # pragma: nocover
60+
message = (
61+
"`cert=...` is deprecated. Use `verify=<ssl_context>` instead,"
62+
"with `.load_cert_chain()` to configure the certificate chain."
4063
)
41-
raise RuntimeError(msg)
64+
warnings.warn(message, DeprecationWarning)
65+
if isinstance(cert, str):
66+
ctx.load_cert_chain(cert)
67+
else:
68+
ctx.load_cert_chain(*cert)
4269

43-
return verify
70+
return ctx
4471

4572

4673
class Timeout:

httpx/_transports/default.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
WriteTimeout,
5454
)
5555
from .._models import Request, Response
56-
from .._types import AsyncByteStream, ProxyTypes, SyncByteStream
56+
from .._types import AsyncByteStream, CertTypes, ProxyTypes, SyncByteStream
5757
from .._urls import URL
5858
from .base import AsyncBaseTransport, BaseTransport
5959

@@ -135,7 +135,9 @@ def close(self) -> None:
135135
class HTTPTransport(BaseTransport):
136136
def __init__(
137137
self,
138-
verify: ssl.SSLContext | bool = True,
138+
verify: ssl.SSLContext | str | bool = True,
139+
cert: CertTypes | None = None,
140+
trust_env: bool = True,
139141
http1: bool = True,
140142
http2: bool = False,
141143
limits: Limits = DEFAULT_LIMITS,
@@ -148,7 +150,7 @@ def __init__(
148150
import httpcore
149151

150152
proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy
151-
ssl_context = create_ssl_context(verify=verify)
153+
ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env)
152154

153155
if proxy is None:
154156
self._pool = httpcore.ConnectionPool(
@@ -277,7 +279,9 @@ async def aclose(self) -> None:
277279
class AsyncHTTPTransport(AsyncBaseTransport):
278280
def __init__(
279281
self,
280-
verify: ssl.SSLContext | bool = True,
282+
verify: ssl.SSLContext | str | bool = True,
283+
cert: CertTypes | None = None,
284+
trust_env: bool = True,
281285
http1: bool = True,
282286
http2: bool = False,
283287
limits: Limits = DEFAULT_LIMITS,
@@ -290,7 +294,7 @@ def __init__(
290294
import httpcore
291295

292296
proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy
293-
ssl_context = create_ssl_context(verify=verify)
297+
ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env)
294298

295299
if proxy is None:
296300
self._pool = httpcore.AsyncConnectionPool(

0 commit comments

Comments
 (0)