fix(pypi): show overridden index urls in pypi download error (#3130)

Closes #2985

Suppose invalid `experimental_index_url_overrides` in `pip.parse` is set
like below.

```bzl
pip.parse(
    experimental_index_url = "https://pypi.org/simple",
    experimental_index_url_overrides = {"mypy": "https://invalid.com"},
    hub_name = "pypi",
    requirements_lock = "//:requirements_lock.txt",
)
```

It fails as follows, showing only "pypi.org" as pypi index url, not
"invalid.com" for for `mypy` package.

```
Error in fail: Failed to download metadata for ["mypy"] for from urls: ["https://pypi.org/simple"].
If you would like to skip downloading metadata for these packages please add 'simpleapi_skip=["mypy"]' to your 'pip.parse' call.
```

To show overridden url for each package, show url of each package that
has been failed to download metadata. The error message with this PR is
like below.

```
Error in fail:
Failed to download metadata of the following packages from urls:
{
    "mypy": "https://invalid.com",
}

If you would like to skip downloading metadata for these packages please add 'simpleapi_skip=["mypy"]' to your 'pip.parse' call.
```
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c52481d..d21ebc7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -99,6 +99,8 @@
   absolute imports (Python 2's behavior without `absolute_import`). Previous
   behavior can be restored using the directive
   `# gazelle:python_resolve_sibling_imports true`
+* (pypi) Show overridden index URL of packages when downloading metadata have failed.
+  ([#2985](https://github.com/bazel-contrib/rules_python/issues/2985)).
 
 {#v0-0-0-added}
 ### Added
diff --git a/python/private/pypi/simpleapi_download.bzl b/python/private/pypi/simpleapi_download.bzl
index a3ba969..52ff02a 100644
--- a/python/private/pypi/simpleapi_download.bzl
+++ b/python/private/pypi/simpleapi_download.bzl
@@ -128,16 +128,24 @@
 
     failed_sources = [pkg for pkg in attr.sources if pkg not in found_on_index]
     if failed_sources:
+        pkg_index_urls = {
+            pkg: index_url_overrides.get(
+                normalize_name(pkg),
+                index_urls,
+            )
+            for pkg in failed_sources
+        }
+
         _fail(
-            "\n".join([
-                "Failed to download metadata for {} for from urls: {}.".format(
-                    failed_sources,
-                    index_urls,
-                ),
-                "If you would like to skip downloading metadata for these packages please add 'simpleapi_skip={}' to your 'pip.parse' call.".format(
-                    render.list(failed_sources),
-                ),
-            ]),
+            """
+Failed to download metadata of the following packages from urls:
+{pkg_index_urls}
+
+If you would like to skip downloading metadata for these packages please add 'simpleapi_skip={failed_sources}' to your 'pip.parse' call.
+""".format(
+                pkg_index_urls = render.dict(pkg_index_urls),
+                failed_sources = render.list(failed_sources),
+            ),
         )
         return None
 
diff --git a/tests/pypi/simpleapi_download/simpleapi_download_tests.bzl b/tests/pypi/simpleapi_download/simpleapi_download_tests.bzl
index a96815c..8dc3072 100644
--- a/tests/pypi/simpleapi_download/simpleapi_download_tests.bzl
+++ b/tests/pypi/simpleapi_download/simpleapi_download_tests.bzl
@@ -87,6 +87,11 @@
                 output = "",
                 success = False,
             )
+        if "bar" in url:
+            return struct(
+                output = "",
+                success = False,
+            )
         else:
             return struct(
                 output = "data from {}".format(url),
@@ -99,7 +104,9 @@
             report_progress = lambda _: None,
         ),
         attr = struct(
-            index_url_overrides = {},
+            index_url_overrides = {
+                "foo": "invalid",
+            },
             index_url = "main",
             extra_index_urls = ["extra"],
             sources = ["foo", "bar", "baz"],
@@ -112,16 +119,25 @@
     )
 
     env.expect.that_collection(fails).contains_exactly([
-        """\
-Failed to download metadata for ["foo"] for from urls: ["main", "extra"].
-If you would like to skip downloading metadata for these packages please add 'simpleapi_skip=["foo"]' to your 'pip.parse' call.\
+        """
+Failed to download metadata of the following packages from urls:
+{
+    "foo": "invalid",
+    "bar": ["main", "extra"],
+}
+
+If you would like to skip downloading metadata for these packages please add 'simpleapi_skip=[
+    "foo",
+    "bar",
+]' to your 'pip.parse' call.
 """,
     ])
     env.expect.that_collection(calls).contains_exactly([
-        "extra/foo/",
+        "invalid/foo/",
         "main/bar/",
         "main/baz/",
-        "main/foo/",
+        "invalid/foo/",
+        "extra/bar/",
     ])
 
 _tests.append(_test_fail)