-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Pip --proxy parameter is not been passed to the request. #9691
Comments
There is a situation that needs to be discussed: So it seems to be wrong in some |
There’s some discussion here as well, and we independenly reached the same conclusion. You’re on the right direction, but the relevant code is actually in the underlying |
So this is a bug of To @junqfisica : pip/src/pip/_internal/cli/req_command.py Lines 109 to 112 in 25114e1
|
@CrazyBoyFeng You are right about the proxy been passed correctly when the system has no proxy set. However, the solution proposed here is quite useless for many users. If you need to pass a proxy is very likely you need it at the system level too, and as I mentioned before, many developers can't change that in working places environments. To @CrazyBoyFeng : pip/src/pip/_internal/cli/req_command.py Lines 109 to 112 in 25114e1
Your reply made me think more about the problem and here is my latest update about it: Okay, when the pip/src/pip/_internal/network/session.py Lines 443 to 449 in 3ab760a
adding a breakpoint there you can notice that self.proxies=pip_proxy , as expected.Then the parent call is made: pip/src/pip/_vendor/requests/sessions.py Lines 530 to 544 in 3ab760a
If you add a breakpoint at line 532, you can still notice that self.proxies = pip_proxy and local proxies are {} . So, self.merge_environment_settings is passed with proxies={} . This now brings us here:pip/src/pip/_vendor/requests/sessions.py Lines 701 to 723 in 3ab760a
on line 722 there is the call to merge_setting , and local parameter proxies is now your system_proxy, and merge_setting , gives the preference to proxies instead of self.proxies , causing the trouble.
Below I'm providing is a small script isolating the problem as a showcase: from collections import OrderedDict, Mapping
def to_key_val_list(value):
"""Take an object and test to see if it can be represented as a
dictionary. If it can be, return a list of tuples, e.g.,
::
>>> to_key_val_list([('key', 'val')])
[('key', 'val')]
>>> to_key_val_list({'key': 'val'})
[('key', 'val')]
>>> to_key_val_list('string')
Traceback (most recent call last):
...
ValueError: cannot encode objects that are not 2-tuples
:rtype: list
"""
if value is None:
return None
if isinstance(value, (str, bytes, bool, int)):
raise ValueError('cannot encode objects that are not 2-tuples')
if isinstance(value, Mapping):
value = value.items()
return list(value)
def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
"""Determines appropriate setting for a given request, taking into account
the explicit setting on that request, and the setting in the session. If a
setting is a dictionary, they will be merged together using `dict_class`
"""
if session_setting is None:
return request_setting
if request_setting is None:
return session_setting
# Bypass if not a dictionary (e.g. verify)
if not (
isinstance(session_setting, Mapping) and
isinstance(request_setting, Mapping)
):
return request_setting
merged_setting = dict_class(to_key_val_list(session_setting))
merged_setting.update(to_key_val_list(request_setting))
# Remove keys that are set to None. Extract keys first to avoid altering
# the dictionary during iteration.
none_keys = [k for (k, v) in merged_setting.items() if v is None]
for key in none_keys:
del merged_setting[key]
return merged_setting
pip_proxy = {'http': 'http://127.0.0.2:80', 'https': 'http://127.0.0.2:80'}
system_proxy = {'http': 'http://127.1.1.1:80', 'https': 'https://127.1.1.1:80'}
print(f"Default merge: {merge_setting(request_setting=system_proxy, session_setting=pip_proxy)}")
# or ??
print(f"Swap settings:? {merge_setting(session_setting=system_proxy, request_setting=pip_proxy)}")
system_proxy = {} # no proxy in the system.
print(f"No system proxy: {merge_setting(request_setting=system_proxy, session_setting=pip_proxy)}") Output:
Default merge: OrderedDict([('http', 'http://127.1.1.1:80'), ('https', 'https://127.1.1.1:80')])
Swap settings:? OrderedDict([('http', 'http://127.0.0.2:80'), ('https', 'http://127.0.0.2:80')])
No system proxy: OrderedDict([('http', 'http://127.0.0.2:80'), ('https', 'http://127.0.0.2:80')]) So, it seems that request.Session gives priority to request_setting, which in this case is the system settings. This may be related to a bug, as mentioned by @uranusjr here, but I'm not entirely sure about it. Also, I'm not sure if pip has much of a saying here, correct me if I'm wrong. So, I still think would be a good idea to pip to explicitly pass the proxy to the request , avoiding this confusion. I would like to reinforce, as a developer behind proxy if you use pip with Ps: In my exemple, I gave different ips to the proxy just to highlight the problem, but usually this is not a real scenario, the critical point there is actually the scheme. |
Okay, I have another solution for this error. pip/src/pip/_vendor/requests/sessions.py Line 530 in 3ab760a
should be replaced by: proxies = proxies or self.proxies
This also fixes the problem. |
I think we should wait on psf/requests#5735 first. It would fix this problem for us (and other |
My conclusion is: this is a bug of requests (psf/requests#5677), mateusduboli has already proposed a patch PR one month ago: psf/requests#5735 , but PSF has not yet approved it. In that patch, mateusduboli adjusted the order of merge setting, finally make parameters higher priority than environment. If the PSF does not approve it in the end, or the processing is too slow, maybe we can consider override the default parameter of session, just like your solution before. |
@CrazyBoyFeng and @uranusjr I wouldn't call it override, since request actually gives you the ability to pass it as a parameter, but yeah I really think this is the best way. Also, I don't think request.Session will entirely fix the problem here, since it's more related to how they fetch proxies from the system env. Let me try to make my point clear:
Also is quite misleading if pip gives you the option to set a proxy and in the end, rely on |
pip sets the proxies here: pip/src/pip/_internal/cli/req_command.py Lines 108 to 113 in 031c34e
where |
parse system env proxies wrongly is another bug of
as i post above, so, the ultimate problem is that besides, |
Looks like the concensus here is that it's a requests bug and needs to be fixed there. I've marked this as "blocked" on the basis of "if it gets fixed in our dependency, we get that fix too".
Do we want to work around the requests bug, or do we want to keep providing broken behavior to our users? TBH, I'm ambivalent. The outcome I do like is $corp folks whose workflows have been disrupted by this contribute to requests, and help get the bug fixed; but maybe I'm too optimistic. :) |
We still have at least a month to go before a release is made anyway, so I’d wait for |
I completely agree that the bug in this situation is on the side of pip/src/pip/_internal/cli/req_command.py Lines 108 to 113 in 031c34e
by the env_proxies , assuming session is an object of PipSession. Therefore, the only fix pip can apply on its end is to explicitly pass the proxies to the request method.
This method will always override pip/src/pip/_vendor/requests/sessions.py Lines 701 to 722 in 3ab760a
|
But that is a change to our vendored copy of requests, and that's not how we handle vendoring. You need to propose that fix to the requests project, and we'll pick it up when they release a fixed version (maybe you have already posted a PR to requests, I haven't been following this discussion closely). |
@pfmoore I just wanted to point the problem there, I know pip can't change the vendor directly. The proposed solution for pip is here, in the case you don't want to wait for
Which definitely is not happing. |
I would encourage putting more effort pushing requests to merge and release a fix. Arguing about semantics here is not very helpful for anything. |
This change will make the hierarchy request **kwargs -> Session args -> environment to be respected. This also solves problems with pip as discussed [here](pypa/pip#9691 (comment)) and psf#5735
After waiting a long time for a fix on the side of the request that could fix pip install issues with proxy as already discussed in pypa#9691. It seems that such changes as mentioned in psf/requests#5735 will take a while to happen, probably only on version 3 of the request. Therefore, I'm suggesting a change on the pip side as already proposed in [issuecomment-79051687]pypa#9568 (comment) and [issuecomment-939373868]psf/requests#5735 (comment) I think it's important that pip address this issue asap since the newer versions of pip are not working properly behind a proxy.
Is this similar to #10691? From what I see in the debugger, the proxy is passed to the request's session, just not applied. |
@meiswjn According to previous research #9568, this is a design flaw in the third-party library However, the maintainers of @junqfisica has been following up on this issue. There is a #10680 already in progress. |
Is fixing this at an impasse then, it seems? How awkward for users (I ran into this again today.) Maybe a way forward would be to perhaps remove the --proxy option from pip (as it doesn't always work). And/or output a warning about possibly broken behaviour. |
@ByteJuggler There have been 2 solutions proposed in the discussions here (there may be others, I only did a quick skim):
All that's lacking is for someone to step up and do the necessary work. Is it something you could help with? |
pip version
21.0.1
Python version
3.8
OS
Windows 10 / Linux
Additional information
The problem is easier to see if you are behind a proxy.
Description
Running pip install package --proxy http://my_proxy DO NOT pass the proxies to requests.Session.request(...).
As far as I could check this problem is there since version 20.2.2 at least. It was unnoticed until now because, before urllib update, it didn't check ssl_certificate so it didn't matter if your scheme was either http or https. Therefore, this bug is partially a root cause for the SSLError in recent versions of pip. Since users don't have the ability to pass the proxy to pip directly.
Expected behavior
I would expect that running pip install with --proxy, the proxy set here would be passed to the request, overriding any system configuration.
How to Reproduce
PS: You can also do the inverse process. Set a fake proxy on the system and run pip --proxy using a working one. Now you should get an error.
Output
Code of Conduct
I have already created a post about it here, including a working solution.
The text was updated successfully, but these errors were encountered: