cleanup: Typos, doc, and formatting cleanup in pip extension et al (#1275)

* Corrects some typos in docs
* Expands the user-facing documentation
* Fixes errors having newlines in the middle of them
* Renames some internal functions to be more self-describing.
diff --git a/docs/pip_repository.md b/docs/pip_repository.md
index 0e0fea3..8536052 100644
--- a/docs/pip_repository.md
+++ b/docs/pip_repository.md
@@ -10,7 +10,7 @@
 pip_hub_repository_bzlmod(<a href="#pip_hub_repository_bzlmod-name">name</a>, <a href="#pip_hub_repository_bzlmod-repo_mapping">repo_mapping</a>, <a href="#pip_hub_repository_bzlmod-repo_name">repo_name</a>, <a href="#pip_hub_repository_bzlmod-whl_library_alias_names">whl_library_alias_names</a>)
 </pre>
 
-A rule for bzlmod mulitple pip repository creation. Intended for private use only.
+A rule for bzlmod mulitple pip repository creation. PRIVATE USE ONLY.
 
 **ATTRIBUTES**
 
diff --git a/examples/bzlmod/MODULE.bazel b/examples/bzlmod/MODULE.bazel
index e09e029..a61e094 100644
--- a/examples/bzlmod/MODULE.bazel
+++ b/examples/bzlmod/MODULE.bazel
@@ -34,16 +34,17 @@
 # You only need to load this repositories if you are using multiple Python versions.
 # See the tests folder for various examples on using multiple Python versions.
 # The names "python_3_9" and "python_3_10" are autmatically created by the repo
-# rules based on the python_versions.
+# rules based on the `python_version` arg values.
 use_repo(python, "python_3_10", "python_3_9", "python_aliases")
 
 pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
 
-# For each pip setup we call pip.parse.  We can pass in various options
-# but typically we are passing in a requirements and an interpreter.
-# If you provide the python_version we will attempt to determine
-# the interpreter target automatically. Otherwise use python_interpreter_target
-# to override the lookup.
+# To fetch pip dependencies, use pip.parse. We can pass in various options,
+# but typically we pass requirements and the Python version. The Python
+# version must have been configured by a corresponding `python.toolchain()`
+# call.
+# Alternatively, `python_interpreter_target` can be used to directly specify
+# the Python interpreter to run to resolve dependencies.
 pip.parse(
     hub_name = "pip",
     python_version = "3.9",
@@ -57,7 +58,8 @@
     requirements_windows = "//:requirements_windows_3_10.txt",
 )
 
-# we use the pip_39 repo because entry points are not yet supported.
+# NOTE: The pip_39 repo is only used because the plain `@pip` repo doesn't
+# yet support entry points; see https://github.com/bazelbuild/rules_python/issues/1262
 use_repo(pip, "pip", "pip_39")
 
 bazel_dep(name = "other_module", version = "", repo_name = "our_other_module")
diff --git a/python/extensions/pip.bzl b/python/extensions/pip.bzl
index e4eb9b5..5cad3b1 100644
--- a/python/extensions/pip.bzl
+++ b/python/extensions/pip.bzl
@@ -27,21 +27,25 @@
 )
 load("@rules_python//python/pip_install:requirements_parser.bzl", parse_requirements = "parse")
 
-def _create_pip(module_ctx, pip_attr, whl_map):
+def _create_versioned_pip_and_whl_repos(module_ctx, pip_attr, whl_map):
     python_interpreter_target = pip_attr.python_interpreter_target
 
     # if we do not have the python_interpreter set in the attributes
     # we programtically find it.
+    hub_name = pip_attr.hub_name
     if python_interpreter_target == None:
         python_name = "python_{}".format(pip_attr.python_version.replace(".", "_"))
         if python_name not in INTERPRETER_LABELS.keys():
-            fail("""
-Unable to find '{}' in the list of interpreters please update your pip.parse call with the correct python name
-""".format(pip_attr.python_name))
-
+            fail((
+                "Unable to find interpreter for pip hub '{hub_name}' for " +
+                "python_version={version}: Make sure a corresponding " +
+                '`python.toolchain(python_version="{version}")` call exists'
+            ).format(
+                hub_name = hub_name,
+                version = pip_attr.python_version,
+            ))
         python_interpreter_target = INTERPRETER_LABELS[python_name]
 
-    hub_name = pip_attr.hub_name
     pip_name = hub_name + "_{}".format(pip_attr.python_version.replace(".", ""))
     requrements_lock = locked_requirements_label(module_ctx, pip_attr)
 
@@ -93,10 +97,10 @@
         whl_map[hub_name][whl_name][pip_attr.python_version] = pip_name + "_"
 
 def _pip_impl(module_ctx):
-    """Implmentation of a class tag that creates the pip hub(s) and corresponding pip spoke, alias and whl repositories.
+    """Implementation of a class tag that creates the pip hub(s) and corresponding pip spoke, alias and whl repositories.
 
-    This implmentation iterates through all of the "pip.parse" calls and creates
-    different pip hubs repositories based on the "hub_name".  Each of the
+    This implmentation iterates through all of the `pip.parse` calls and creates
+    different pip hub repositories based on the "hub_name".  Each of the
     pip calls create spoke repos that uses a specific Python interpreter.
 
     In a MODULES.bazel file we have:
@@ -115,13 +119,13 @@
     )
 
 
-    For instance we have a hub with the name of "pip".
+    For instance, we have a hub with the name of "pip".
     A repository named the following is created. It is actually called last when
     all of the pip spokes are collected.
 
     - @@rules_python~override~pip~pip
 
-    As show in the example code above we have the following.
+    As shown in the example code above we have the following.
     Two different pip.parse statements exist in MODULE.bazel provide the hub_name "pip".
     These definitions create two different pip spoke repositories that are
     related to the hub "pip".
@@ -185,22 +189,32 @@
 
     for mod in module_ctx.modules:
         for pip_attr in mod.tags.parse:
-            if pip_attr.hub_name in pip_hub_map:
+            hub_name = pip_attr.hub_name
+            if hub_name in pip_hub_map:
                 # We cannot have two hubs with the same name in different
                 # modules.
-                if pip_hub_map[pip_attr.hub_name].module_name != mod.name:
-                    fail("""Unable to create pip with the hub_name '{}', same hub name 
-                        in a different module found.""".format(pip_attr.hub_name))
+                if pip_hub_map[hub_name].module_name != mod.name:
+                    fail((
+                        "Duplicate cross-module pip hub named '{hub}': pip hub " +
+                        "names must be unique across modules. First defined " +
+                        "by module '{first_module}', second attempted by " +
+                        "module '{second_module}'"
+                    ).format(
+                        hub = hub_name,
+                        first_module = pip_hub_map[hub_name].module_name,
+                        second_module = mod.name,
+                    ))
 
-                if pip_attr.python_version in pip_hub_map[pip_attr.hub_name].python_versions:
-                    fail(
-                        """Unable to create pip with the hub_name '{}', same hub name 
-                        using the same Python repo name '{}' found in module '{}'.""".format(
-                            pip_attr.hub_name,
-                            pip_attr.python_version,
-                            mod.name,
-                        ),
-                    )
+                if pip_attr.python_version in pip_hub_map[hub_name].python_versions:
+                    fail((
+                        "Duplicate pip python version '{version}' for hub " +
+                        "'{hub}' in module '{module}': the Python versions " +
+                        "used for a hub must be unique"
+                    ).format(
+                        hub = hub_name,
+                        module = mod.name,
+                        version = pip_attr.python_version,
+                    ))
                 else:
                     pip_hub_map[pip_attr.hub_name].python_versions.append(pip_attr.python_version)
             else:
@@ -209,17 +223,19 @@
                     python_versions = [pip_attr.python_version],
                 )
 
-            _create_pip(module_ctx, pip_attr, hub_whl_map)
+            _create_versioned_pip_and_whl_repos(module_ctx, pip_attr, hub_whl_map)
 
     for hub_name, whl_map in hub_whl_map.items():
         for whl_name, version_map in whl_map.items():
             if DEFAULT_PYTHON_VERSION not in version_map:
-                fail(
-                    """
-Unable to find the default python version in the version map, please update your requirements files
-to include Python '{}'.
-""".format(DEFAULT_PYTHON_VERSION),
-                )
+                fail((
+                    "Default python version '{version}' missing in pip " +
+                    "hub '{hub}': update your pip.parse() calls so that " +
+                    'includes `python_version = "{version}"`'
+                ).format(
+                    version = DEFAULT_PYTHON_VERSION,
+                    hub = hub_name,
+                ))
 
             # Create the alias repositories which contains different select
             # statements  These select statements point to the different pip
@@ -247,14 +263,34 @@
         "hub_name": attr.string(
             mandatory = True,
             doc = """
-The unique hub name.  Mulitple pip.parse calls that contain the same hub name, 
-create spokes for specific Python versions.                                
+The name of the repo pip dependencies will be accessible from.
+
+This name must be unique between modules; unless your module is guaranteed to
+always be the root module, it's highly recommended to include your module name
+in the hub name. Repo mapping, `use_repo(..., pip="my_modules_pip_deps")`, can
+be used for shorter local names within your module.
+
+Within a module, the same `hub_name` can be specified to group different Python
+versions of pip dependencies under one repository name. This allows using a
+Python version-agnostic name when referring to pip dependencies; the
+correct version will be automatically selected.
+
+Typically, a module will only have a single hub of pip dependencies, but this
+is not required. Each hub is a separate resolution of pip dependencies. This
+means if different programs need different versions of some library, separate
+hubs can be created, and each program can use its respective hub's targets.
+Targets from different hubs should not be used together.
 """,
         ),
         "python_version": attr.string(
             mandatory = True,
             doc = """
-The Python version for the pip spoke. 
+The Python version to use for resolving the pip dependencies. If not specified,
+then the default Python version (as set by the root module or rules_python)
+will be used.
+
+The version specified here must have a corresponding `python.toolchain()`
+configured.
 """,
         ),
     }, **pip_repository_attrs)
@@ -270,12 +306,16 @@
 
 pip = module_extension(
     doc = """\
-This extension is used to create a pip hub and all of the spokes that are part of that hub.
-We can have multiple different hubs, but we cannot have hubs that have the same name in
-different modules.  Each hub needs one or more spokes.  A spoke contains a specific version
-of Python, and the requirement(s) files that are unquie to that Python version.
-In order to add more spokes you call this extension mulitiple times using the same hub
-name.
+This extension is used to make dependencies from pip available.
+
+To use, call `pip.parse()` and specify `hub_name` and your requirements file.
+Dependencies will be downloaded and made available in a repo named after the
+`hub_name` argument.
+
+Each `pip.parse()` call configures a particular Python version. Multiple calls
+can be made to configure different Python versions, and will be grouped by
+the `hub_name` argument. This allows the same logical name, e.g. `@pip//numpy`
+to automatically resolve to different, Python version-specific, libraries.
 """,
     implementation = _pip_impl,
     tag_classes = {
diff --git a/python/pip_install/pip_repository.bzl b/python/pip_install/pip_repository.bzl
index f18b69b..866a834 100644
--- a/python/pip_install/pip_repository.bzl
+++ b/python/pip_install/pip_repository.bzl
@@ -363,7 +363,7 @@
 
 pip_hub_repository_bzlmod = repository_rule(
     attrs = pip_hub_repository_bzlmod_attrs,
-    doc = """A rule for bzlmod mulitple pip repository creation. Intended for private use only.""",
+    doc = """A rule for bzlmod mulitple pip repository creation. PRIVATE USE ONLY.""",
     implementation = _pip_hub_repository_bzlmod_impl,
 )