We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add a documentation string to pip._internal.network.auth
Follow up to #7045

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/pip/_internal/network/__init__.py`
Content:
```
```

Path: `src/pip/_internal/network/auth.py`
Content:
```
1 import logging
2 
3 from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth
4 from pip._vendor.requests.utils import get_netrc_auth
5 from pip._vendor.six.moves.urllib import parse as urllib_parse
6 
7 from pip._internal.utils.misc import (
8     ask,
9     ask_input,
10     ask_password,
11     remove_auth_from_url,
12     split_auth_netloc_from_url,
13 )
14 from pip._internal.utils.typing import MYPY_CHECK_RUNNING
15 
16 if MYPY_CHECK_RUNNING:
17     from optparse import Values
18     from typing import Dict, Optional, Tuple
19 
20     from pip._internal.vcs.versioncontrol import AuthInfo
21 
22     Credentials = Tuple[str, str, str]
23 
24 logger = logging.getLogger(__name__)
25 
26 try:
27     import keyring  # noqa
28 except ImportError:
29     keyring = None
30 except Exception as exc:
31     logger.warning(
32         "Keyring is skipped due to an exception: %s", str(exc),
33     )
34     keyring = None
35 
36 
37 def get_keyring_auth(url, username):
38     """Return the tuple auth for a given url from keyring."""
39     if not url or not keyring:
40         return None
41 
42     try:
43         try:
44             get_credential = keyring.get_credential
45         except AttributeError:
46             pass
47         else:
48             logger.debug("Getting credentials from keyring for %s", url)
49             cred = get_credential(url, username)
50             if cred is not None:
51                 return cred.username, cred.password
52             return None
53 
54         if username:
55             logger.debug("Getting password from keyring for %s", url)
56             password = keyring.get_password(url, username)
57             if password:
58                 return username, password
59 
60     except Exception as exc:
61         logger.warning(
62             "Keyring is skipped due to an exception: %s", str(exc),
63         )
64 
65 
66 class MultiDomainBasicAuth(AuthBase):
67 
68     def __init__(self, prompting=True, index_urls=None):
69         # type: (bool, Optional[Values]) -> None
70         self.prompting = prompting
71         self.index_urls = index_urls
72         self.passwords = {}  # type: Dict[str, AuthInfo]
73         # When the user is prompted to enter credentials and keyring is
74         # available, we will offer to save them. If the user accepts,
75         # this value is set to the credentials they entered. After the
76         # request authenticates, the caller should call
77         # ``save_credentials`` to save these.
78         self._credentials_to_save = None  # type: Optional[Credentials]
79 
80     def _get_index_url(self, url):
81         """Return the original index URL matching the requested URL.
82 
83         Cached or dynamically generated credentials may work against
84         the original index URL rather than just the netloc.
85 
86         The provided url should have had its username and password
87         removed already. If the original index url had credentials then
88         they will be included in the return value.
89 
90         Returns None if no matching index was found, or if --no-index
91         was specified by the user.
92         """
93         if not url or not self.index_urls:
94             return None
95 
96         for u in self.index_urls:
97             prefix = remove_auth_from_url(u).rstrip("/") + "/"
98             if url.startswith(prefix):
99                 return u
100 
101     def _get_new_credentials(self, original_url, allow_netrc=True,
102                              allow_keyring=True):
103         """Find and return credentials for the specified URL."""
104         # Split the credentials and netloc from the url.
105         url, netloc, url_user_password = split_auth_netloc_from_url(
106             original_url,
107         )
108 
109         # Start with the credentials embedded in the url
110         username, password = url_user_password
111         if username is not None and password is not None:
112             logger.debug("Found credentials in url for %s", netloc)
113             return url_user_password
114 
115         # Find a matching index url for this request
116         index_url = self._get_index_url(url)
117         if index_url:
118             # Split the credentials from the url.
119             index_info = split_auth_netloc_from_url(index_url)
120             if index_info:
121                 index_url, _, index_url_user_password = index_info
122                 logger.debug("Found index url %s", index_url)
123 
124         # If an index URL was found, try its embedded credentials
125         if index_url and index_url_user_password[0] is not None:
126             username, password = index_url_user_password
127             if username is not None and password is not None:
128                 logger.debug("Found credentials in index url for %s", netloc)
129                 return index_url_user_password
130 
131         # Get creds from netrc if we still don't have them
132         if allow_netrc:
133             netrc_auth = get_netrc_auth(original_url)
134             if netrc_auth:
135                 logger.debug("Found credentials in netrc for %s", netloc)
136                 return netrc_auth
137 
138         # If we don't have a password and keyring is available, use it.
139         if allow_keyring:
140             # The index url is more specific than the netloc, so try it first
141             kr_auth = (
142                 get_keyring_auth(index_url, username) or
143                 get_keyring_auth(netloc, username)
144             )
145             if kr_auth:
146                 logger.debug("Found credentials in keyring for %s", netloc)
147                 return kr_auth
148 
149         return username, password
150 
151     def _get_url_and_credentials(self, original_url):
152         """Return the credentials to use for the provided URL.
153 
154         If allowed, netrc and keyring may be used to obtain the
155         correct credentials.
156 
157         Returns (url_without_credentials, username, password). Note
158         that even if the original URL contains credentials, this
159         function may return a different username and password.
160         """
161         url, netloc, _ = split_auth_netloc_from_url(original_url)
162 
163         # Use any stored credentials that we have for this netloc
164         username, password = self.passwords.get(netloc, (None, None))
165 
166         if username is None and password is None:
167             # No stored credentials. Acquire new credentials without prompting
168             # the user. (e.g. from netrc, keyring, or the URL itself)
169             username, password = self._get_new_credentials(original_url)
170 
171         if username is not None or password is not None:
172             # Convert the username and password if they're None, so that
173             # this netloc will show up as "cached" in the conditional above.
174             # Further, HTTPBasicAuth doesn't accept None, so it makes sense to
175             # cache the value that is going to be used.
176             username = username or ""
177             password = password or ""
178 
179             # Store any acquired credentials.
180             self.passwords[netloc] = (username, password)
181 
182         assert (
183             # Credentials were found
184             (username is not None and password is not None) or
185             # Credentials were not found
186             (username is None and password is None)
187         ), "Could not load credentials from url: {}".format(original_url)
188 
189         return url, username, password
190 
191     def __call__(self, req):
192         # Get credentials for this request
193         url, username, password = self._get_url_and_credentials(req.url)
194 
195         # Set the url of the request to the url without any credentials
196         req.url = url
197 
198         if username is not None and password is not None:
199             # Send the basic auth with this request
200             req = HTTPBasicAuth(username, password)(req)
201 
202         # Attach a hook to handle 401 responses
203         req.register_hook("response", self.handle_401)
204 
205         return req
206 
207     # Factored out to allow for easy patching in tests
208     def _prompt_for_password(self, netloc):
209         username = ask_input("User for %s: " % netloc)
210         if not username:
211             return None, None
212         auth = get_keyring_auth(netloc, username)
213         if auth:
214             return auth[0], auth[1], False
215         password = ask_password("Password: ")
216         return username, password, True
217 
218     # Factored out to allow for easy patching in tests
219     def _should_save_password_to_keyring(self):
220         if not keyring:
221             return False
222         return ask("Save credentials to keyring [y/N]: ", ["y", "n"]) == "y"
223 
224     def handle_401(self, resp, **kwargs):
225         # We only care about 401 responses, anything else we want to just
226         #   pass through the actual response
227         if resp.status_code != 401:
228             return resp
229 
230         # We are not able to prompt the user so simply return the response
231         if not self.prompting:
232             return resp
233 
234         parsed = urllib_parse.urlparse(resp.url)
235 
236         # Prompt the user for a new username and password
237         username, password, save = self._prompt_for_password(parsed.netloc)
238 
239         # Store the new username and password to use for future requests
240         self._credentials_to_save = None
241         if username is not None and password is not None:
242             self.passwords[parsed.netloc] = (username, password)
243 
244             # Prompt to save the password to keyring
245             if save and self._should_save_password_to_keyring():
246                 self._credentials_to_save = (parsed.netloc, username, password)
247 
248         # Consume content and release the original connection to allow our new
249         #   request to reuse the same one.
250         resp.content
251         resp.raw.release_conn()
252 
253         # Add our new username and password to the request
254         req = HTTPBasicAuth(username or "", password or "")(resp.request)
255         req.register_hook("response", self.warn_on_401)
256 
257         # On successful request, save the credentials that were used to
258         # keyring. (Note that if the user responded "no" above, this member
259         # is not set and nothing will be saved.)
260         if self._credentials_to_save:
261             req.register_hook("response", self.save_credentials)
262 
263         # Send our new request
264         new_resp = resp.connection.send(req, **kwargs)
265         new_resp.history.append(resp)
266 
267         return new_resp
268 
269     def warn_on_401(self, resp, **kwargs):
270         """Response callback to warn about incorrect credentials."""
271         if resp.status_code == 401:
272             logger.warning(
273                 '401 Error, Credentials not correct for %s', resp.request.url,
274             )
275 
276     def save_credentials(self, resp, **kwargs):
277         """Response callback to save credentials on success."""
278         assert keyring is not None, "should never reach here without keyring"
279         if not keyring:
280             return
281 
282         creds = self._credentials_to_save
283         self._credentials_to_save = None
284         if creds and resp.status_code < 400:
285             try:
286                 logger.info('Saving credentials to keyring')
287                 keyring.set_password(*creds)
288             except Exception:
289                 logger.exception('Failed to save credentials')
290
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/pip/_internal/network/__init__.py b/src/pip/_internal/network/__init__.py
--- a/src/pip/_internal/network/__init__.py
+++ b/src/pip/_internal/network/__init__.py
@@ -0,0 +1,2 @@
+"""Contains purely network-related utilities.
+"""
diff --git a/src/pip/_internal/network/auth.py b/src/pip/_internal/network/auth.py
--- a/src/pip/_internal/network/auth.py
+++ b/src/pip/_internal/network/auth.py
@@ -1,3 +1,9 @@
+"""Network Authentication Helpers
+
+Contains interface (MultiDomainBasicAuth) and associated glue code for
+providing credentials in the context of network requests.
+"""
+
 import logging
 
 from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth

{"golden_diff": "diff --git a/src/pip/_internal/network/__init__.py b/src/pip/_internal/network/__init__.py\n--- a/src/pip/_internal/network/__init__.py\n+++ b/src/pip/_internal/network/__init__.py\n@@ -0,0 +1,2 @@\n+\"\"\"Contains purely network-related utilities.\n+\"\"\"\ndiff --git a/src/pip/_internal/network/auth.py b/src/pip/_internal/network/auth.py\n--- a/src/pip/_internal/network/auth.py\n+++ b/src/pip/_internal/network/auth.py\n@@ -1,3 +1,9 @@\n+\"\"\"Network Authentication Helpers\n+\n+Contains interface (MultiDomainBasicAuth) and associated glue code for\n+providing credentials in the context of network requests.\n+\"\"\"\n+\n import logging\n \n from pip._vendor.requests.auth import AuthBase, HTTPBasicAuth\n", "issue": "Add a documentation string to pip._internal.network.auth\nFollow up to #7045\n", "before_files": [{"content": "", "path": "src/pip/_internal/network/__init__.py"}, {"content": "import logging\n\nfrom pip._vendor.requests.auth import AuthBase, HTTPBasicAuth\nfrom pip._vendor.requests.utils import get_netrc_auth\nfrom pip._vendor.six.moves.urllib import parse as urllib_parse\n\nfrom pip._internal.utils.misc import (\n    ask,\n    ask_input,\n    ask_password,\n    remove_auth_from_url,\n    split_auth_netloc_from_url,\n)\nfrom pip._internal.utils.typing import MYPY_CHECK_RUNNING\n\nif MYPY_CHECK_RUNNING:\n    from optparse import Values\n    from typing import Dict, Optional, Tuple\n\n    from pip._internal.vcs.versioncontrol import AuthInfo\n\n    Credentials = Tuple[str, str, str]\n\nlogger = logging.getLogger(__name__)\n\ntry:\n    import keyring  # noqa\nexcept ImportError:\n    keyring = None\nexcept Exception as exc:\n    logger.warning(\n        \"Keyring is skipped due to an exception: %s\", str(exc),\n    )\n    keyring = None\n\n\ndef get_keyring_auth(url, username):\n    \"\"\"Return the tuple auth for a given url from keyring.\"\"\"\n    if not url or not keyring:\n        return None\n\n    try:\n        try:\n            get_credential = keyring.get_credential\n        except AttributeError:\n            pass\n        else:\n            logger.debug(\"Getting credentials from keyring for %s\", url)\n            cred = get_credential(url, username)\n            if cred is not None:\n                return cred.username, cred.password\n            return None\n\n        if username:\n            logger.debug(\"Getting password from keyring for %s\", url)\n            password = keyring.get_password(url, username)\n            if password:\n                return username, password\n\n    except Exception as exc:\n        logger.warning(\n            \"Keyring is skipped due to an exception: %s\", str(exc),\n        )\n\n\nclass MultiDomainBasicAuth(AuthBase):\n\n    def __init__(self, prompting=True, index_urls=None):\n        # type: (bool, Optional[Values]) -> None\n        self.prompting = prompting\n        self.index_urls = index_urls\n        self.passwords = {}  # type: Dict[str, AuthInfo]\n        # When the user is prompted to enter credentials and keyring is\n        # available, we will offer to save them. If the user accepts,\n        # this value is set to the credentials they entered. After the\n        # request authenticates, the caller should call\n        # ``save_credentials`` to save these.\n        self._credentials_to_save = None  # type: Optional[Credentials]\n\n    def _get_index_url(self, url):\n        \"\"\"Return the original index URL matching the requested URL.\n\n        Cached or dynamically generated credentials may work against\n        the original index URL rather than just the netloc.\n\n        The provided url should have had its username and password\n        removed already. If the original index url had credentials then\n        they will be included in the return value.\n\n        Returns None if no matching index was found, or if --no-index\n        was specified by the user.\n        \"\"\"\n        if not url or not self.index_urls:\n            return None\n\n        for u in self.index_urls:\n            prefix = remove_auth_from_url(u).rstrip(\"/\") + \"/\"\n            if url.startswith(prefix):\n                return u\n\n    def _get_new_credentials(self, original_url, allow_netrc=True,\n                             allow_keyring=True):\n        \"\"\"Find and return credentials for the specified URL.\"\"\"\n        # Split the credentials and netloc from the url.\n        url, netloc, url_user_password = split_auth_netloc_from_url(\n            original_url,\n        )\n\n        # Start with the credentials embedded in the url\n        username, password = url_user_password\n        if username is not None and password is not None:\n            logger.debug(\"Found credentials in url for %s\", netloc)\n            return url_user_password\n\n        # Find a matching index url for this request\n        index_url = self._get_index_url(url)\n        if index_url:\n            # Split the credentials from the url.\n            index_info = split_auth_netloc_from_url(index_url)\n            if index_info:\n                index_url, _, index_url_user_password = index_info\n                logger.debug(\"Found index url %s\", index_url)\n\n        # If an index URL was found, try its embedded credentials\n        if index_url and index_url_user_password[0] is not None:\n            username, password = index_url_user_password\n            if username is not None and password is not None:\n                logger.debug(\"Found credentials in index url for %s\", netloc)\n                return index_url_user_password\n\n        # Get creds from netrc if we still don't have them\n        if allow_netrc:\n            netrc_auth = get_netrc_auth(original_url)\n            if netrc_auth:\n                logger.debug(\"Found credentials in netrc for %s\", netloc)\n                return netrc_auth\n\n        # If we don't have a password and keyring is available, use it.\n        if allow_keyring:\n            # The index url is more specific than the netloc, so try it first\n            kr_auth = (\n                get_keyring_auth(index_url, username) or\n                get_keyring_auth(netloc, username)\n            )\n            if kr_auth:\n                logger.debug(\"Found credentials in keyring for %s\", netloc)\n                return kr_auth\n\n        return username, password\n\n    def _get_url_and_credentials(self, original_url):\n        \"\"\"Return the credentials to use for the provided URL.\n\n        If allowed, netrc and keyring may be used to obtain the\n        correct credentials.\n\n        Returns (url_without_credentials, username, password). Note\n        that even if the original URL contains credentials, this\n        function may return a different username and password.\n        \"\"\"\n        url, netloc, _ = split_auth_netloc_from_url(original_url)\n\n        # Use any stored credentials that we have for this netloc\n        username, password = self.passwords.get(netloc, (None, None))\n\n        if username is None and password is None:\n            # No stored credentials. Acquire new credentials without prompting\n            # the user. (e.g. from netrc, keyring, or the URL itself)\n            username, password = self._get_new_credentials(original_url)\n\n        if username is not None or password is not None:\n            # Convert the username and password if they're None, so that\n            # this netloc will show up as \"cached\" in the conditional above.\n            # Further, HTTPBasicAuth doesn't accept None, so it makes sense to\n            # cache the value that is going to be used.\n            username = username or \"\"\n            password = password or \"\"\n\n            # Store any acquired credentials.\n            self.passwords[netloc] = (username, password)\n\n        assert (\n            # Credentials were found\n            (username is not None and password is not None) or\n            # Credentials were not found\n            (username is None and password is None)\n        ), \"Could not load credentials from url: {}\".format(original_url)\n\n        return url, username, password\n\n    def __call__(self, req):\n        # Get credentials for this request\n        url, username, password = self._get_url_and_credentials(req.url)\n\n        # Set the url of the request to the url without any credentials\n        req.url = url\n\n        if username is not None and password is not None:\n            # Send the basic auth with this request\n            req = HTTPBasicAuth(username, password)(req)\n\n        # Attach a hook to handle 401 responses\n        req.register_hook(\"response\", self.handle_401)\n\n        return req\n\n    # Factored out to allow for easy patching in tests\n    def _prompt_for_password(self, netloc):\n        username = ask_input(\"User for %s: \" % netloc)\n        if not username:\n            return None, None\n        auth = get_keyring_auth(netloc, username)\n        if auth:\n            return auth[0], auth[1], False\n        password = ask_password(\"Password: \")\n        return username, password, True\n\n    # Factored out to allow for easy patching in tests\n    def _should_save_password_to_keyring(self):\n        if not keyring:\n            return False\n        return ask(\"Save credentials to keyring [y/N]: \", [\"y\", \"n\"]) == \"y\"\n\n    def handle_401(self, resp, **kwargs):\n        # We only care about 401 responses, anything else we want to just\n        #   pass through the actual response\n        if resp.status_code != 401:\n            return resp\n\n        # We are not able to prompt the user so simply return the response\n        if not self.prompting:\n            return resp\n\n        parsed = urllib_parse.urlparse(resp.url)\n\n        # Prompt the user for a new username and password\n        username, password, save = self._prompt_for_password(parsed.netloc)\n\n        # Store the new username and password to use for future requests\n        self._credentials_to_save = None\n        if username is not None and password is not None:\n            self.passwords[parsed.netloc] = (username, password)\n\n            # Prompt to save the password to keyring\n            if save and self._should_save_password_to_keyring():\n                self._credentials_to_save = (parsed.netloc, username, password)\n\n        # Consume content and release the original connection to allow our new\n        #   request to reuse the same one.\n        resp.content\n        resp.raw.release_conn()\n\n        # Add our new username and password to the request\n        req = HTTPBasicAuth(username or \"\", password or \"\")(resp.request)\n        req.register_hook(\"response\", self.warn_on_401)\n\n        # On successful request, save the credentials that were used to\n        # keyring. (Note that if the user responded \"no\" above, this member\n        # is not set and nothing will be saved.)\n        if self._credentials_to_save:\n            req.register_hook(\"response\", self.save_credentials)\n\n        # Send our new request\n        new_resp = resp.connection.send(req, **kwargs)\n        new_resp.history.append(resp)\n\n        return new_resp\n\n    def warn_on_401(self, resp, **kwargs):\n        \"\"\"Response callback to warn about incorrect credentials.\"\"\"\n        if resp.status_code == 401:\n            logger.warning(\n                '401 Error, Credentials not correct for %s', resp.request.url,\n            )\n\n    def save_credentials(self, resp, **kwargs):\n        \"\"\"Response callback to save credentials on success.\"\"\"\n        assert keyring is not None, \"should never reach here without keyring\"\n        if not keyring:\n            return\n\n        creds = self._credentials_to_save\n        self._credentials_to_save = None\n        if creds and resp.status_code < 400:\n            try:\n                logger.info('Saving credentials to keyring')\n                keyring.set_password(*creds)\n            except Exception:\n                logger.exception('Failed to save credentials')\n", "path": "src/pip/_internal/network/auth.py"}], "after_files": [{"content": "\"\"\"Contains purely network-related utilities.\n\"\"\"\n", "path": "src/pip/_internal/network/__init__.py"}, {"content": "\"\"\"Network Authentication Helpers\n\nContains interface (MultiDomainBasicAuth) and associated glue code for\nproviding credentials in the context of network requests.\n\"\"\"\n\nimport logging\n\nfrom pip._vendor.requests.auth import AuthBase, HTTPBasicAuth\nfrom pip._vendor.requests.utils import get_netrc_auth\nfrom pip._vendor.six.moves.urllib import parse as urllib_parse\n\nfrom pip._internal.utils.misc import (\n    ask,\n    ask_input,\n    ask_password,\n    remove_auth_from_url,\n    split_auth_netloc_from_url,\n)\nfrom pip._internal.utils.typing import MYPY_CHECK_RUNNING\n\nif MYPY_CHECK_RUNNING:\n    from optparse import Values\n    from typing import Dict, Optional, Tuple\n\n    from pip._internal.vcs.versioncontrol import AuthInfo\n\n    Credentials = Tuple[str, str, str]\n\nlogger = logging.getLogger(__name__)\n\ntry:\n    import keyring  # noqa\nexcept ImportError:\n    keyring = None\nexcept Exception as exc:\n    logger.warning(\n        \"Keyring is skipped due to an exception: %s\", str(exc),\n    )\n    keyring = None\n\n\ndef get_keyring_auth(url, username):\n    \"\"\"Return the tuple auth for a given url from keyring.\"\"\"\n    if not url or not keyring:\n        return None\n\n    try:\n        try:\n            get_credential = keyring.get_credential\n        except AttributeError:\n            pass\n        else:\n            logger.debug(\"Getting credentials from keyring for %s\", url)\n            cred = get_credential(url, username)\n            if cred is not None:\n                return cred.username, cred.password\n            return None\n\n        if username:\n            logger.debug(\"Getting password from keyring for %s\", url)\n            password = keyring.get_password(url, username)\n            if password:\n                return username, password\n\n    except Exception as exc:\n        logger.warning(\n            \"Keyring is skipped due to an exception: %s\", str(exc),\n        )\n\n\nclass MultiDomainBasicAuth(AuthBase):\n\n    def __init__(self, prompting=True, index_urls=None):\n        # type: (bool, Optional[Values]) -> None\n        self.prompting = prompting\n        self.index_urls = index_urls\n        self.passwords = {}  # type: Dict[str, AuthInfo]\n        # When the user is prompted to enter credentials and keyring is\n        # available, we will offer to save them. If the user accepts,\n        # this value is set to the credentials they entered. After the\n        # request authenticates, the caller should call\n        # ``save_credentials`` to save these.\n        self._credentials_to_save = None  # type: Optional[Credentials]\n\n    def _get_index_url(self, url):\n        \"\"\"Return the original index URL matching the requested URL.\n\n        Cached or dynamically generated credentials may work against\n        the original index URL rather than just the netloc.\n\n        The provided url should have had its username and password\n        removed already. If the original index url had credentials then\n        they will be included in the return value.\n\n        Returns None if no matching index was found, or if --no-index\n        was specified by the user.\n        \"\"\"\n        if not url or not self.index_urls:\n            return None\n\n        for u in self.index_urls:\n            prefix = remove_auth_from_url(u).rstrip(\"/\") + \"/\"\n            if url.startswith(prefix):\n                return u\n\n    def _get_new_credentials(self, original_url, allow_netrc=True,\n                             allow_keyring=True):\n        \"\"\"Find and return credentials for the specified URL.\"\"\"\n        # Split the credentials and netloc from the url.\n        url, netloc, url_user_password = split_auth_netloc_from_url(\n            original_url,\n        )\n\n        # Start with the credentials embedded in the url\n        username, password = url_user_password\n        if username is not None and password is not None:\n            logger.debug(\"Found credentials in url for %s\", netloc)\n            return url_user_password\n\n        # Find a matching index url for this request\n        index_url = self._get_index_url(url)\n        if index_url:\n            # Split the credentials from the url.\n            index_info = split_auth_netloc_from_url(index_url)\n            if index_info:\n                index_url, _, index_url_user_password = index_info\n                logger.debug(\"Found index url %s\", index_url)\n\n        # If an index URL was found, try its embedded credentials\n        if index_url and index_url_user_password[0] is not None:\n            username, password = index_url_user_password\n            if username is not None and password is not None:\n                logger.debug(\"Found credentials in index url for %s\", netloc)\n                return index_url_user_password\n\n        # Get creds from netrc if we still don't have them\n        if allow_netrc:\n            netrc_auth = get_netrc_auth(original_url)\n            if netrc_auth:\n                logger.debug(\"Found credentials in netrc for %s\", netloc)\n                return netrc_auth\n\n        # If we don't have a password and keyring is available, use it.\n        if allow_keyring:\n            # The index url is more specific than the netloc, so try it first\n            kr_auth = (\n                get_keyring_auth(index_url, username) or\n                get_keyring_auth(netloc, username)\n            )\n            if kr_auth:\n                logger.debug(\"Found credentials in keyring for %s\", netloc)\n                return kr_auth\n\n        return username, password\n\n    def _get_url_and_credentials(self, original_url):\n        \"\"\"Return the credentials to use for the provided URL.\n\n        If allowed, netrc and keyring may be used to obtain the\n        correct credentials.\n\n        Returns (url_without_credentials, username, password). Note\n        that even if the original URL contains credentials, this\n        function may return a different username and password.\n        \"\"\"\n        url, netloc, _ = split_auth_netloc_from_url(original_url)\n\n        # Use any stored credentials that we have for this netloc\n        username, password = self.passwords.get(netloc, (None, None))\n\n        if username is None and password is None:\n            # No stored credentials. Acquire new credentials without prompting\n            # the user. (e.g. from netrc, keyring, or the URL itself)\n            username, password = self._get_new_credentials(original_url)\n\n        if username is not None or password is not None:\n            # Convert the username and password if they're None, so that\n            # this netloc will show up as \"cached\" in the conditional above.\n            # Further, HTTPBasicAuth doesn't accept None, so it makes sense to\n            # cache the value that is going to be used.\n            username = username or \"\"\n            password = password or \"\"\n\n            # Store any acquired credentials.\n            self.passwords[netloc] = (username, password)\n\n        assert (\n            # Credentials were found\n            (username is not None and password is not None) or\n            # Credentials were not found\n            (username is None and password is None)\n        ), \"Could not load credentials from url: {}\".format(original_url)\n\n        return url, username, password\n\n    def __call__(self, req):\n        # Get credentials for this request\n        url, username, password = self._get_url_and_credentials(req.url)\n\n        # Set the url of the request to the url without any credentials\n        req.url = url\n\n        if username is not None and password is not None:\n            # Send the basic auth with this request\n            req = HTTPBasicAuth(username, password)(req)\n\n        # Attach a hook to handle 401 responses\n        req.register_hook(\"response\", self.handle_401)\n\n        return req\n\n    # Factored out to allow for easy patching in tests\n    def _prompt_for_password(self, netloc):\n        username = ask_input(\"User for %s: \" % netloc)\n        if not username:\n            return None, None\n        auth = get_keyring_auth(netloc, username)\n        if auth:\n            return auth[0], auth[1], False\n        password = ask_password(\"Password: \")\n        return username, password, True\n\n    # Factored out to allow for easy patching in tests\n    def _should_save_password_to_keyring(self):\n        if not keyring:\n            return False\n        return ask(\"Save credentials to keyring [y/N]: \", [\"y\", \"n\"]) == \"y\"\n\n    def handle_401(self, resp, **kwargs):\n        # We only care about 401 responses, anything else we want to just\n        #   pass through the actual response\n        if resp.status_code != 401:\n            return resp\n\n        # We are not able to prompt the user so simply return the response\n        if not self.prompting:\n            return resp\n\n        parsed = urllib_parse.urlparse(resp.url)\n\n        # Prompt the user for a new username and password\n        username, password, save = self._prompt_for_password(parsed.netloc)\n\n        # Store the new username and password to use for future requests\n        self._credentials_to_save = None\n        if username is not None and password is not None:\n            self.passwords[parsed.netloc] = (username, password)\n\n            # Prompt to save the password to keyring\n            if save and self._should_save_password_to_keyring():\n                self._credentials_to_save = (parsed.netloc, username, password)\n\n        # Consume content and release the original connection to allow our new\n        #   request to reuse the same one.\n        resp.content\n        resp.raw.release_conn()\n\n        # Add our new username and password to the request\n        req = HTTPBasicAuth(username or \"\", password or \"\")(resp.request)\n        req.register_hook(\"response\", self.warn_on_401)\n\n        # On successful request, save the credentials that were used to\n        # keyring. (Note that if the user responded \"no\" above, this member\n        # is not set and nothing will be saved.)\n        if self._credentials_to_save:\n            req.register_hook(\"response\", self.save_credentials)\n\n        # Send our new request\n        new_resp = resp.connection.send(req, **kwargs)\n        new_resp.history.append(resp)\n\n        return new_resp\n\n    def warn_on_401(self, resp, **kwargs):\n        \"\"\"Response callback to warn about incorrect credentials.\"\"\"\n        if resp.status_code == 401:\n            logger.warning(\n                '401 Error, Credentials not correct for %s', resp.request.url,\n            )\n\n    def save_credentials(self, resp, **kwargs):\n        \"\"\"Response callback to save credentials on success.\"\"\"\n        assert keyring is not None, \"should never reach here without keyring\"\n        if not keyring:\n            return\n\n        creds = self._credentials_to_save\n        self._credentials_to_save = None\n        if creds and resp.status_code < 400:\n            try:\n                logger.info('Saving credentials to keyring')\n                keyring.set_password(*creds)\n            except Exception:\n                logger.exception('Failed to save credentials')\n", "path": "src/pip/_internal/network/auth.py"}]}