feat(bzlmod): expose platform-agnostic repo target for toolchain interpreter (#1155)

This exposes a new repo and target, `@{name}_host_interpreter//:python`,
created by `python.toolchain()`, that points to the host OS's Python
interpreter for that particular toolchain.

This solves two problems:
1. `pip.parse()` can now refer to the same interpreter used in the
toolchains
2. There is now a canonical, public, way to refer to the host OS Python
interpreter
    for repository rules.

The above were _sort of_ possible for users to do already, but it
required them to write much more configuration and extension code to do
so. This moves that sort of boilerplate into our code so they have a
simpler configuration.

Also:
- removing bzlmod support in the build_file_generation example; making
examples work
  with both WORKSPACE and MODULE is a pain, hence splitting them.
- adding an example of bzlmod and gazelle
- improved documentation in the pip arguments

Closes: https://github.com/bazelbuild/rules_python/issues/1161
diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml
index 8ad0489..b468970 100644
--- a/.bazelci/presubmit.yml
+++ b/.bazelci/presubmit.yml
@@ -48,6 +48,8 @@
   test_targets: ["..."]
 .coverage_targets_example_bzlmod: &coverage_targets_example_bzlmod
   coverage_targets: ["//:test"]
+.coverage_targets_example_bzlmod_build_file_generation: &coverage_targets_example_bzlmod_build_file_generation
+  coverage_targets: ["//:bzlmod_build_file_generation_test"]
 .coverage_targets_example_multi_python: &coverage_targets_example_multi_python
   coverage_targets:
     - //tests:my_lib_3_10_test
@@ -179,6 +181,46 @@
     working_directory: examples/bzlmod
     platform: windows
 
+  integration_test_bzlmod_generate_build_file_generation_ubuntu_min:
+    <<: *minimum_supported_bzlmod_version
+    <<: *reusable_build_test_all
+    <<: *coverage_targets_example_bzlmod_build_file_generation
+    name: example bzlmod build file min bazel version integration test
+    working_directory: examples/bzlmod_build_file_generation
+    platform: ubuntu2004
+  integration_test_bzlmod_generation_build_files_ubuntu:
+    <<: *reusable_build_test_all
+    <<: *coverage_targets_example_bzlmod_build_file_generation
+    name: example bzlmod build file integration test
+    working_directory: examples/bzlmod_build_file_generation
+    platform: ubuntu2004
+  integration_test_bzlmod_generation_build_files_ubuntu_run:
+    <<: *reusable_build_test_all
+    name: example bzlmod build file running gazelle and pip integration test
+    working_directory: examples/bzlmod_build_file_generation
+    platform: ubuntu2004
+    shell_commands:
+    - "bazel run //:gazelle_python_manifest.update"
+    - "bazel run //:gazelle -- update"
+  integration_test_bzlmod_build_file_generation_debian:
+    <<: *reusable_build_test_all
+    <<: *coverage_targets_example_bzlmod_build_file_generation
+    name: example bzlmod build file integration test
+    working_directory: examples/bzlmod_build_file_generation
+    platform: debian11
+  integration_test_bzlmod_build_file_generation_macos:
+    <<: *reusable_build_test_all
+    <<: *coverage_targets_example_bzlmod_build_file_generation
+    name: example bzlmod build file integration test
+    working_directory: examples/bzlmod_build_file_generation
+    platform: macos
+  integration_test_bzlmod_build_file_generation_windows:
+    <<: *reusable_build_test_all
+    # coverage is not supported on Windows
+    name: example bzlmod build file integration test
+    working_directory: examples/bzlmod_build_file_generation
+    platform: windows
+
   integration_test_multi_python_versions_ubuntu_min:
     <<: *minimum_supported_version
     <<: *reusable_build_test_all
diff --git a/.bazelrc b/.bazelrc
index d607cdd..e7e4af7 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -3,8 +3,8 @@
 # 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/build_file_generation,examples/build_file_generation/get_url,examples/bzlmod,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/multi_python_versions,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_import,examples/py_proto_library,examples/relative_requirements,tests/compile_pip_requirements,tests/pip_repository_entry_points,tests/pip_deps
-query --deleted_packages=examples/build_file_generation,examples/build_file_generation/get_url,examples/bzlmod,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/multi_python_versions,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_import,examples/py_proto_library,examples/relative_requirements,tests/compile_pip_requirements,tests/pip_repository_entry_points,tests/pip_deps
+build --deleted_packages=examples/build_file_generation,examples/build_file_generation/get_url,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/multi_python_versions,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_import,examples/py_proto_library,examples/relative_requirements,tests/compile_pip_requirements,tests/pip_repository_entry_points,tests/pip_deps
+query --deleted_packages=examples/build_file_generation,examples/build_file_generation/get_url,examples/bzlmod_build_file_generation,examples/bzlmod_build_file_generation/other_module/other_module/pkg,examples/bzlmod_build_file_generation/runfiles,examples/bzlmod,examples/bzlmod/other_module/other_module/pkg,examples/bzlmod/runfiles,examples/multi_python_versions,examples/multi_python_versions/libs/my_lib,examples/multi_python_versions/requirements,examples/multi_python_versions/tests,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_import,examples/py_proto_library,examples/relative_requirements,tests/compile_pip_requirements,tests/pip_repository_entry_points,tests/pip_deps
 
 test --test_output=errors
 
diff --git a/docs/pip_repository.md b/docs/pip_repository.md
index 99af4ba..29cb3d9 100644
--- a/docs/pip_repository.md
+++ b/docs/pip_repository.md
@@ -69,7 +69,7 @@
 | <a id="pip_repository-isolated"></a>isolated |  Whether or not to pass the [--isolated](https://pip.pypa.io/en/stable/cli/pip/#cmdoption-isolated) flag to the underlying pip command. Alternatively, the <code>RULES_PYTHON_PIP_ISOLATED</code> environment variable can be used to control this flag.   | Boolean | optional | <code>True</code> |
 | <a id="pip_repository-pip_data_exclude"></a>pip_data_exclude |  Additional data exclusion parameters to add to the pip packages BUILD file.   | List of strings | optional | <code>[]</code> |
 | <a id="pip_repository-python_interpreter"></a>python_interpreter |  The python interpreter to use. This can either be an absolute path or the name of a binary found on the host's <code>PATH</code> environment variable. If no value is set <code>python3</code> is defaulted for Unix systems and <code>python.exe</code> for Windows.   | String | optional | <code>""</code> |
-| <a id="pip_repository-python_interpreter_target"></a>python_interpreter_target |  If you are using a custom python interpreter built by another repository rule, use this attribute to specify its BUILD target. This allows pip_repository to invoke pip using the same interpreter as your toolchain. If set, takes precedence over python_interpreter.   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>None</code> |
+| <a id="pip_repository-python_interpreter_target"></a>python_interpreter_target |  If you are using a custom python interpreter built by another repository rule, use this attribute to specify its BUILD target. This allows pip_repository to invoke pip using the same interpreter as your toolchain. If set, takes precedence over python_interpreter. An example value: "@python3_x86_64-unknown-linux-gnu//:python".   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>None</code> |
 | <a id="pip_repository-quiet"></a>quiet |  If True, suppress printing stdout and stderr output to the terminal.   | Boolean | optional | <code>True</code> |
 | <a id="pip_repository-repo_mapping"></a>repo_mapping |  A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.&lt;p&gt;For example, an entry <code>"@foo": "@bar"</code> declares that, for any time this repository depends on <code>@foo</code> (such as a dependency on <code>@foo//some:target</code>, it should actually resolve that dependency within globally-declared <code>@bar</code> (<code>@bar//some:target</code>).   | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | required |  |
 | <a id="pip_repository-repo_prefix"></a>repo_prefix |  Prefix for the generated packages will be of the form <code>@&lt;prefix&gt;&lt;sanitized-package-name&gt;//...</code>   | String | optional | <code>""</code> |
@@ -133,7 +133,7 @@
 | <a id="whl_library-isolated"></a>isolated |  Whether or not to pass the [--isolated](https://pip.pypa.io/en/stable/cli/pip/#cmdoption-isolated) flag to the underlying pip command. Alternatively, the <code>RULES_PYTHON_PIP_ISOLATED</code> environment variable can be used to control this flag.   | Boolean | optional | <code>True</code> |
 | <a id="whl_library-pip_data_exclude"></a>pip_data_exclude |  Additional data exclusion parameters to add to the pip packages BUILD file.   | List of strings | optional | <code>[]</code> |
 | <a id="whl_library-python_interpreter"></a>python_interpreter |  The python interpreter to use. This can either be an absolute path or the name of a binary found on the host's <code>PATH</code> environment variable. If no value is set <code>python3</code> is defaulted for Unix systems and <code>python.exe</code> for Windows.   | String | optional | <code>""</code> |
-| <a id="whl_library-python_interpreter_target"></a>python_interpreter_target |  If you are using a custom python interpreter built by another repository rule, use this attribute to specify its BUILD target. This allows pip_repository to invoke pip using the same interpreter as your toolchain. If set, takes precedence over python_interpreter.   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>None</code> |
+| <a id="whl_library-python_interpreter_target"></a>python_interpreter_target |  If you are using a custom python interpreter built by another repository rule, use this attribute to specify its BUILD target. This allows pip_repository to invoke pip using the same interpreter as your toolchain. If set, takes precedence over python_interpreter. An example value: "@python3_x86_64-unknown-linux-gnu//:python".   | <a href="https://bazel.build/concepts/labels">Label</a> | optional | <code>None</code> |
 | <a id="whl_library-quiet"></a>quiet |  If True, suppress printing stdout and stderr output to the terminal.   | Boolean | optional | <code>True</code> |
 | <a id="whl_library-repo"></a>repo |  Pointer to parent repo name. Used to make these rules rerun if the parent repo changes.   | String | required |  |
 | <a id="whl_library-repo_mapping"></a>repo_mapping |  A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.&lt;p&gt;For example, an entry <code>"@foo": "@bar"</code> declares that, for any time this repository depends on <code>@foo</code> (such as a dependency on <code>@foo//some:target</code>, it should actually resolve that dependency within globally-declared <code>@bar</code> (<code>@bar//some:target</code>).   | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | required |  |
diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel
index 3ef8905..feb1cfb 100644
--- a/examples/BUILD.bazel
+++ b/examples/BUILD.bazel
@@ -23,6 +23,11 @@
 )
 
 bazel_integration_test(
+    name = "bzlmod_build_file_generation_example",
+    timeout = "long",
+)
+
+bazel_integration_test(
     name = "pip_install_example",
     timeout = "long",
 )
diff --git a/examples/build_file_generation/.bazelrc b/examples/build_file_generation/.bazelrc
index f23315a..28f634b 100644
--- a/examples/build_file_generation/.bazelrc
+++ b/examples/build_file_generation/.bazelrc
@@ -1,4 +1,4 @@
-test --test_output=errors
+test --test_output=errors --enable_runfiles
 
 # Windows requires these for multi-python support:
 build --enable_runfiles
diff --git a/examples/bzlmod/gazelle_python.yaml b/examples/bzlmod/gazelle_python.yaml
new file mode 100644
index 0000000..12096e5
--- /dev/null
+++ b/examples/bzlmod/gazelle_python.yaml
@@ -0,0 +1,590 @@
+# GENERATED FILE - DO NOT EDIT!
+#
+# To update this file, run:
+#   bazel run //:gazelle_python_manifest.update
+
+manifest:
+  modules_mapping:
+    S3: s3cmd
+    S3.ACL: s3cmd
+    S3.AccessLog: s3cmd
+    S3.BidirMap: s3cmd
+    S3.CloudFront: s3cmd
+    S3.Config: s3cmd
+    S3.ConnMan: s3cmd
+    S3.Crypto: s3cmd
+    S3.Custom_httplib27: s3cmd
+    S3.Custom_httplib3x: s3cmd
+    S3.Exceptions: s3cmd
+    S3.ExitCodes: s3cmd
+    S3.FileDict: s3cmd
+    S3.FileLists: s3cmd
+    S3.HashCache: s3cmd
+    S3.MultiPart: s3cmd
+    S3.PkgInfo: s3cmd
+    S3.Progress: s3cmd
+    S3.S3: s3cmd
+    S3.S3Uri: s3cmd
+    S3.SortedDict: s3cmd
+    S3.Utils: s3cmd
+    astroid: astroid
+    astroid.arguments: astroid
+    astroid.astroid_manager: astroid
+    astroid.bases: astroid
+    astroid.brain: astroid
+    astroid.brain.brain_argparse: astroid
+    astroid.brain.brain_attrs: astroid
+    astroid.brain.brain_boto3: astroid
+    astroid.brain.brain_builtin_inference: astroid
+    astroid.brain.brain_collections: astroid
+    astroid.brain.brain_crypt: astroid
+    astroid.brain.brain_ctypes: astroid
+    astroid.brain.brain_curses: astroid
+    astroid.brain.brain_dataclasses: astroid
+    astroid.brain.brain_dateutil: astroid
+    astroid.brain.brain_fstrings: astroid
+    astroid.brain.brain_functools: astroid
+    astroid.brain.brain_gi: astroid
+    astroid.brain.brain_hashlib: astroid
+    astroid.brain.brain_http: astroid
+    astroid.brain.brain_hypothesis: astroid
+    astroid.brain.brain_io: astroid
+    astroid.brain.brain_mechanize: astroid
+    astroid.brain.brain_multiprocessing: astroid
+    astroid.brain.brain_namedtuple_enum: astroid
+    astroid.brain.brain_nose: astroid
+    astroid.brain.brain_numpy_core_einsumfunc: astroid
+    astroid.brain.brain_numpy_core_fromnumeric: astroid
+    astroid.brain.brain_numpy_core_function_base: astroid
+    astroid.brain.brain_numpy_core_multiarray: astroid
+    astroid.brain.brain_numpy_core_numeric: astroid
+    astroid.brain.brain_numpy_core_numerictypes: astroid
+    astroid.brain.brain_numpy_core_umath: astroid
+    astroid.brain.brain_numpy_ma: astroid
+    astroid.brain.brain_numpy_ndarray: astroid
+    astroid.brain.brain_numpy_random_mtrand: astroid
+    astroid.brain.brain_numpy_utils: astroid
+    astroid.brain.brain_pathlib: astroid
+    astroid.brain.brain_pkg_resources: astroid
+    astroid.brain.brain_pytest: astroid
+    astroid.brain.brain_qt: astroid
+    astroid.brain.brain_random: astroid
+    astroid.brain.brain_re: astroid
+    astroid.brain.brain_responses: astroid
+    astroid.brain.brain_scipy_signal: astroid
+    astroid.brain.brain_signal: astroid
+    astroid.brain.brain_six: astroid
+    astroid.brain.brain_sqlalchemy: astroid
+    astroid.brain.brain_ssl: astroid
+    astroid.brain.brain_subprocess: astroid
+    astroid.brain.brain_threading: astroid
+    astroid.brain.brain_type: astroid
+    astroid.brain.brain_typing: astroid
+    astroid.brain.brain_unittest: astroid
+    astroid.brain.brain_uuid: astroid
+    astroid.brain.helpers: astroid
+    astroid.builder: astroid
+    astroid.const: astroid
+    astroid.context: astroid
+    astroid.decorators: astroid
+    astroid.exceptions: astroid
+    astroid.filter_statements: astroid
+    astroid.helpers: astroid
+    astroid.inference: astroid
+    astroid.inference_tip: astroid
+    astroid.interpreter: astroid
+    astroid.interpreter.dunder_lookup: astroid
+    astroid.interpreter.objectmodel: astroid
+    astroid.manager: astroid
+    astroid.mixins: astroid
+    astroid.modutils: astroid
+    astroid.node_classes: astroid
+    astroid.nodes: astroid
+    astroid.nodes.as_string: astroid
+    astroid.nodes.const: astroid
+    astroid.nodes.node_classes: astroid
+    astroid.nodes.node_ng: astroid
+    astroid.nodes.scoped_nodes: astroid
+    astroid.nodes.scoped_nodes.mixin: astroid
+    astroid.nodes.scoped_nodes.scoped_nodes: astroid
+    astroid.nodes.scoped_nodes.utils: astroid
+    astroid.nodes.utils: astroid
+    astroid.objects: astroid
+    astroid.protocols: astroid
+    astroid.raw_building: astroid
+    astroid.rebuilder: astroid
+    astroid.scoped_nodes: astroid
+    astroid.test_utils: astroid
+    astroid.transforms: astroid
+    astroid.typing: astroid
+    astroid.util: astroid
+    certifi: certifi
+    certifi.core: certifi
+    chardet: chardet
+    chardet.big5freq: chardet
+    chardet.big5prober: chardet
+    chardet.chardistribution: chardet
+    chardet.charsetgroupprober: chardet
+    chardet.charsetprober: chardet
+    chardet.cli: chardet
+    chardet.cli.chardetect: chardet
+    chardet.codingstatemachine: chardet
+    chardet.compat: chardet
+    chardet.cp949prober: chardet
+    chardet.enums: chardet
+    chardet.escprober: chardet
+    chardet.escsm: chardet
+    chardet.eucjpprober: chardet
+    chardet.euckrfreq: chardet
+    chardet.euckrprober: chardet
+    chardet.euctwfreq: chardet
+    chardet.euctwprober: chardet
+    chardet.gb2312freq: chardet
+    chardet.gb2312prober: chardet
+    chardet.hebrewprober: chardet
+    chardet.jisfreq: chardet
+    chardet.jpcntx: chardet
+    chardet.langbulgarianmodel: chardet
+    chardet.langgreekmodel: chardet
+    chardet.langhebrewmodel: chardet
+    chardet.langhungarianmodel: chardet
+    chardet.langrussianmodel: chardet
+    chardet.langthaimodel: chardet
+    chardet.langturkishmodel: chardet
+    chardet.latin1prober: chardet
+    chardet.mbcharsetprober: chardet
+    chardet.mbcsgroupprober: chardet
+    chardet.mbcssm: chardet
+    chardet.metadata: chardet
+    chardet.metadata.languages: chardet
+    chardet.sbcharsetprober: chardet
+    chardet.sbcsgroupprober: chardet
+    chardet.sjisprober: chardet
+    chardet.universaldetector: chardet
+    chardet.utf8prober: chardet
+    chardet.version: chardet
+    dateutil: python_dateutil
+    dateutil.easter: python_dateutil
+    dateutil.parser: python_dateutil
+    dateutil.parser.isoparser: python_dateutil
+    dateutil.relativedelta: python_dateutil
+    dateutil.rrule: python_dateutil
+    dateutil.tz: python_dateutil
+    dateutil.tz.tz: python_dateutil
+    dateutil.tz.win: python_dateutil
+    dateutil.tzwin: python_dateutil
+    dateutil.utils: python_dateutil
+    dateutil.zoneinfo: python_dateutil
+    dateutil.zoneinfo.rebuild: python_dateutil
+    dill: dill
+    dill.detect: dill
+    dill.logger: dill
+    dill.objtypes: dill
+    dill.pointers: dill
+    dill.session: dill
+    dill.settings: dill
+    dill.source: dill
+    dill.temp: dill
+    idna: idna
+    idna.codec: idna
+    idna.compat: idna
+    idna.core: idna
+    idna.idnadata: idna
+    idna.intranges: idna
+    idna.package_data: idna
+    idna.uts46data: idna
+    isort: isort
+    isort.api: isort
+    isort.comments: isort
+    isort.core: isort
+    isort.deprecated: isort
+    isort.deprecated.finders: isort
+    isort.exceptions: isort
+    isort.files: isort
+    isort.format: isort
+    isort.hooks: isort
+    isort.identify: isort
+    isort.io: isort
+    isort.literal: isort
+    isort.logo: isort
+    isort.main: isort
+    isort.output: isort
+    isort.parse: isort
+    isort.place: isort
+    isort.profiles: isort
+    isort.pylama_isort: isort
+    isort.sections: isort
+    isort.settings: isort
+    isort.setuptools_commands: isort
+    isort.sorting: isort
+    isort.stdlibs: isort
+    isort.stdlibs.all: isort
+    isort.stdlibs.py2: isort
+    isort.stdlibs.py27: isort
+    isort.stdlibs.py3: isort
+    isort.stdlibs.py310: isort
+    isort.stdlibs.py311: isort
+    isort.stdlibs.py36: isort
+    isort.stdlibs.py37: isort
+    isort.stdlibs.py38: isort
+    isort.stdlibs.py39: isort
+    isort.utils: isort
+    isort.wrap: isort
+    isort.wrap_modes: isort
+    lazy_object_proxy: lazy_object_proxy
+    lazy_object_proxy.compat: lazy_object_proxy
+    lazy_object_proxy.simple: lazy_object_proxy
+    lazy_object_proxy.slots: lazy_object_proxy
+    lazy_object_proxy.utils: lazy_object_proxy
+    magic: python_magic
+    magic.compat: python_magic
+    magic.loader: python_magic
+    mccabe: mccabe
+    pathspec: pathspec
+    pathspec.gitignore: pathspec
+    pathspec.pathspec: pathspec
+    pathspec.pattern: pathspec
+    pathspec.patterns: pathspec
+    pathspec.patterns.gitwildmatch: pathspec
+    pathspec.util: pathspec
+    pkg_resources: setuptools
+    pkg_resources.extern: setuptools
+    platformdirs: platformdirs
+    platformdirs.android: platformdirs
+    platformdirs.api: platformdirs
+    platformdirs.macos: platformdirs
+    platformdirs.unix: platformdirs
+    platformdirs.version: platformdirs
+    platformdirs.windows: platformdirs
+    pylint: pylint
+    pylint.checkers: pylint
+    pylint.checkers.async: pylint
+    pylint.checkers.base: pylint
+    pylint.checkers.base.basic_checker: pylint
+    pylint.checkers.base.basic_error_checker: pylint
+    pylint.checkers.base.comparison_checker: pylint
+    pylint.checkers.base.docstring_checker: pylint
+    pylint.checkers.base.name_checker: pylint
+    pylint.checkers.base.name_checker.checker: pylint
+    pylint.checkers.base.name_checker.naming_style: pylint
+    pylint.checkers.base.pass_checker: pylint
+    pylint.checkers.base_checker: pylint
+    pylint.checkers.classes: pylint
+    pylint.checkers.classes.class_checker: pylint
+    pylint.checkers.classes.special_methods_checker: pylint
+    pylint.checkers.deprecated: pylint
+    pylint.checkers.design_analysis: pylint
+    pylint.checkers.dunder_methods: pylint
+    pylint.checkers.ellipsis_checker: pylint
+    pylint.checkers.exceptions: pylint
+    pylint.checkers.format: pylint
+    pylint.checkers.imports: pylint
+    pylint.checkers.lambda_expressions: pylint
+    pylint.checkers.logging: pylint
+    pylint.checkers.mapreduce_checker: pylint
+    pylint.checkers.method_args: pylint
+    pylint.checkers.misc: pylint
+    pylint.checkers.modified_iterating_checker: pylint
+    pylint.checkers.newstyle: pylint
+    pylint.checkers.non_ascii_names: pylint
+    pylint.checkers.raw_metrics: pylint
+    pylint.checkers.refactoring: pylint
+    pylint.checkers.refactoring.implicit_booleaness_checker: pylint
+    pylint.checkers.refactoring.not_checker: pylint
+    pylint.checkers.refactoring.recommendation_checker: pylint
+    pylint.checkers.refactoring.refactoring_checker: pylint
+    pylint.checkers.similar: pylint
+    pylint.checkers.spelling: pylint
+    pylint.checkers.stdlib: pylint
+    pylint.checkers.strings: pylint
+    pylint.checkers.threading_checker: pylint
+    pylint.checkers.typecheck: pylint
+    pylint.checkers.unicode: pylint
+    pylint.checkers.unsupported_version: pylint
+    pylint.checkers.utils: pylint
+    pylint.checkers.variables: pylint
+    pylint.config: pylint
+    pylint.config.argument: pylint
+    pylint.config.arguments_manager: pylint
+    pylint.config.arguments_provider: pylint
+    pylint.config.callback_actions: pylint
+    pylint.config.config_file_parser: pylint
+    pylint.config.config_initialization: pylint
+    pylint.config.configuration_mixin: pylint
+    pylint.config.deprecation_actions: pylint
+    pylint.config.environment_variable: pylint
+    pylint.config.exceptions: pylint
+    pylint.config.find_default_config_files: pylint
+    pylint.config.help_formatter: pylint
+    pylint.config.option: pylint
+    pylint.config.option_manager_mixin: pylint
+    pylint.config.option_parser: pylint
+    pylint.config.options_provider_mixin: pylint
+    pylint.config.utils: pylint
+    pylint.constants: pylint
+    pylint.epylint: pylint
+    pylint.exceptions: pylint
+    pylint.extensions: pylint
+    pylint.extensions.bad_builtin: pylint
+    pylint.extensions.broad_try_clause: pylint
+    pylint.extensions.check_elif: pylint
+    pylint.extensions.code_style: pylint
+    pylint.extensions.comparetozero: pylint
+    pylint.extensions.comparison_placement: pylint
+    pylint.extensions.confusing_elif: pylint
+    pylint.extensions.consider_ternary_expression: pylint
+    pylint.extensions.docparams: pylint
+    pylint.extensions.docstyle: pylint
+    pylint.extensions.empty_comment: pylint
+    pylint.extensions.emptystring: pylint
+    pylint.extensions.eq_without_hash: pylint
+    pylint.extensions.for_any_all: pylint
+    pylint.extensions.mccabe: pylint
+    pylint.extensions.no_self_use: pylint
+    pylint.extensions.overlapping_exceptions: pylint
+    pylint.extensions.private_import: pylint
+    pylint.extensions.redefined_loop_name: pylint
+    pylint.extensions.redefined_variable_type: pylint
+    pylint.extensions.set_membership: pylint
+    pylint.extensions.typing: pylint
+    pylint.extensions.while_used: pylint
+    pylint.graph: pylint
+    pylint.interfaces: pylint
+    pylint.lint: pylint
+    pylint.lint.base_options: pylint
+    pylint.lint.caching: pylint
+    pylint.lint.expand_modules: pylint
+    pylint.lint.message_state_handler: pylint
+    pylint.lint.parallel: pylint
+    pylint.lint.pylinter: pylint
+    pylint.lint.report_functions: pylint
+    pylint.lint.run: pylint
+    pylint.lint.utils: pylint
+    pylint.message: pylint
+    pylint.message.message: pylint
+    pylint.message.message_definition: pylint
+    pylint.message.message_definition_store: pylint
+    pylint.message.message_id_store: pylint
+    pylint.pyreverse: pylint
+    pylint.pyreverse.diadefslib: pylint
+    pylint.pyreverse.diagrams: pylint
+    pylint.pyreverse.dot_printer: pylint
+    pylint.pyreverse.inspector: pylint
+    pylint.pyreverse.main: pylint
+    pylint.pyreverse.mermaidjs_printer: pylint
+    pylint.pyreverse.plantuml_printer: pylint
+    pylint.pyreverse.printer: pylint
+    pylint.pyreverse.printer_factory: pylint
+    pylint.pyreverse.utils: pylint
+    pylint.pyreverse.vcg_printer: pylint
+    pylint.pyreverse.writer: pylint
+    pylint.reporters: pylint
+    pylint.reporters.base_reporter: pylint
+    pylint.reporters.collecting_reporter: pylint
+    pylint.reporters.json_reporter: pylint
+    pylint.reporters.multi_reporter: pylint
+    pylint.reporters.reports_handler_mix_in: pylint
+    pylint.reporters.text: pylint
+    pylint.reporters.ureports: pylint
+    pylint.reporters.ureports.base_writer: pylint
+    pylint.reporters.ureports.nodes: pylint
+    pylint.reporters.ureports.text_writer: pylint
+    pylint.testutils: pylint
+    pylint.testutils.checker_test_case: pylint
+    pylint.testutils.configuration_test: pylint
+    pylint.testutils.constants: pylint
+    pylint.testutils.decorator: pylint
+    pylint.testutils.functional: pylint
+    pylint.testutils.functional.find_functional_tests: pylint
+    pylint.testutils.functional.lint_module_output_update: pylint
+    pylint.testutils.functional.test_file: pylint
+    pylint.testutils.functional_test_file: pylint
+    pylint.testutils.get_test_info: pylint
+    pylint.testutils.global_test_linter: pylint
+    pylint.testutils.lint_module_test: pylint
+    pylint.testutils.output_line: pylint
+    pylint.testutils.pyreverse: pylint
+    pylint.testutils.reporter_for_tests: pylint
+    pylint.testutils.tokenize_str: pylint
+    pylint.testutils.unittest_linter: pylint
+    pylint.testutils.utils: pylint
+    pylint.typing: pylint
+    pylint.utils: pylint
+    pylint.utils.ast_walker: pylint
+    pylint.utils.docs: pylint
+    pylint.utils.file_state: pylint
+    pylint.utils.linterstats: pylint
+    pylint.utils.pragma_parser: pylint
+    pylint.utils.utils: pylint
+    requests: requests
+    requests.adapters: requests
+    requests.api: requests
+    requests.auth: requests
+    requests.certs: requests
+    requests.compat: requests
+    requests.cookies: requests
+    requests.exceptions: requests
+    requests.help: requests
+    requests.hooks: requests
+    requests.models: requests
+    requests.packages: requests
+    requests.sessions: requests
+    requests.status_codes: requests
+    requests.structures: requests
+    requests.utils: requests
+    setuptools: setuptools
+    setuptools.archive_util: setuptools
+    setuptools.build_meta: setuptools
+    setuptools.command: setuptools
+    setuptools.command.alias: setuptools
+    setuptools.command.bdist_egg: setuptools
+    setuptools.command.bdist_rpm: setuptools
+    setuptools.command.build: setuptools
+    setuptools.command.build_clib: setuptools
+    setuptools.command.build_ext: setuptools
+    setuptools.command.build_py: setuptools
+    setuptools.command.develop: setuptools
+    setuptools.command.dist_info: setuptools
+    setuptools.command.easy_install: setuptools
+    setuptools.command.editable_wheel: setuptools
+    setuptools.command.egg_info: setuptools
+    setuptools.command.install: setuptools
+    setuptools.command.install_egg_info: setuptools
+    setuptools.command.install_lib: setuptools
+    setuptools.command.install_scripts: setuptools
+    setuptools.command.py36compat: setuptools
+    setuptools.command.register: setuptools
+    setuptools.command.rotate: setuptools
+    setuptools.command.saveopts: setuptools
+    setuptools.command.sdist: setuptools
+    setuptools.command.setopt: setuptools
+    setuptools.command.test: setuptools
+    setuptools.command.upload: setuptools
+    setuptools.command.upload_docs: setuptools
+    setuptools.config: setuptools
+    setuptools.config.expand: setuptools
+    setuptools.config.pyprojecttoml: setuptools
+    setuptools.config.setupcfg: setuptools
+    setuptools.dep_util: setuptools
+    setuptools.depends: setuptools
+    setuptools.discovery: setuptools
+    setuptools.dist: setuptools
+    setuptools.errors: setuptools
+    setuptools.extension: setuptools
+    setuptools.extern: setuptools
+    setuptools.glob: setuptools
+    setuptools.installer: setuptools
+    setuptools.launch: setuptools
+    setuptools.logging: setuptools
+    setuptools.monkey: setuptools
+    setuptools.msvc: setuptools
+    setuptools.namespaces: setuptools
+    setuptools.package_index: setuptools
+    setuptools.py34compat: setuptools
+    setuptools.sandbox: setuptools
+    setuptools.unicode_utils: setuptools
+    setuptools.version: setuptools
+    setuptools.wheel: setuptools
+    setuptools.windows_support: setuptools
+    six: six
+    tabulate: tabulate
+    tabulate.version: tabulate
+    tomli: tomli
+    tomlkit: tomlkit
+    tomlkit.api: tomlkit
+    tomlkit.container: tomlkit
+    tomlkit.exceptions: tomlkit
+    tomlkit.items: tomlkit
+    tomlkit.parser: tomlkit
+    tomlkit.source: tomlkit
+    tomlkit.toml_char: tomlkit
+    tomlkit.toml_document: tomlkit
+    tomlkit.toml_file: tomlkit
+    typing_extensions: typing_extensions
+    urllib3: urllib3
+    urllib3.connection: urllib3
+    urllib3.connectionpool: urllib3
+    urllib3.contrib: urllib3
+    urllib3.contrib.appengine: urllib3
+    urllib3.contrib.ntlmpool: urllib3
+    urllib3.contrib.pyopenssl: urllib3
+    urllib3.contrib.securetransport: urllib3
+    urllib3.contrib.socks: urllib3
+    urllib3.exceptions: urllib3
+    urllib3.fields: urllib3
+    urllib3.filepost: urllib3
+    urllib3.packages: urllib3
+    urllib3.packages.backports: urllib3
+    urllib3.packages.backports.makefile: urllib3
+    urllib3.packages.six: urllib3
+    urllib3.poolmanager: urllib3
+    urllib3.request: urllib3
+    urllib3.response: urllib3
+    urllib3.util: urllib3
+    urllib3.util.connection: urllib3
+    urllib3.util.proxy: urllib3
+    urllib3.util.queue: urllib3
+    urllib3.util.request: urllib3
+    urllib3.util.response: urllib3
+    urllib3.util.retry: urllib3
+    urllib3.util.ssl_: urllib3
+    urllib3.util.ssl_match_hostname: urllib3
+    urllib3.util.ssltransport: urllib3
+    urllib3.util.timeout: urllib3
+    urllib3.util.url: urllib3
+    urllib3.util.wait: urllib3
+    wrapt: wrapt
+    wrapt.arguments: wrapt
+    wrapt.decorators: wrapt
+    wrapt.importer: wrapt
+    wrapt.wrappers: wrapt
+    yaml: PyYAML
+    yaml.composer: PyYAML
+    yaml.constructor: PyYAML
+    yaml.cyaml: PyYAML
+    yaml.dumper: PyYAML
+    yaml.emitter: PyYAML
+    yaml.error: PyYAML
+    yaml.events: PyYAML
+    yaml.loader: PyYAML
+    yaml.nodes: PyYAML
+    yaml.parser: PyYAML
+    yaml.reader: PyYAML
+    yaml.representer: PyYAML
+    yaml.resolver: PyYAML
+    yaml.scanner: PyYAML
+    yaml.serializer: PyYAML
+    yaml.tokens: PyYAML
+    yamllint: yamllint
+    yamllint.cli: yamllint
+    yamllint.config: yamllint
+    yamllint.linter: yamllint
+    yamllint.parser: yamllint
+    yamllint.rules: yamllint
+    yamllint.rules.braces: yamllint
+    yamllint.rules.brackets: yamllint
+    yamllint.rules.colons: yamllint
+    yamllint.rules.commas: yamllint
+    yamllint.rules.comments: yamllint
+    yamllint.rules.comments_indentation: yamllint
+    yamllint.rules.common: yamllint
+    yamllint.rules.document_end: yamllint
+    yamllint.rules.document_start: yamllint
+    yamllint.rules.empty_lines: yamllint
+    yamllint.rules.empty_values: yamllint
+    yamllint.rules.float_values: yamllint
+    yamllint.rules.hyphens: yamllint
+    yamllint.rules.indentation: yamllint
+    yamllint.rules.key_duplicates: yamllint
+    yamllint.rules.key_ordering: yamllint
+    yamllint.rules.line_length: yamllint
+    yamllint.rules.new_line_at_end_of_file: yamllint
+    yamllint.rules.new_lines: yamllint
+    yamllint.rules.octal_values: yamllint
+    yamllint.rules.quoted_strings: yamllint
+    yamllint.rules.trailing_spaces: yamllint
+    yamllint.rules.truthy: yamllint
+  pip_repository:
+    name: pip
+    use_pip_repository_aliases: true
+integrity: d979738b10adbbaff0884837e4414688990491c6c40f6a25d58b9bb564411477
diff --git a/examples/bzlmod/runfiles/BUILD.bazel b/examples/bzlmod/runfiles/BUILD.bazel
index add56b3..3503ac3 100644
--- a/examples/bzlmod/runfiles/BUILD.bazel
+++ b/examples/bzlmod/runfiles/BUILD.bazel
@@ -1,5 +1,6 @@
 load("@rules_python//python:defs.bzl", "py_test")
 
+# gazelle:ignore
 py_test(
     name = "runfiles_test",
     srcs = ["runfiles_test.py"],
diff --git a/examples/bzlmod_build_file_generation/.bazelignore b/examples/bzlmod_build_file_generation/.bazelignore
new file mode 100644
index 0000000..ab3eb16
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/.bazelignore
@@ -0,0 +1 @@
+other_module
diff --git a/examples/bzlmod_build_file_generation/.bazelrc b/examples/bzlmod_build_file_generation/.bazelrc
new file mode 100644
index 0000000..1fbada7
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/.bazelrc
@@ -0,0 +1,9 @@
+test --test_output=errors --enable_runfiles
+
+# Windows requires these for multi-python support:
+build --enable_runfiles
+startup --windows_enable_symlinks
+
+common --experimental_enable_bzlmod
+
+coverage --java_runtime_version=remotejdk_11
diff --git a/examples/bzlmod_build_file_generation/.bazelversion b/examples/bzlmod_build_file_generation/.bazelversion
new file mode 100644
index 0000000..09b254e
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/.bazelversion
@@ -0,0 +1 @@
+6.0.0
diff --git a/examples/bzlmod_build_file_generation/.gitignore b/examples/bzlmod_build_file_generation/.gitignore
new file mode 100644
index 0000000..ac51a05
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/.gitignore
@@ -0,0 +1 @@
+bazel-*
diff --git a/examples/bzlmod_build_file_generation/BUILD.bazel b/examples/bzlmod_build_file_generation/BUILD.bazel
new file mode 100644
index 0000000..c667f1e
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/BUILD.bazel
@@ -0,0 +1,103 @@
+# Load various rules so that we can have bazel download
+# various rulesets and dependencies.
+# The `load` statement imports the symbol for the rule, in the defined
+# ruleset. When the symbol is loaded you can use the rule.
+
+# The following code loads the base python requirements and gazelle
+# requirements.
+load("@bazel_gazelle//:def.bzl", "gazelle")
+load("@pip//:requirements.bzl", "all_whl_requirements")
+load("@python3//:defs.bzl", py_test_with_transition = "py_test")
+load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test")
+load("@rules_python//python:pip.bzl", "compile_pip_requirements")
+load("@rules_python_gazelle_plugin//:def.bzl", "GAZELLE_PYTHON_RUNTIME_DEPS")
+load("@rules_python_gazelle_plugin//manifest:defs.bzl", "gazelle_python_manifest")
+load("@rules_python_gazelle_plugin//modules_mapping:def.bzl", "modules_mapping")
+
+# This stanza calls a rule that generates targets for managing pip dependencies
+# with pip-compile.
+compile_pip_requirements(
+    name = "requirements",
+    extra_args = ["--allow-unsafe"],
+    requirements_in = "requirements.in",
+    requirements_txt = "requirements_lock.txt",
+    requirements_windows = "requirements_windows.txt",
+)
+
+# This repository rule fetches the metadata for python packages we
+# depend on. That data is required for the gazelle_python_manifest
+# rule to update our manifest file.
+modules_mapping(
+    name = "modules_map",
+    exclude_patterns = [
+        "^_|(\\._)+",  # This is the default.
+        "(\\.tests)+",  # Add a custom one to get rid of the psutil tests.
+    ],
+    wheels = all_whl_requirements,
+)
+
+# Gazelle python extension needs a manifest file mapping from
+# an import to the installed package that provides it.
+# This macro produces two targets:
+# - //:gazelle_python_manifest.update can be used with `bazel run`
+#   to recalculate the manifest
+# - //:gazelle_python_manifest.test is a test target ensuring that
+#   the manifest doesn't need to be updated
+# This target updates a file called gazelle_python.yaml, and
+# requires that file exist before the target is run.
+# When you are using gazelle you need to run this target first.
+gazelle_python_manifest(
+    name = "gazelle_python_manifest",
+    modules_mapping = ":modules_map",
+    pip_repository_name = "pip",
+    requirements = "//:requirements_lock.txt",
+    # NOTE: we can use this flag in order to make our setup compatible with
+    # bzlmod.
+    use_pip_repository_aliases = True,
+)
+
+# Our gazelle target points to the python gazelle binary.
+# This is the simple case where we only need one language supported.
+# If you also had proto, go, or other gazelle-supported languages,
+# you would also need a gazelle_binary rule.
+# See https://github.com/bazelbuild/bazel-gazelle/blob/master/extend.rst#example
+# This is the primary gazelle target to run, so that you can update BUILD.bazel files.
+# You can execute:
+# - bazel run //:gazelle update
+# - bazel run //:gazelle fix
+# See: https://github.com/bazelbuild/bazel-gazelle#fix-and-update
+gazelle(
+    name = "gazelle",
+    data = GAZELLE_PYTHON_RUNTIME_DEPS,
+    gazelle = "@rules_python_gazelle_plugin//python:gazelle_binary",
+)
+
+py_test_with_transition(
+    name = "test_with_transition",
+    srcs = ["__test__.py"],
+    main = "__test__.py",
+    deps = [":bzlmod_build_file_generation"],
+)
+
+# The following targets are created and maintained by gazelle
+py_library(
+    name = "bzlmod_build_file_generation",
+    srcs = ["lib.py"],
+    visibility = ["//:__subpackages__"],
+    deps = ["@pip//tabulate"],
+)
+
+py_binary(
+    name = "bzlmod_build_file_generation_bin",
+    srcs = ["__main__.py"],
+    main = "__main__.py",
+    visibility = ["//:__subpackages__"],
+    deps = [":bzlmod_build_file_generation"],
+)
+
+py_test(
+    name = "bzlmod_build_file_generation_test",
+    srcs = ["__test__.py"],
+    main = "__test__.py",
+    deps = [":bzlmod_build_file_generation"],
+)
diff --git a/examples/bzlmod_build_file_generation/MODULE.bazel b/examples/bzlmod_build_file_generation/MODULE.bazel
new file mode 100644
index 0000000..781b0cb
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/MODULE.bazel
@@ -0,0 +1,113 @@
+# This file replaces the WORKSPACE file when using bzlmod.
+
+# module declares certain properties of the Bazel module represented by the current Bazel repo.
+# These properties are either essential metadata of the module (such as the name and version),
+# or affect behavior of the current module and its dependents.
+module(
+    name = "example_bzlmod_build_file_generation",
+    version = "0.0.0",
+    compatibility_level = 1,
+)
+
+# The following stanza defines the dependency rules_python.
+# For typical setups you set the version.
+# See the releases page for available versions.
+# https://github.com/bazelbuild/rules_python/releases
+bazel_dep(name = "rules_python", version = "0.0.0")
+
+# The following loads rules_python from the file system.
+# For usual setups you should remove this local_path_override block.
+local_path_override(
+    module_name = "rules_python",
+    path = "../..",
+)
+
+# The following stanza defines the dependency rules_python_gazelle_plugin.
+# For typical setups you set the version.
+# See the releases page for available versions.
+# https://github.com/bazelbuild/rules_python/releases
+bazel_dep(name = "rules_python_gazelle_plugin", version = "0.0.0")
+
+# The following starlark loads the gazelle plugin from the file system.
+# For usual setups you should remove this local_path_override block.
+local_path_override(
+    module_name = "rules_python_gazelle_plugin",
+    path = "../../gazelle",
+)
+
+# The following stanza defines the dependency for gazelle
+# See here https://github.com/bazelbuild/bazel-gazelle/releases/ for the
+# latest version.
+bazel_dep(name = "gazelle", version = "0.30.0", repo_name = "bazel_gazelle")
+
+# The following stanze returns a proxy object representing a module extension;
+# its methods can be invoked to create module extension tags.
+python = use_extension("@rules_python//python:extensions.bzl", "python")
+
+# This name is passed into python.toolchain and it's use_repo statement.
+# We also use the same name for python.host_python_interpreter.
+PYTHON_NAME = "python3"
+
+# This is the name that is used for the host interpreter
+PYTHON_INTERPRETER = PYTHON_NAME + "_host_interpreter"
+
+# We next initialize the python toolchain using the extension.
+# You can set different Python versions in this block.
+python.toolchain(
+    # This name is used in the various use_repo statements
+    # below, and in the local extension that is in this
+    # example.
+    name = PYTHON_NAME,
+    configure_coverage_tool = True,
+    python_version = "3.9",
+)
+
+# Import the python repositories generated by the given module extension
+# into the scope of the current module.
+# All of the python3 repositories use the PYTHON_NAME as there prefix.  They
+# are not catenated for ease of reading.
+use_repo(python, PYTHON_NAME)
+use_repo(python, "python3_toolchains")
+use_repo(python, PYTHON_INTERPRETER)
+
+# Register an already-defined toolchain so that Bazel can use it during toolchain resolution.
+register_toolchains(
+    "@python3_toolchains//:all",
+)
+
+# Use the pip extension
+pip = use_extension("@rules_python//python:extensions.bzl", "pip")
+
+# Use the extension to call the `pip_repository` rule that invokes `pip`, with `incremental` set.
+# Accepts a locked/compiled requirements file and installs the dependencies listed within.
+# Those dependencies become available in a generated `requirements.bzl` file.
+# You can instead check this `requirements.bzl` file into your repo.
+# Because this project has different requirements for windows vs other
+# operating systems, we have requirements for each.
+pip.parse(
+    name = "pip",
+    # When using gazelle you must use set the following flag
+    # in order for the generation of gazelle dependency resolution.
+    incompatible_generate_aliases = True,
+    # The interpreter attribute points to the interpreter to use for running
+    # pip commands to download the packages in the requirements file.
+    # As a best practice, we use the same interpreter as the toolchain
+    # that was configured above; this ensures the same Python version
+    # is used for both resolving dependencies and running tests/binaries.
+    # If this isn't specified, then you'll get whatever is locally installed
+    # on your system.
+    python_interpreter_target = "@" + PYTHON_INTERPRETER + "//:python",
+    requirements_lock = "//:requirements_lock.txt",
+    requirements_windows = "//:requirements_windows.txt",
+)
+
+# Imports the pip toolchain generated by the given module extension into the scope of the current module.
+use_repo(pip, "pip")
+
+# This project includes a different module that is on the local file system.
+# Add the module to this parent project.
+bazel_dep(name = "other_module", version = "", repo_name = "our_other_module")
+local_path_override(
+    module_name = "other_module",
+    path = "other_module",
+)
diff --git a/examples/bzlmod_build_file_generation/README.md b/examples/bzlmod_build_file_generation/README.md
new file mode 100644
index 0000000..703fd38
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/README.md
@@ -0,0 +1,28 @@
+# Bzlmod build file generation example
+
+This example demostrates how to use `rules_python` and gazelle with `bzlmod` enabled.
+[Bzlmod](https://bazel.build/external/overview#bzlmod), the new external dependency 
+subsystem, does not directly work with repo definitions. Instead, it builds a dependency 
+graph from modules, runs extensions on top of the graph, and defines repos accordingly.
+
+Gazelle is setup with the `rules_python`
+extension, so that targets like `py_library` and `py_binary` can be
+automatically created just by running:
+
+```sh
+$ bazel run //:gazelle update
+```
+
+The are other targets that allow you to update the gazelle dependency management
+when you update the requirements.in file.  See:
+
+```bash
+bazel run //:gazelle_python_manifest.update
+```
+
+For more information on the behavior of the `rules_python` gazelle extension,
+see the [README.md](../../gazelle/README.md) file in the /gazelle folder.
+
+This example uses a `MODULE.bazel` file that configures the bzlmod dependency
+management. See comments in the `MODULE.bazel` and `BUILD.bazel` files for more
+information.
diff --git a/examples/bzlmod_build_file_generation/WORKSPACE b/examples/bzlmod_build_file_generation/WORKSPACE
new file mode 100644
index 0000000..78cc252
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/WORKSPACE
@@ -0,0 +1,2 @@
+# Empty file indicating the root of a Bazel workspace.
+# Dependencies and setup are in MODULE.bazel.
diff --git a/examples/bzlmod_build_file_generation/__main__.py b/examples/bzlmod_build_file_generation/__main__.py
new file mode 100644
index 0000000..099493b
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/__main__.py
@@ -0,0 +1,18 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+from lib import main
+
+if __name__ == "__main__":
+    print(main([["A", 1], ["B", 2]]))
diff --git a/examples/bzlmod_build_file_generation/__test__.py b/examples/bzlmod_build_file_generation/__test__.py
new file mode 100644
index 0000000..cdc1c89
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/__test__.py
@@ -0,0 +1,33 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+import unittest
+
+from lib import main
+
+
+class ExampleTest(unittest.TestCase):
+    def test_main(self):
+        self.assertEquals(
+            """\
+-  -
+A  1
+B  2
+-  -""",
+            main([["A", 1], ["B", 2]]),
+        )
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/examples/bzlmod_build_file_generation/gazelle_python.yaml b/examples/bzlmod_build_file_generation/gazelle_python.yaml
new file mode 100644
index 0000000..12096e5
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/gazelle_python.yaml
@@ -0,0 +1,590 @@
+# GENERATED FILE - DO NOT EDIT!
+#
+# To update this file, run:
+#   bazel run //:gazelle_python_manifest.update
+
+manifest:
+  modules_mapping:
+    S3: s3cmd
+    S3.ACL: s3cmd
+    S3.AccessLog: s3cmd
+    S3.BidirMap: s3cmd
+    S3.CloudFront: s3cmd
+    S3.Config: s3cmd
+    S3.ConnMan: s3cmd
+    S3.Crypto: s3cmd
+    S3.Custom_httplib27: s3cmd
+    S3.Custom_httplib3x: s3cmd
+    S3.Exceptions: s3cmd
+    S3.ExitCodes: s3cmd
+    S3.FileDict: s3cmd
+    S3.FileLists: s3cmd
+    S3.HashCache: s3cmd
+    S3.MultiPart: s3cmd
+    S3.PkgInfo: s3cmd
+    S3.Progress: s3cmd
+    S3.S3: s3cmd
+    S3.S3Uri: s3cmd
+    S3.SortedDict: s3cmd
+    S3.Utils: s3cmd
+    astroid: astroid
+    astroid.arguments: astroid
+    astroid.astroid_manager: astroid
+    astroid.bases: astroid
+    astroid.brain: astroid
+    astroid.brain.brain_argparse: astroid
+    astroid.brain.brain_attrs: astroid
+    astroid.brain.brain_boto3: astroid
+    astroid.brain.brain_builtin_inference: astroid
+    astroid.brain.brain_collections: astroid
+    astroid.brain.brain_crypt: astroid
+    astroid.brain.brain_ctypes: astroid
+    astroid.brain.brain_curses: astroid
+    astroid.brain.brain_dataclasses: astroid
+    astroid.brain.brain_dateutil: astroid
+    astroid.brain.brain_fstrings: astroid
+    astroid.brain.brain_functools: astroid
+    astroid.brain.brain_gi: astroid
+    astroid.brain.brain_hashlib: astroid
+    astroid.brain.brain_http: astroid
+    astroid.brain.brain_hypothesis: astroid
+    astroid.brain.brain_io: astroid
+    astroid.brain.brain_mechanize: astroid
+    astroid.brain.brain_multiprocessing: astroid
+    astroid.brain.brain_namedtuple_enum: astroid
+    astroid.brain.brain_nose: astroid
+    astroid.brain.brain_numpy_core_einsumfunc: astroid
+    astroid.brain.brain_numpy_core_fromnumeric: astroid
+    astroid.brain.brain_numpy_core_function_base: astroid
+    astroid.brain.brain_numpy_core_multiarray: astroid
+    astroid.brain.brain_numpy_core_numeric: astroid
+    astroid.brain.brain_numpy_core_numerictypes: astroid
+    astroid.brain.brain_numpy_core_umath: astroid
+    astroid.brain.brain_numpy_ma: astroid
+    astroid.brain.brain_numpy_ndarray: astroid
+    astroid.brain.brain_numpy_random_mtrand: astroid
+    astroid.brain.brain_numpy_utils: astroid
+    astroid.brain.brain_pathlib: astroid
+    astroid.brain.brain_pkg_resources: astroid
+    astroid.brain.brain_pytest: astroid
+    astroid.brain.brain_qt: astroid
+    astroid.brain.brain_random: astroid
+    astroid.brain.brain_re: astroid
+    astroid.brain.brain_responses: astroid
+    astroid.brain.brain_scipy_signal: astroid
+    astroid.brain.brain_signal: astroid
+    astroid.brain.brain_six: astroid
+    astroid.brain.brain_sqlalchemy: astroid
+    astroid.brain.brain_ssl: astroid
+    astroid.brain.brain_subprocess: astroid
+    astroid.brain.brain_threading: astroid
+    astroid.brain.brain_type: astroid
+    astroid.brain.brain_typing: astroid
+    astroid.brain.brain_unittest: astroid
+    astroid.brain.brain_uuid: astroid
+    astroid.brain.helpers: astroid
+    astroid.builder: astroid
+    astroid.const: astroid
+    astroid.context: astroid
+    astroid.decorators: astroid
+    astroid.exceptions: astroid
+    astroid.filter_statements: astroid
+    astroid.helpers: astroid
+    astroid.inference: astroid
+    astroid.inference_tip: astroid
+    astroid.interpreter: astroid
+    astroid.interpreter.dunder_lookup: astroid
+    astroid.interpreter.objectmodel: astroid
+    astroid.manager: astroid
+    astroid.mixins: astroid
+    astroid.modutils: astroid
+    astroid.node_classes: astroid
+    astroid.nodes: astroid
+    astroid.nodes.as_string: astroid
+    astroid.nodes.const: astroid
+    astroid.nodes.node_classes: astroid
+    astroid.nodes.node_ng: astroid
+    astroid.nodes.scoped_nodes: astroid
+    astroid.nodes.scoped_nodes.mixin: astroid
+    astroid.nodes.scoped_nodes.scoped_nodes: astroid
+    astroid.nodes.scoped_nodes.utils: astroid
+    astroid.nodes.utils: astroid
+    astroid.objects: astroid
+    astroid.protocols: astroid
+    astroid.raw_building: astroid
+    astroid.rebuilder: astroid
+    astroid.scoped_nodes: astroid
+    astroid.test_utils: astroid
+    astroid.transforms: astroid
+    astroid.typing: astroid
+    astroid.util: astroid
+    certifi: certifi
+    certifi.core: certifi
+    chardet: chardet
+    chardet.big5freq: chardet
+    chardet.big5prober: chardet
+    chardet.chardistribution: chardet
+    chardet.charsetgroupprober: chardet
+    chardet.charsetprober: chardet
+    chardet.cli: chardet
+    chardet.cli.chardetect: chardet
+    chardet.codingstatemachine: chardet
+    chardet.compat: chardet
+    chardet.cp949prober: chardet
+    chardet.enums: chardet
+    chardet.escprober: chardet
+    chardet.escsm: chardet
+    chardet.eucjpprober: chardet
+    chardet.euckrfreq: chardet
+    chardet.euckrprober: chardet
+    chardet.euctwfreq: chardet
+    chardet.euctwprober: chardet
+    chardet.gb2312freq: chardet
+    chardet.gb2312prober: chardet
+    chardet.hebrewprober: chardet
+    chardet.jisfreq: chardet
+    chardet.jpcntx: chardet
+    chardet.langbulgarianmodel: chardet
+    chardet.langgreekmodel: chardet
+    chardet.langhebrewmodel: chardet
+    chardet.langhungarianmodel: chardet
+    chardet.langrussianmodel: chardet
+    chardet.langthaimodel: chardet
+    chardet.langturkishmodel: chardet
+    chardet.latin1prober: chardet
+    chardet.mbcharsetprober: chardet
+    chardet.mbcsgroupprober: chardet
+    chardet.mbcssm: chardet
+    chardet.metadata: chardet
+    chardet.metadata.languages: chardet
+    chardet.sbcharsetprober: chardet
+    chardet.sbcsgroupprober: chardet
+    chardet.sjisprober: chardet
+    chardet.universaldetector: chardet
+    chardet.utf8prober: chardet
+    chardet.version: chardet
+    dateutil: python_dateutil
+    dateutil.easter: python_dateutil
+    dateutil.parser: python_dateutil
+    dateutil.parser.isoparser: python_dateutil
+    dateutil.relativedelta: python_dateutil
+    dateutil.rrule: python_dateutil
+    dateutil.tz: python_dateutil
+    dateutil.tz.tz: python_dateutil
+    dateutil.tz.win: python_dateutil
+    dateutil.tzwin: python_dateutil
+    dateutil.utils: python_dateutil
+    dateutil.zoneinfo: python_dateutil
+    dateutil.zoneinfo.rebuild: python_dateutil
+    dill: dill
+    dill.detect: dill
+    dill.logger: dill
+    dill.objtypes: dill
+    dill.pointers: dill
+    dill.session: dill
+    dill.settings: dill
+    dill.source: dill
+    dill.temp: dill
+    idna: idna
+    idna.codec: idna
+    idna.compat: idna
+    idna.core: idna
+    idna.idnadata: idna
+    idna.intranges: idna
+    idna.package_data: idna
+    idna.uts46data: idna
+    isort: isort
+    isort.api: isort
+    isort.comments: isort
+    isort.core: isort
+    isort.deprecated: isort
+    isort.deprecated.finders: isort
+    isort.exceptions: isort
+    isort.files: isort
+    isort.format: isort
+    isort.hooks: isort
+    isort.identify: isort
+    isort.io: isort
+    isort.literal: isort
+    isort.logo: isort
+    isort.main: isort
+    isort.output: isort
+    isort.parse: isort
+    isort.place: isort
+    isort.profiles: isort
+    isort.pylama_isort: isort
+    isort.sections: isort
+    isort.settings: isort
+    isort.setuptools_commands: isort
+    isort.sorting: isort
+    isort.stdlibs: isort
+    isort.stdlibs.all: isort
+    isort.stdlibs.py2: isort
+    isort.stdlibs.py27: isort
+    isort.stdlibs.py3: isort
+    isort.stdlibs.py310: isort
+    isort.stdlibs.py311: isort
+    isort.stdlibs.py36: isort
+    isort.stdlibs.py37: isort
+    isort.stdlibs.py38: isort
+    isort.stdlibs.py39: isort
+    isort.utils: isort
+    isort.wrap: isort
+    isort.wrap_modes: isort
+    lazy_object_proxy: lazy_object_proxy
+    lazy_object_proxy.compat: lazy_object_proxy
+    lazy_object_proxy.simple: lazy_object_proxy
+    lazy_object_proxy.slots: lazy_object_proxy
+    lazy_object_proxy.utils: lazy_object_proxy
+    magic: python_magic
+    magic.compat: python_magic
+    magic.loader: python_magic
+    mccabe: mccabe
+    pathspec: pathspec
+    pathspec.gitignore: pathspec
+    pathspec.pathspec: pathspec
+    pathspec.pattern: pathspec
+    pathspec.patterns: pathspec
+    pathspec.patterns.gitwildmatch: pathspec
+    pathspec.util: pathspec
+    pkg_resources: setuptools
+    pkg_resources.extern: setuptools
+    platformdirs: platformdirs
+    platformdirs.android: platformdirs
+    platformdirs.api: platformdirs
+    platformdirs.macos: platformdirs
+    platformdirs.unix: platformdirs
+    platformdirs.version: platformdirs
+    platformdirs.windows: platformdirs
+    pylint: pylint
+    pylint.checkers: pylint
+    pylint.checkers.async: pylint
+    pylint.checkers.base: pylint
+    pylint.checkers.base.basic_checker: pylint
+    pylint.checkers.base.basic_error_checker: pylint
+    pylint.checkers.base.comparison_checker: pylint
+    pylint.checkers.base.docstring_checker: pylint
+    pylint.checkers.base.name_checker: pylint
+    pylint.checkers.base.name_checker.checker: pylint
+    pylint.checkers.base.name_checker.naming_style: pylint
+    pylint.checkers.base.pass_checker: pylint
+    pylint.checkers.base_checker: pylint
+    pylint.checkers.classes: pylint
+    pylint.checkers.classes.class_checker: pylint
+    pylint.checkers.classes.special_methods_checker: pylint
+    pylint.checkers.deprecated: pylint
+    pylint.checkers.design_analysis: pylint
+    pylint.checkers.dunder_methods: pylint
+    pylint.checkers.ellipsis_checker: pylint
+    pylint.checkers.exceptions: pylint
+    pylint.checkers.format: pylint
+    pylint.checkers.imports: pylint
+    pylint.checkers.lambda_expressions: pylint
+    pylint.checkers.logging: pylint
+    pylint.checkers.mapreduce_checker: pylint
+    pylint.checkers.method_args: pylint
+    pylint.checkers.misc: pylint
+    pylint.checkers.modified_iterating_checker: pylint
+    pylint.checkers.newstyle: pylint
+    pylint.checkers.non_ascii_names: pylint
+    pylint.checkers.raw_metrics: pylint
+    pylint.checkers.refactoring: pylint
+    pylint.checkers.refactoring.implicit_booleaness_checker: pylint
+    pylint.checkers.refactoring.not_checker: pylint
+    pylint.checkers.refactoring.recommendation_checker: pylint
+    pylint.checkers.refactoring.refactoring_checker: pylint
+    pylint.checkers.similar: pylint
+    pylint.checkers.spelling: pylint
+    pylint.checkers.stdlib: pylint
+    pylint.checkers.strings: pylint
+    pylint.checkers.threading_checker: pylint
+    pylint.checkers.typecheck: pylint
+    pylint.checkers.unicode: pylint
+    pylint.checkers.unsupported_version: pylint
+    pylint.checkers.utils: pylint
+    pylint.checkers.variables: pylint
+    pylint.config: pylint
+    pylint.config.argument: pylint
+    pylint.config.arguments_manager: pylint
+    pylint.config.arguments_provider: pylint
+    pylint.config.callback_actions: pylint
+    pylint.config.config_file_parser: pylint
+    pylint.config.config_initialization: pylint
+    pylint.config.configuration_mixin: pylint
+    pylint.config.deprecation_actions: pylint
+    pylint.config.environment_variable: pylint
+    pylint.config.exceptions: pylint
+    pylint.config.find_default_config_files: pylint
+    pylint.config.help_formatter: pylint
+    pylint.config.option: pylint
+    pylint.config.option_manager_mixin: pylint
+    pylint.config.option_parser: pylint
+    pylint.config.options_provider_mixin: pylint
+    pylint.config.utils: pylint
+    pylint.constants: pylint
+    pylint.epylint: pylint
+    pylint.exceptions: pylint
+    pylint.extensions: pylint
+    pylint.extensions.bad_builtin: pylint
+    pylint.extensions.broad_try_clause: pylint
+    pylint.extensions.check_elif: pylint
+    pylint.extensions.code_style: pylint
+    pylint.extensions.comparetozero: pylint
+    pylint.extensions.comparison_placement: pylint
+    pylint.extensions.confusing_elif: pylint
+    pylint.extensions.consider_ternary_expression: pylint
+    pylint.extensions.docparams: pylint
+    pylint.extensions.docstyle: pylint
+    pylint.extensions.empty_comment: pylint
+    pylint.extensions.emptystring: pylint
+    pylint.extensions.eq_without_hash: pylint
+    pylint.extensions.for_any_all: pylint
+    pylint.extensions.mccabe: pylint
+    pylint.extensions.no_self_use: pylint
+    pylint.extensions.overlapping_exceptions: pylint
+    pylint.extensions.private_import: pylint
+    pylint.extensions.redefined_loop_name: pylint
+    pylint.extensions.redefined_variable_type: pylint
+    pylint.extensions.set_membership: pylint
+    pylint.extensions.typing: pylint
+    pylint.extensions.while_used: pylint
+    pylint.graph: pylint
+    pylint.interfaces: pylint
+    pylint.lint: pylint
+    pylint.lint.base_options: pylint
+    pylint.lint.caching: pylint
+    pylint.lint.expand_modules: pylint
+    pylint.lint.message_state_handler: pylint
+    pylint.lint.parallel: pylint
+    pylint.lint.pylinter: pylint
+    pylint.lint.report_functions: pylint
+    pylint.lint.run: pylint
+    pylint.lint.utils: pylint
+    pylint.message: pylint
+    pylint.message.message: pylint
+    pylint.message.message_definition: pylint
+    pylint.message.message_definition_store: pylint
+    pylint.message.message_id_store: pylint
+    pylint.pyreverse: pylint
+    pylint.pyreverse.diadefslib: pylint
+    pylint.pyreverse.diagrams: pylint
+    pylint.pyreverse.dot_printer: pylint
+    pylint.pyreverse.inspector: pylint
+    pylint.pyreverse.main: pylint
+    pylint.pyreverse.mermaidjs_printer: pylint
+    pylint.pyreverse.plantuml_printer: pylint
+    pylint.pyreverse.printer: pylint
+    pylint.pyreverse.printer_factory: pylint
+    pylint.pyreverse.utils: pylint
+    pylint.pyreverse.vcg_printer: pylint
+    pylint.pyreverse.writer: pylint
+    pylint.reporters: pylint
+    pylint.reporters.base_reporter: pylint
+    pylint.reporters.collecting_reporter: pylint
+    pylint.reporters.json_reporter: pylint
+    pylint.reporters.multi_reporter: pylint
+    pylint.reporters.reports_handler_mix_in: pylint
+    pylint.reporters.text: pylint
+    pylint.reporters.ureports: pylint
+    pylint.reporters.ureports.base_writer: pylint
+    pylint.reporters.ureports.nodes: pylint
+    pylint.reporters.ureports.text_writer: pylint
+    pylint.testutils: pylint
+    pylint.testutils.checker_test_case: pylint
+    pylint.testutils.configuration_test: pylint
+    pylint.testutils.constants: pylint
+    pylint.testutils.decorator: pylint
+    pylint.testutils.functional: pylint
+    pylint.testutils.functional.find_functional_tests: pylint
+    pylint.testutils.functional.lint_module_output_update: pylint
+    pylint.testutils.functional.test_file: pylint
+    pylint.testutils.functional_test_file: pylint
+    pylint.testutils.get_test_info: pylint
+    pylint.testutils.global_test_linter: pylint
+    pylint.testutils.lint_module_test: pylint
+    pylint.testutils.output_line: pylint
+    pylint.testutils.pyreverse: pylint
+    pylint.testutils.reporter_for_tests: pylint
+    pylint.testutils.tokenize_str: pylint
+    pylint.testutils.unittest_linter: pylint
+    pylint.testutils.utils: pylint
+    pylint.typing: pylint
+    pylint.utils: pylint
+    pylint.utils.ast_walker: pylint
+    pylint.utils.docs: pylint
+    pylint.utils.file_state: pylint
+    pylint.utils.linterstats: pylint
+    pylint.utils.pragma_parser: pylint
+    pylint.utils.utils: pylint
+    requests: requests
+    requests.adapters: requests
+    requests.api: requests
+    requests.auth: requests
+    requests.certs: requests
+    requests.compat: requests
+    requests.cookies: requests
+    requests.exceptions: requests
+    requests.help: requests
+    requests.hooks: requests
+    requests.models: requests
+    requests.packages: requests
+    requests.sessions: requests
+    requests.status_codes: requests
+    requests.structures: requests
+    requests.utils: requests
+    setuptools: setuptools
+    setuptools.archive_util: setuptools
+    setuptools.build_meta: setuptools
+    setuptools.command: setuptools
+    setuptools.command.alias: setuptools
+    setuptools.command.bdist_egg: setuptools
+    setuptools.command.bdist_rpm: setuptools
+    setuptools.command.build: setuptools
+    setuptools.command.build_clib: setuptools
+    setuptools.command.build_ext: setuptools
+    setuptools.command.build_py: setuptools
+    setuptools.command.develop: setuptools
+    setuptools.command.dist_info: setuptools
+    setuptools.command.easy_install: setuptools
+    setuptools.command.editable_wheel: setuptools
+    setuptools.command.egg_info: setuptools
+    setuptools.command.install: setuptools
+    setuptools.command.install_egg_info: setuptools
+    setuptools.command.install_lib: setuptools
+    setuptools.command.install_scripts: setuptools
+    setuptools.command.py36compat: setuptools
+    setuptools.command.register: setuptools
+    setuptools.command.rotate: setuptools
+    setuptools.command.saveopts: setuptools
+    setuptools.command.sdist: setuptools
+    setuptools.command.setopt: setuptools
+    setuptools.command.test: setuptools
+    setuptools.command.upload: setuptools
+    setuptools.command.upload_docs: setuptools
+    setuptools.config: setuptools
+    setuptools.config.expand: setuptools
+    setuptools.config.pyprojecttoml: setuptools
+    setuptools.config.setupcfg: setuptools
+    setuptools.dep_util: setuptools
+    setuptools.depends: setuptools
+    setuptools.discovery: setuptools
+    setuptools.dist: setuptools
+    setuptools.errors: setuptools
+    setuptools.extension: setuptools
+    setuptools.extern: setuptools
+    setuptools.glob: setuptools
+    setuptools.installer: setuptools
+    setuptools.launch: setuptools
+    setuptools.logging: setuptools
+    setuptools.monkey: setuptools
+    setuptools.msvc: setuptools
+    setuptools.namespaces: setuptools
+    setuptools.package_index: setuptools
+    setuptools.py34compat: setuptools
+    setuptools.sandbox: setuptools
+    setuptools.unicode_utils: setuptools
+    setuptools.version: setuptools
+    setuptools.wheel: setuptools
+    setuptools.windows_support: setuptools
+    six: six
+    tabulate: tabulate
+    tabulate.version: tabulate
+    tomli: tomli
+    tomlkit: tomlkit
+    tomlkit.api: tomlkit
+    tomlkit.container: tomlkit
+    tomlkit.exceptions: tomlkit
+    tomlkit.items: tomlkit
+    tomlkit.parser: tomlkit
+    tomlkit.source: tomlkit
+    tomlkit.toml_char: tomlkit
+    tomlkit.toml_document: tomlkit
+    tomlkit.toml_file: tomlkit
+    typing_extensions: typing_extensions
+    urllib3: urllib3
+    urllib3.connection: urllib3
+    urllib3.connectionpool: urllib3
+    urllib3.contrib: urllib3
+    urllib3.contrib.appengine: urllib3
+    urllib3.contrib.ntlmpool: urllib3
+    urllib3.contrib.pyopenssl: urllib3
+    urllib3.contrib.securetransport: urllib3
+    urllib3.contrib.socks: urllib3
+    urllib3.exceptions: urllib3
+    urllib3.fields: urllib3
+    urllib3.filepost: urllib3
+    urllib3.packages: urllib3
+    urllib3.packages.backports: urllib3
+    urllib3.packages.backports.makefile: urllib3
+    urllib3.packages.six: urllib3
+    urllib3.poolmanager: urllib3
+    urllib3.request: urllib3
+    urllib3.response: urllib3
+    urllib3.util: urllib3
+    urllib3.util.connection: urllib3
+    urllib3.util.proxy: urllib3
+    urllib3.util.queue: urllib3
+    urllib3.util.request: urllib3
+    urllib3.util.response: urllib3
+    urllib3.util.retry: urllib3
+    urllib3.util.ssl_: urllib3
+    urllib3.util.ssl_match_hostname: urllib3
+    urllib3.util.ssltransport: urllib3
+    urllib3.util.timeout: urllib3
+    urllib3.util.url: urllib3
+    urllib3.util.wait: urllib3
+    wrapt: wrapt
+    wrapt.arguments: wrapt
+    wrapt.decorators: wrapt
+    wrapt.importer: wrapt
+    wrapt.wrappers: wrapt
+    yaml: PyYAML
+    yaml.composer: PyYAML
+    yaml.constructor: PyYAML
+    yaml.cyaml: PyYAML
+    yaml.dumper: PyYAML
+    yaml.emitter: PyYAML
+    yaml.error: PyYAML
+    yaml.events: PyYAML
+    yaml.loader: PyYAML
+    yaml.nodes: PyYAML
+    yaml.parser: PyYAML
+    yaml.reader: PyYAML
+    yaml.representer: PyYAML
+    yaml.resolver: PyYAML
+    yaml.scanner: PyYAML
+    yaml.serializer: PyYAML
+    yaml.tokens: PyYAML
+    yamllint: yamllint
+    yamllint.cli: yamllint
+    yamllint.config: yamllint
+    yamllint.linter: yamllint
+    yamllint.parser: yamllint
+    yamllint.rules: yamllint
+    yamllint.rules.braces: yamllint
+    yamllint.rules.brackets: yamllint
+    yamllint.rules.colons: yamllint
+    yamllint.rules.commas: yamllint
+    yamllint.rules.comments: yamllint
+    yamllint.rules.comments_indentation: yamllint
+    yamllint.rules.common: yamllint
+    yamllint.rules.document_end: yamllint
+    yamllint.rules.document_start: yamllint
+    yamllint.rules.empty_lines: yamllint
+    yamllint.rules.empty_values: yamllint
+    yamllint.rules.float_values: yamllint
+    yamllint.rules.hyphens: yamllint
+    yamllint.rules.indentation: yamllint
+    yamllint.rules.key_duplicates: yamllint
+    yamllint.rules.key_ordering: yamllint
+    yamllint.rules.line_length: yamllint
+    yamllint.rules.new_line_at_end_of_file: yamllint
+    yamllint.rules.new_lines: yamllint
+    yamllint.rules.octal_values: yamllint
+    yamllint.rules.quoted_strings: yamllint
+    yamllint.rules.trailing_spaces: yamllint
+    yamllint.rules.truthy: yamllint
+  pip_repository:
+    name: pip
+    use_pip_repository_aliases: true
+integrity: d979738b10adbbaff0884837e4414688990491c6c40f6a25d58b9bb564411477
diff --git a/examples/bzlmod_build_file_generation/lib.py b/examples/bzlmod_build_file_generation/lib.py
new file mode 100644
index 0000000..646c6e8
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/lib.py
@@ -0,0 +1,19 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+from tabulate import tabulate
+
+
+def main(table):
+    return tabulate(table)
diff --git a/examples/bzlmod_build_file_generation/other_module/MODULE.bazel b/examples/bzlmod_build_file_generation/other_module/MODULE.bazel
new file mode 100644
index 0000000..992e120
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/other_module/MODULE.bazel
@@ -0,0 +1,5 @@
+module(
+    name = "other_module",
+)
+
+bazel_dep(name = "rules_python", version = "")
diff --git a/examples/bzlmod_build_file_generation/other_module/WORKSPACE b/examples/bzlmod_build_file_generation/other_module/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/other_module/WORKSPACE
diff --git a/examples/bzlmod_build_file_generation/other_module/other_module/pkg/BUILD.bazel b/examples/bzlmod_build_file_generation/other_module/other_module/pkg/BUILD.bazel
new file mode 100644
index 0000000..9a130e3
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/other_module/other_module/pkg/BUILD.bazel
@@ -0,0 +1,11 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+py_library(
+    name = "lib",
+    srcs = ["lib.py"],
+    data = ["data/data.txt"],
+    visibility = ["//visibility:public"],
+    deps = ["@rules_python//python/runfiles"],
+)
+
+exports_files(["data/data.txt"])
diff --git a/examples/bzlmod_build_file_generation/other_module/other_module/pkg/data/data.txt b/examples/bzlmod_build_file_generation/other_module/other_module/pkg/data/data.txt
new file mode 100644
index 0000000..e975eaf
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/other_module/other_module/pkg/data/data.txt
@@ -0,0 +1 @@
+Hello, other_module!
diff --git a/examples/bzlmod_build_file_generation/other_module/other_module/pkg/lib.py b/examples/bzlmod_build_file_generation/other_module/other_module/pkg/lib.py
new file mode 100644
index 0000000..eaf65fb
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/other_module/other_module/pkg/lib.py
@@ -0,0 +1,27 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+from python.runfiles import runfiles
+
+
+def GetRunfilePathWithCurrentRepository():
+    r = runfiles.Create()
+    own_repo = r.CurrentRepository()
+    # For a non-main repository, the name of the runfiles directory is equal to
+    # the canonical repository name.
+    return r.Rlocation(own_repo + "/other_module/pkg/data/data.txt")
+
+
+def GetRunfilePathWithRepoMapping():
+    return runfiles.Create().Rlocation("other_module/other_module/pkg/data/data.txt")
diff --git a/examples/bzlmod_build_file_generation/requirements.in b/examples/bzlmod_build_file_generation/requirements.in
new file mode 100644
index 0000000..a709195
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/requirements.in
@@ -0,0 +1,6 @@
+requests~=2.25.1
+s3cmd~=2.1.0
+yamllint>=1.28.0
+tabulate~=0.9.0
+pylint~=2.15.5
+python-dateutil>=2.8.2
diff --git a/examples/bzlmod_build_file_generation/requirements_lock.txt b/examples/bzlmod_build_file_generation/requirements_lock.txt
new file mode 100644
index 0000000..2160fe1
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/requirements_lock.txt
@@ -0,0 +1,227 @@
+#
+# This file is autogenerated by pip-compile with Python 3.9
+# by the following command:
+#
+#    bazel run //:requirements.update
+#
+astroid==2.12.13 \
+    --hash=sha256:10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907 \
+    --hash=sha256:1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7
+    # via pylint
+certifi==2022.12.7 \
+    --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \
+    --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18
+    # via requests
+chardet==4.0.0 \
+    --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \
+    --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5
+    # via requests
+dill==0.3.6 \
+    --hash=sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0 \
+    --hash=sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373
+    # via pylint
+idna==2.10 \
+    --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \
+    --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0
+    # via requests
+isort==5.11.4 \
+    --hash=sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6 \
+    --hash=sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b
+    # via pylint
+lazy-object-proxy==1.8.0 \
+    --hash=sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada \
+    --hash=sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d \
+    --hash=sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7 \
+    --hash=sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe \
+    --hash=sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd \
+    --hash=sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c \
+    --hash=sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858 \
+    --hash=sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288 \
+    --hash=sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec \
+    --hash=sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f \
+    --hash=sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891 \
+    --hash=sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c \
+    --hash=sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25 \
+    --hash=sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156 \
+    --hash=sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8 \
+    --hash=sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f \
+    --hash=sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e \
+    --hash=sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0 \
+    --hash=sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b
+    # via astroid
+mccabe==0.7.0 \
+    --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \
+    --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e
+    # via pylint
+pathspec==0.10.3 \
+    --hash=sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6 \
+    --hash=sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6
+    # via yamllint
+platformdirs==2.6.0 \
+    --hash=sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca \
+    --hash=sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e
+    # via pylint
+pylint==2.15.9 \
+    --hash=sha256:18783cca3cfee5b83c6c5d10b3cdb66c6594520ffae61890858fe8d932e1c6b4 \
+    --hash=sha256:349c8cd36aede4d50a0754a8c0218b43323d13d5d88f4b2952ddfe3e169681eb
+    # via -r requirements.in
+python-dateutil==2.8.2 \
+    --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
+    --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
+    # via
+    #   -r requirements.in
+    #   s3cmd
+python-magic==0.4.27 \
+    --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \
+    --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3
+    # via s3cmd
+pyyaml==6.0 \
+    --hash=sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf \
+    --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \
+    --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \
+    --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \
+    --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \
+    --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \
+    --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \
+    --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \
+    --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \
+    --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \
+    --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \
+    --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \
+    --hash=sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782 \
+    --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \
+    --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \
+    --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \
+    --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \
+    --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \
+    --hash=sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1 \
+    --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \
+    --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \
+    --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \
+    --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \
+    --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \
+    --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \
+    --hash=sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d \
+    --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \
+    --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \
+    --hash=sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7 \
+    --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \
+    --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \
+    --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \
+    --hash=sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358 \
+    --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \
+    --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \
+    --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \
+    --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \
+    --hash=sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f \
+    --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \
+    --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5
+    # via yamllint
+requests==2.25.1 \
+    --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \
+    --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e
+    # via -r requirements.in
+s3cmd==2.1.0 \
+    --hash=sha256:49cd23d516b17974b22b611a95ce4d93fe326feaa07320bd1d234fed68cbccfa \
+    --hash=sha256:966b0a494a916fc3b4324de38f089c86c70ee90e8e1cae6d59102103a4c0cc03
+    # via -r requirements.in
+setuptools==65.6.3 \
+    --hash=sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54 \
+    --hash=sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75
+    # via yamllint
+six==1.16.0 \
+    --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
+    --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
+    # via python-dateutil
+tabulate==0.9.0 \
+    --hash=sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c \
+    --hash=sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f
+    # via -r requirements.in
+tomli==2.0.1 \
+    --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
+    --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
+    # via pylint
+tomlkit==0.11.6 \
+    --hash=sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b \
+    --hash=sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73
+    # via pylint
+typing-extensions==4.4.0 \
+    --hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \
+    --hash=sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e
+    # via
+    #   astroid
+    #   pylint
+urllib3==1.26.13 \
+    --hash=sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc \
+    --hash=sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8
+    # via requests
+wrapt==1.14.1 \
+    --hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \
+    --hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \
+    --hash=sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4 \
+    --hash=sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2 \
+    --hash=sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656 \
+    --hash=sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3 \
+    --hash=sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff \
+    --hash=sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310 \
+    --hash=sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a \
+    --hash=sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57 \
+    --hash=sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069 \
+    --hash=sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383 \
+    --hash=sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe \
+    --hash=sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87 \
+    --hash=sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d \
+    --hash=sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b \
+    --hash=sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907 \
+    --hash=sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f \
+    --hash=sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0 \
+    --hash=sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28 \
+    --hash=sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1 \
+    --hash=sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853 \
+    --hash=sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc \
+    --hash=sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3 \
+    --hash=sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3 \
+    --hash=sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164 \
+    --hash=sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1 \
+    --hash=sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c \
+    --hash=sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1 \
+    --hash=sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7 \
+    --hash=sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1 \
+    --hash=sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320 \
+    --hash=sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed \
+    --hash=sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1 \
+    --hash=sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248 \
+    --hash=sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c \
+    --hash=sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456 \
+    --hash=sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77 \
+    --hash=sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef \
+    --hash=sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1 \
+    --hash=sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7 \
+    --hash=sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86 \
+    --hash=sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4 \
+    --hash=sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d \
+    --hash=sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d \
+    --hash=sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8 \
+    --hash=sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5 \
+    --hash=sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471 \
+    --hash=sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00 \
+    --hash=sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68 \
+    --hash=sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3 \
+    --hash=sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d \
+    --hash=sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735 \
+    --hash=sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d \
+    --hash=sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569 \
+    --hash=sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7 \
+    --hash=sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59 \
+    --hash=sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5 \
+    --hash=sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb \
+    --hash=sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b \
+    --hash=sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f \
+    --hash=sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462 \
+    --hash=sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015 \
+    --hash=sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af
+    # via astroid
+yamllint==1.28.0 \
+    --hash=sha256:89bb5b5ac33b1ade059743cf227de73daa34d5e5a474b06a5e17fc16583b0cf2 \
+    --hash=sha256:9e3d8ddd16d0583214c5fdffe806c9344086721f107435f68bad990e5a88826b
+    # via -r requirements.in
diff --git a/examples/bzlmod_build_file_generation/requirements_windows.txt b/examples/bzlmod_build_file_generation/requirements_windows.txt
new file mode 100644
index 0000000..06cfdc3
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/requirements_windows.txt
@@ -0,0 +1,231 @@
+#
+# This file is autogenerated by pip-compile with Python 3.9
+# by the following command:
+#
+#    bazel run //:requirements.update
+#
+astroid==2.12.13 \
+    --hash=sha256:10e0ad5f7b79c435179d0d0f0df69998c4eef4597534aae44910db060baeb907 \
+    --hash=sha256:1493fe8bd3dfd73dc35bd53c9d5b6e49ead98497c47b2307662556a5692d29d7
+    # via pylint
+certifi==2022.12.7 \
+    --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \
+    --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18
+    # via requests
+chardet==4.0.0 \
+    --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \
+    --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5
+    # via requests
+colorama==0.4.6 \
+    --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \
+    --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6
+    # via pylint
+dill==0.3.6 \
+    --hash=sha256:a07ffd2351b8c678dfc4a856a3005f8067aea51d6ba6c700796a4d9e280f39f0 \
+    --hash=sha256:e5db55f3687856d8fbdab002ed78544e1c4559a130302693d839dfe8f93f2373
+    # via pylint
+idna==2.10 \
+    --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \
+    --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0
+    # via requests
+isort==5.11.4 \
+    --hash=sha256:6db30c5ded9815d813932c04c2f85a360bcdd35fed496f4d8f35495ef0a261b6 \
+    --hash=sha256:c033fd0edb91000a7f09527fe5c75321878f98322a77ddcc81adbd83724afb7b
+    # via pylint
+lazy-object-proxy==1.8.0 \
+    --hash=sha256:0c1c7c0433154bb7c54185714c6929acc0ba04ee1b167314a779b9025517eada \
+    --hash=sha256:14010b49a2f56ec4943b6cf925f597b534ee2fe1f0738c84b3bce0c1a11ff10d \
+    --hash=sha256:4e2d9f764f1befd8bdc97673261b8bb888764dfdbd7a4d8f55e4fbcabb8c3fb7 \
+    --hash=sha256:4fd031589121ad46e293629b39604031d354043bb5cdf83da4e93c2d7f3389fe \
+    --hash=sha256:5b51d6f3bfeb289dfd4e95de2ecd464cd51982fe6f00e2be1d0bf94864d58acd \
+    --hash=sha256:6850e4aeca6d0df35bb06e05c8b934ff7c533734eb51d0ceb2d63696f1e6030c \
+    --hash=sha256:6f593f26c470a379cf7f5bc6db6b5f1722353e7bf937b8d0d0b3fba911998858 \
+    --hash=sha256:71d9ae8a82203511a6f60ca5a1b9f8ad201cac0fc75038b2dc5fa519589c9288 \
+    --hash=sha256:7e1561626c49cb394268edd00501b289053a652ed762c58e1081224c8d881cec \
+    --hash=sha256:8f6ce2118a90efa7f62dd38c7dbfffd42f468b180287b748626293bf12ed468f \
+    --hash=sha256:ae032743794fba4d171b5b67310d69176287b5bf82a21f588282406a79498891 \
+    --hash=sha256:afcaa24e48bb23b3be31e329deb3f1858f1f1df86aea3d70cb5c8578bfe5261c \
+    --hash=sha256:b70d6e7a332eb0217e7872a73926ad4fdc14f846e85ad6749ad111084e76df25 \
+    --hash=sha256:c219a00245af0f6fa4e95901ed28044544f50152840c5b6a3e7b2568db34d156 \
+    --hash=sha256:ce58b2b3734c73e68f0e30e4e725264d4d6be95818ec0a0be4bb6bf9a7e79aa8 \
+    --hash=sha256:d176f392dbbdaacccf15919c77f526edf11a34aece58b55ab58539807b85436f \
+    --hash=sha256:e20bfa6db17a39c706d24f82df8352488d2943a3b7ce7d4c22579cb89ca8896e \
+    --hash=sha256:eac3a9a5ef13b332c059772fd40b4b1c3d45a3a2b05e33a361dee48e54a4dad0 \
+    --hash=sha256:eb329f8d8145379bf5dbe722182410fe8863d186e51bf034d2075eb8d85ee25b
+    # via astroid
+mccabe==0.7.0 \
+    --hash=sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325 \
+    --hash=sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e
+    # via pylint
+pathspec==0.10.3 \
+    --hash=sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6 \
+    --hash=sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6
+    # via yamllint
+platformdirs==2.6.0 \
+    --hash=sha256:1a89a12377800c81983db6be069ec068eee989748799b946cce2a6e80dcc54ca \
+    --hash=sha256:b46ffafa316e6b83b47489d240ce17173f123a9b9c83282141c3daf26ad9ac2e
+    # via pylint
+pylint==2.15.9 \
+    --hash=sha256:18783cca3cfee5b83c6c5d10b3cdb66c6594520ffae61890858fe8d932e1c6b4 \
+    --hash=sha256:349c8cd36aede4d50a0754a8c0218b43323d13d5d88f4b2952ddfe3e169681eb
+    # via -r requirements.in
+python-dateutil==2.8.2 \
+    --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \
+    --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9
+    # via
+    #   -r requirements.in
+    #   s3cmd
+python-magic==0.4.27 \
+    --hash=sha256:c1ba14b08e4a5f5c31a302b7721239695b2f0f058d125bd5ce1ee36b9d9d3c3b \
+    --hash=sha256:c212960ad306f700aa0d01e5d7a325d20548ff97eb9920dcd29513174f0294d3
+    # via s3cmd
+pyyaml==6.0 \
+    --hash=sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf \
+    --hash=sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293 \
+    --hash=sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b \
+    --hash=sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57 \
+    --hash=sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b \
+    --hash=sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4 \
+    --hash=sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07 \
+    --hash=sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba \
+    --hash=sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9 \
+    --hash=sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287 \
+    --hash=sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513 \
+    --hash=sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0 \
+    --hash=sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782 \
+    --hash=sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \
+    --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \
+    --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \
+    --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \
+    --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \
+    --hash=sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1 \
+    --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \
+    --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \
+    --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \
+    --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \
+    --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \
+    --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \
+    --hash=sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d \
+    --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \
+    --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \
+    --hash=sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7 \
+    --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \
+    --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \
+    --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \
+    --hash=sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358 \
+    --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \
+    --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \
+    --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \
+    --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \
+    --hash=sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f \
+    --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \
+    --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5
+    # via yamllint
+requests==2.25.1 \
+    --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \
+    --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e
+    # via -r requirements.in
+s3cmd==2.1.0 \
+    --hash=sha256:49cd23d516b17974b22b611a95ce4d93fe326feaa07320bd1d234fed68cbccfa \
+    --hash=sha256:966b0a494a916fc3b4324de38f089c86c70ee90e8e1cae6d59102103a4c0cc03
+    # via -r requirements.in
+setuptools==65.6.3 \
+    --hash=sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54 \
+    --hash=sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75
+    # via yamllint
+six==1.16.0 \
+    --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
+    --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
+    # via python-dateutil
+tabulate==0.9.0 \
+    --hash=sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c \
+    --hash=sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f
+    # via -r requirements.in
+tomli==2.0.1 \
+    --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
+    --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
+    # via pylint
+tomlkit==0.11.6 \
+    --hash=sha256:07de26b0d8cfc18f871aec595fda24d95b08fef89d147caa861939f37230bf4b \
+    --hash=sha256:71b952e5721688937fb02cf9d354dbcf0785066149d2855e44531ebdd2b65d73
+    # via pylint
+typing-extensions==4.4.0 \
+    --hash=sha256:1511434bb92bf8dd198c12b1cc812e800d4181cfcb867674e0f8279cc93087aa \
+    --hash=sha256:16fa4864408f655d35ec496218b85f79b3437c829e93320c7c9215ccfd92489e
+    # via
+    #   astroid
+    #   pylint
+urllib3==1.26.13 \
+    --hash=sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc \
+    --hash=sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8
+    # via requests
+wrapt==1.14.1 \
+    --hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \
+    --hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \
+    --hash=sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4 \
+    --hash=sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2 \
+    --hash=sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656 \
+    --hash=sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3 \
+    --hash=sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff \
+    --hash=sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310 \
+    --hash=sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a \
+    --hash=sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57 \
+    --hash=sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069 \
+    --hash=sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383 \
+    --hash=sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe \
+    --hash=sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87 \
+    --hash=sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d \
+    --hash=sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b \
+    --hash=sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907 \
+    --hash=sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f \
+    --hash=sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0 \
+    --hash=sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28 \
+    --hash=sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1 \
+    --hash=sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853 \
+    --hash=sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc \
+    --hash=sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3 \
+    --hash=sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3 \
+    --hash=sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164 \
+    --hash=sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1 \
+    --hash=sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c \
+    --hash=sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1 \
+    --hash=sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7 \
+    --hash=sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1 \
+    --hash=sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320 \
+    --hash=sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed \
+    --hash=sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1 \
+    --hash=sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248 \
+    --hash=sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c \
+    --hash=sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456 \
+    --hash=sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77 \
+    --hash=sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef \
+    --hash=sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1 \
+    --hash=sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7 \
+    --hash=sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86 \
+    --hash=sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4 \
+    --hash=sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d \
+    --hash=sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d \
+    --hash=sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8 \
+    --hash=sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5 \
+    --hash=sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471 \
+    --hash=sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00 \
+    --hash=sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68 \
+    --hash=sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3 \
+    --hash=sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d \
+    --hash=sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735 \
+    --hash=sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d \
+    --hash=sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569 \
+    --hash=sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7 \
+    --hash=sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59 \
+    --hash=sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5 \
+    --hash=sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb \
+    --hash=sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b \
+    --hash=sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f \
+    --hash=sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462 \
+    --hash=sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015 \
+    --hash=sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af
+    # via astroid
+yamllint==1.28.0 \
+    --hash=sha256:89bb5b5ac33b1ade059743cf227de73daa34d5e5a474b06a5e17fc16583b0cf2 \
+    --hash=sha256:9e3d8ddd16d0583214c5fdffe806c9344086721f107435f68bad990e5a88826b
+    # via -r requirements.in
diff --git a/examples/bzlmod_build_file_generation/runfiles/BUILD.bazel b/examples/bzlmod_build_file_generation/runfiles/BUILD.bazel
new file mode 100644
index 0000000..3503ac3
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/runfiles/BUILD.bazel
@@ -0,0 +1,19 @@
+load("@rules_python//python:defs.bzl", "py_test")
+
+# gazelle:ignore
+py_test(
+    name = "runfiles_test",
+    srcs = ["runfiles_test.py"],
+    data = [
+        "data/data.txt",
+        "@our_other_module//other_module/pkg:data/data.txt",
+    ],
+    env = {
+        "DATA_RLOCATIONPATH": "$(rlocationpath data/data.txt)",
+        "OTHER_MODULE_DATA_RLOCATIONPATH": "$(rlocationpath @our_other_module//other_module/pkg:data/data.txt)",
+    },
+    deps = [
+        "@our_other_module//other_module/pkg:lib",
+        "@rules_python//python/runfiles",
+    ],
+)
diff --git a/examples/bzlmod_build_file_generation/runfiles/data/data.txt b/examples/bzlmod_build_file_generation/runfiles/data/data.txt
new file mode 100644
index 0000000..fb17e0d
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/runfiles/data/data.txt
@@ -0,0 +1 @@
+Hello, example_bzlmod!
diff --git a/examples/bzlmod_build_file_generation/runfiles/runfiles_test.py b/examples/bzlmod_build_file_generation/runfiles/runfiles_test.py
new file mode 100644
index 0000000..a588040
--- /dev/null
+++ b/examples/bzlmod_build_file_generation/runfiles/runfiles_test.py
@@ -0,0 +1,64 @@
+# Copyright 2023 The Bazel Authors. All rights reserved.
+#
+# 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.
+
+import os
+import unittest
+
+from other_module.pkg import lib
+
+from python.runfiles import runfiles
+
+
+class RunfilesTest(unittest.TestCase):
+    # """Unit tests for `runfiles.Runfiles`."""
+    def testCurrentRepository(self):
+        self.assertEqual(runfiles.Create().CurrentRepository(), "")
+
+    def testRunfilesWithRepoMapping(self):
+        data_path = runfiles.Create().Rlocation("example_bzlmod_build_file_generation/runfiles/data/data.txt")
+        with open(data_path) as f:
+            self.assertEqual(f.read().strip(), "Hello, example_bzlmod!")
+
+    def testRunfileWithRlocationpath(self):
+        data_rlocationpath = os.getenv("DATA_RLOCATIONPATH")
+        data_path = runfiles.Create().Rlocation(data_rlocationpath)
+        with open(data_path) as f:
+            self.assertEqual(f.read().strip(), "Hello, example_bzlmod!")
+
+    def testRunfileInOtherModuleWithOurRepoMapping(self):
+        data_path = runfiles.Create().Rlocation(
+            "our_other_module/other_module/pkg/data/data.txt"
+        )
+        with open(data_path) as f:
+            self.assertEqual(f.read().strip(), "Hello, other_module!")
+
+    def testRunfileInOtherModuleWithItsRepoMapping(self):
+        data_path = lib.GetRunfilePathWithRepoMapping()
+        with open(data_path) as f:
+            self.assertEqual(f.read().strip(), "Hello, other_module!")
+
+    def testRunfileInOtherModuleWithCurrentRepository(self):
+        data_path = lib.GetRunfilePathWithCurrentRepository()
+        with open(data_path) as f:
+            self.assertEqual(f.read().strip(), "Hello, other_module!")
+
+    def testRunfileInOtherModuleWithRlocationpath(self):
+        data_rlocationpath = os.getenv("OTHER_MODULE_DATA_RLOCATIONPATH")
+        data_path = runfiles.Create().Rlocation(data_rlocationpath)
+        with open(data_path) as f:
+            self.assertEqual(f.read().strip(), "Hello, other_module!")
+
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/gazelle/README.md b/gazelle/README.md
index 2cd3876..e36f3a3 100644
--- a/gazelle/README.md
+++ b/gazelle/README.md
@@ -15,7 +15,10 @@
 
 ## Example
 
-We have an example of using Gazelle with Python located [here](https://github.com/bazelbuild/rules_python/tree/main/examples/build_file_generation).
+We have an example of using Gazelle with Python located [here](https://github.com/bazelbuild/rules_python/tree/main/examples/bzlmod).
+A fully-working example without using bzlmod is in [`examples/build_file_generation`](../examples/build_file_generation).
+
+The following documentation covers using bzlmod.
 
 ## Adding Gazelle to your project
 
@@ -40,10 +43,37 @@
 
 # The following stanza defines the dependency rules_python.
 bazel_dep(name = "gazelle", version = "0.30.0", repo_name = "bazel_gazelle")
-```
-You will also need to do the other usual configuration for `rules_python` in your
-`MODULE.bazel` file.
 
+# Import the python repositories generated by the given module extension into the scope of the current module.
+use_repo(python, "python3_9")
+use_repo(python, "python3_9_toolchains")
+
+# Register an already-defined toolchain so that Bazel can use it during toolchain resolution.
+register_toolchains(
+    "@python3_9_toolchains//:all",
+)
+
+# Use the pip extension
+pip = use_extension("@rules_python//python:extensions.bzl", "pip")
+
+# Use the extension to call the `pip_repository` rule that invokes `pip`, with `incremental` set.
+# Accepts a locked/compiled requirements file and installs the dependencies listed within.
+# Those dependencies become available in a generated `requirements.bzl` file.
+# You can instead check this `requirements.bzl` file into your repo.
+# Because this project has different requirements for windows vs other
+# operating systems, we have requirements for each.
+pip.parse(
+    name = "pip",
+    # When using gazelle you must use set the following flag
+    # in order for the generation of gazelle dependency resolution.
+    incompatible_generate_aliases = True,
+    requirements_lock = "//:requirements_lock.txt",
+    requirements_windows = "//:requirements_windows.txt",
+)
+
+# Imports the pip toolchain generated by the given module extension into the scope of the current module.
+use_repo(pip, "pip")
+```
 Next, we'll fetch metadata about your Python dependencies, so that gazelle can
 determine which package a given import statement comes from. This is provided
 by the `modules_mapping` rule. We'll make a target for consuming this
@@ -86,6 +116,9 @@
     # This should point to wherever we declare our python dependencies
     # (the same as what we passed to the modules_mapping rule in WORKSPACE)
     requirements = "//:requirements_lock.txt",
+    # NOTE: we can use this flag in order to make our setup compatible with
+    # bzlmod.
+    use_pip_repository_aliases = True,
 )
 ```
 
@@ -112,8 +145,6 @@
 That's it, now you can finally run `bazel run //:gazelle` anytime
 you edit Python code, and it should update your `BUILD` files correctly.
 
-A fully-working example is in [`examples/build_file_generation`](../examples/build_file_generation).
-
 ## Usage
 
 Gazelle is non-destructive.
diff --git a/python/extensions.bzl b/python/extensions.bzl
index 2b0c188..3bcbb50 100644
--- a/python/extensions.bzl
+++ b/python/extensions.bzl
@@ -19,18 +19,24 @@
 load("@rules_python//python/pip_install:repositories.bzl", "pip_install_dependencies")
 load("@rules_python//python/pip_install:requirements_parser.bzl", parse_requirements = "parse")
 load("@rules_python//python/private:coverage_deps.bzl", "install_coverage_deps")
+load("@rules_python//python/private:toolchains_repo.bzl", "get_host_os_arch", "get_host_platform")
 
 def _python_impl(module_ctx):
     for mod in module_ctx.modules:
-        for attr in mod.tags.toolchain:
+        for toolchain_attr in mod.tags.toolchain:
             python_register_toolchains(
-                name = attr.name,
-                python_version = attr.python_version,
+                name = toolchain_attr.name,
+                python_version = toolchain_attr.python_version,
                 bzlmod = True,
                 # Toolchain registration in bzlmod is done in MODULE file
                 register_toolchains = False,
-                register_coverage_tool = attr.configure_coverage_tool,
-                ignore_root_user_error = attr.ignore_root_user_error,
+                register_coverage_tool = toolchain_attr.configure_coverage_tool,
+                ignore_root_user_error = toolchain_attr.ignore_root_user_error,
+            )
+            host_hub_name = toolchain_attr.name + "_host_interpreter"
+            _host_hub(
+                name = host_hub_name,
+                user_repo_prefix = toolchain_attr.name,
             )
 
 python = module_extension(
@@ -127,3 +133,89 @@
         "parse": tag_class(attrs = _pip_parse_ext_attrs()),
     },
 )
+
+# This function allows us to build the label name of a label
+# that is not passed into the current context.
+# The module_label is the key element that is passed in.
+# This value provides the root location of the labels
+# See https://bazel.build/external/extension#repository_names_and_visibility
+def _repo_mapped_label(module_label, extension_name, apparent):
+    """Construct a canonical repo label accounting for repo mapping.
+
+    Args:
+        module_label: Label object of the module hosting the extension; see
+          "_module" implicit attribute.
+        extension_name: str, name of the extension that created the repo in `apparent`.
+        apparent: str, a repo-qualified target string, but without the "@". e.g.
+          "python38_x86_linux//:python". The repo name should use the apparent
+          name used by the extension named by `ext_name` (i.e. the value of the
+          `name` arg the extension passes to repository rules)
+    """
+    return Label("@@{module}~{extension_name}~{apparent}".format(
+        module = module_label.workspace_name,
+        extension_name = extension_name,
+        apparent = apparent,
+    ))
+
+# We are doing some bazel stuff here that could use an explanation.
+# The basis of this function is that we need to create a symlink to
+# the python binary that exists in a different repo that we know is
+# setup by rules_python.
+#
+# We are building a Label like
+# @@rules_python~override~python~python3_x86_64-unknown-linux-gnu//:python
+# and then the function creates a symlink named python to that Label.
+# The tricky part is the "~override~" part can't be known in advance
+# and will change depending on how and what version of rules_python
+# is used. To figure that part out, an implicit attribute is used to
+# resolve the module's current name (see "_module" attribute)
+#
+# We are building the Label name dynamically, and can do this even
+# though the Label is not passed into this function.  If we choose
+# not do this a user would have to write another 16 lines
+# of configuration code, but we are able to save them that work
+# because we know how rules_python works internally.  We are using
+# functions from private:toolchains_repo.bzl which is where the repo
+# is being built. The repo name differs between host OS and platforms
+# and the functions from toolchains_repo gives us this functions that
+# information.
+def _host_hub_impl(repo_ctx):
+    # Intentionally empty; this is only intended to be used by repository
+    # rules, which don't process build file contents.
+    repo_ctx.file("BUILD.bazel", "")
+
+    # The two get_ functions we use are also utilized when building
+    # the repositories for the different interpreters.
+    (os, arch) = get_host_os_arch(repo_ctx)
+    host_platform = "{}_{}//:python".format(
+        repo_ctx.attr.user_repo_prefix,
+        get_host_platform(os, arch),
+    )
+
+    # the attribute is set to attr.label(default = "//:_"), which
+    # provides us the resolved, canonical, prefix for the module's repos.
+    # The extension_name "python" is determined by the
+    # name bound to the module_extension() call.
+    # We then have the OS and platform specific name of the python
+    # interpreter.
+    label = _repo_mapped_label(repo_ctx.attr._module, "python", host_platform)
+
+    # create the symlink in order to set the interpreter for pip.
+    repo_ctx.symlink(label, "python")
+
+# We use this rule to set the pip interpreter target when using different operating
+# systems with the same project
+_host_hub = repository_rule(
+    implementation = _host_hub_impl,
+    local = True,
+    attrs = {
+        "user_repo_prefix": attr.string(
+            mandatory = True,
+            doc = """\
+The prefix to create the repository name.  Usually the name you used when you created the 
+Python toolchain.
+""",
+        ),
+        "_module": attr.label(default = "//:_"),
+    },
+)
diff --git a/python/pip_install/pip_repository.bzl b/python/pip_install/pip_repository.bzl
index f58c2af..032f23f 100644
--- a/python/pip_install/pip_repository.bzl
+++ b/python/pip_install/pip_repository.bzl
@@ -559,7 +559,7 @@
 If you are using a custom python interpreter built by another repository rule,
 use this attribute to specify its BUILD target. This allows pip_repository to invoke
 pip using the same interpreter as your toolchain. If set, takes precedence over
-python_interpreter.
+python_interpreter. An example value: "@python3_x86_64-unknown-linux-gnu//:python".
 """,
     ),
     "quiet": attr.bool(