Environment
Description
When fetching a package from a private repo for the first time, pip asks
for the username, then fails without asking for the password.
Expected behavior
Pip should ask for the password.
How to Reproduce
https://py.briq.it/simple/
as extra index url pip install -v briq_utils
Output
$ pip install -v briq_utils
Defaulting to user installation because normal site-packages is not writeable
Created temporary directory: /tmp/pip-ephem-wheel-cache-h0go_59q
Created temporary directory: /tmp/pip-req-tracker-_zrb96hk
Initialized build tracking at /tmp/pip-req-tracker-_zrb96hk
Created build tracker: /tmp/pip-req-tracker-_zrb96hk
Entered build tracker: /tmp/pip-req-tracker-_zrb96hk
Created temporary directory: /tmp/pip-install-cjulioxj
Looking in indexes: https://pypi.org/simple, https://py.briq.it/simple/
2 location(s) to search for versions of briq-utils:
* https://pypi.org/simple/briq-utils/
* https://py.briq.it/simple/briq-utils/
Fetching project page and analyzing links: https://pypi.org/simple/briq-utils/
Getting page https://pypi.org/simple/briq-utils/
Found index url https://pypi.org/simple
Getting credentials from keyring for https://pypi.org/simple
Getting credentials from keyring for pypi.org
Looking up "https://pypi.org/simple/briq-utils/" in the cache
Request header has "max_age" as 0, cache bypassed
Starting new HTTPS connection (1): pypi.org:443
https://pypi.org:443 "GET /simple/briq-utils/ HTTP/1.1" 404 13
Status code 404 not in (200, 203, 300, 301)
Could not fetch URL https://pypi.org/simple/briq-utils/: 404 Client Error: Not Found for url: https://pypi.org/simple/briq-utils/ - skipping
Fetching project page and analyzing links: https://py.briq.it/simple/briq-utils/
Getting page https://py.briq.it/simple/briq-utils/
Found index url https://py.briq.it/simple/
Getting credentials from keyring for https://py.briq.it/simple/
Getting credentials from keyring for py.briq.it
Looking up "https://py.briq.it/simple/briq-utils/" in the cache
Request header has "max_age" as 0, cache bypassed
Starting new HTTPS connection (1): py.briq.it:443
https://py.briq.it:443 "GET /simple/briq-utils/ HTTP/1.1" 401 727
User for py.briq.it: [type username]
Getting credentials from keyring for py.briq.it
Status code 401 not in (200, 203, 300, 301)
Looking up "https://py.briq.it/simple/briq-utils/" in the cache
Request header has "max_age" as 0, cache bypassed
https://py.briq.it:443 "GET /simple/briq-utils/ HTTP/1.1" 403 721
Status code 403 not in (200, 203, 300, 301)
Could not fetch URL https://py.briq.it/simple/briq-utils/: 403 Client Error: Forbidden for url: https://py.briq.it/simple/briq-utils/ - skipping
[...]
Workaround
>>> import keyring
>>> keyring.set_password("py.briq.it", "myusername", "mypassword")
The problem seems to be in _internal/network/auth.py
, where function _prompt_for_password
calls get_keyring_auth
after reading the username; however, at least with my keyring, an object that is not None is returned even if there is no credential (auth.password
is None).
It is a matter of opinion if get_keyring_auth
should be called at all: one would expect the program to always ask for the password after the username.
one would expect the program to always ask for the password after the username.
I’m less sure about that; it seems natural to me for a program to auto-fill the password if it “knows” it already.
Do you happen to know what is returned by keyring.get_password()
in this case? Maybe it’s just a matter of improving detection.
I’m less sure about that; it seems natural to me for a program to auto-fill the password if it “knows” it already.
For CLI, I don't think that's a good idea, since it's difficult to make good UX for accessing (changing/deleting) the saved pasword.
Do you happen to know what is returned by
keyring.get_password()
in this case? Maybe it’s just a matter of improving detection.
In my case, keyring.get_password
returns None
, so that in _prompt_for_password
, auth[0] == "myusername"
and auth[1] is None
In my case,
keyring.get_password
returns None, so that in_prompt_for_password
,auth[0] == "myusername"
andauth[1]
is None
Sounds like we just need to get_keyring_auth()
to detect this.
I would like to resolve this issue.
To me, it seems that using the condition as if auth[0] and auth[1]
in place of the currently used condition i.e. if auth
here https://github.com/pypa/pip/blob/master/src/pip/_internal/network/auth.py#L222, would resolve the problem. Am I seeking the correct approach?