Make rules_python_external functional (#354)

- Clean up files that aren't needed now that it's a subdirectory of rules_python
- Move the example into the top-level examples

From the checklist in https://github.com/bazelbuild/rules_python/pull/352
this is the third item.
diff --git a/.bazelignore b/.bazelignore
index 83e04c0..e69de29 100644
--- a/.bazelignore
+++ b/.bazelignore
@@ -1,2 +0,0 @@
-experimental/rules_python_external
-
diff --git a/.bazelrc b/.bazelrc
index 2b60f35..ca4af76 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -3,7 +3,7 @@
 # This lets us glob() up all the files inside the examples to make them inputs to tests
 # (Note, we cannot use `common --deleted_packages` because the bazel version command doesn't support it)
 # To update these lines, run tools/bazel_integration_test/update_deleted_packages.sh
-build --deleted_packages=examples/pip/boto,examples/pip/extras,examples/pip/helloworld
-query --deleted_packages=examples/pip/boto,examples/pip/extras,examples/pip/helloworld
+build --deleted_packages=examples/pip/boto,examples/pip/extras,examples/pip/helloworld,examples/rules_python_external
+query --deleted_packages=examples/pip/boto,examples/pip/extras,examples/pip/helloworld,examples/rules_python_external
 
 test --test_output=errors
diff --git a/BUILD b/BUILD
index 3fb3f8d..d3487d2 100644
--- a/BUILD
+++ b/BUILD
@@ -27,6 +27,7 @@
         "LICENSE",
         "internal_deps.bzl",
         "internal_setup.bzl",
+        "//experimental/rules_python_external:distribution",
         "//python:distribution",
         "//tools:distribution",
     ],
diff --git a/WORKSPACE b/WORKSPACE
index 29c289a..b620de1 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -28,3 +28,8 @@
 load("//:internal_setup.bzl", "rules_python_internal_setup")
 
 rules_python_internal_setup()
+
+# TODO(alexeagle): vendor these dependencies so we don't expose new ones to users
+load("//experimental/rules_python_external:repositories.bzl", "rules_python_external_dependencies")
+
+rules_python_external_dependencies()
diff --git a/distro/BUILD b/distro/BUILD
index 2b51033..2bf48fe 100644
--- a/distro/BUILD
+++ b/distro/BUILD
@@ -19,6 +19,7 @@
     owner = "0.0",
     package_dir = ".",
     strip_prefix = ".",
+    visibility = ["//examples:__pkg__"],
 )
 
 # TODO(brandjon): print_rel_notes doesn't appear to handle our use case of
diff --git a/examples/BUILD b/examples/BUILD
index 18377eb..2d9bdaa 100644
--- a/examples/BUILD
+++ b/examples/BUILD
@@ -21,3 +21,8 @@
     name = "pip_example",
     timeout = "long",
 )
+
+bazel_integration_test(
+    name = "rules_python_external_example",
+    timeout = "long",
+)
diff --git a/experimental/rules_python_external/example/BUILD b/examples/rules_python_external/BUILD
similarity index 86%
rename from experimental/rules_python_external/example/BUILD
rename to examples/rules_python_external/BUILD
index 6c77b7b..0907052 100644
--- a/experimental/rules_python_external/example/BUILD
+++ b/examples/rules_python_external/BUILD
@@ -1,5 +1,5 @@
 load("@pip//:requirements.bzl", "requirement")
-load("@rules_python//python:defs.bzl", "py_binary")
+load("@rules_python//python:defs.bzl", "py_binary", "py_test")
 
 # Toolchain setup, this is optional.
 # Demonstrate that we can use the same python interpreter for the toolchain and executing pip in pip install (see WORKSPACE).
@@ -34,3 +34,9 @@
         requirement("boto3"),
     ],
 )
+
+py_test(
+    name = "test",
+    srcs = ["test.py"],
+    deps = [":main"],
+)
diff --git a/examples/rules_python_external/README.md b/examples/rules_python_external/README.md
new file mode 100644
index 0000000..b432f0b
--- /dev/null
+++ b/examples/rules_python_external/README.md
@@ -0,0 +1,7 @@
+# rules_python_external example
+
+This example shows how to use rules_python_external which was developed as an alternative
+to packaging rules in rules_python.
+
+We will soon migrate this example to be the canonical one for rules_python.
+See https://github.com/bazelbuild/rules_python/pull/352 for the longer plan.
diff --git a/experimental/rules_python_external/example/WORKSPACE b/examples/rules_python_external/WORKSPACE
similarity index 89%
rename from experimental/rules_python_external/example/WORKSPACE
rename to examples/rules_python_external/WORKSPACE
index 639308a..4bb21f7 100644
--- a/experimental/rules_python_external/example/WORKSPACE
+++ b/examples/rules_python_external/WORKSPACE
@@ -4,6 +4,8 @@
 
 http_archive(
     name = "rules_python",
+    # NB: this doesn't work with 0.0.2 but will work in the next release
+    # TODO(alexeagle): ensure the example/*/WORKSPACE files get bumped as a release step
     url = "https://github.com/bazelbuild/rules_python/releases/download/0.0.2/rules_python-0.0.2.tar.gz",
     strip_prefix = "rules_python-0.0.2",
     sha256 = "b5668cde8bb6e3515057ef465a35ad712214962f0b3a314e551204266c7be90c",
@@ -13,16 +15,11 @@
 
 py_repositories()
 
-local_repository(
-    name = "rules_python_external",
-    path = "../",
-)
-
-load("@rules_python_external//:repositories.bzl", "rules_python_external_dependencies")
+load("@rules_python//experimental/rules_python_external:repositories.bzl", "rules_python_external_dependencies")
 
 rules_python_external_dependencies()
 
-load("@rules_python_external//:defs.bzl", "pip_install")
+load("@rules_python//experimental/rules_python_external:defs.bzl", "pip_install")
 
 pip_install(
     # (Optional) You can provide extra parameters to pip.
diff --git a/examples/rules_python_external/main.py b/examples/rules_python_external/main.py
new file mode 100644
index 0000000..fdb3c65
--- /dev/null
+++ b/examples/rules_python_external/main.py
@@ -0,0 +1,7 @@
+import boto3
+
+def the_dir():
+    return dir(boto3)
+
+if __name__ == "__main__":
+    print(the_dir())
diff --git a/examples/rules_python_external/requirements.txt b/examples/rules_python_external/requirements.txt
new file mode 100644
index 0000000..cbc5542
--- /dev/null
+++ b/examples/rules_python_external/requirements.txt
@@ -0,0 +1 @@
+boto3==1.14.51
diff --git a/examples/rules_python_external/test.py b/examples/rules_python_external/test.py
new file mode 100644
index 0000000..0b3b333
--- /dev/null
+++ b/examples/rules_python_external/test.py
@@ -0,0 +1,9 @@
+import unittest
+import main
+
+class ExampleTest(unittest.TestCase):
+    def test_main(self):
+        self.assertIn("set_stream_logger", main.the_dir())
+
+if __name__ == '__main__':
+  unittest.main()
diff --git a/experimental/rules_python_external/.bazelignore b/experimental/rules_python_external/.bazelignore
deleted file mode 100644
index 90c978b..0000000
--- a/experimental/rules_python_external/.bazelignore
+++ /dev/null
@@ -1 +0,0 @@
-example/
diff --git a/experimental/rules_python_external/.gitattributes b/experimental/rules_python_external/.gitattributes
deleted file mode 100644
index 0bad51c..0000000
--- a/experimental/rules_python_external/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-example/* linguist-vendored
diff --git a/experimental/rules_python_external/.github/workflows/continuous-integration.yml b/experimental/rules_python_external/.github/workflows/continuous-integration.yml
deleted file mode 100644
index 01c4cdb..0000000
--- a/experimental/rules_python_external/.github/workflows/continuous-integration.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-name: CI
-
-on:
-  push:
-    branches: [ master ]
-  pull_request:
-    branches: [ master ]
-
-jobs:
-  test:
-    runs-on: ubuntu-latest
-
-    steps:
-    # Checks-out the repository under $GITHUB_WORKSPACE, so the job can access it
-    - uses: actions/checkout@v2
-
-    - name: Setup Bazel
-      uses: abhinavsingh/setup-bazel@v3
-      with:
-        # Bazel version to install e.g. 1.2.1, 2.0.0, ...
-        version: 2.0.0 # optional, default is 2.0.0
-
-    - name: Run tests
-      run: bazel test //...
diff --git a/experimental/rules_python_external/BUILD b/experimental/rules_python_external/BUILD
index e69de29..657c956 100644
--- a/experimental/rules_python_external/BUILD
+++ b/experimental/rules_python_external/BUILD
@@ -0,0 +1,8 @@
+filegroup(
+    name = "distribution",
+    srcs = glob(["*.bzl"]) + [
+        "BUILD",
+        "//experimental/rules_python_external/extract_wheels:distribution",
+    ],
+    visibility = ["//:__pkg__"],
+)
diff --git a/experimental/rules_python_external/LICENSE b/experimental/rules_python_external/LICENSE
deleted file mode 100644
index 261eeb9..0000000
--- a/experimental/rules_python_external/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "[]"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright [yyyy] [name of copyright owner]
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/experimental/rules_python_external/WORKSPACE b/experimental/rules_python_external/WORKSPACE
deleted file mode 100644
index 6361ec0..0000000
--- a/experimental/rules_python_external/WORKSPACE
+++ /dev/null
@@ -1,16 +0,0 @@
-workspace(name = "rules_python_external")
-
-load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
-
-http_archive(
-    name = "rules_python",
-    sha256 = "d2865e2ce23ee217aaa408ddaa024ca472114a6f250b46159d27de05530c75e3",
-    strip_prefix = "rules_python-7b222cfdb4e59b9fd2a609e1fbb233e94fdcde7c",
-    url = "https://github.com/bazelbuild/rules_python/archive/7b222cfdb4e59b9fd2a609e1fbb233e94fdcde7c.tar.gz",
-)
-
-load("@rules_python//python:repositories.bzl", "py_repositories")
-py_repositories()
-
-load("//:repositories.bzl", "rules_python_external_dependencies")
-rules_python_external_dependencies()
diff --git a/experimental/rules_python_external/defs.bzl b/experimental/rules_python_external/defs.bzl
index a11bff1..1e64c3c 100644
--- a/experimental/rules_python_external/defs.bzl
+++ b/experimental/rules_python_external/defs.bzl
@@ -1,6 +1,6 @@
 ""
 
-load("//:repositories.bzl", "all_requirements")
+load("//experimental/rules_python_external:repositories.bzl", "all_requirements")
 
 DEFAULT_REPOSITORY_NAME = "pip"
 
@@ -30,7 +30,7 @@
     args = [
         python_interpreter,
         "-m",
-        "extract_wheels",
+        "experimental.rules_python_external.extract_wheels",
         "--requirements",
         rctx.path(rctx.attr.requirements),
         "--repo",
diff --git a/experimental/rules_python_external/example/main.py b/experimental/rules_python_external/example/main.py
deleted file mode 100644
index e5c690b..0000000
--- a/experimental/rules_python_external/example/main.py
+++ /dev/null
@@ -1,4 +0,0 @@
-import boto3
-
-if __name__ == "__main__":
-    pass
diff --git a/experimental/rules_python_external/example/requirements.txt b/experimental/rules_python_external/example/requirements.txt
deleted file mode 100644
index 30ddf82..0000000
--- a/experimental/rules_python_external/example/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-boto3
diff --git a/experimental/rules_python_external/extract_wheels/BUILD b/experimental/rules_python_external/extract_wheels/BUILD
index 27434db..f780b0d 100644
--- a/experimental/rules_python_external/extract_wheels/BUILD
+++ b/experimental/rules_python_external/extract_wheels/BUILD
@@ -2,7 +2,18 @@
 
 py_binary(
     name = "extract_wheels",
-    srcs = ["__init__.py", "__main__.py"],
+    srcs = [
+        "__init__.py",
+        "__main__.py",
+    ],
     main = "__main__.py",
-    deps = ["//extract_wheels/lib"],
+    deps = ["//experimental/rules_python_external/extract_wheels/lib"],
+)
+
+filegroup(
+    name = "distribution",
+    srcs = glob(["*"]) + [
+        "//experimental/rules_python_external/extract_wheels/lib:distribution",
+    ],
+    visibility = ["//experimental/rules_python_external:__subpackages__"],
 )
diff --git a/experimental/rules_python_external/extract_wheels/__init__.py b/experimental/rules_python_external/extract_wheels/__init__.py
index 8184dac..79f87e5 100644
--- a/experimental/rules_python_external/extract_wheels/__init__.py
+++ b/experimental/rules_python_external/extract_wheels/__init__.py
@@ -12,7 +12,7 @@
 import sys
 import json
 
-from extract_wheels.lib import bazel, requirements
+from experimental.rules_python_external.extract_wheels.lib import bazel, requirements
 
 
 def configure_reproducible_wheels() -> None:
diff --git a/experimental/rules_python_external/extract_wheels/__main__.py b/experimental/rules_python_external/extract_wheels/__main__.py
index 939e8b9..6aed7cd 100644
--- a/experimental/rules_python_external/extract_wheels/__main__.py
+++ b/experimental/rules_python_external/extract_wheels/__main__.py
@@ -1,5 +1,5 @@
 """Main entry point."""
-import extract_wheels
+from experimental.rules_python_external.extract_wheels import main
 
 if __name__ == "__main__":
-    extract_wheels.main()
+    main()
diff --git a/experimental/rules_python_external/extract_wheels/lib/BUILD b/experimental/rules_python_external/extract_wheels/lib/BUILD
index 415bb1b..c78c517 100644
--- a/experimental/rules_python_external/extract_wheels/lib/BUILD
+++ b/experimental/rules_python_external/extract_wheels/lib/BUILD
@@ -1,9 +1,8 @@
 load("@rules_python//python:defs.bzl", "py_library", "py_test")
-load("//:repositories.bzl", "requirement")
+load("//experimental/rules_python_external:repositories.bzl", "requirement")
 
 py_library(
     name = "lib",
-    visibility = ["//extract_wheels:__subpackages__"],
     srcs = [
         "bazel.py",
         "namespace_pkgs.py",
@@ -11,6 +10,7 @@
         "requirements.py",
         "wheel.py",
     ],
+    visibility = ["//experimental/rules_python_external/extract_wheels:__subpackages__"],
     deps = [
         requirement("pkginfo"),
         requirement("setuptools"),
@@ -40,3 +40,12 @@
         ":lib",
     ],
 )
+
+filegroup(
+    name = "distribution",
+    srcs = glob(
+        ["*"],
+        exclude = ["*_test.py"],
+    ),
+    visibility = ["//experimental/rules_python_external:__subpackages__"],
+)
diff --git a/experimental/rules_python_external/extract_wheels/lib/bazel.py b/experimental/rules_python_external/extract_wheels/lib/bazel.py
index acda4c2..fb92c0a 100644
--- a/experimental/rules_python_external/extract_wheels/lib/bazel.py
+++ b/experimental/rules_python_external/extract_wheels/lib/bazel.py
@@ -4,7 +4,7 @@
 import json
 from typing import Iterable, List, Dict, Set
 
-from extract_wheels.lib import namespace_pkgs, wheel, purelib
+from experimental.rules_python_external.extract_wheels.lib import namespace_pkgs, wheel, purelib
 
 
 def generate_build_file_contents(
diff --git a/experimental/rules_python_external/extract_wheels/lib/namespace_pkgs.py b/experimental/rules_python_external/extract_wheels/lib/namespace_pkgs.py
index cb9e164..8f7e57f 100644
--- a/experimental/rules_python_external/extract_wheels/lib/namespace_pkgs.py
+++ b/experimental/rules_python_external/extract_wheels/lib/namespace_pkgs.py
@@ -3,7 +3,7 @@
 import textwrap
 from typing import Set, List, Optional
 
-from extract_wheels.lib import wheel
+from experimental.rules_python_external.extract_wheels.lib import wheel
 
 
 def implicit_namespace_packages(
diff --git a/experimental/rules_python_external/extract_wheels/lib/namespace_pkgs_test.py b/experimental/rules_python_external/extract_wheels/lib/namespace_pkgs_test.py
index 899f7bc..f1b299e 100644
--- a/experimental/rules_python_external/extract_wheels/lib/namespace_pkgs_test.py
+++ b/experimental/rules_python_external/extract_wheels/lib/namespace_pkgs_test.py
@@ -4,7 +4,7 @@
 from typing import Optional
 import unittest
 
-from extract_wheels.lib import namespace_pkgs
+from experimental.rules_python_external.extract_wheels.lib import namespace_pkgs
 
 
 class TempDir:
diff --git a/experimental/rules_python_external/extract_wheels/lib/purelib.py b/experimental/rules_python_external/extract_wheels/lib/purelib.py
index ffcda8f..830ce70 100644
--- a/experimental/rules_python_external/extract_wheels/lib/purelib.py
+++ b/experimental/rules_python_external/extract_wheels/lib/purelib.py
@@ -2,7 +2,7 @@
 import pathlib
 import shutil
 
-from extract_wheels.lib import wheel
+from experimental.rules_python_external.extract_wheels.lib import wheel
 
 
 def spread_purelib_into_root(wheel_dir: str) -> None:
diff --git a/experimental/rules_python_external/extract_wheels/lib/requirements_test.py b/experimental/rules_python_external/extract_wheels/lib/requirements_test.py
index 2b96a75..c06297a 100644
--- a/experimental/rules_python_external/extract_wheels/lib/requirements_test.py
+++ b/experimental/rules_python_external/extract_wheels/lib/requirements_test.py
@@ -1,6 +1,6 @@
 import unittest
 
-from extract_wheels.lib import requirements
+from experimental.rules_python_external.extract_wheels.lib import requirements
 
 
 class TestRequirementExtrasParsing(unittest.TestCase):
diff --git a/tools/bazel_integration_test/bazel_integration_test.bzl b/tools/bazel_integration_test/bazel_integration_test.bzl
index b23360b..caac0d9 100644
--- a/tools/bazel_integration_test/bazel_integration_test.bzl
+++ b/tools/bazel_integration_test/bazel_integration_test.bzl
@@ -1,6 +1,6 @@
 "Define a rule for running bazel test under Bazel"
 
-load("//:version.bzl", "SUPPORTED_BAZEL_VERSIONS")
+load("//:version.bzl", "SUPPORTED_BAZEL_VERSIONS", "version")
 load("//python:defs.bzl", "py_test")
 
 BAZEL_BINARY = "@build_bazel_bazel_%s//:bazel_binary" % SUPPORTED_BAZEL_VERSIONS[0].replace(".", "_")
@@ -19,6 +19,10 @@
 Note that if a command contains a bare `--` argument, the --test_arg passed to Bazel will appear before it.
 """,
     ),
+    "distro": attr.label(
+        allow_single_file = True,
+        doc = "the .tar.gz distribution file of rules_python to test",
+    ),
     "workspace_files": attr.label(
         doc = """A filegroup of all files in the workspace-under-test necessary to run the test.""",
     ),
@@ -51,12 +55,14 @@
 {{
     "workspaceRoot": "{TMPL_workspace_root}",
     "bazelBinaryWorkspace": "{TMPL_bazel_binary_workspace}",
-    "bazelCommands": [ {TMPL_bazel_commands} ]
+    "bazelCommands": [ {TMPL_bazel_commands} ],
+    "distro": "rules_python/{TMPL_distro_path}"
 }}
 """.format(
             TMPL_workspace_root = ctx.files.workspace_files[0].dirname,
             TMPL_bazel_binary_workspace = ctx.attr.bazel_binary.label.workspace_name,
             TMPL_bazel_commands = ", ".join(["\"%s\"" % s for s in ctx.attr.bazel_commands]),
+            TMPL_distro_path = ctx.file.distro.short_path,
         ),
     )
 
@@ -78,6 +84,7 @@
         name: name of the resulting py_test
         **kwargs: additional attributes like timeout and visibility
     """
+
     # By default, we assume sources for "pip_example" are in examples/pip/**/*
     dirname = name[:-len("_example")]
     native.filegroup(
@@ -92,6 +99,7 @@
     _config(
         name = "_%s_config" % name,
         workspace_files = workspace_files,
+        distro = "//distro:rules_python-%s" % version,
     )
 
     py_test(
@@ -102,10 +110,9 @@
         deps = [Label("//python/runfiles")],
         data = [
             BAZEL_BINARY,
+            "//distro:rules_python-%s.tar.gz" % version,
             "_%s_config" % name,
             workspace_files,
         ],
-        **kwargs,
+        **kwargs
     )
-
-    
\ No newline at end of file
diff --git a/tools/bazel_integration_test/test_runner.py b/tools/bazel_integration_test/test_runner.py
index 46ba734..20f8256 100644
--- a/tools/bazel_integration_test/test_runner.py
+++ b/tools/bazel_integration_test/test_runner.py
@@ -2,15 +2,40 @@
 import json
 import os
 import platform
+import re
+import shutil
 from subprocess import Popen
 import sys
+import tempfile
 
 from rules_python.python.runfiles import runfiles
+r = runfiles.Create()
+
+def modify_WORKSPACE(wksp, distro_path):
+    """Update the WORKSPACE file in the example to point to our locally-built tar.gz
+    This allows users to clone rules_python, cd into the example/dir, and run the example directly,
+    while our integration tests use the locally-built copy.
+
+    Args:
+        wksp: filesystem absolute path of the bazel WORKSPACE file under test
+        distro_path: runfiles path of the distro .tar.gz
+    """
+    with open(wksp, 'r') as wksp_file:
+        content = wksp_file.read()
+    # Replace the url for rules_python with our locally built one
+    content = re.sub(
+        r'url = "https://github.com/bazelbuild/rules_python/releases/download/[^"]+"',
+        'url = "file://%s"' % r.Rlocation(distro_path),
+        content)
+    content = re.sub(r'sha256 = "', '#\1', content)
+    # TODO(alexeagle): can remove this after 0.0.3 since the strip_prefix was an accident during 0.0.2 release
+    content = re.sub(r'strip_prefix = "', '#\1', content)
+    with open(wksp, 'w') as wksp_file:
+        wksp_file.write(content)
 
 def main(conf_file):
     with open(conf_file) as j:
         config = json.load(j)
-    r = runfiles.Create()
 
     isWindows = platform.system() == 'Windows'
     bazelBinary = r.Rlocation(os.path.join(config['bazelBinaryWorkspace'], 'bazel.exe' if isWindows else 'bazel'))
@@ -20,26 +45,33 @@
     if workspacePath.startswith('external/'):
         workspacePath = '..' + workspacePath[len('external'):]
 
-    for command in config['bazelCommands']:
-        bazel_args = command.split(' ')
-        try:
-            doubleHyphenPos = bazel_args.index('--')
-            print("patch that in ", doubleHyphenPos)
-        except ValueError:
-            pass
+    with tempfile.TemporaryDirectory(dir = os.environ['TEST_TMPDIR']) as tmpdir:
+        workdir = os.path.join(tmpdir, "wksp")
+        print("copying workspace under test %s to %s" % (workspacePath, workdir))
+        shutil.copytree(workspacePath, workdir)
+
+        modify_WORKSPACE(os.path.join(workdir, 'WORKSPACE'), config['distro'])
+
+        for command in config['bazelCommands']:
+            bazel_args = command.split(' ')
+            try:
+                doubleHyphenPos = bazel_args.index('--')
+                print("patch that in ", doubleHyphenPos)
+            except ValueError:
+                pass
 
 
-        # Bazel's wrapper script needs this or you get 
-        # 2020/07/13 21:58:11 could not get the user's cache directory: $HOME is not defined
-        os.environ['HOME'] = str(Path.home())
+            # Bazel's wrapper script needs this or you get 
+            # 2020/07/13 21:58:11 could not get the user's cache directory: $HOME is not defined
+            os.environ['HOME'] = str(Path.home())
 
-        bazel_args.insert(0, bazelBinary)
-        bazel_process = Popen(bazel_args, cwd = workspacePath)
-        bazel_process.wait()
-        if bazel_process.returncode != 0:
-            # Test failure in Bazel is exit 3
-            # https://github.com/bazelbuild/bazel/blob/486206012a664ecb20bdb196a681efc9a9825049/src/main/java/com/google/devtools/build/lib/util/ExitCode.java#L44
-            sys.exit(3)
+            bazel_args.insert(0, bazelBinary)
+            bazel_process = Popen(bazel_args, cwd = workdir)
+            bazel_process.wait()
+            if bazel_process.returncode != 0:
+                # Test failure in Bazel is exit 3
+                # https://github.com/bazelbuild/bazel/blob/486206012a664ecb20bdb196a681efc9a9825049/src/main/java/com/google/devtools/build/lib/util/ExitCode.java#L44
+                sys.exit(3)
 
 if __name__ == '__main__':
   main(sys.argv[1])