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 --no-proxy Option #13051

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions news/5378.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add ``--no-proxy`` option to bypass http proxy. Using this option will ignore any configured http proxies, including any environmental variables
10 changes: 10 additions & 0 deletions src/pip/_internal/cli/cmdoptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,15 @@ class PipOption(Option):
help="Specify a proxy in the form scheme://[user:passwd@]proxy.server:port.",
)

no_proxy: Callable[..., Option] = partial(
Option,
"--no-proxy",
dest="no_proxy",
action="store_true",
default=False,
help="Ignore all configured proxy settings, including environmental variables.",
)

retries: Callable[..., Option] = partial(
Option,
"--retries",
Expand Down Expand Up @@ -1048,6 +1057,7 @@ def check_list_path_option(options: Values) -> None:
no_input,
keyring_provider,
proxy,
no_proxy,
retries,
timeout,
exists_action,
Expand Down
13 changes: 13 additions & 0 deletions src/pip/_internal/cli/index_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,19 @@ def _build_session(
}
session.trust_env = False

# Handle no proxy option
if options.no_proxy:
# Handle case of both --no-proxy being set along with --proxy=<proxy>.
# In this case, the proxies from the environmental variables will be
# ignored, but the command line proxy will be used.
http_proxy = options.proxy if options.proxy else None
https_proxy = options.proxy if options.proxy else None
session.proxies = {
"http": http_proxy,
"https": https_proxy,
}
session.trust_env = False

# Determine if we can prompt the user for authentication or not
session.auth.prompting = not options.no_input
session.auth.keyring_provider = options.keyring_provider
Expand Down
6 changes: 6 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ def pytest_addoption(parser: Parser) -> None:
default=None,
help="use given proxy in session network tests",
)
parser.addoption(
"--no-proxy",
action="store_true",
default=False,
help="ignore any configured proxies in session network tests",
)
parser.addoption(
"--use-zipapp",
action="store_true",
Expand Down
38 changes: 37 additions & 1 deletion tests/unit/test_network_session.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging
import os
from pathlib import Path
from typing import Any, List, Optional
from typing import Any, List, Optional, Union
from urllib.parse import urlparse
from urllib.request import getproxies

Expand Down Expand Up @@ -282,3 +282,39 @@ def test_proxy(self, proxy: Optional[str]) -> None:
f"Invalid proxy {proxy} or session.proxies: "
f"{session.proxies} is not correctly passed to session.request."
)

@pytest.mark.network
def test_no_proxy(self) -> None:
def _set_no_proxy(session: PipSession) -> PipSession:
"""Mimic logic for command line `no_proxy` option"""
session.trust_env = False
session.proxies = {
"http": None,
"https": None,
}
return session

session = PipSession(trusted_hosts=[])

connection_error_http: Union[requests.exceptions.RequestException, None] = None
# setup with known bad (hopefully) http proxy, and then test connection
# expecting a failure if the proxy is used
with requests.utils.set_environ("http_proxy", "http://127.0.0.1:88888"):
try:
session = _set_no_proxy(session)
session.request("GET", "https://pypi.org", timeout=1)
except requests.exceptions.ConnectionError as e:
connection_error_http = e

connection_error_https: Union[requests.exceptions.RequestException, None] = None
# setup with known bad (hopefully) https proxy, and then test connection
# expecting a failure if the proxy is used
with requests.utils.set_environ("https_proxy", "http://127.0.0.1:65534"):
try:
session = _set_no_proxy(session)
session.request("GET", "https://pypi.org", timeout=1)
except requests.exceptions.ConnectionError as e:
connection_error_https = e

assert connection_error_http is None, "Unexpected use of http proxy"
assert connection_error_https is None, "Unexpected use of https proxy"
7 changes: 7 additions & 0 deletions tests/unit/test_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,13 @@ def test_proxy(self) -> None:
)
assert options1.proxy == options2.proxy == "path"

def test_no_proxy(self) -> None:
# FakeCommand intentionally returns the wrong type.
options1, _ = cast(Tuple[Values, List[str]], main(["--no-proxy", "fake"]))
options2, _ = cast(Tuple[Values, List[str]], main(["fake", "--no-proxy"]))
assert options1.no_proxy
assert options2.no_proxy

def test_retries(self) -> None:
# FakeCommand intentionally returns the wrong type.
options1, args1 = cast(
Expand Down
Loading