简体   繁体   中英

Installing packages from private pypi fails if password contains '@' character

Installing packages from private pypi fails if password contains '@' character

So if I have login: someLogin password: Password@

then my pip.conf looks like:

[global]
extra-index-url = https://someLogin:Password@@nexus.privatepypy.com/repository/pypi/xxx

And this actually does not work. I have to type my password all the time.

Is there any workaround for this?

PS: Changing password is not a solution :-)

Maybe you need to percent scape it, as it is a URL.

>>> from urllib.parse import quote_plus
>>> quote_plus('Password@')
'Password%40'

For example in Mongo, to connect you can use:

http://api.mongodb.com/python/current/examples/authentication.html#percent-escaping-username-and-password

>>> from pymongo import MongoClient
>>> import urllib.parse
>>> username = urllib.parse.quote_plus('user')
>>> username
'user'
>>> password = urllib.parse.quote_plus('pass/word')
>>> password
'pass%2Fword'
>>> MongoClient('mongodb://%s:%s@127.0.0.1' % (username, password))

--- EDIT ---

I followed the code in pip to here:

https://github.com/pypa/pip/blob/1ea3f89ff9f005c78413907b36e55b3e76092612/src/pip/_internal/download.py#L140

And run the code manually, with both quoting and unquoting the password:

Unquoted:

>>> from urllib import parse as urllib_parse
>>> url = 'https://someLogin:Password@@nexus.privatepypy.com/repository/pypi/xxx'

>>> parsed = urllib_parse.urlparse(url)
>>> parsed
ParseResult(scheme='https', netloc='someLogin:Password@@nexus.privatepypy.com', path='/repository/pypi/xxx', params='', query='', fragment='')

>>> netloc = parsed.netloc.rsplit("@", 1)[-1]
>>> netloc
'nexus.privatepypy.com'

>>> url = urllib_parse.urlunparse(parsed[:1] + (netloc,) + parsed[2:])
>>> url
'https://nexus.privatepypy.com/repository/pypi/xxx'

>>> def parse_credentials(netloc):
...    if "@" in netloc:
...        userinfo = netloc.rsplit("@", 1)[0]
...        if ":" in userinfo:
...            user, pwd = userinfo.split(":", 1)
...            return (urllib_parse.unquote(user), urllib_parse.unquote(pwd))
...        return urllib_parse.unquote(userinfo), None
...    return None, None

>>> username, password = parse_credentials(parsed.netloc)
>>> username, password
('someLogin', 'Password@')

Quoted:

>>> url = 'https://{}:{}@nexus.privatepypy.com/repository/pypi/xxx'.format(
...     urllib_parse.quote_plus('someLogin'), 
...     urllib_parse.quote_plus('Password@'), 
... )
>>> url
'https://someLogin:Password%40@nexus.privatepypy.com/repository/pypi/xxx'

>>> parsed = urllib_parse.urlparse(url)
>>> parsed
ParseResult(scheme='https', netloc='someLogin:Password%40@nexus.privatepypy.com', path='/repository/pypi/xxx', params='', query='', fragment='')

>>> username, password = parse_credentials(parsed.netloc)
>>> username, password
('someLogin', 'Password@')

As you can see it should work in both cases. As I could confirm pip unquotes the username and password, so it is better to quote them.

If it still doens't work I would check the pip version or file a bug.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM