Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the 'proxy' parameter and deprecate 'proxies'. #2879

Merged
merged 23 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
170cbef
Add the proxy parameter and deprecate proxies
karpetrosyan Oct 6, 2023
189a4f2
Merge branch 'master' into drop-proxies
tomchristie Oct 10, 2023
88eb99b
Make the Client.proxy and HTTPTransport.proxy types the same
karpetrosyan Oct 10, 2023
11bec08
Update httpx/_transports/default.py
tomchristie Oct 11, 2023
fad66cf
Update httpx/_transports/default.py
tomchristie Oct 11, 2023
d16f9f3
Drop unneeded noqa
karpetrosyan Oct 11, 2023
64a4596
Changelog
karpetrosyan Oct 11, 2023
541cc31
update documentation
karpetrosyan Oct 12, 2023
812a70b
Allow None in mounts
karpetrosyan Oct 12, 2023
ccf1803
typos
karpetrosyan Oct 12, 2023
bacd8b2
Update httpx/_types.py
karpetrosyan Oct 12, 2023
d5fa2b0
Changes proxies to proxy in CLI app
karpetrosyan Oct 12, 2023
17c6cde
Add proxy to request function
karpetrosyan Oct 12, 2023
5aed92e
Merge branch 'drop-proxies' of github.com:karosis88/httpx into drop-p…
karpetrosyan Oct 12, 2023
6e5e4ac
Merge branch 'master' into drop-proxies
karpetrosyan Oct 29, 2023
2672d8a
Merge branch 'master' into drop-proxies
tomchristie Nov 2, 2023
fffa454
Merge branch 'master' into drop-proxies
karpetrosyan Nov 17, 2023
4deebb8
Merge branch 'master' into drop-proxies
karpetrosyan Nov 17, 2023
0b73817
Update CHANGELOG.md
karpetrosyan Dec 11, 2023
1e3bf1e
Update docs/troubleshooting.md
karpetrosyan Dec 11, 2023
725a32e
Update docs/troubleshooting.md
karpetrosyan Dec 11, 2023
cbd63da
Merge branch 'master' into drop-proxies
karpetrosyan Dec 11, 2023
c7971ce
Lint
karpetrosyan Dec 11, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions httpx/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
CookieTypes,
HeaderTypes,
ProxiesTypes,
ProxyTypes,
QueryParamTypes,
RequestContent,
RequestData,
Expand Down Expand Up @@ -628,6 +629,7 @@ def __init__(
cert: typing.Optional[CertTypes] = None,
http1: bool = True,
http2: bool = False,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
mounts: typing.Optional[typing.Mapping[str, BaseTransport]] = None,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
Expand Down Expand Up @@ -666,8 +668,14 @@ def __init__(
"Make sure to install httpx using `pip install httpx[http2]`."
) from None

if proxies:
message = "The 'proxies' argument is now deprecated. Use 'proxy' or 'mounts' instead."
warnings.warn(message, DeprecationWarning)
if proxy:
raise RuntimeError("Use either `proxy` or 'proxies', not both.")

allow_env_proxies = trust_env and app is None and transport is None
proxy_map = self._get_proxy_map(proxies, allow_env_proxies)
proxy_map = self._get_proxy_map(proxies or proxy, allow_env_proxies)

self._transport = self._init_transport(
verify=verify,
Expand Down Expand Up @@ -1353,6 +1361,7 @@ def __init__(
cert: typing.Optional[CertTypes] = None,
http1: bool = True,
http2: bool = False,
proxy: typing.Optional[ProxyTypes] = None,
proxies: typing.Optional[ProxiesTypes] = None,
mounts: typing.Optional[typing.Mapping[str, AsyncBaseTransport]] = None,
timeout: TimeoutTypes = DEFAULT_TIMEOUT_CONFIG,
Expand Down Expand Up @@ -1391,8 +1400,14 @@ def __init__(
"Make sure to install httpx using `pip install httpx[http2]`."
) from None

if proxies:
message = "The 'proxies' argument is now deprecated. Use 'proxy' or 'mounts' instead."
warnings.warn(message, DeprecationWarning)
if proxy:
raise RuntimeError("Use either `proxy` or 'proxies', not both.")

allow_env_proxies = trust_env and app is None and transport is None
proxy_map = self._get_proxy_map(proxies, allow_env_proxies)
proxy_map = self._get_proxy_map(proxies or proxy, allow_env_proxies)

self._transport = self._init_transport(
verify=verify,
Expand Down
10 changes: 7 additions & 3 deletions httpx/_transports/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

import httpcore

from httpx import URL
tomchristie marked this conversation as resolved.
Show resolved Hide resolved

from .._config import DEFAULT_LIMITS, Limits, Proxy, create_ssl_context
from .._exceptions import (
ConnectError,
Expand All @@ -47,7 +49,7 @@
WriteTimeout,
)
from .._models import Request, Response
from .._types import AsyncByteStream, CertTypes, SyncByteStream, VerifyTypes
from .._types import AsyncByteStream, CertTypes, ProxyTypes, SyncByteStream, VerifyTypes
tomchristie marked this conversation as resolved.
Show resolved Hide resolved
from .base import AsyncBaseTransport, BaseTransport

T = typing.TypeVar("T", bound="HTTPTransport")
Expand Down Expand Up @@ -124,13 +126,14 @@ def __init__(
http2: bool = False,
limits: Limits = DEFAULT_LIMITS,
trust_env: bool = True,
proxy: typing.Optional[Proxy] = None,
proxy: typing.Optional[ProxyTypes] = None,
uds: typing.Optional[str] = None,
local_address: typing.Optional[str] = None,
retries: int = 0,
socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None,
) -> None:
ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env)
proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy

if proxy is None:
self._pool = httpcore.ConnectionPool(
Expand Down Expand Up @@ -263,13 +266,14 @@ def __init__(
http2: bool = False,
limits: Limits = DEFAULT_LIMITS,
trust_env: bool = True,
proxy: typing.Optional[Proxy] = None,
proxy: typing.Optional[ProxyTypes] = None,
uds: typing.Optional[str] = None,
local_address: typing.Optional[str] = None,
retries: int = 0,
socket_options: typing.Optional[typing.Iterable[SOCKET_OPTION]] = None,
) -> None:
ssl_context = create_ssl_context(verify=verify, cert=cert, trust_env=trust_env)
proxy = Proxy(url=proxy) if isinstance(proxy, (str, URL)) else proxy

if proxy is None:
self._pool = httpcore.AsyncConnectionPool(
Expand Down
3 changes: 2 additions & 1 deletion httpx/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@
Tuple[Optional[float], Optional[float], Optional[float], Optional[float]],
"Timeout",
]
ProxiesTypes = Union[URLTypes, "Proxy", Dict[URLTypes, Union[None, URLTypes, "Proxy"]]]
ProxyTypes = Union[URLTypes, "Proxy"]
ProxiesTypes = Union[URLTypes, ProxyTypes, Dict[URLTypes, Union[None, ProxyTypes]]]
karpetrosyan marked this conversation as resolved.
Show resolved Hide resolved

AuthTypes = Union[
Tuple[Union[str, bytes], Union[str, bytes]],
Expand Down
74 changes: 63 additions & 11 deletions tests/client/test_proxies.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ def url_to_origin(url: str) -> httpcore.URL:
],
)
def test_proxies_parameter(proxies, expected_proxies):
client = httpx.Client(proxies=proxies)
with pytest.warns(DeprecationWarning):
client = httpx.Client(proxies=proxies)
client_patterns = [p.pattern for p in client._mounts.keys()]
client_proxies = list(client._mounts.values())

Expand All @@ -47,15 +48,31 @@ def test_proxies_parameter(proxies, expected_proxies):
assert len(expected_proxies) == len(client._mounts)


def test_socks_proxy_deprecated():
url = httpx.URL("http://www.example.com")

with pytest.warns(DeprecationWarning):
client = httpx.Client(proxies="socks5://localhost/")
transport = client._transport_for_url(url)
assert isinstance(transport, httpx.HTTPTransport)
assert isinstance(transport._pool, httpcore.SOCKSProxy)

with pytest.warns(DeprecationWarning):
async_client = httpx.AsyncClient(proxies="socks5://localhost/")
async_transport = async_client._transport_for_url(url)
assert isinstance(async_transport, httpx.AsyncHTTPTransport)
assert isinstance(async_transport._pool, httpcore.AsyncSOCKSProxy)


def test_socks_proxy():
url = httpx.URL("http://www.example.com")

client = httpx.Client(proxies="socks5://localhost/")
client = httpx.Client(proxy="socks5://localhost/")
transport = client._transport_for_url(url)
assert isinstance(transport, httpx.HTTPTransport)
assert isinstance(transport._pool, httpcore.SOCKSProxy)

async_client = httpx.AsyncClient(proxies="socks5://localhost/")
async_client = httpx.AsyncClient(proxy="socks5://localhost/")
async_transport = async_client._transport_for_url(url)
assert isinstance(async_transport, httpx.AsyncHTTPTransport)
assert isinstance(async_transport._pool, httpcore.AsyncSOCKSProxy)
Expand Down Expand Up @@ -121,7 +138,12 @@ def test_socks_proxy():
],
)
def test_transport_for_request(url, proxies, expected):
client = httpx.Client(proxies=proxies)
if proxies:
with pytest.warns(DeprecationWarning):
client = httpx.Client(proxies=proxies)
else:
client = httpx.Client(proxies=proxies)

transport = client._transport_for_url(httpx.URL(url))

if expected is None:
Expand All @@ -136,7 +158,8 @@ def test_transport_for_request(url, proxies, expected):
@pytest.mark.network
async def test_async_proxy_close():
try:
client = httpx.AsyncClient(proxies={"https://": PROXY_URL})
with pytest.warns(DeprecationWarning):
client = httpx.AsyncClient(proxies={"https://": PROXY_URL})
await client.get("http://example.com")
finally:
await client.aclose()
Expand All @@ -145,15 +168,21 @@ async def test_async_proxy_close():
@pytest.mark.network
def test_sync_proxy_close():
try:
client = httpx.Client(proxies={"https://": PROXY_URL})
with pytest.warns(DeprecationWarning):
client = httpx.Client(proxies={"https://": PROXY_URL})
client.get("http://example.com")
finally:
client.close()


def test_unsupported_proxy_scheme_deprecated():
with pytest.warns(DeprecationWarning), pytest.raises(ValueError):
httpx.Client(proxies="ftp://127.0.0.1")


def test_unsupported_proxy_scheme():
with pytest.raises(ValueError):
httpx.Client(proxies="ftp://127.0.0.1")
httpx.Client(proxy="ftp://127.0.0.1")


@pytest.mark.parametrize(
Expand Down Expand Up @@ -279,8 +308,31 @@ def test_proxies_environ(monkeypatch, client_class, url, env, expected):
],
)
def test_for_deprecated_proxy_params(proxies, is_valid):
if not is_valid:
with pytest.raises(ValueError):
with pytest.warns(DeprecationWarning):
if not is_valid:
with pytest.raises(ValueError):
httpx.Client(proxies=proxies)
else:
httpx.Client(proxies=proxies)
else:
httpx.Client(proxies=proxies)


def test_proxy_and_proxies_together():
with pytest.warns(DeprecationWarning), pytest.raises(
RuntimeError,
):
httpx.Client(proxies={"all://": "http://127.0.0.1"}, proxy="http://127.0.0.1")

with pytest.warns(DeprecationWarning), pytest.raises(
RuntimeError,
):
httpx.AsyncClient(
proxies={"all://": "http://127.0.0.1"}, proxy="http://127.0.0.1"
)


def test_proxy_with_mounts():
proxy_transport = httpx.HTTPTransport(proxy="http://127.0.0.1")
client = httpx.Client(mounts={"http://": proxy_transport})

transport = client._transport_for_url(httpx.URL("http://example.com"))
assert transport == proxy_transport