diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 03687d442c..b468970fbb 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -1,41 +1,565 @@ +# 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. + --- buildifier: - version: latest - # keep this argument in sync with .pre-commit-config.yaml + # keep these arguments in sync with .pre-commit-config.yaml + # Use a specific version to avoid skew issues when new versions are released. + version: 6.1.0 warnings: "all" -all_targets: &all_targets +.minimum_supported_version: &minimum_supported_version + # For testing minimum supported version. + # NOTE: Keep in sync with //:version.bzl + bazel: 5.4.0 +.minimum_supported_bzlmod_version: &minimum_supported_bzlmod_version + bazel: 6.0.0 # test minimum supported version of bazel for bzlmod tests +.reusable_config: &reusable_config build_targets: + - "--" - "..." # As a regression test for #225, check that wheel targets still build when # their package path is qualified with the repo name. - "@rules_python//examples/wheel/..." - # We control Bazel version in integration tests, so we don't need USE_BAZEL_VERSION for tests. - skip_use_bazel_version_for_test: true + build_flags: + - "--keep_going" test_targets: + - "--" - "..." -platforms: - ubuntu1804: - <<: *all_targets + test_flags: + - "--test_tag_filters=-integration-test" +.common_bzlmod_flags: &common_bzlmod_flags + test_flags: + - "--experimental_enable_bzlmod" + build_flags: + - "--experimental_enable_bzlmod" +.reusable_build_test_all: &reusable_build_test_all + build_targets: ["..."] + 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 + - //tests:my_lib_3_11_test + - //tests:my_lib_3_8_test + - //tests:my_lib_3_9_test + - //tests:my_lib_default_test + - //tests:version_3_10_test + - //tests:version_3_11_test + - //tests:version_3_8_test + - //tests:version_3_9_test + - //tests:version_default_test +tasks: + gazelle_extension_min: + <<: *minimum_supported_version + name: Test the Gazelle extension using minimum supported Bazel version + platform: ubuntu2004 + build_targets: ["//..."] + test_targets: ["//..."] + working_directory: gazelle + gazelle_extension: + name: Test the Gazelle extension + platform: ubuntu2004 + build_targets: ["//..."] + test_targets: ["//..."] + working_directory: gazelle + gazelle_extension_bzlmod: + <<: *common_bzlmod_flags + name: Test the Gazelle extension under bzlmod + platform: ubuntu2004 + build_targets: ["//..."] + test_targets: ["//..."] + working_directory: gazelle + + ubuntu_min: + <<: *minimum_supported_version + <<: *reusable_config + name: Default test on Ubuntu using minimum supported Bazel version + platform: ubuntu2004 + ubuntu: + <<: *reusable_config + name: Default test on Ubuntu + platform: ubuntu2004 + debian: + <<: *reusable_config + name: Default test on Debian + platform: debian11 macos: - <<: *all_targets + <<: *reusable_config + name: Default test on macOS + platform: macos windows: - build_targets: - - "--" # Allows negative patterns; hack for https://github.com/bazelbuild/continuous-integration/pull/245 - - "..." - # Gazelle is not fully Windows compatible: https://github.com/bazelbuild/bazel-gazelle/issues/1122 - - "-//gazelle/..." - # As a regression test for #225, check that wheel targets still build when - # their package path is qualified with the repo name. - - "@rules_python//examples/wheel/..." - # We control Bazel version in integration tests, so we don't need USE_BAZEL_VERSION for tests. - skip_use_bazel_version_for_test: true - test_targets: - - "--" # Allows negative patterns; hack for https://github.com/bazelbuild/continuous-integration/pull/245 - - "..." - # Gazelle is not fully Windows compatible: https://github.com/bazelbuild/bazel-gazelle/issues/1122 - - "-//gazelle/..." - # The dependencies needed for this test are not cross-platform: https://github.com/bazelbuild/rules_python/issues/260 - - "-//tests:pip_repository_entry_points_example" - - "-//tests:pip_deps_example" + <<: *reusable_config + name: Default test on Windows + platform: windows test_flags: - - "--test_tag_filters=-fix-windows" \ No newline at end of file + - "--test_tag_filters=-integration-test,-fix-windows" + + rbe_min: + <<: *minimum_supported_version + <<: *reusable_config + name: Test on RBE using minimum supported Bazel version + platform: rbe_ubuntu1604 + test_flags: + - "--test_tag_filters=-integration-test,-acceptance-test" + rbe: + <<: *reusable_config + name: Test on RBE + platform: rbe_ubuntu1604 + test_flags: + - "--test_tag_filters=-integration-test,-acceptance-test" + + integration_test_build_file_generation_ubuntu_minimum_supported: + <<: *minimum_supported_version + <<: *reusable_build_test_all + name: build_file_generation integration tests on Ubuntu using minimum supported Bazel version + working_directory: examples/build_file_generation + platform: ubuntu2004 + integration_test_build_file_generation_ubuntu: + <<: *reusable_build_test_all + name: build_file_generation integration tests on Ubuntu + working_directory: examples/build_file_generation + platform: ubuntu2004 + integration_test_build_file_generation_debian: + <<: *reusable_build_test_all + name: build_file_generation integration tests on Debian + working_directory: examples/build_file_generation + platform: debian11 + integration_test_build_file_generation_macos: + <<: *reusable_build_test_all + name: build_file_generation integration tests on macOS + working_directory: examples/build_file_generation + platform: macos + integration_test_build_file_generation_windows: + <<: *reusable_build_test_all + name: build_file_generation integration tests on Windows + working_directory: examples/build_file_generation + platform: windows + + integration_test_bzlmod_ubuntu_min: + <<: *minimum_supported_bzlmod_version + <<: *reusable_build_test_all + <<: *coverage_targets_example_bzlmod + name: bzlmod integration tests on Ubuntu using minimum supported Bazel version + working_directory: examples/bzlmod + platform: ubuntu2004 + integration_test_bzlmod_ubuntu: + <<: *reusable_build_test_all + <<: *coverage_targets_example_bzlmod + name: bzlmod integration tests on Ubuntu + working_directory: examples/bzlmod + platform: ubuntu2004 + integration_test_bzlmod_debian: + <<: *reusable_build_test_all + <<: *coverage_targets_example_bzlmod + name: bzlmod integration tests on Debian + working_directory: examples/bzlmod + platform: debian11 + integration_test_bzlmod_macos: + <<: *reusable_build_test_all + <<: *coverage_targets_example_bzlmod + name: bzlmod integration tests on macOS + working_directory: examples/bzlmod + platform: macos + integration_test_bzlmod_windows: + <<: *reusable_build_test_all + # coverage is not supported on Windows + name: bzlmod integration tests on Windows + 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 + name: multi_python_versions integration tests on Ubuntu using minimum supported Bazel version + working_directory: examples/multi_python_versions + platform: ubuntu2004 + integration_test_multi_python_versions_ubuntu: + <<: *reusable_build_test_all + <<: *coverage_targets_example_multi_python + name: multi_python_versions integration tests on Ubuntu + working_directory: examples/multi_python_versions + platform: ubuntu2004 + integration_test_multi_python_versions_debian: + <<: *reusable_build_test_all + <<: *coverage_targets_example_multi_python + name: multi_python_versions integration tests on Debian + working_directory: examples/multi_python_versions + platform: debian11 + integration_test_multi_python_versions_macos: + <<: *reusable_build_test_all + <<: *coverage_targets_example_multi_python + name: multi_python_versions integration tests on macOS + working_directory: examples/multi_python_versions + platform: macos + integration_test_multi_python_versions_windows: + <<: *reusable_build_test_all + # coverage is not supported on Windows + name: multi_python_versions integration tests on Windows + working_directory: examples/multi_python_versions + platform: windows + + integration_test_pip_install_ubuntu_min: + <<: *minimum_supported_version + <<: *reusable_build_test_all + name: pip_install integration tests on Ubuntu using minimum supported Bazel version + working_directory: examples/pip_install + platform: ubuntu2004 + integration_test_pip_install_ubuntu: + <<: *reusable_build_test_all + name: pip_install integration tests on Ubuntu + working_directory: examples/pip_install + platform: ubuntu2004 + integration_test_pip_install_debian: + <<: *reusable_build_test_all + name: pip_install integration tests on Debian + working_directory: examples/pip_install + platform: debian11 + integration_test_pip_install_macos: + <<: *reusable_build_test_all + name: pip_install integration tests on macOS + working_directory: examples/pip_install + platform: macos + integration_test_pip_install_windows: + <<: *reusable_build_test_all + name: pip_install integration tests on Windows + working_directory: examples/pip_install + platform: windows + + integration_test_pip_parse_ubuntu_min: + <<: *minimum_supported_version + <<: *reusable_build_test_all + name: pip_parse integration tests on Ubuntu using minimum supported Bazel version + working_directory: examples/pip_parse + platform: ubuntu2004 + integration_test_pip_parse_ubuntu: + <<: *reusable_build_test_all + name: pip_parse integration tests on Ubuntu + working_directory: examples/pip_parse + platform: ubuntu2004 + integration_test_pip_parse_debian: + <<: *reusable_build_test_all + name: pip_parse integration tests on Debian + working_directory: examples/pip_parse + platform: debian11 + integration_test_pip_parse_macos: + <<: *reusable_build_test_all + name: pip_parse integration tests on macOS + working_directory: examples/pip_parse + platform: macos + integration_test_pip_parse_windows: + <<: *reusable_build_test_all + name: pip_parse integration tests on Windows + working_directory: examples/pip_parse + platform: windows + + integration_test_pip_parse_vendored_ubuntu_min: + <<: *minimum_supported_version + <<: *reusable_build_test_all + name: pip_parse_vendored integration tests on Ubuntu using minimum supported Bazel version + working_directory: examples/pip_parse_vendored + platform: ubuntu2004 + integration_test_pip_parse_vendored_ubuntu: + <<: *reusable_build_test_all + name: pip_parse_vendored integration tests on Ubuntu + working_directory: examples/pip_parse_vendored + platform: ubuntu2004 + integration_test_pip_parse_vendored_debian: + <<: *reusable_build_test_all + name: pip_parse_vendored integration tests on Debian + working_directory: examples/pip_parse_vendored + platform: debian11 + integration_test_pip_parse_vendored_macos: + <<: *reusable_build_test_all + name: pip_parse_vendored integration tests on macOS + working_directory: examples/pip_parse_vendored + platform: macos + # We don't run pip_parse_vendored under Windows as the file checked in is + # generated from a repository rule containing OS-specific rendered paths. + + integration_test_py_proto_library_ubuntu_min: + <<: *minimum_supported_version + <<: *reusable_build_test_all + name: py_proto_library integration tests on Ubuntu using minimum supported Bazel version + working_directory: examples/py_proto_library + platform: ubuntu2004 + integration_test_py_proto_library_ubuntu: + <<: *reusable_build_test_all + name: py_proto_library integration tests on Ubuntu + working_directory: examples/py_proto_library + platform: ubuntu2004 + integration_test_py_proto_library_debian: + <<: *reusable_build_test_all + name: py_proto_library integration tests on Debian + working_directory: examples/py_proto_library + platform: debian11 + integration_test_py_proto_library_macos: + <<: *reusable_build_test_all + name: py_proto_library integration tests on macOS + working_directory: examples/py_proto_library + platform: macos + integration_test_py_proto_library_windows: + <<: *reusable_build_test_all + name: py_proto_library integration tests on Windows + working_directory: examples/py_proto_library + platform: windows + + # Check the same using bzlmod as well + integration_test_py_proto_library_bzlmod_ubuntu_min: + <<: *minimum_supported_bzlmod_version + <<: *common_bzlmod_flags + <<: *reusable_build_test_all + name: py_proto_library bzlmod integration tests on Ubuntu using minimum supported Bazel version + working_directory: examples/py_proto_library + platform: ubuntu2004 + integration_test_py_proto_library_bzlmod_ubuntu: + <<: *reusable_build_test_all + <<: *common_bzlmod_flags + name: py_proto_library bzlmod integration tests on Ubuntu + working_directory: examples/py_proto_library + platform: ubuntu2004 + integration_test_py_proto_library_bzlmod_debian: + <<: *reusable_build_test_all + <<: *common_bzlmod_flags + name: py_proto_library bzlmod integration tests on Debian + working_directory: examples/py_proto_library + platform: debian11 + integration_test_py_proto_library_bzlmod_macos: + <<: *reusable_build_test_all + <<: *common_bzlmod_flags + name: py_proto_library bzlmod integration tests on macOS + working_directory: examples/py_proto_library + platform: macos + integration_test_py_proto_library_bzlmod_windows: + <<: *reusable_build_test_all + <<: *common_bzlmod_flags + name: py_proto_library bzlmod integration tests on Windows + working_directory: examples/py_proto_library + platform: windows + + integration_test_pip_repository_annotations_ubuntu_min: + <<: *minimum_supported_version + <<: *reusable_build_test_all + name: pip_repository_annotations integration tests on Ubuntu using minimum supported Bazel version + working_directory: examples/pip_repository_annotations + platform: ubuntu2004 + integration_test_pip_repository_annotations_ubuntu: + <<: *reusable_build_test_all + name: pip_repository_annotations integration tests on Ubuntu + working_directory: examples/pip_repository_annotations + platform: ubuntu2004 + integration_test_pip_repository_annotations_debian: + <<: *reusable_build_test_all + name: pip_repository_annotations integration tests on Debian + working_directory: examples/pip_repository_annotations + platform: debian11 + integration_test_pip_repository_annotations_macos: + <<: *reusable_build_test_all + name: pip_repository_annotations integration tests on macOS + working_directory: examples/pip_repository_annotations + platform: macos + integration_test_pip_repository_annotations_windows: + <<: *reusable_build_test_all + name: pip_repository_annotations integration tests on Windows + working_directory: examples/pip_repository_annotations + platform: windows + + integration_test_compile_pip_requirements_ubuntu_min: + <<: *minimum_supported_version + <<: *reusable_build_test_all + name: compile_pip_requirements integration tests on Ubuntu using minimum supported Bazel version + working_directory: tests/compile_pip_requirements + platform: ubuntu2004 + integration_test_compile_pip_requirements_ubuntu: + <<: *reusable_build_test_all + name: compile_pip_requirements integration tests on Ubuntu + working_directory: tests/compile_pip_requirements + platform: ubuntu2004 + shell_commands: + # Make a change to the locked requirements and then assert that //:requirements.update does the + # right thing. + - "echo '' > requirements_lock.txt" + - "! git diff --exit-code" + - "bazel run //:requirements.update" + - "git diff --exit-code" + integration_test_compile_pip_requirements_debian: + <<: *reusable_build_test_all + name: compile_pip_requirements integration tests on Debian + working_directory: tests/compile_pip_requirements + platform: debian11 + shell_commands: + # Make a change to the locked requirements and then assert that //:requirements.update does the + # right thing. + - "echo '' > requirements_lock.txt" + - "! git diff --exit-code" + - "bazel run //:requirements.update" + - "git diff --exit-code" + integration_test_compile_pip_requirements_macos: + <<: *reusable_build_test_all + name: compile_pip_requirements integration tests on macOS + working_directory: tests/compile_pip_requirements + platform: macos + shell_commands: + # Make a change to the locked requirements and then assert that //:requirements.update does the + # right thing. + - "echo '' > requirements_lock.txt" + - "! git diff --exit-code" + - "bazel run //:requirements.update" + - "git diff --exit-code" + integration_test_compile_pip_requirements_windows: + <<: *reusable_build_test_all + name: compile_pip_requirements integration tests on Windows + working_directory: tests/compile_pip_requirements + platform: windows + shell_commands: + # Make a change to the locked requirements and then assert that //:requirements.update does the + # right thing. + - "echo '' > requirements_lock.txt" + - "! git diff --exit-code" + - "bazel run //:requirements.update" + - "git diff --exit-code" + + integration_test_pip_repository_entry_points_ubuntu_min: + <<: *minimum_supported_version + <<: *reusable_build_test_all + name: pip_repository_entry_points integration tests on Ubuntu using minimum supported Bazel version + working_directory: tests/pip_repository_entry_points + platform: ubuntu2004 + integration_test_pip_repository_entry_points_ubuntu: + <<: *reusable_build_test_all + name: pip_repository_entry_points integration tests on Ubuntu + working_directory: tests/pip_repository_entry_points + platform: ubuntu2004 + integration_test_pip_repository_entry_points_debian: + <<: *reusable_build_test_all + name: pip_repository_entry_points integration tests on Debian + working_directory: tests/pip_repository_entry_points + platform: debian11 + integration_test_pip_repository_entry_points_macos: + <<: *reusable_build_test_all + name: pip_repository_entry_points integration tests on macOS + working_directory: tests/pip_repository_entry_points + platform: macos + integration_test_pip_repository_entry_points_windows: + <<: *reusable_build_test_all + name: pip_repository_entry_points integration tests on Windows + working_directory: tests/pip_repository_entry_points + platform: windows + + integration_test_ignore_root_user_error_ubuntu_min: + <<: *minimum_supported_version + <<: *reusable_build_test_all + name: ignore_root_user_error integration tests on Ubuntu using minimum supported Bazel version + working_directory: tests/ignore_root_user_error + platform: ubuntu2004 + integration_test_ignore_root_user_error_ubuntu: + <<: *reusable_build_test_all + name: ignore_root_user_error integration tests on Ubuntu + working_directory: tests/ignore_root_user_error + platform: ubuntu2004 + integration_test_ignore_root_user_error_debian: + <<: *reusable_build_test_all + name: ignore_root_user_error integration tests on Debian + working_directory: tests/ignore_root_user_error + platform: debian11 + integration_test_ignore_root_user_error_macos: + <<: *reusable_build_test_all + name: ignore_root_user_error integration tests on macOS + working_directory: tests/ignore_root_user_error + platform: macos + integration_test_ignore_root_user_error_windows: + <<: *reusable_build_test_all + name: ignore_root_user_error integration tests on Windows + working_directory: tests/ignore_root_user_error + platform: windows + + integration_compile_pip_requirements_test_from_external_repo_ubuntu_min: + <<: *minimum_supported_version + name: compile_pip_requirements test from external repo on Ubuntu using minimum supported Bazel version + working_directory: tests/compile_pip_requirements_test_from_external_workspace + platform: ubuntu2004 + shell_commands: + # Assert that @external_repository//:requirements_test does the right thing. + - "bazel test @external_repository//..." + integration_compile_pip_requirements_test_from_external_repo_ubuntu: + name: compile_pip_requirements test from external repo on Ubuntu + working_directory: tests/compile_pip_requirements_test_from_external_workspace + platform: ubuntu2004 + shell_commands: + # Assert that @external_repository//:requirements_test does the right thing. + - "bazel test @external_repository//..." + integration_compile_pip_requirements_test_from_external_repo_debian: + name: compile_pip_requirements test from external repo on Debian + working_directory: tests/compile_pip_requirements_test_from_external_workspace + platform: debian11 + shell_commands: + # Assert that @external_repository//:requirements_test does the right thing. + - "bazel test @external_repository//..." + integration_compile_pip_requirements_test_from_external_repo_macos: + name: compile_pip_requirements test from external repo on macOS + working_directory: tests/compile_pip_requirements_test_from_external_workspace + platform: macos + shell_commands: + # Assert that @external_repository//:requirements_test does the right thing. + - "bazel test @external_repository//..." + integration_compile_pip_requirements_test_from_external_repo_windows: + name: compile_pip_requirements test from external repo on Windows + working_directory: tests/compile_pip_requirements_test_from_external_workspace + platform: windows + shell_commands: + # Assert that @external_repository//:requirements_test does the right thing. + - "bazel test @external_repository//..." + diff --git a/.bazelignore b/.bazelignore index e69de29bb2..a603a7bd8a 100644 --- a/.bazelignore +++ b/.bazelignore @@ -0,0 +1,8 @@ +# Normally these are ignored, but if you're using a custom +# build of Bazel with a custom --output_user_root value, Bazel +# tries to follow the symlinks of the other builds and finds +# the WORKSPACE, BUILD, etc files and tries to build them. +bazel-rules_python +bazel-bin +bazel-out +bazel-testlogs diff --git a/.bazelrc b/.bazelrc index a4bcccfd67..e7e4af7bbd 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/bzlmod,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_import,examples/relative_requirements,tests/pip_repository_entry_points,tests/pip_deps -query --deleted_packages=examples/build_file_generation,examples/bzlmod,examples/pip_install,examples/pip_parse,examples/pip_parse_vendored,examples/pip_repository_annotations,examples/py_import,examples/relative_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 @@ -18,5 +18,4 @@ build --incompatible_default_to_explicit_init_py # Windows makes use of runfiles for some rules build --enable_runfiles -# TODO(f0rmiga): remove this so that other features don't start relying on it. startup --windows_enable_symlinks diff --git a/.bazelversion b/.bazelversion index 91ff57278e..09b254e90c 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -5.2.0 +6.0.0 diff --git a/.bcr/config.yml b/.bcr/config.yml new file mode 100644 index 0000000000..024e524293 --- /dev/null +++ b/.bcr/config.yml @@ -0,0 +1,17 @@ +# 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. + +fixedReleaser: + login: f0rmiga + email: thulio@aspect.dev diff --git a/.bcr/metadata.template.json b/.bcr/metadata.template.json new file mode 100644 index 0000000000..7b16b53b62 --- /dev/null +++ b/.bcr/metadata.template.json @@ -0,0 +1,20 @@ +{ + "homepage": "https://github.com/bazelbuild/rules_python", + "maintainers": [ + { + "name": "Richard Levasseur", + "email": "rlevasseur@google.com", + "github": "rickeylev" + }, + { + "name": "Thulio Ferraz Assis", + "email": "thulio@aspect.dev", + "github": "f0rmiga" + } + ], + "repository": [ + "github:bazelbuild/rules_python" + ], + "versions": [], + "yanked_versions": {} +} diff --git a/.bcr/presubmit.yml b/.bcr/presubmit.yml new file mode 100644 index 0000000000..252df6b3d4 --- /dev/null +++ b/.bcr/presubmit.yml @@ -0,0 +1,24 @@ +# 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. + +bcr_test_module: + module_path: "examples/bzlmod" + matrix: + platform: ["debian11", "macos", "ubuntu2004", "windows"] + tasks: + run_tests: + name: "Run test module" + platform: ${{ platform }} + test_targets: + - "//..." diff --git a/.bcr/source.template.json b/.bcr/source.template.json new file mode 100644 index 0000000000..c23b7652e7 --- /dev/null +++ b/.bcr/source.template.json @@ -0,0 +1,5 @@ +{ + "integrity": "", + "strip_prefix": "{REPO}-{VERSION}", + "url": "https://github.com/{OWNER}/{REPO}/releases/download/{TAG}/rules_python-{TAG}.tar.gz" +} diff --git a/.gitattributes b/.gitattributes index fb496ed760..64d09fff91 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ docs/*.md linguist-generated=true +tools/publish/*.txt linguist-generated=true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 073f6989dd..3449bcfac0 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,21 +1,15 @@ # NB: Last matching rule takes precedence in CODEOWNERS. -# Fall-through to community maintainers. -* @thundergolfer - -# Core Python rules belong to the Bazel team. -/python/ @brandjon @lberki -# But not everything under python/ is the core Python rules. -/python/pip.bzl @thundergolfer -/python/requirements.txt @thundergolfer +* @rickeylev # Directory containing the Gazelle extension and Go code. /gazelle/ @f0rmiga +/examples/build_file_generation/ @f0rmiga -# The proposals dir corresponds to the Bazel proposals process, documented -# here: https://bazel.build/designs/index.html -/proposals/ @brandjon @lberki +# Toolchains +/python/repositories.bzl @f0rmiga +/python/private/toolchains_repo.bzl @f0rmiga +/python/tests/toolchains/ @f0rmiga -# Certain repo metadata files should stay as-is, particularly these. -/LICENSE @brandjon @lberki -/CONTRIBUTING.md @brandjon @lberki +# pip_parse related code +/python/pip_install/ @hrfuller diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 38e0658e44..0d305b8816 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,44 +1,11 @@ -## PR Checklist - -Please check if your PR fulfills the following requirements: - -- [ ] Tests for the changes have been added (for bug fixes / features) -- [ ] Docs have been added / updated (for bug fixes / features) - - -## PR Type - -What kind of change does this PR introduce? - - - -- [ ] Bugfix -- [ ] Feature (please, look at the "Scope of the project" section in the README.md file) -- [ ] Code style update (formatting, local variables) -- [ ] Refactoring (no functional changes, no api changes) -- [ ] Build related changes -- [ ] CI related changes -- [ ] Documentation content changes -- [ ] Other... Please describe: - - -## What is the current behavior? - - -Issue Number: N/A - - -## What is the new behavior? - - -## Does this PR introduce a breaking change? - -- [ ] Yes -- [ ] No - - - - - -## Other information - +PR Instructions/requirements +* Title uses `type: description` format. See CONTRIBUTING.md for types. +* Common types are: build, docs, feat, fix, refactor, revert, test +* Breaking changes include "!" after the type and a "BREAKING CHANGES:" + section at the bottom. +* Body text describes: + * Why this change is being made, briefly. + * Before and after behavior, as applicable + * References issue number, as applicable +* Update docs and tests, as applicable +* Delete these instructions prior to sending the PR diff --git a/.github/workflows/ci.bazelrc b/.github/workflows/ci.bazelrc deleted file mode 100644 index bd2d20b46d..0000000000 --- a/.github/workflows/ci.bazelrc +++ /dev/null @@ -1,8 +0,0 @@ -# Bazel settings to apply on CI only -# Included with a --bazelrc option in the call to bazel -build --announce_rc -test --test_output=errors -build --disk_cache=$HOME/.cache/bazel -build --repository_cache=$HOME/.cache/bazel-repo -# For bazel-in-bazel testing -test --test_env=XDG_CACHE_HOME diff --git a/.github/workflows/create_archive_and_notes.sh b/.github/workflows/create_archive_and_notes.sh new file mode 100755 index 0000000000..0c0c4acf41 --- /dev/null +++ b/.github/workflows/create_archive_and_notes.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# 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. + +set -o errexit -o nounset -o pipefail + +# Set by GH actions, see +# https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables +TAG=${GITHUB_REF_NAME} +# A prefix is added to better match the GitHub generated archives. +PREFIX="rules_python-${TAG}" +ARCHIVE="rules_python-$TAG.tar.gz" +git archive --format=tar --prefix=${PREFIX}/ ${TAG} | gzip > $ARCHIVE +SHA=$(shasum -a 256 $ARCHIVE | awk '{print $1}') + +cat > release_notes.txt << EOF +## Using Bzlmod with Bazel 6 + +Add to your \`MODULE.bazel\` file: + +\`\`\`starlark +bazel_dep(name = "rules_python", version = "${TAG}") + +pip = use_extension("@rules_python//python:extensions.bzl", "pip") + +pip.parse( + name = "pip", + requirements_lock = "//:requirements_lock.txt", +) + +use_repo(pip, "pip") + +# (Optional) Register a specific python toolchain instead of using the host version +python = use_extension("@rules_python//python:extensions.bzl", "python") + +python.toolchain( + name = "python3_9", + python_version = "3.9", +) + +use_repo(python, "python3_9_toolchains") + +register_toolchains( + "@python3_9_toolchains//:all", +) +\`\`\` + +## Using WORKSPACE + +Paste this snippet into your \`WORKSPACE\` file: + +\`\`\`starlark +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "rules_python", + sha256 = "${SHA}", + strip_prefix = "${PREFIX}", + url = "https://github.com/bazelbuild/rules_python/releases/download/${TAG}/rules_python-${TAG}.tar.gz", +) + +load("@rules_python//python:repositories.bzl", "py_repositories") + +py_repositories() +\`\`\` + +### Gazelle plugin + +Paste this snippet into your \`WORKSPACE\` file: + +\`\`\`starlark +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +http_archive( + name = "rules_python_gazelle_plugin", + sha256 = "${SHA}", + strip_prefix = "${PREFIX}/gazelle", + url = "https://github.com/bazelbuild/rules_python/releases/download/${TAG}/rules_python-${TAG}.tar.gz", +) + +# To compile the rules_python gazelle extension from source, +# we must fetch some third-party go dependencies that it uses. + +load("@rules_python_gazelle_plugin//:deps.bzl", _py_gazelle_deps = "gazelle_deps") + +_py_gazelle_deps() +\`\`\` +EOF diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b6bba429bb..eb23bc8411 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,3 +1,17 @@ +# 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. + # Cut a release whenever a new tag is pushed to the repo. name: Release @@ -12,16 +26,21 @@ jobs: steps: - name: Checkout uses: actions/checkout@v2 - - name: bazel test //... + - name: Create release archive and notes + run: .github/workflows/create_archive_and_notes.sh + - name: Publish wheel dist env: - # Bazelisk will download bazel to here - XDG_CACHE_HOME: ~/.cache/bazel-repo - run: bazel --bazelrc=.github/workflows/ci.bazelrc --bazelrc=.bazelrc test //... - - name: Prepare workspace snippet - run: .github/workflows/workspace_snippet.sh ${{ env.GITHUB_REF_NAME }} > release_notes.txt + # This special value tells pypi that the user identity is supplied within the token + TWINE_USERNAME: __token__ + # Note, the PYPI_API_TOKEN is for the rules-python pypi user, added by @rickylev on + # https://github.com/bazelbuild/rules_python/settings/secrets/actions + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + run: bazel run --stamp --embed_label=${{ github.ref_name }} //python/runfiles:wheel.publish - name: Release uses: softprops/action-gh-release@v1 with: # Use GH feature to populate the changelog automatically generate_release_notes: true body_path: release_notes.txt + fail_on_unmatched_files: true + files: rules_python-*.tar.gz diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 816ce1da21..8d388e2f7a 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -1,3 +1,17 @@ +# 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. + # See https://github.com/marketplace/actions/close-stale-issues name: Mark stale issues and pull requests diff --git a/.github/workflows/workspace_snippet.sh b/.github/workflows/workspace_snippet.sh deleted file mode 100755 index 6fdaad35e7..0000000000 --- a/.github/workflows/workspace_snippet.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit -o nounset -o pipefail - -# Set by GH actions, see -# https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables -TAG=${GITHUB_REF_NAME} -PREFIX="rules_python-${TAG}" -SHA=$(git archive --format=tar --prefix=${PREFIX}/ ${TAG} | gzip | shasum -a 256 | awk '{print $1}') - -cat << EOF -WORKSPACE setup: - -\`\`\`starlark -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -http_archive( - name = "rules_python", - sha256 = "${SHA}", - strip_prefix = "${PREFIX}", - url = "https://github.com/bazelbuild/rules_python/archive/refs/tags/${TAG}.tar.gz", -) -\`\`\` -EOF diff --git a/.gitignore b/.gitignore index a68c6f05cc..bf901e2fca 100644 --- a/.gitignore +++ b/.gitignore @@ -43,10 +43,5 @@ user.bazelrc *.swp *.swo -# Go/Gazelle files -# These otherwise match patterns above -!go.mod -!BUILD.out - # Python cache **/__pycache__/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d84dec87af..be5f47fc45 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,9 +1,23 @@ +# 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. + # See CONTRIBUTING.md for instructions. # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/keith/pre-commit-buildifier - rev: 4.0.1.1 + rev: 6.1.0 hooks: - id: buildifier args: &args @@ -12,7 +26,7 @@ repos: - id: buildifier-lint args: *args - repo: https://github.com/pycqa/isort - rev: 5.10.1 + rev: 5.12.0 hooks: - id: isort name: isort (python) @@ -20,6 +34,6 @@ repos: - --profile - black - repo: https://github.com/psf/black - rev: 21.12b0 + rev: 23.1.0 hooks: - id: black diff --git a/BUILD b/BUILD.bazel similarity index 66% rename from BUILD rename to BUILD.bazel index ebdf74e788..35a3df892f 100644 --- a/BUILD +++ b/BUILD.bazel @@ -11,11 +11,12 @@ # 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. -load("@bazel_gazelle//:def.bzl", "gazelle") + +load(":version.bzl", "BAZEL_VERSION") package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) exports_files([ "LICENSE", @@ -25,20 +26,20 @@ exports_files([ filegroup( name = "distribution", srcs = [ - "BUILD", + "BUILD.bazel", "MODULE.bazel", "WORKSPACE", "internal_deps.bzl", "internal_setup.bzl", + "version.bzl", "//python:distribution", "//python/pip_install:distribution", - "//third_party/github.com/bazelbuild/bazel-skylib/lib:distribution", - "//third_party/github.com/bazelbuild/bazel-skylib/rules:distribution", - "//third_party/github.com/bazelbuild/bazel-skylib/rules/private:distribution", "//tools:distribution", + "@rules_python_gazelle_plugin//:distribution", ], visibility = [ "//examples:__pkg__", + "//python/tests/toolchains:__pkg__", "//tests:__pkg__", ], ) @@ -51,7 +52,6 @@ filegroup( "//python/pip_install:bzl", "//python:bzl", # Requires Bazel 0.29 onward for public visibility of these .bzl files. - "@bazel_tools//tools/python:private/defs.bzl", "@bazel_tools//tools/python:python_version.bzl", "@bazel_tools//tools/python:srcs_version.bzl", "@bazel_tools//tools/python:toolchain.bzl", @@ -60,18 +60,28 @@ filegroup( visibility = ["//visibility:public"], ) -# Gazelle configuration options. -# See https://github.com/bazelbuild/bazel-gazelle#running-gazelle-with-bazel -# gazelle:prefix github.com/bazelbuild/rules_python -# gazelle:exclude bazel-out -gazelle(name = "gazelle") +genrule( + name = "assert_bazelversion", + srcs = [".bazelversion"], + outs = ["assert_bazelversion_test.sh"], + cmd = """\ +set -o errexit -o nounset -o pipefail +current=$$(cat "$(execpath .bazelversion)") +cat > "$@" <&2 echo "ERROR: current bazel version '$${{current}}' is not the expected '{expected}'" + exit 1 +fi +EOF +""".format( + expected = BAZEL_VERSION, + ), + executable = True, +) -gazelle( - name = "update_go_deps", - args = [ - "-from_file=go.mod", - "-to_macro=gazelle/deps.bzl%gazelle_deps", - "-prune", - ], - command = "update-repos", +sh_test( + name = "assert_bazelversion_test", + srcs = [":assert_bazelversion_test.sh"], ) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 41dbd96fc2..54ecfb01e5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,9 +3,77 @@ We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow. +## Getting started + +Before we can work on the code, we need to get a copy of it and setup some +local environment and tools. + +First, fork the code to your user and clone your fork. This gives you a private +playground where you can do any edits you'd like. For this guide, we'll use +the [GitHub `gh` tool](https://github.com/cli/cli) +([Linux install](https://github.com/cli/cli/blob/trunk/docs/install_linux.md)). +(More advanced users may prefer the GitHub UI and raw `git` commands). + +```shell +gh repo fork bazelbuild/rules_python --clone --remote +``` + +Next, make sure you have a new enough version of Python installed that supports the +various code formatters and other devtools. For a quick start, +[install pyenv](https://github.com/pyenv/pyenv-installer) and +at least Python 3.9.15: + +```shell +curl https://pyenv.run | bash +pyenv install 3.9.15 +pyenv shell 3.9.15 +``` + +## Development workflow + +It's suggested that you create what is called a "feature/topic branch" in your +fork when you begin working on code you want to eventually send or code review. + +``` +git checkout main # Start our branch from the latest code +git checkout -b my-feature # Create and switch to our feature branch +git push origin my-feature # Cause the branch to be created in your fork. +``` + +From here, you then edit code and commit to your local branch. If you want to +save your work to github, you use `git push` to do so: + +``` +git push origin my-feature +``` + +Once the code is in your github repo, you can then turn it into a Pull Request +to the actual rules_python project and begin the code review process. + + +## Running tests + +Running tests is particularly easy thanks to Bazel, simply run: + +``` +bazel test //... +``` + +And it will run all the tests it can find. The first time you do this, it will +probably take long time because various dependencies will need to be downloaded +and setup. Subsequent runs will be faster, but there are many tests, and some of +them are slow. If you're working on a particular area of code, you can run just +the tests in those directories instead, which can speed up your edit-run cycle. + +Note that there are tests to verify generated documentation is correct -- if +you're modifying the signature of a public function, these tests will likely +fail and you'll need to [regenerate the api docs](#documentation). + ## Formatting -Starlark files should be formatted by buildifier. +Starlark files should be formatted by +[buildifier](https://github.com/bazelbuild/buildtools/blob/master/buildifier/README.md). +Otherwise the Buildkite CI will fail with formatting/linting violations. We suggest using a pre-commit hook to automate this. First [install pre-commit](https://pre-commit.com/#installation), then run @@ -14,7 +82,17 @@ then run pre-commit install ``` -Otherwise the Buildkite CI will yell at you about formatting/linting violations. +### Running buildifer manually + +You can also run buildifier manually. To do this, +[install buildifier](https://github.com/bazelbuild/buildtools/blob/master/buildifier/README.md), +and run the following command: + +```shell +$ buildifier --lint=fix --warnings=native-py -warnings=all WORKSPACE +``` + +Replace the argument "WORKSPACE" with the file that you are linting. ## Contributor License Agreement @@ -36,6 +114,35 @@ information on using pull requests. [GitHub Help]: https://help.github.com/articles/about-pull-requests/ +### Commit messages + +Commit messages (upon merging) and PR messages should follow the [Conventional +Commits](https://www.conventionalcommits.org/) style: + +``` +type(scope)!: + + + +BREAKING CHANGE: +``` + +Where `(scope)` is optional, and `!` is only required if there is a breaking change. +If a breaking change is introduced, then `BREAKING CHANGE:` is required. + +Common `type`s: + +* `build:` means it affects the building or development workflow. +* `docs:` means only documentation is being added, updated, or fixed. +* `feat:` means a user-visible feature is being added. +* `fix:` means a user-visible behavior is being fixed. +* `refactor:` means some sort of code cleanup that doesn't change user-visible behavior. +* `revert:` means a prior change is being reverted in some way. +* `test:` means only tests are being added. + +For the full details of types, see +[Conventional Commits](https://www.conventionalcommits.org/). + ## Generated files Some checked-in files are generated and need to be updated when a new PR is @@ -76,3 +183,23 @@ Issues should be triaged as follows: - Anything else, such as feature requests not related to existing core rules functionality, should also be filed in this repository but without the `core-rules` label. + +## FAQ + +### Installation errors when during `git commit` + +If you did `pre-commit install`, various tools are run when you do `git commit`. +This might show as an error such as: + +``` +[INFO] Installing environment for https://github.com/psf/black. +[INFO] Once installed this environment will be reused. +[INFO] This may take a few minutes... +An unexpected error has occurred: CalledProcessError: command: ... +``` + +To fix, you'll need to figure out what command is failing and why. Because these +are tools that run locally, its likely you'll need to fix something with your +environment or the installation of the tools. For Python tools (e.g. black or +isort), you can try using a different Python version in your shell by using +tools such as [pyenv](https://github.com/pyenv/pyenv). diff --git a/DEVELOPING.md b/DEVELOPING.md index 96db780e7e..2972d96b79 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -7,18 +7,33 @@ Start from a clean checkout at `main`. Before running through the release it's good to run the build and the tests locally, and make sure CI is passing. You can also test-drive the commit in an existing Bazel workspace to sanity check functionality. +#### Steps +1. [Determine the next semantic version number](#determining-semantic-version) +1. Create a tag and push, e.g. `git tag 0.5.0 upstream/main && git push upstream --tags` + NOTE: Pushing the tag will trigger release automation. +1. Watch the release automation run on https://github.com/bazelbuild/rules_python/actions +1. Add missing information to the release notes. The automatic release note + generation only includes commits associated with issues. + #### Determining Semantic Version **rules_python** is currently using [Zero-based versioning](https://0ver.org/) and thus backwards-incompatible API changes still come under the minor-version digit. So releases with API changes and new features bump the minor, and -those with only bug fixes and other minor changes bump the patch digit. +those with only bug fixes and other minor changes bump the patch digit. + +To find if there were any features added or incompatible changes made, review +the commit history. This can be done using github by going to the url: +`https://github.com/bazelbuild/rules_python/compare/...main`. -#### Steps -1. Determine what will be the next release, following semver. -1. Create a tag and push, e.g. `git tag 0.5.0 upstream/main && git push upstream --tags` -1. Watch the release automation run on https://github.com/bazelbuild/rules_python/actions - #### After release creation in Github 1. Ping @philwo to get the new release added to mirror.bazel.build. See [this comment on issue #400](https://github.com/bazelbuild/rules_python/issues/400#issuecomment-779159530) for more context. -1. Announce the release in the #python channel in the Bazel slack (bazelbuild.slack.com). +1. Announce the release in the #python channel in the Bazel slack (bazelbuild.slack.com). + +## Secrets + +### PyPI user rules-python + +Part of the release process uploads packages to PyPI as the user `rules-python`. +This account is managed by Google; contact rules-python-pyi@google.com if +something needs to be done with the PyPI account. diff --git a/MODULE.bazel b/MODULE.bazel index ed619e4300..92da4020b9 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -1,17 +1,48 @@ module( name = "rules_python", - compatibility_level = 1, version = "0.0.0", + compatibility_level = 1, ) -pip_install = use_extension("@rules_python//python:extensions.bzl", "pip_install") +bazel_dep(name = "platforms", version = "0.0.4") +bazel_dep(name = "bazel_skylib", version = "1.3.0") + +# Those are loaded only when using py_proto_library +bazel_dep(name = "rules_proto", version = "5.3.0-21.7") +bazel_dep(name = "protobuf", version = "21.7", repo_name = "com_google_protobuf") +internal_deps = use_extension("@rules_python//python:extensions.bzl", "internal_deps") +internal_deps.install() use_repo( - pip_install, + internal_deps, + "pypi__build", "pypi__click", + "pypi__colorama", + "pypi__importlib_metadata", + "pypi__installer", + "pypi__more_itertools", + "pypi__packaging", + "pypi__pep517", "pypi__pip", "pypi__pip_tools", - "pypi__pkginfo", "pypi__setuptools", + "pypi__tomli", "pypi__wheel", + "pypi__zipp", + # coverage_deps managed by running ./tools/update_coverage_deps.py + "pypi__coverage_cp310_aarch64-apple-darwin", + "pypi__coverage_cp310_aarch64-unknown-linux-gnu", + "pypi__coverage_cp310_x86_64-apple-darwin", + "pypi__coverage_cp310_x86_64-unknown-linux-gnu", + "pypi__coverage_cp311_aarch64-unknown-linux-gnu", + "pypi__coverage_cp311_x86_64-apple-darwin", + "pypi__coverage_cp311_x86_64-unknown-linux-gnu", + "pypi__coverage_cp38_aarch64-apple-darwin", + "pypi__coverage_cp38_aarch64-unknown-linux-gnu", + "pypi__coverage_cp38_x86_64-apple-darwin", + "pypi__coverage_cp38_x86_64-unknown-linux-gnu", + "pypi__coverage_cp39_aarch64-apple-darwin", + "pypi__coverage_cp39_aarch64-unknown-linux-gnu", + "pypi__coverage_cp39_x86_64-apple-darwin", + "pypi__coverage_cp39_x86_64-unknown-linux-gnu", ) diff --git a/README.md b/README.md index 7359a2ae4e..a095a352c1 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,8 @@ ## Overview This repository is the home of the core Python rules -- `py_library`, -`py_binary`, `py_test`, and related symbols that provide the basis for Python -support in Bazel. It also contains packaging rules for integrating with PyPI -(`pip`). Documentation lives in the +`py_binary`, `py_test`, `py_proto_library`, and related symbols that provide the basis for Python +support in Bazel. It also contains package installation rules for integrating with PyPI and other package indices. Documentation lives in the [`docs/`](https://github.com/bazelbuild/rules_python/tree/main/docs) directory and in the [Bazel Build Encyclopedia](https://docs.bazel.build/versions/master/be/python.html). @@ -24,7 +23,7 @@ Once they are fully migrated to rules_python, they may evolve at a different rate, but this repository will still follow [semantic versioning](https://semver.org). -The packaging rules (`pip_install`, etc.) are less stable. We may make breaking +The package installation rules (`pip_install`, `pip_parse` etc.) are less stable. We may make breaking changes as they evolve. This repository is maintained by the Bazel community. Neither Google, nor the @@ -34,6 +33,47 @@ contribute](CONTRIBUTING.md) page for information on our development workflow. ## Getting started +The next two sections cover using `rules_python` with bzlmod and +the older way of configuring bazel with a `WORKSPACE` file. + +### Using bzlmod + +To import rules_python in your project, you first need to add it to your +`MODULE.bazel` file, using the snippet provided in the +[release you choose](https://github.com/bazelbuild/rules_python/releases). + +#### Toolchain registration with bzlmod + +To register a hermetic Python toolchain rather than rely on a system-installed interpreter for runtime execution, you can add to the `MODULE.bazel` file: + +```python +# Find the latest version number here: https://github.com/bazelbuild/rules_python/releases +# and change the version number if needed in the line below. +bazel_dep(name = "rules_python", version = "0.20.0") + +# You do not have to use pip for the toolchain, but most people +# will use it for the dependency management. +pip = use_extension("@rules_python//python:extensions.bzl", "pip") + +pip.parse( + name = "pip", + requirements_lock = "//:requirements_lock.txt", +) + +use_repo(pip, "pip") + +# Register a specific python toolchain instead of using the host version +python = use_extension("@rules_python//python:extensions.bzl", "python") + +use_repo(python, "python3_10_toolchains") + +register_toolchains( + "@python3_10_toolchains//:all", +) +``` + +### Using a WORKSPACE file + To import rules_python in your project, you first need to add it to your `WORKSPACE` file, using the snippet provided in the [release you choose](https://github.com/bazelbuild/rules_python/releases) @@ -47,13 +87,14 @@ rules_python_version = "740825b7f74930c62f44af95c9a4c1bd428d2c53" # Latest @ 202 http_archive( name = "rules_python", - sha256 = "3474c5815da4cb003ff22811a36a11894927eda1c2e64bf2dac63e914bfdf30f", + # Bazel will print the proper value to add here during the first build. + # sha256 = "FIXME", strip_prefix = "rules_python-{}".format(rules_python_version), url = "https://github.com/bazelbuild/rules_python/archive/{}.zip".format(rules_python_version), ) ``` -### Toolchain registration +#### Toolchain registration To register a hermetic Python toolchain rather than rely on a system-installed interpreter for runtime execution, you can add to the `WORKSPACE` file: @@ -84,7 +125,7 @@ You may also find some quirks while using this toolchain. Please refer to [pytho ### Toolchain usage in other rules -Python toolchains can be utilised in other bazel rules, such as `genrule()`, by adding the `toolchains=["@rules_python//python:current_py_toolchain"]` attribute. The path to the python interpreter can be obtained by using the `$(PYTHON2)` and `$(PYTHON3)` ["Make" Variables](https://bazel.build/reference/be/make-variables). See the [`test_current_py_toolchain`](tests/load_from_macro/BUILD) target for an example. +Python toolchains can be utilised in other bazel rules, such as `genrule()`, by adding the `toolchains=["@rules_python//python:current_py_toolchain"]` attribute. The path to the python interpreter can be obtained by using the `$(PYTHON2)` and `$(PYTHON3)` ["Make" Variables](https://bazel.build/reference/be/make-variables). See the [`test_current_py_toolchain`](tests/load_from_macro/BUILD.bazel) target for an example. ### "Hello World" @@ -101,14 +142,14 @@ py_binary( ) ``` -## Using the packaging rules +## Using the package installation rules Usage of the packaging rules involves two main steps. -1. [Installing `pip` dependencies](#installing-pip-dependencies) -2. [Consuming `pip` dependencies](#consuming-pip-dependencies) +1. [Installing third_party packages](#installing-third_party-packages) +2. [Using third_party packages as dependencies](#using-third_party-packages-as-dependencies) -The packaging rules create two kinds of repositories: A central external repo that holds +The package installation rules create two kinds of repositories: A central external repo that holds downloaded wheel files, and individual external repos for each wheel's extracted contents. Users only need to interact with the central external repo; the wheel repos are essentially an implementation detail. The central external repo provides a @@ -116,53 +157,26 @@ are essentially an implementation detail. The central external repo provides a `BUILD` files that translates a pip package name into the label of a `py_library` target in the appropriate wheel repo. -### Installing `pip` dependencies +### Installing third_party packages -To add pip dependencies to your `WORKSPACE`, load the `pip_install` function, and call it to create the -central external repo and individual wheel external repos. +#### Using bzlmod +To add pip dependencies to your `MODULE.bazel` file, use the `pip.parse` extension, and call it to create the +central external repo and individual wheel external repos. ```python -load("@rules_python//python:pip.bzl", "pip_install") - -# Create a central external repo, @my_deps, that contains Bazel targets for all the -# third-party packages specified in the requirements.txt file. -pip_install( - name = "my_deps", - requirements = "//path/to:requirements.txt", +pip.parse( + name = "my_deps", + requirements_lock = "//:requirements_lock.txt", ) -``` - -Note that since `pip_install` is a repository rule and therefore executes pip at WORKSPACE-evaluation time, Bazel has no -information about the Python toolchain and cannot enforce that the interpreter -used to invoke pip matches the interpreter used to run `py_binary` targets. By -default, `pip_install` uses the system command `"python3"`. This can be overridden by passing the -`python_interpreter` attribute or `python_interpreter_target` attribute to `pip_install`. - -You can have multiple `pip_install`s in the same workspace. This will create multiple external repos that have no relation to -one another, and may result in downloading the same wheels multiple times. -As with any repository rule, if you would like to ensure that `pip_install` is -re-executed in order to pick up a non-hermetic change to your environment (e.g., -updating your system `python` interpreter), you can force it to re-execute by running -`bazel sync --only [pip_install name]`. - -### Fetch `pip` dependencies lazily - -One pain point with `pip_install` is the need to download all dependencies resolved by -your requirements.txt before the bazel analysis phase can start. For large python monorepos -this can take a long time, especially on slow connections. +use_repo(pip, "my_deps") +``` -`pip_parse` provides a solution to this problem. If you can provide a lock -file of all your python dependencies `pip_parse` will translate each requirement into its own external repository. -Bazel will only fetch/build wheels for the requirements in the subgraph of your build target. +#### Using a WORKSPACE file -There are API differences between `pip_parse` and `pip_install`: -1. `pip_parse` requires a fully resolved lock file of your python dependencies. You can generate this by using the `compile_pip_requirements` rule, - running `pip-compile` directly, or using virtualenv and `pip freeze`. `pip_parse` uses a label argument called `requirements_lock` instead of - `requirements` to make this distinction clear. -2. `pip_parse` translates your requirements into a starlark macro called `install_deps`. You must call this macro in your WORKSPACE to - declare your dependencies. +To add pip dependencies to your `WORKSPACE`, load the `pip_parse` function, and call it to create the +central external repo and individual wheel external repos. ```python @@ -174,14 +188,34 @@ pip_parse( name = "my_deps", requirements_lock = "//path/to:requirements_lock.txt", ) - # Load the starlark macro which will define your dependencies. load("@my_deps//:requirements.bzl", "install_deps") # Call it to define repos for your requirements. install_deps() ``` -### Consuming `pip` dependencies +#### pip rules + +Note that since `pip_parse` is a repository rule and therefore executes pip at WORKSPACE-evaluation time, Bazel has no +information about the Python toolchain and cannot enforce that the interpreter +used to invoke pip matches the interpreter used to run `py_binary` targets. By +default, `pip_parse` uses the system command `"python3"`. This can be overridden by passing the +`python_interpreter` attribute or `python_interpreter_target` attribute to `pip_parse`. + +You can have multiple `pip_parse`s in the same workspace. This will create multiple external repos that have no relation to one another, and may result in downloading the same wheels multiple times. + +As with any repository rule, if you would like to ensure that `pip_parse` is +re-executed in order to pick up a non-hermetic change to your environment (e.g., +updating your system `python` interpreter), you can force it to re-execute by running +`bazel sync --only [pip_parse name]`. + +Note: The `pip_install` rule is deprecated. `pip_parse` offers identical functionality and both `pip_install` +and `pip_parse` now have the same implementation. The name `pip_install` may be removed in a future version of the rules. +The maintainers have taken all reasonable efforts to faciliate a smooth transition, but some users of `pip_install` will +need to replace their existing `requirements.txt` with a fully resolved set of dependencies using a tool such as +`pip-tools` or the `compile_pip_requirements` repository rule. + +### Using third_party packages as dependencies Each extracted wheel repo contains a `py_library` target representing the wheel's contents. There are two ways to access this library. The diff --git a/WORKSPACE b/WORKSPACE index b43a8d8e1d..a833de8384 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -25,16 +25,64 @@ load("//:internal_setup.bzl", "rules_python_internal_setup") rules_python_internal_setup() -load("//python:repositories.bzl", "python_register_toolchains") +load("//python:repositories.bzl", "python_register_multi_toolchains") load("//python:versions.bzl", "MINOR_MAPPING") -python_register_toolchains( +python_register_multi_toolchains( name = "python", - # We always use the latest Python internally. - python_version = MINOR_MAPPING.values()[-1], + default_version = MINOR_MAPPING.values()[-1], + python_versions = MINOR_MAPPING.values(), ) -load("//gazelle:deps.bzl", "gazelle_deps") +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") -# gazelle:repository_macro gazelle/deps.bzl%gazelle_deps -gazelle_deps() +# Used for Bazel CI +http_archive( + name = "bazelci_rules", + sha256 = "eca21884e6f66a88c358e580fd67a6b148d30ab57b1680f62a96c00f9bc6a07e", + strip_prefix = "bazelci_rules-1.0.0", + url = "https://github.com/bazelbuild/continuous-integration/releases/download/rules-1.0.0/bazelci_rules-1.0.0.tar.gz", +) + +load("@bazelci_rules//:rbe_repo.bzl", "rbe_preconfig") + +# Creates a default toolchain config for RBE. +# Use this as is if you are using the rbe_ubuntu16_04 container, +# otherwise refer to RBE docs. +rbe_preconfig( + name = "buildkite_config", + toolchain = "ubuntu1804-bazel-java11", +) + +local_repository( + name = "rules_python_gazelle_plugin", + path = "gazelle", +) + +# The rules_python gazelle extension has some third-party go dependencies +# which we need to fetch in order to compile it. +load("@rules_python_gazelle_plugin//:deps.bzl", _py_gazelle_deps = "gazelle_deps") + +# See: https://github.com/bazelbuild/rules_python/blob/main/gazelle/README.md +# This rule loads and compiles various go dependencies that running gazelle +# for python requirements. +_py_gazelle_deps() + +##################### +# Install twine for our own runfiles wheel publishing. +# Eventually we might want to install twine automatically for users too, see: +# https://github.com/bazelbuild/rules_python/issues/1016. +load("@python//3.11.1:defs.bzl", "interpreter") +load("@rules_python//python:pip.bzl", "pip_parse") + +pip_parse( + name = "publish_deps", + python_interpreter_target = interpreter, + requirements_darwin = "//tools/publish:requirements_darwin.txt", + requirements_lock = "//tools/publish:requirements.txt", + requirements_windows = "//tools/publish:requirements_windows.txt", +) + +load("@publish_deps//:requirements.bzl", "install_deps") + +install_deps() diff --git a/addlicense.sh b/addlicense.sh new file mode 100755 index 0000000000..8cc8fb33bc --- /dev/null +++ b/addlicense.sh @@ -0,0 +1,23 @@ +#!/bin/bash +# 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. + + +if ! command -v addlicense @>&1 >/dev/null; then + echo "ERROR: addlicense not installed." + echo "Install using https://github.com/google/addlicense#install" + exit 1 +fi + +addlicense -v -l apache -c 'The Bazel Authors. All rights reserved.' "$@" diff --git a/docs/BUILD b/docs/BUILD.bazel similarity index 87% rename from docs/BUILD rename to docs/BUILD.bazel index d2958219f0..938ba85dd5 100644 --- a/docs/BUILD +++ b/docs/BUILD.bazel @@ -32,17 +32,6 @@ _DOCS = { # because they're only used for doc generation. This way, we avoid requiring # our users to depend on Skylib. -# Requires Bazel 0.29 onward for public visibility of these .bzl files. -bzl_library( - name = "bazel_python_tools", - srcs = [ - "@bazel_tools//tools/python:private/defs.bzl", - "@bazel_tools//tools/python:srcs_version.bzl", - "@bazel_tools//tools/python:toolchain.bzl", - "@bazel_tools//tools/python:utils.bzl", - ], -) - bzl_library( name = "bazel_repo_tools", srcs = [ @@ -56,7 +45,11 @@ bzl_library( "//python:defs.bzl", "//python/private:reexports.bzl", ], - deps = [":bazel_python_tools"], + deps = [ + ":bazel_repo_tools", + "//python:defs_bzl", + "//python/private:reexports_bzl", + ], ) bzl_library( @@ -67,6 +60,14 @@ bzl_library( ], deps = [ ":defs", + "//:version.bzl", + ], +) + +bzl_library( + name = "requirements_parser_bzl", + srcs = [ + "//python/pip_install:requirements_parser.bzl", ], ) @@ -74,7 +75,13 @@ bzl_library( name = "packaging_bzl", srcs = [ "//python:packaging.bzl", + "//python/private:py_package.bzl", + "//python/private:py_wheel.bzl", "//python/private:stamp.bzl", + "//python/private:util.bzl", + ], + deps = [ + "//python/private:util_bzl", ], ) @@ -102,7 +109,7 @@ stardoc( deps = [ ":bazel_repo_tools", ":pip_install_bzl", - "//third_party/github.com/bazelbuild/bazel-skylib/lib:versions", + "@bazel_skylib//lib:versions", ], ) @@ -114,7 +121,8 @@ stardoc( deps = [ ":bazel_repo_tools", ":pip_install_bzl", - "//third_party/github.com/bazelbuild/bazel-skylib/lib:versions", + ":requirements_parser_bzl", + "@bazel_skylib//lib:versions", ], ) diff --git a/docs/coverage.md b/docs/coverage.md new file mode 100644 index 0000000000..63f25782e0 --- /dev/null +++ b/docs/coverage.md @@ -0,0 +1,58 @@ +# Setting up coverage + +As of Bazel 6, the Python toolchains and bootstrap logic supports providing +coverage information using the `coverage` library. + +As of `rules_python` version `0.18.1`, builtin coverage support can be enabled +when configuring toolchains. + +## Enabling `rules_python` coverage support + +Enabling the coverage support bundled with `rules_python` just requires setting an +argument when registerting toolchains. + +For Bzlmod: + +```starlark +python.toolchain( + "@python3_9_toolchains//:all", + configure_coverage_tool = True, +) +``` + +For WORKSPACE configuration: + +```starlark +python_register_toolchains( + register_coverage_tool = True, +) +``` + +NOTE: This will implicitly add the version of `coverage` bundled with +`rules_python` to the dependencies of `py_test` rules when `bazel coverage` is +run. If a target already transitively depends on a different version of +`coverage`, then behavior is undefined -- it is undefined which version comes +first in the import path. If you find yourself in this situation, then you'll +need to manually configure coverage (see below). + +## Manually configuring coverage + +To manually configure coverage support, you'll need to set the +`py_runtime.coverage_tool` attribute. This attribute is a target that specifies +the coverage entry point file and, optionally, client libraries that are added +to `py_test` targets. Typically, this would be a `filegroup` that looked like: + +```starlark +filegroup( + name = "coverage", + srcs = ["coverage_main.py"], + data = ["coverage_lib1.py", ...] +) +``` + +Using `filegroup` isn't required, nor are including client libraries. The +important behaviors of the target are: + +* It provides a single output file OR it provides an executable output; this + output is treated as the coverage entry point. +* If it provides runfiles, then `runfiles.files` are included into `py_test`. diff --git a/docs/packaging.md b/docs/packaging.md index d3595c46be..b244b42767 100755 --- a/docs/packaging.md +++ b/docs/packaging.md @@ -1,6 +1,8 @@ - +Public API for for building wheels. + + ## py_package @@ -11,35 +13,123 @@ py_package(name, deps< A rule to select all files in transitive dependencies of deps which belong to given set of Python packages. -This rule is intended to be used as data dependency to py_wheel rule +This rule is intended to be used as data dependency to py_wheel rule. **ATTRIBUTES** | Name | Description | Type | Mandatory | Default | -| :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | -| name | A unique name for this target. | Name | required | | -| deps | - | List of labels | optional | [] | -| packages | List of Python packages to include in the distribution. Sub-packages are automatically included. | List of strings | optional | [] | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| deps | - | List of labels | optional | [] | +| packages | List of Python packages to include in the distribution. Sub-packages are automatically included. | List of strings | optional | [] | - + -## py_wheel +## py_wheel_dist + +
+py_wheel_dist(name, out, wheel)
+
+ +Prepare a dist/ folder, following Python's packaging standard practice. + +See https://packaging.python.org/en/latest/tutorials/packaging-projects/#generating-distribution-archives +which recommends a dist/ folder containing the wheel file(s), source distributions, etc. + +This also has the advantage that stamping information is included in the wheel's filename. + + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| out | name of the resulting directory | String | required | | +| wheel | a [py_wheel rule](/docs/packaging.md#py_wheel_rule) | Label | optional | None | + + + + +## py_wheel_rule + +
+py_wheel_rule(name, abi, author, author_email, classifiers, console_scripts, deps, description_file,
+              distribution, entry_points, extra_distinfo_files, extra_requires, homepage, license,
+              platform, python_requires, python_tag, requires, stamp, strip_path_prefixes, version)
+
+ +Internal rule used by the [py_wheel macro](/docs/packaging.md#py_wheel). + +These intentionally have the same name to avoid sharp edges with Bazel macros. +For example, a `bazel query` for a user's `py_wheel` macro expands to `py_wheel` targets, +in the way they expect. + + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| abi | Python ABI tag. 'none' for pure-Python wheels. | String | optional | "none" | +| author | A string specifying the author of the package. | String | optional | "" | +| author_email | A string specifying the email address of the package author. | String | optional | "" | +| classifiers | A list of strings describing the categories for the package. For valid classifiers see https://pypi.org/classifiers | List of strings | optional | [] | +| console_scripts | Deprecated console_script entry points, e.g. {'main': 'examples.wheel.main:main'}.

Deprecated: prefer the entry_points attribute, which supports console_scripts as well as other entry points. | Dictionary: String -> String | optional | {} | +| deps | Targets to be included in the distribution.

The targets to package are usually py_library rules or filesets (for packaging data files).

Note it's usually better to package py_library targets and use entry_points attribute to specify console_scripts than to package py_binary rules. py_binary targets would wrap a executable script that tries to locate .runfiles directory which is not packaged in the wheel. | List of labels | optional | [] | +| description_file | A file containing text describing the package. | Label | optional | None | +| distribution | Name of the distribution.

This should match the project name onm PyPI. It's also the name that is used to refer to the package in other packages' dependencies. | String | required | | +| entry_points | entry_points, e.g. {'console_scripts': ['main = examples.wheel.main:main']}. | Dictionary: String -> List of strings | optional | {} | +| extra_distinfo_files | Extra files to add to distinfo directory in the archive. | Dictionary: Label -> String | optional | {} | +| extra_requires | List of optional requirements for this package | Dictionary: String -> List of strings | optional | {} | +| homepage | A string specifying the URL for the package homepage. | String | optional | "" | +| license | A string specifying the license of the package. | String | optional | "" | +| platform | Supported platform. Use 'any' for pure-Python wheel.

If you have included platform-specific data, such as a .pyd or .so extension module, you will need to specify the platform in standard pip format. If you support multiple platforms, you can define platform constraints, then use a select() to specify the appropriate specifier, eg:

platform = select({ "//platforms:windows_x86_64": "win_amd64", "//platforms:macos_x86_64": "macosx_10_7_x86_64", "//platforms:linux_x86_64": "manylinux2014_x86_64", }) | String | optional | "any" | +| python_requires | Python versions required by this distribution, e.g. '>=3.5,<3.7' | String | optional | "" | +| python_tag | Supported Python version(s), eg py3, cp35.cp36, etc | String | optional | "py3" | +| requires | List of requirements for this package. See the section on [Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) for details and examples of the format of this argument. | List of strings | optional | [] | +| stamp | Whether to encode build information into the wheel. Possible values:

- stamp = 1: Always stamp the build information into the wheel, even in [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. This setting should be avoided, since it potentially kills remote caching for the target and any downstream actions that depend on it.

- stamp = 0: Always replace build information by constant values. This gives good build result caching.

- stamp = -1: Embedding of build information is controlled by the [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.

Stamped targets are not rebuilt unless their dependencies change. | Integer | optional | -1 | +| strip_path_prefixes | path prefixes to strip from files added to the generated package | List of strings | optional | [] | +| version | Version number of the package.

Note that this attribute supports stamp format strings as well as 'make variables'. For example: - version = "1.2.3-{BUILD_TIMESTAMP}" - version = "{BUILD_EMBED_LABEL}" - version = "$(VERSION)"

Note that Bazel's output filename cannot include the stamp information, as outputs must be known during the analysis phase and the stamp data is available only during the action execution.

The [py_wheel](/docs/packaging.md#py_wheel) macro produces a .dist-suffix target which creates a dist/ folder containing the wheel with the stamped name, suitable for publishing.

See [py_wheel_dist](/docs/packaging.md#py_wheel_dist) for more info. | String | required | | + + + + +## PyWheelInfo
-py_wheel(name, abi, author, author_email, classifiers, console_scripts, deps, description_file,
-         distribution, entry_points, extra_requires, homepage, license, platform, python_requires,
-         python_tag, requires, stamp, strip_path_prefixes, version)
+PyWheelInfo(name_file, wheel)
 
+Information about a wheel produced by `py_wheel` + +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| name_file | File: A file containing the canonical name of the wheel (after stamping, if enabled). | +| wheel | File: The wheel file itself. | + + + + +## py_wheel + +
+py_wheel(name, twine, kwargs)
+
-A rule for building Python Wheels. +Builds a Python Wheel. Wheels are Python distribution format defined in https://www.python.org/dev/peps/pep-0427/. -This rule packages a set of targets into a single wheel. +This macro packages a set of targets into a single wheel. +It wraps the [py_wheel rule](#py_wheel_rule). Currently only pure-python wheels are supported. @@ -78,50 +168,39 @@ py_wheel( ) ``` +To publish the wheel to Pypi, the twine package is required. +rules_python doesn't provide twine itself, see https://github.com/bazelbuild/rules_python/issues/1016 +However you can install it with pip_parse, just like we do in the WORKSPACE file in rules_python. -**ATTRIBUTES** +Once you've installed twine, you can pass its label to the `twine` attribute of this macro, +to get a "[name].publish" target. +Example: -| Name | Description | Type | Mandatory | Default | -| :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | -| name | A unique name for this target. | Name | required | | -| abi | Python ABI tag. 'none' for pure-Python wheels. | String | optional | "none" | -| author | A string specifying the author of the package. | String | optional | "" | -| author_email | A string specifying the email address of the package author. | String | optional | "" | -| classifiers | A list of strings describing the categories for the package. For valid classifiers see https://pypi.org/classifiers | List of strings | optional | [] | -| console_scripts | Deprecated console_script entry points, e.g. {'main': 'examples.wheel.main:main'}.

Deprecated: prefer the entry_points attribute, which supports console_scripts as well as other entry points. | Dictionary: String -> String | optional | {} | -| deps | Targets to be included in the distribution.

The targets to package are usually py_library rules or filesets (for packaging data files).

Note it's usually better to package py_library targets and use entry_points attribute to specify console_scripts than to package py_binary rules. py_binary targets would wrap a executable script that tries to locate .runfiles directory which is not packaged in the wheel. | List of labels | optional | [] | -| description_file | A file containing text describing the package in a single line. | Label | optional | None | -| distribution | Name of the distribution.

This should match the project name onm PyPI. It's also the name that is used to refer to the package in other packages' dependencies. | String | required | | -| entry_points | entry_points, e.g. {'console_scripts': ['main = examples.wheel.main:main']}. | Dictionary: String -> List of strings | optional | {} | -| extra_requires | List of optional requirements for this package | Dictionary: String -> List of strings | optional | {} | -| homepage | A string specifying the URL for the package homepage. | String | optional | "" | -| license | A string specifying the license of the package. | String | optional | "" | -| platform | Supported platform. Use 'any' for pure-Python wheel.

If you have included platform-specific data, such as a .pyd or .so extension module, you will need to specify the platform in standard pip format. If you support multiple platforms, you can define platform constraints, then use a select() to specify the appropriate specifier, eg:

platform = select({ "//platforms:windows_x86_64": "win_amd64", "//platforms:macos_x86_64": "macosx_10_7_x86_64", "//platforms:linux_x86_64": "manylinux2014_x86_64", }) | String | optional | "any" | -| python_requires | A string specifying what other distributions need to be installed when this one is. See the section on [Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) for details and examples of the format of this argument. | String | optional | "" | -| python_tag | Supported Python version(s), eg py3, cp35.cp36, etc | String | optional | "py3" | -| requires | List of requirements for this package | List of strings | optional | [] | -| stamp | Whether to encode build information into the wheel. Possible values:

- stamp = 1: Always stamp the build information into the wheel, even in [--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. This setting should be avoided, since it potentially kills remote caching for the target and any downstream actions that depend on it.

- stamp = 0: Always replace build information by constant values. This gives good build result caching.

- stamp = -1: Embedding of build information is controlled by the [--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag.

Stamped targets are not rebuilt unless their dependencies change. | Integer | optional | -1 | -| strip_path_prefixes | path prefixes to strip from files added to the generated package | List of strings | optional | [] | -| version | Version number of the package. Note that this attribute supports stamp format strings (eg. 1.2.3-{BUILD_TIMESTAMP}) as well as 'make variables' (e.g. 1.2.3-$(VERSION)). | String | required | | - - - +```python +py_wheel( + name = "my_wheel", + twine = "@publish_deps_twine//:pkg", + ... +) +``` -## PyWheelInfo +Now you can run a command like the following, which publishes to https://test.pypi.org/ -
-PyWheelInfo(name_file, wheel)
-
+```sh +% TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-*** \ + bazel run --stamp --embed_label=1.2.4 -- \ + //path/to:my_wheel.publish --repository testpypi +``` -Information about a wheel produced by `py_wheel` -**FIELDS** +**PARAMETERS** -| Name | Description | -| :-------------: | :-------------: | -| name_file | File: A file containing the canonical name of the wheel (after stamping, if enabled). | -| wheel | File: The wheel file itself. | +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| name | A unique name for this target. | none | +| twine | A label of the external location of the py_library target for twine | None | +| kwargs | other named parameters passed to the underlying [py_wheel rule](#py_wheel_rule) | none | diff --git a/docs/pip.md b/docs/pip.md index 4853e5252d..e4c3f21b79 100644 --- a/docs/pip.md +++ b/docs/pip.md @@ -1,12 +1,37 @@ - +Import pip requirements into Bazel. + + + +## whl_library_alias + +
+whl_library_alias(name, default_version, repo_mapping, version_map, wheel_name)
+
+ + + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this repository. | Name | required | | +| default_version | - | String | required | | +| repo_mapping | A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.<p>For example, an entry "@foo": "@bar" declares that, for any time this repository depends on @foo (such as a dependency on @foo//some:target, it should actually resolve that dependency within globally-declared @bar (@bar//some:target). | Dictionary: String -> String | required | | +| version_map | - | Dictionary: String -> String | required | | +| wheel_name | - | String | required | | + + + ## compile_pip_requirements
-compile_pip_requirements(name, extra_args, visibility, requirements_in, requirements_txt,
-                         requirements_linux, requirements_darwin, requirements_windows, tags, kwargs)
+compile_pip_requirements(name, extra_args, extra_deps, py_binary, py_test, requirements_in,
+                         requirements_txt, requirements_darwin, requirements_linux,
+                         requirements_windows, visibility, tags, kwargs)
 
Generates targets for managing pip dependencies with pip-compile. @@ -17,28 +42,63 @@ of some other compile_pip_requirements rule that references these requirements It also generates two targets for running pip-compile: -- validate with `bazel test _test` -- update with `bazel run .update` +- validate with `bazel test [name]_test` +- update with `bazel run [name].update` **PARAMETERS** | Name | Description | Default Value | -| :-------------: | :-------------: | :-------------: | -| name | base name for generated targets, typically "requirements" | none | -| extra_args | passed to pip-compile | [] | -| visibility | passed to both the _test and .update rules | ["//visibility:private"] | -| requirements_in | file expressing desired dependencies | None | -| requirements_txt | result of "compiling" the requirements.in file | None | -| requirements_linux | File of linux specific resolve output to check validate if requirement.in has changes. | None | -| requirements_darwin | File of darwin specific resolve output to check validate if requirement.in has changes. | None | -| requirements_windows | File of windows specific resolve output to check validate if requirement.in has changes. | None | -| tags | tagging attribute common to all build rules, passed to both the _test and .update rules | None | -| kwargs | other bazel attributes passed to the "_test" rule | none | +| :------------- | :------------- | :------------- | +| name | base name for generated targets, typically "requirements". | none | +| extra_args | passed to pip-compile. | [] | +| extra_deps | extra dependencies passed to pip-compile. | [] | +| py_binary | the py_binary rule to be used. | <function py_binary> | +| py_test | the py_test rule to be used. | <function py_test> | +| requirements_in | file expressing desired dependencies. | None | +| requirements_txt | result of "compiling" the requirements.in file. | None | +| requirements_darwin | File of darwin specific resolve output to check validate if requirement.in has changes. | None | +| requirements_linux | File of linux specific resolve output to check validate if requirement.in has changes. | None | +| requirements_windows | File of windows specific resolve output to check validate if requirement.in has changes. | None | +| visibility | passed to both the _test and .update rules. | ["//visibility:private"] | +| tags | tagging attribute common to all build rules, passed to both the _test and .update rules. | None | +| kwargs | other bazel attributes passed to the "_test" rule. | none | + + + + +## multi_pip_parse + +
+multi_pip_parse(name, default_version, python_versions, python_interpreter_target,
+                requirements_lock, kwargs)
+
+NOT INTENDED FOR DIRECT USE! - +This is intended to be used by the multi_pip_parse implementation in the template of the +multi_toolchain_aliases repository rule. + + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| name | the name of the multi_pip_parse repository. | none | +| default_version | the default Python version. | none | +| python_versions | all Python toolchain versions currently registered. | none | +| python_interpreter_target | a dictionary which keys are Python versions and values are resolved host interpreters. | none | +| requirements_lock | a dictionary which keys are Python versions and values are locked requirements files. | none | +| kwargs | extra arguments passed to all wrapped pip_parse. | none | + +**RETURNS** + +The internal implementation of multi_pip_parse repository rule. + + + ## package_annotation @@ -56,16 +116,20 @@ Annotations to apply to the BUILD file content from package generated from a `pi | Name | Description | Default Value | -| :-------------: | :-------------: | :-------------: | -| additive_build_content | Raw text to add to the generated BUILD file of a package. | None | -| copy_files | A mapping of src and out files for [@bazel_skylib//rules:copy_file.bzl][cf] | {} | -| copy_executables | A mapping of src and out files for [@bazel_skylib//rules:copy_file.bzl][cf]. Targets generated here will also be flagged as executable. | {} | -| data | A list of labels to add as data dependencies to the generated py_library target. | [] | -| data_exclude_glob | A list of exclude glob patterns to add as data to the generated py_library target. | [] | -| srcs_exclude_glob | A list of labels to add as srcs to the generated py_library target. | [] | +| :------------- | :------------- | :------------- | +| additive_build_content | Raw text to add to the generated BUILD file of a package. | None | +| copy_files | A mapping of src and out files for [@bazel_skylib//rules:copy_file.bzl][cf] | {} | +| copy_executables | A mapping of src and out files for [@bazel_skylib//rules:copy_file.bzl][cf]. Targets generated here will also be flagged as executable. | {} | +| data | A list of labels to add as data dependencies to the generated py_library target. | [] | +| data_exclude_glob | A list of exclude glob patterns to add as data to the generated py_library target. | [] | +| srcs_exclude_glob | A list of labels to add as srcs to the generated py_library target. | [] | +**RETURNS** - +str: A json encoded string of the provided content. + + + ## pip_install @@ -73,69 +137,19 @@ Annotations to apply to the BUILD file content from package generated from a `pi pip_install(requirements, name, kwargs) -Accepts a `requirements.txt` file and installs the dependencies listed within. - -Those dependencies become available in a generated `requirements.bzl` file. - -This macro wraps the [`pip_repository`](./pip_repository.md) rule that invokes `pip`. -In your WORKSPACE file: +Accepts a locked/compiled requirements file and installs the dependencies listed within. ```python +load("@rules_python//python:pip.bzl", "pip_install") + pip_install( + name = "pip_deps", requirements = ":requirements.txt", ) -``` - -You can then reference installed dependencies from a `BUILD` file with: - -```python -load("@pip//:requirements.bzl", "requirement") -py_library( - name = "bar", - ... - deps = [ - "//my/other:dep", - requirement("requests"), - requirement("numpy"), - ], -) -``` - -> Note that this convenience comes with a cost. -> Analysis of any BUILD file which loads the requirements helper in this way will -> cause an eager-fetch of all the pip dependencies, -> even if no python targets are requested to be built. -> In a multi-language repo, this may cause developers to fetch dependencies they don't need, -> so consider using the long form for dependencies if this happens. - -In addition to the `requirement` macro, which is used to access the `py_library` -target generated from a package's wheel, the generated `requirements.bzl` file contains -functionality for exposing [entry points][whl_ep] as `py_binary` targets. - -[whl_ep]: https://packaging.python.org/specifications/entry-points/ -```python -load("@pip_deps//:requirements.bzl", "entry_point") - -alias( - name = "pip-compile", - actual = entry_point( - pkg = "pip-tools", - script = "pip-compile", - ), -) -``` - -Note that for packages whose name and script are the same, only the name of the package -is needed when calling the `entry_point` macro. - -```python -load("@pip_deps//:requirements.bzl", "entry_point") +load("@pip_deps//:requirements.bzl", "install_deps") -alias( - name = "flake8", - actual = entry_point("flake8"), -) +install_deps() ``` @@ -143,18 +157,18 @@ alias( | Name | Description | Default Value | -| :-------------: | :-------------: | :-------------: | -| requirements | A 'requirements.txt' pip requirements file. | None | -| name | A unique name for the created external repository (default 'pip'). | "pip" | -| kwargs | Additional arguments to the [pip_repository](./pip_repository.md) repository rule. | none | +| :------------- | :------------- | :------------- | +| requirements | A 'requirements.txt' pip requirements file. | None | +| name | A unique name for the created external repository (default 'pip'). | "pip" | +| kwargs | Additional arguments to the [pip_repository](./pip_repository.md) repository rule. | none | - + ## pip_parse
-pip_parse(requirements_lock, name, kwargs)
+pip_parse(requirements, requirements_lock, name, kwargs)
 
Accepts a locked/compiled requirements file and installs the dependencies listed within. @@ -162,7 +176,7 @@ Accepts a locked/compiled requirements file and installs the dependencies listed Those dependencies become available in a generated `requirements.bzl` file. You can instead check this `requirements.bzl` file into your repo, see the "vendoring" section below. -This macro wraps the [`pip_repository`](./pip_repository.md) rule that invokes `pip`, with `incremental` set. +This macro wraps the [`pip_repository`](./pip_repository.md) rule that invokes `pip`. In your WORKSPACE file: ```python @@ -246,9 +260,10 @@ See the example in rules_python/examples/pip_parse_vendored. | Name | Description | Default Value | -| :-------------: | :-------------: | :-------------: | -| requirements_lock | A fully resolved 'requirements.txt' pip requirement file containing the transitive set of your dependencies. If this file is passed instead of 'requirements' no resolve will take place and pip_repository will create individual repositories for each of your dependencies so that wheels are fetched/built only for the targets specified by 'build/run/test'. Note that if your lockfile is platform-dependent, you can use the requirements_[platform] attributes. | none | -| name | The name of the generated repository. The generated repositories containing each requirement will be of the form <name>_<requirement-name>. | "pip_parsed_deps" | -| kwargs | Additional arguments to the [pip_repository](./pip_repository.md) repository rule. | none | +| :------------- | :------------- | :------------- | +| requirements | Deprecated. See requirements_lock. | None | +| requirements_lock | A fully resolved 'requirements.txt' pip requirement file containing the transitive set of your dependencies. If this file is passed instead of 'requirements' no resolve will take place and pip_repository will create individual repositories for each of your dependencies so that wheels are fetched/built only for the targets specified by 'build/run/test'. Note that if your lockfile is platform-dependent, you can use the requirements_[platform] attributes. | None | +| name | The name of the generated repository. The generated repositories containing each requirement will be of the form <name>_<requirement-name>. | "pip_parsed_deps" | +| kwargs | Additional arguments to the [pip_repository](./pip_repository.md) repository rule. | none | diff --git a/docs/pip_repository.md b/docs/pip_repository.md index ef7f72bcd2..29cb3d9c32 100644 --- a/docs/pip_repository.md +++ b/docs/pip_repository.md @@ -1,14 +1,17 @@ - + + + ## pip_repository
-pip_repository(name, annotations, enable_implicit_namespace_pkgs, environment, extra_pip_args,
-               incremental, isolated, pip_data_exclude, python_interpreter, python_interpreter_target,
-               quiet, repo_prefix, requirements, requirements_darwin, requirements_linux,
-               requirements_lock, requirements_windows, timeout)
+pip_repository(name, annotations, download_only, enable_implicit_namespace_pkgs, environment,
+               extra_pip_args, incompatible_generate_aliases, isolated, pip_data_exclude,
+               python_interpreter, python_interpreter_target, quiet, repo_mapping, repo_prefix,
+               requirements_darwin, requirements_linux, requirements_lock, requirements_windows,
+               timeout)
 
A rule for importing `requirements.txt` dependencies into Bazel. @@ -55,35 +58,61 @@ py_binary( | Name | Description | Type | Mandatory | Default | -| :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | -| name | A unique name for this repository. | Name | required | | -| annotations | Optional annotations to apply to packages | Dictionary: String -> String | optional | {} | -| enable_implicit_namespace_pkgs | If true, disables conversion of native namespace packages into pkg-util style namespace packages. When set all py_binary and py_test targets must specify either legacy_create_init=False or the global Bazel option --incompatible_default_to_explicit_init_py to prevent __init__.py being automatically generated in every directory.

This option is required to support some packages which cannot handle the conversion to pkg-util style. | Boolean | optional | False | -| environment | Environment variables to set in the pip subprocess. Can be used to set common variables such as http_proxy, https_proxy and no_proxy Note that pip is run with "--isolated" on the CLI so PIP_<VAR>_<NAME> style env vars are ignored, but env vars that control requests and urllib3 can be passed. | Dictionary: String -> String | optional | {} | -| extra_pip_args | Extra arguments to pass on to pip. Must not contain spaces. | List of strings | optional | [] | -| incremental | Create the repository in incremental mode. | Boolean | optional | False | -| 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 RULES_PYTHON_PIP_ISOLATED enviornment varaible can be used to control this flag. | Boolean | optional | True | -| pip_data_exclude | Additional data exclusion parameters to add to the pip packages BUILD file. | List of strings | optional | [] | -| 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 PATH environment variable. If no value is set python3 is defaulted for Unix systems and python.exe for Windows. | String | optional | "" | -| 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. | Label | optional | None | -| quiet | If True, suppress printing stdout and stderr output to the terminal. | Boolean | optional | True | -| repo_prefix | Prefix for the generated packages. For non-incremental mode the packages will be of the form

@<name>//<prefix><sanitized-package-name>/...

For incremental mode the packages will be of the form

@<prefix><sanitized-package-name>//... | String | optional | "" | -| requirements | A 'requirements.txt' pip requirements file. | Label | optional | None | -| requirements_darwin | Override the requirements_lock attribute when the host platform is Mac OS | Label | optional | None | -| requirements_linux | Override the requirements_lock attribute when the host platform is Linux | Label | optional | None | -| requirements_lock | A fully resolved 'requirements.txt' pip requirement file containing the transitive set of your dependencies. If this file is passed instead of 'requirements' no resolve will take place and pip_repository will create individual repositories for each of your dependencies so that wheels are fetched/built only for the targets specified by 'build/run/test'. | Label | optional | None | -| requirements_windows | Override the requirements_lock attribute when the host platform is Windows | Label | optional | None | -| timeout | Timeout (in seconds) on the rule's execution duration. | Integer | optional | 600 | - - - +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this repository. | Name | required | | +| annotations | Optional annotations to apply to packages | Dictionary: String -> String | optional | {} | +| download_only | Whether to use "pip download" instead of "pip wheel". Disables building wheels from source, but allows use of --platform, --python-version, --implementation, and --abi in --extra_pip_args to download wheels for a different platform from the host platform. | Boolean | optional | False | +| enable_implicit_namespace_pkgs | If true, disables conversion of native namespace packages into pkg-util style namespace packages. When set all py_binary and py_test targets must specify either legacy_create_init=False or the global Bazel option --incompatible_default_to_explicit_init_py to prevent __init__.py being automatically generated in every directory.

This option is required to support some packages which cannot handle the conversion to pkg-util style. | Boolean | optional | False | +| environment | Environment variables to set in the pip subprocess. Can be used to set common variables such as http_proxy, https_proxy and no_proxy Note that pip is run with "--isolated" on the CLI so PIP_<VAR>_<NAME> style env vars are ignored, but env vars that control requests and urllib3 can be passed. | Dictionary: String -> String | optional | {} | +| extra_pip_args | Extra arguments to pass on to pip. Must not contain spaces. | List of strings | optional | [] | +| incompatible_generate_aliases | Allow generating aliases '@pip//<pkg>' -> '@pip_<pkg>//:pkg'. | Boolean | optional | False | +| 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 RULES_PYTHON_PIP_ISOLATED environment variable can be used to control this flag. | Boolean | optional | True | +| pip_data_exclude | Additional data exclusion parameters to add to the pip packages BUILD file. | List of strings | optional | [] | +| 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 PATH environment variable. If no value is set python3 is defaulted for Unix systems and python.exe for Windows. | String | optional | "" | +| 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". | Label | optional | None | +| quiet | If True, suppress printing stdout and stderr output to the terminal. | Boolean | optional | True | +| repo_mapping | A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.<p>For example, an entry "@foo": "@bar" declares that, for any time this repository depends on @foo (such as a dependency on @foo//some:target, it should actually resolve that dependency within globally-declared @bar (@bar//some:target). | Dictionary: String -> String | required | | +| repo_prefix | Prefix for the generated packages will be of the form @<prefix><sanitized-package-name>//... | String | optional | "" | +| requirements_darwin | Override the requirements_lock attribute when the host platform is Mac OS | Label | optional | None | +| requirements_linux | Override the requirements_lock attribute when the host platform is Linux | Label | optional | None | +| requirements_lock | A fully resolved 'requirements.txt' pip requirement file containing the transitive set of your dependencies. If this file is passed instead of 'requirements' no resolve will take place and pip_repository will create individual repositories for each of your dependencies so that wheels are fetched/built only for the targets specified by 'build/run/test'. | Label | optional | None | +| requirements_windows | Override the requirements_lock attribute when the host platform is Windows | Label | optional | None | +| timeout | Timeout (in seconds) on the rule's execution duration. | Integer | optional | 600 | + + + + +## pip_repository_bzlmod + +
+pip_repository_bzlmod(name, incompatible_generate_aliases, repo_mapping, requirements_darwin,
+                      requirements_linux, requirements_lock, requirements_windows)
+
+ +A rule for bzlmod pip_repository creation. Intended for private use only. + +**ATTRIBUTES** + + +| Name | Description | Type | Mandatory | Default | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this repository. | Name | required | | +| incompatible_generate_aliases | Allow generating aliases in '@pip//:<pkg>' -> '@pip_<pkg>//:pkg'. This replaces the aliases generated by the bzlmod tooling. | Boolean | optional | False | +| repo_mapping | A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.<p>For example, an entry "@foo": "@bar" declares that, for any time this repository depends on @foo (such as a dependency on @foo//some:target, it should actually resolve that dependency within globally-declared @bar (@bar//some:target). | Dictionary: String -> String | required | | +| requirements_darwin | Override the requirements_lock attribute when the host platform is Mac OS | Label | optional | None | +| requirements_linux | Override the requirements_lock attribute when the host platform is Linux | Label | optional | None | +| requirements_lock | A fully resolved 'requirements.txt' pip requirement file containing the transitive set of your dependencies. If this file is passed instead of 'requirements' no resolve will take place and pip_repository will create individual repositories for each of your dependencies so that wheels are fetched/built only for the targets specified by 'build/run/test'. | Label | optional | None | +| requirements_windows | Override the requirements_lock attribute when the host platform is Windows | Label | optional | None | + + + ## whl_library
-whl_library(name, annotation, enable_implicit_namespace_pkgs, environment, extra_pip_args, isolated,
-            pip_data_exclude, python_interpreter, python_interpreter_target, quiet, repo, repo_prefix,
-            requirement, timeout)
+whl_library(name, annotation, download_only, enable_implicit_namespace_pkgs, environment,
+            extra_pip_args, isolated, pip_data_exclude, python_interpreter, python_interpreter_target,
+            quiet, repo, repo_mapping, repo_prefix, requirement, timeout)
 
@@ -94,24 +123,49 @@ Instantiated from pip_repository and inherits config options from there. | Name | Description | Type | Mandatory | Default | -| :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | -| name | A unique name for this repository. | Name | required | | -| annotation | Optional json encoded file containing annotation to apply to the extracted wheel. See package_annotation | Label | optional | None | -| enable_implicit_namespace_pkgs | If true, disables conversion of native namespace packages into pkg-util style namespace packages. When set all py_binary and py_test targets must specify either legacy_create_init=False or the global Bazel option --incompatible_default_to_explicit_init_py to prevent __init__.py being automatically generated in every directory.

This option is required to support some packages which cannot handle the conversion to pkg-util style. | Boolean | optional | False | -| environment | Environment variables to set in the pip subprocess. Can be used to set common variables such as http_proxy, https_proxy and no_proxy Note that pip is run with "--isolated" on the CLI so PIP_<VAR>_<NAME> style env vars are ignored, but env vars that control requests and urllib3 can be passed. | Dictionary: String -> String | optional | {} | -| extra_pip_args | Extra arguments to pass on to pip. Must not contain spaces. | List of strings | optional | [] | -| 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 RULES_PYTHON_PIP_ISOLATED enviornment varaible can be used to control this flag. | Boolean | optional | True | -| pip_data_exclude | Additional data exclusion parameters to add to the pip packages BUILD file. | List of strings | optional | [] | -| 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 PATH environment variable. If no value is set python3 is defaulted for Unix systems and python.exe for Windows. | String | optional | "" | -| 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. | Label | optional | None | -| quiet | If True, suppress printing stdout and stderr output to the terminal. | Boolean | optional | True | -| repo | Pointer to parent repo name. Used to make these rules rerun if the parent repo changes. | String | required | | -| repo_prefix | Prefix for the generated packages. For non-incremental mode the packages will be of the form

@<name>//<prefix><sanitized-package-name>/...

For incremental mode the packages will be of the form

@<prefix><sanitized-package-name>//... | String | optional | "" | -| requirement | Python requirement string describing the package to make available | String | required | | -| timeout | Timeout (in seconds) on the rule's execution duration. | Integer | optional | 600 | - - - +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this repository. | Name | required | | +| annotation | Optional json encoded file containing annotation to apply to the extracted wheel. See package_annotation | Label | optional | None | +| download_only | Whether to use "pip download" instead of "pip wheel". Disables building wheels from source, but allows use of --platform, --python-version, --implementation, and --abi in --extra_pip_args to download wheels for a different platform from the host platform. | Boolean | optional | False | +| enable_implicit_namespace_pkgs | If true, disables conversion of native namespace packages into pkg-util style namespace packages. When set all py_binary and py_test targets must specify either legacy_create_init=False or the global Bazel option --incompatible_default_to_explicit_init_py to prevent __init__.py being automatically generated in every directory.

This option is required to support some packages which cannot handle the conversion to pkg-util style. | Boolean | optional | False | +| environment | Environment variables to set in the pip subprocess. Can be used to set common variables such as http_proxy, https_proxy and no_proxy Note that pip is run with "--isolated" on the CLI so PIP_<VAR>_<NAME> style env vars are ignored, but env vars that control requests and urllib3 can be passed. | Dictionary: String -> String | optional | {} | +| extra_pip_args | Extra arguments to pass on to pip. Must not contain spaces. | List of strings | optional | [] | +| 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 RULES_PYTHON_PIP_ISOLATED environment variable can be used to control this flag. | Boolean | optional | True | +| pip_data_exclude | Additional data exclusion parameters to add to the pip packages BUILD file. | List of strings | optional | [] | +| 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 PATH environment variable. If no value is set python3 is defaulted for Unix systems and python.exe for Windows. | String | optional | "" | +| 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". | Label | optional | None | +| quiet | If True, suppress printing stdout and stderr output to the terminal. | Boolean | optional | True | +| repo | Pointer to parent repo name. Used to make these rules rerun if the parent repo changes. | String | required | | +| repo_mapping | A dictionary from local repository name to global repository name. This allows controls over workspace dependency resolution for dependencies of this repository.<p>For example, an entry "@foo": "@bar" declares that, for any time this repository depends on @foo (such as a dependency on @foo//some:target, it should actually resolve that dependency within globally-declared @bar (@bar//some:target). | Dictionary: String -> String | required | | +| repo_prefix | Prefix for the generated packages will be of the form @<prefix><sanitized-package-name>//... | String | optional | "" | +| requirement | Python requirement string describing the package to make available | String | required | | +| timeout | Timeout (in seconds) on the rule's execution duration. | Integer | optional | 600 | + + + + +## locked_requirements_label + +
+locked_requirements_label(ctx, attr)
+
+ +Get the preferred label for a locked requirements file based on platform. + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| ctx | repository or module context | none | +| attr | attributes for the repo rule or tag extension | none | + +**RETURNS** + +Label + + + ## package_annotation @@ -129,12 +183,39 @@ Annotations to apply to the BUILD file content from package generated from a `pi | Name | Description | Default Value | -| :-------------: | :-------------: | :-------------: | -| additive_build_content | Raw text to add to the generated BUILD file of a package. | None | -| copy_files | A mapping of src and out files for [@bazel_skylib//rules:copy_file.bzl][cf] | {} | -| copy_executables | A mapping of src and out files for [@bazel_skylib//rules:copy_file.bzl][cf]. Targets generated here will also be flagged as executable. | {} | -| data | A list of labels to add as data dependencies to the generated py_library target. | [] | -| data_exclude_glob | A list of exclude glob patterns to add as data to the generated py_library target. | [] | -| srcs_exclude_glob | A list of labels to add as srcs to the generated py_library target. | [] | +| :------------- | :------------- | :------------- | +| additive_build_content | Raw text to add to the generated BUILD file of a package. | None | +| copy_files | A mapping of src and out files for [@bazel_skylib//rules:copy_file.bzl][cf] | {} | +| copy_executables | A mapping of src and out files for [@bazel_skylib//rules:copy_file.bzl][cf]. Targets generated here will also be flagged as executable. | {} | +| data | A list of labels to add as data dependencies to the generated py_library target. | [] | +| data_exclude_glob | A list of exclude glob patterns to add as data to the generated py_library target. | [] | +| srcs_exclude_glob | A list of labels to add as srcs to the generated py_library target. | [] | + +**RETURNS** + +str: A json encoded string of the provided content. + + + + +## use_isolated + +
+use_isolated(ctx, attr)
+
+ +Determine whether or not to pass the pip `--isolated` flag to the pip invocation. + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| ctx | repository or module context | none | +| attr | attributes for the repo rule or tag extension | none | + +**RETURNS** + +True if --isolated should be passed diff --git a/docs/python.md b/docs/python.md index bd14b8258e..e42375ad60 100755 --- a/docs/python.md +++ b/docs/python.md @@ -1,6 +1,8 @@ - +Core rules for building Python projects. + + ## current_py_toolchain @@ -19,11 +21,11 @@ current_py_toolchain(name) | Name | Description | Type | Mandatory | Default | -| :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | -| name | A unique name for this target. | Name | required | | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | - + ## py_import @@ -45,26 +47,79 @@ This rule allows the use of Python packages as dependencies. | Name | Description | Type | Mandatory | Default | -| :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | -| name | A unique name for this target. | Name | required | | -| deps | The list of other libraries to be linked in to the binary target. | List of labels | optional | [] | -| srcs | The list of Python package files provided to Python targets that depend on this target. Note that currently only the .egg format is accepted. For .whl files, try the whl_library rule. We accept contributions to extend py_import to handle .whl. | List of labels | optional | [] | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | +| deps | The list of other libraries to be linked in to the binary target. | List of labels | optional | [] | +| srcs | The list of Python package files provided to Python targets that depend on this target. Note that currently only the .egg format is accepted. For .whl files, try the whl_library rule. We accept contributions to extend py_import to handle .whl. | List of labels | optional | [] | + + + + +## py_binary + +
+py_binary(attrs)
+
+ +See the Bazel core [py_binary](https://docs.bazel.build/versions/master/be/python.html#py_binary) documentation. + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| attrs | Rule attributes | none | + + + +## py_library + +
+py_library(attrs)
+
- +See the Bazel core [py_library](https://docs.bazel.build/versions/master/be/python.html#py_library) documentation. + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| attrs | Rule attributes | none | + + + + +## py_runtime + +
+py_runtime(attrs)
+
+ +See the Bazel core [py_runtime](https://docs.bazel.build/versions/master/be/python.html#py_runtime) documentation. + +**PARAMETERS** + + +| Name | Description | Default Value | +| :------------- | :------------- | :------------- | +| attrs | Rule attributes | none | + + + ## py_runtime_pair
-py_runtime_pair(name, py2_runtime, py3_runtime)
+py_runtime_pair(name, py2_runtime, py3_runtime, attrs)
 
A toolchain rule for Python. -This wraps up to two Python runtimes, one for Python 2 and one for Python 3. -The rule consuming this toolchain will choose which runtime is appropriate. -Either runtime may be omitted, in which case the resulting toolchain will be -unusable for building Python code using that version. +This used to wrap up to two Python runtimes, one for Python 2 and one for Python 3. +However, Python 2 is no longer supported, so it now only wraps a single Python 3 +runtime. Usually the wrapped runtimes are declared using the `py_runtime` rule, but any rule returning a `PyRuntimeInfo` provider may be used. @@ -74,8 +129,8 @@ schema: ```python platform_common.ToolchainInfo( - py2_runtime = , - py3_runtime = , + py2_runtime = None, + py3_runtime = <PyRuntimeInfo or None>, ) ``` @@ -86,12 +141,6 @@ Example usage: load("@rules_python//python:defs.bzl", "py_runtime_pair") -py_runtime( - name = "my_py2_runtime", - interpreter_path = "/system/python2", - python_version = "PY2", -) - py_runtime( name = "my_py3_runtime", interpreter_path = "/system/python3", @@ -100,13 +149,12 @@ py_runtime( py_runtime_pair( name = "my_py_runtime_pair", - py2_runtime = ":my_py2_runtime", py3_runtime = ":my_py3_runtime", ) toolchain( name = "my_toolchain", - target_compatible_with = <...>, + target_compatible_with = <...>, toolchain = ":my_py_runtime_pair", toolchain_type = "@rules_python//python:toolchain_type", ) @@ -119,71 +167,18 @@ register_toolchains("//my_pkg:my_toolchain") ``` -**ATTRIBUTES** - - -| Name | Description | Type | Mandatory | Default | -| :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | -| name | A unique name for this target. | Name | required | | -| py2_runtime | The runtime to use for Python 2 targets. Must have python_version set to PY2. | Label | optional | None | -| py3_runtime | The runtime to use for Python 3 targets. Must have python_version set to PY3. | Label | optional | None | - - - - -## py_binary - -
-py_binary(attrs)
-
- -See the Bazel core [py_binary](https://docs.bazel.build/versions/master/be/python.html#py_binary) documentation. - -**PARAMETERS** - - -| Name | Description | Default Value | -| :-------------: | :-------------: | :-------------: | -| attrs | Rule attributes | none | - - - - -## py_library - -
-py_library(attrs)
-
- -See the Bazel core [py_library](https://docs.bazel.build/versions/master/be/python.html#py_library) documentation. - -**PARAMETERS** - - -| Name | Description | Default Value | -| :-------------: | :-------------: | :-------------: | -| attrs | Rule attributes | none | - - - - -## py_runtime - -
-py_runtime(attrs)
-
- -See the Bazel core [py_runtime](https://docs.bazel.build/versions/master/be/python.html#py_runtime) documentation. - **PARAMETERS** | Name | Description | Default Value | -| :-------------: | :-------------: | :-------------: | -| attrs | Rule attributes | none | +| :------------- | :------------- | :------------- | +| name | str, the name of the target | none | +| py2_runtime | optional Label; must be unset or None; an error is raised otherwise. | None | +| py3_runtime | Label; a target with PyRuntimeInfo for Python 3. | None | +| attrs | Extra attrs passed onto the native rule | none | - + ## py_test @@ -197,11 +192,11 @@ See the Bazel core [py_test](https://docs.bazel.build/versions/master/be/python. | Name | Description | Default Value | -| :-------------: | :-------------: | :-------------: | -| attrs | Rule attributes | none | +| :------------- | :------------- | :------------- | +| attrs | Rule attributes | none | - + ## find_requirements @@ -218,7 +213,7 @@ The aspect definition. Can be invoked on the command line as | Name | Type | -| :-------------: | :-------------: | +| :------------- | :------------- | | deps| String | @@ -226,7 +221,7 @@ The aspect definition. Can be invoked on the command line as | Name | Description | Type | Mandatory | Default | -| :-------------: | :-------------: | :-------------: | :-------------: | :-------------: | -| name | A unique name for this target. | Name | required | | +| :------------- | :------------- | :------------- | :------------- | :------------- | +| name | A unique name for this target. | Name | required | | diff --git a/examples/BUILD b/examples/BUILD.bazel similarity index 70% rename from examples/BUILD rename to examples/BUILD.bazel index 41dd87505a..feb1cfbd4e 100644 --- a/examples/BUILD +++ b/examples/BUILD.bazel @@ -17,6 +17,16 @@ package(default_visibility = ["//visibility:public"]) licenses(["notice"]) # Apache 2.0 +bazel_integration_test( + name = "build_file_generation_example", + timeout = "long", +) + +bazel_integration_test( + name = "bzlmod_build_file_generation_example", + timeout = "long", +) + bazel_integration_test( name = "pip_install_example", timeout = "long", @@ -39,10 +49,24 @@ bazel_integration_test( ) bazel_integration_test( - name = "relative_requirements_example", + name = "py_proto_library_example", + timeout = "long", +) + +bazel_integration_test( + name = "py_proto_library_example_bzlmod", + timeout = "long", + bzlmod = True, + dirname = "py_proto_library", +) + +bazel_integration_test( + name = "multi_python_versions_example", timeout = "long", ) bazel_integration_test( name = "bzlmod_example", + bzlmod = True, + override_bazel_version = "6.0.0", ) diff --git a/examples/build_file_generation/.bazelrc b/examples/build_file_generation/.bazelrc new file mode 100644 index 0000000000..28f634bef6 --- /dev/null +++ b/examples/build_file_generation/.bazelrc @@ -0,0 +1,5 @@ +test --test_output=errors --enable_runfiles + +# Windows requires these for multi-python support: +build --enable_runfiles +startup --windows_enable_symlinks diff --git a/examples/build_file_generation/BUILD b/examples/build_file_generation/BUILD deleted file mode 100644 index ef9e967d5a..0000000000 --- a/examples/build_file_generation/BUILD +++ /dev/null @@ -1,56 +0,0 @@ -load("@bazel_gazelle//:def.bzl", "gazelle") -load("@pip//:requirements.bzl", "all_whl_requirements") -load("@rules_python//gazelle:def.bzl", "GAZELLE_PYTHON_RUNTIME_DEPS") -load("@rules_python//gazelle/manifest:defs.bzl", "gazelle_python_manifest") -load("@rules_python//gazelle/modules_mapping:def.bzl", "modules_mapping") -load("@rules_python//python:defs.bzl", "py_binary", "py_library") - -# This 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", - 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 -gazelle_python_manifest( - name = "gazelle_python_manifest", - modules_mapping = ":modules_map", - pip_repository_incremental = True, - pip_repository_name = "pip", - requirements = "//:requirements_lock.txt", -) - -# 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 -gazelle( - name = "gazelle", - data = GAZELLE_PYTHON_RUNTIME_DEPS, - gazelle = "@rules_python//gazelle:gazelle_python_binary", -) - -# This rule is auto-generated and managed by Gazelle, -# because it found the __init__.py file in this folder. -py_library( - name = "build_file_generation", - srcs = ["__init__.py"], - visibility = ["//:__subpackages__"], - deps = ["@pip_requests//:pkg"], -) - -py_binary( - name = "build_file_generation_bin", - srcs = ["__main__.py"], - main = "__main__.py", - visibility = ["//:__subpackages__"], - deps = [":build_file_generation"], -) diff --git a/examples/build_file_generation/BUILD.bazel b/examples/build_file_generation/BUILD.bazel new file mode 100644 index 0000000000..7c88d9203d --- /dev/null +++ b/examples/build_file_generation/BUILD.bazel @@ -0,0 +1,104 @@ +# 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. +load("@bazel_gazelle//:def.bzl", "gazelle") +load("@pip//:requirements.bzl", "all_whl_requirements") +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") + +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. +# To see what this rule does, try `bazel run @modules_map//:print` +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 +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 +gazelle( + name = "gazelle", + data = GAZELLE_PYTHON_RUNTIME_DEPS, + gazelle = "@rules_python_gazelle_plugin//python:gazelle_binary", +) + +# This rule is auto-generated and managed by Gazelle, +# because it found the __init__.py file in this folder. +# See: https://bazel.build/reference/be/python#py_library +py_library( + name = "build_file_generation", + srcs = ["__init__.py"], + visibility = ["//:__subpackages__"], + deps = [ + "//random_number_generator", + "@pip//flask", + ], +) + +# A py_binary is an executable Python program consisting of a collection of .py source files. +# See: https://bazel.build/reference/be/python#py_binary +# +# This rule is auto-generated and managed by Gazelle, +# because it found the __main__.py file in this folder. +# This rule creates a target named //:build_file_generation_bin and you can use +# bazel to run the target: +# `bazel run //:build_file_generation_bin` +py_binary( + name = "build_file_generation_bin", + srcs = ["__main__.py"], + main = "__main__.py", + visibility = ["//:__subpackages__"], + deps = [":build_file_generation"], +) + +# A py_test is a Python unit test. +# See: https://bazel.build/reference/be/python#py_test +# +# This rule is auto-generated and managed by Gazelle, +# because it found the __test__.py file in this folder. +# This rule creates a target named //:build_file_generation_test and you can use +# bazel to run the target: +# `bazel test //:build_file_generation_test` +py_test( + name = "build_file_generation_test", + srcs = ["__test__.py"], + main = "__test__.py", + deps = [":build_file_generation"], +) diff --git a/examples/build_file_generation/WORKSPACE b/examples/build_file_generation/WORKSPACE index 51c923f133..65e0a6e5f3 100644 --- a/examples/build_file_generation/WORKSPACE +++ b/examples/build_file_generation/WORKSPACE @@ -1,66 +1,121 @@ +# Set the name of the bazel workspace. workspace(name = "build_file_generation_example") +# Load the http_archive rule so that we can have bazel download +# various rulesets and dependencies. +# The `load` statement imports the symbol for http_archive from the http.bzl +# file. When the symbol is loaded you can use the rule. load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") ###################################################################### # We need rules_go and bazel_gazelle, to build the gazelle plugin from source. # Setup instructions for this section are at # https://github.com/bazelbuild/bazel-gazelle#running-gazelle-with-bazel +# You may need to update the version of the rule, which is listed in the above +# documentation. +###################################################################### -# Note, you could omit the rules_go dependency, if you have some way to statically -# compile the gazelle binary for your workspace and distribute it to users on all -# needed platforms. +# Define an http_archive rule that will download the below ruleset, +# test the sha, and extract the ruleset to you local bazel cache. http_archive( name = "io_bazel_rules_go", - sha256 = "69de5c704a05ff37862f7e0f5534d4f479418afc21806c887db544a316f3cb6b", + sha256 = "099a9fb96a376ccbbb7d291ed4ecbdfd42f6bc822ab77ae6f1b5cb9e914e94fa", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.27.0/rules_go-v0.27.0.tar.gz", - "https://github.com/bazelbuild/rules_go/releases/download/v0.27.0/rules_go-v0.27.0.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.35.0/rules_go-v0.35.0.zip", + "https://github.com/bazelbuild/rules_go/releases/download/v0.35.0/rules_go-v0.35.0.zip", ], ) -# NB: bazel-gazelle version must be after 18 August 2021 -# to include https://github.com/bazelbuild/bazel-gazelle/commit/2834ea4 +# Download the bazel_gazelle ruleset. http_archive( name = "bazel_gazelle", - sha256 = "fd8d852ebcb770b41c1c933fc3085b4a23e1426a1af4e791d39b67bb8d894eb7", - strip_prefix = "bazel-gazelle-41b542f9b0fefe916a95ca5460458abf916f5fe5", + sha256 = "448e37e0dbf61d6fa8f00aaa12d191745e14f07c31cabfa731f0c8e8a4f41b97", urls = [ - # No release since March, and we need subsequent fixes - "https://github.com/bazelbuild/bazel-gazelle/archive/41b542f9b0fefe916a95ca5460458abf916f5fe5.zip", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.28.0/bazel-gazelle-v0.28.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.28.0/bazel-gazelle-v0.28.0.tar.gz", ], ) +# Load rules_go ruleset and expose the toolchain and dep rules. load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") +# go_rules_dependencies is a function that registers external dependencies +# needed by the Go rules. +# See: https://github.com/bazelbuild/rules_go/blob/master/go/dependencies.rst#go_rules_dependencies go_rules_dependencies() -go_register_toolchains(version = "1.16.5") +# go_rules_dependencies is a function that registers external dependencies +# needed by the Go rules. +# See: https://github.com/bazelbuild/rules_go/blob/master/go/dependencies.rst#go_rules_dependencies +go_register_toolchains(version = "1.19.4") +# The following call configured the gazelle dependencies, Go environment and Go SDK. gazelle_dependencies() -###################################################################### -# Remaining setup is for rules_python +# Remaining setup is for rules_python. +# DON'T COPY_PASTE THIS. +# Our example uses `local_repository` to point to the HEAD version of rules_python. +# Users should instead use the installation instructions from the release they use. +# See https://github.com/bazelbuild/rules_python/releases local_repository( name = "rules_python", path = "../..", ) +local_repository( + name = "rules_python_gazelle_plugin", + path = "../../gazelle", +) + +# Next we load the toolchain from rules_python. +load("@rules_python//python:repositories.bzl", "python_register_toolchains") + +# We now register a hermetic Python interpreter rather than relying on a system-installed interpreter. +# This toolchain will allow bazel to download a specific python version, and use that version +# for compilation. +python_register_toolchains( + name = "python39", + python_version = "3.9", +) + +# Load the interpreter and pip_parse rules. +load("@python39//:defs.bzl", "interpreter") load("@rules_python//python:pip.bzl", "pip_parse") +# This macro wraps 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. pip_parse( name = "pip", + # Generate user friendly alias labels for each dependency that we have. + incompatible_generate_aliases = True, + # (Optional) You can provide a python_interpreter (path) or a python_interpreter_target (a Bazel target, that + # acts as an executable). The latter can be anything that could be used as Python interpreter. E.g.: + # 1. Python interpreter that you compile in the build file. + # 2. Pre-compiled python interpreter included with http_archive. + # 3. Wrapper script, like in the autodetecting python toolchain. + # + # Here, we use the interpreter constant that resolves to the host interpreter from the default Python toolchain. + python_interpreter_target = interpreter, + # Set the location of the lock file. requirements_lock = "//:requirements_lock.txt", + requirements_windows = "//:requirements_windows.txt", ) +# Load the install_deps macro. load("@pip//:requirements.bzl", "install_deps") +# Initialize repositories for all packages in requirements_lock.txt. install_deps() # The rules_python gazelle extension has some third-party go dependencies # which we need to fetch in order to compile it. -load("@rules_python//gazelle:deps.bzl", _py_gazelle_deps = "gazelle_deps") +load("@rules_python_gazelle_plugin//:deps.bzl", _py_gazelle_deps = "gazelle_deps") +# See: https://github.com/bazelbuild/rules_python/blob/main/gazelle/README.md +# This rule loads and compiles various go dependencies that running gazelle +# for python requirements. _py_gazelle_deps() diff --git a/examples/build_file_generation/__init__.py b/examples/build_file_generation/__init__.py index 851fefb528..add73dafcc 100644 --- a/examples/build_file_generation/__init__.py +++ b/examples/build_file_generation/__init__.py @@ -1,5 +1,26 @@ -import requests +# Copyright 2022 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. -def main(url): - r = requests.get(url) - print(r.text) +from flask import Flask, jsonify +from random_number_generator import generate_random_number + +app = Flask(__name__) + +@app.route('/random-number', methods=['GET']) +def get_random_number(): + return jsonify({'number': generate_random_number.generate_random_number()}) + +"""Start the python web server""" +def main(): + app.run() diff --git a/examples/build_file_generation/__main__.py b/examples/build_file_generation/__main__.py index 8b6189cbbc..8f8efbaaa3 100644 --- a/examples/build_file_generation/__main__.py +++ b/examples/build_file_generation/__main__.py @@ -1,5 +1,18 @@ -from __init__ import main +# Copyright 2022 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 __init__ import main -if __name__ == "__main__": - main("https://example.com") +if __name__ == '__main__': + main() diff --git a/examples/build_file_generation/__test__.py b/examples/build_file_generation/__test__.py new file mode 100644 index 0000000000..c4fa5ef9b5 --- /dev/null +++ b/examples/build_file_generation/__test__.py @@ -0,0 +1,28 @@ +# Copyright 2022 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 __init__ import app + +class TestServer(unittest.TestCase): + def setUp(self): + self.app = app.test_client() + + def test_get_random_number(self): + response = self.app.get('/random-number') + self.assertEqual(response.status_code, 200) + self.assertIn('number', response.json) + +if __name__ == '__main__': + unittest.main() diff --git a/examples/build_file_generation/gazelle_python.yaml b/examples/build_file_generation/gazelle_python.yaml index a005b43d0f..1000757ea5 100644 --- a/examples/build_file_generation/gazelle_python.yaml +++ b/examples/build_file_generation/gazelle_python.yaml @@ -5,128 +5,114 @@ manifest: modules_mapping: - certifi: certifi - certifi.__init__: certifi - certifi.__main__: certifi - certifi.core: certifi - chardet: chardet - chardet.__init__: chardet - chardet.big5freq: chardet - chardet.big5prober: chardet - chardet.chardistribution: chardet - chardet.charsetgroupprober: chardet - chardet.charsetprober: chardet - chardet.cli: chardet - chardet.cli.__init__: 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.langcyrillicmodel: chardet - chardet.langgreekmodel: chardet - chardet.langhebrewmodel: chardet - chardet.langhungarianmodel: chardet - chardet.langthaimodel: chardet - chardet.langturkishmodel: chardet - chardet.latin1prober: chardet - chardet.mbcharsetprober: chardet - chardet.mbcsgroupprober: chardet - chardet.mbcssm: chardet - chardet.sbcharsetprober: chardet - chardet.sbcsgroupprober: chardet - chardet.sjisprober: chardet - chardet.universaldetector: chardet - chardet.utf8prober: chardet - chardet.version: chardet - idna: idna - idna.__init__: idna - idna.codec: idna - idna.compat: idna - idna.core: idna - idna.idnadata: idna - idna.intranges: idna - idna.package_data: idna - idna.uts46data: idna - requests: requests - requests.__init__: requests - requests.__version__: requests - requests._internal_utils: 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 - urllib3: urllib3 - urllib3.__init__: urllib3 - urllib3._collections: urllib3 - urllib3._version: urllib3 - urllib3.connection: urllib3 - urllib3.connectionpool: urllib3 - urllib3.contrib: urllib3 - urllib3.contrib.__init__: urllib3 - urllib3.contrib._appengine_environ: urllib3 - urllib3.contrib._securetransport: urllib3 - urllib3.contrib._securetransport.__init__: urllib3 - urllib3.contrib._securetransport.bindings: urllib3 - urllib3.contrib._securetransport.low_level: 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.__init__: urllib3 - urllib3.packages.backports: urllib3 - urllib3.packages.backports.__init__: urllib3 - urllib3.packages.backports.makefile: urllib3 - urllib3.packages.six: urllib3 - urllib3.packages.ssl_match_hostname: urllib3 - urllib3.packages.ssl_match_hostname.__init__: urllib3 - urllib3.packages.ssl_match_hostname._implementation: urllib3 - urllib3.poolmanager: urllib3 - urllib3.request: urllib3 - urllib3.response: urllib3 - urllib3.util: urllib3 - urllib3.util.__init__: 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.ssltransport: urllib3 - urllib3.util.timeout: urllib3 - urllib3.util.url: urllib3 - urllib3.util.wait: urllib3 + click: click + click.core: click + click.decorators: click + click.exceptions: click + click.formatting: click + click.globals: click + click.parser: click + click.shell_completion: click + click.termui: click + click.testing: click + click.types: click + click.utils: click + flask: Flask + flask.app: Flask + flask.blueprints: Flask + flask.cli: Flask + flask.config: Flask + flask.ctx: Flask + flask.debughelpers: Flask + flask.globals: Flask + flask.helpers: Flask + flask.json: Flask + flask.json.provider: Flask + flask.json.tag: Flask + flask.logging: Flask + flask.scaffold: Flask + flask.sessions: Flask + flask.signals: Flask + flask.templating: Flask + flask.testing: Flask + flask.typing: Flask + flask.views: Flask + flask.wrappers: Flask + importlib_metadata: importlib_metadata + itsdangerous: itsdangerous + itsdangerous.encoding: itsdangerous + itsdangerous.exc: itsdangerous + itsdangerous.serializer: itsdangerous + itsdangerous.signer: itsdangerous + itsdangerous.timed: itsdangerous + itsdangerous.url_safe: itsdangerous + jinja2: Jinja2 + jinja2.async_utils: Jinja2 + jinja2.bccache: Jinja2 + jinja2.compiler: Jinja2 + jinja2.constants: Jinja2 + jinja2.debug: Jinja2 + jinja2.defaults: Jinja2 + jinja2.environment: Jinja2 + jinja2.exceptions: Jinja2 + jinja2.ext: Jinja2 + jinja2.filters: Jinja2 + jinja2.idtracking: Jinja2 + jinja2.lexer: Jinja2 + jinja2.loaders: Jinja2 + jinja2.meta: Jinja2 + jinja2.nativetypes: Jinja2 + jinja2.nodes: Jinja2 + jinja2.optimizer: Jinja2 + jinja2.parser: Jinja2 + jinja2.runtime: Jinja2 + jinja2.sandbox: Jinja2 + jinja2.utils: Jinja2 + jinja2.visitor: Jinja2 + markupsafe: MarkupSafe + werkzeug: Werkzeug + werkzeug.datastructures: Werkzeug + werkzeug.debug: Werkzeug + werkzeug.debug.console: Werkzeug + werkzeug.debug.repr: Werkzeug + werkzeug.debug.tbtools: Werkzeug + werkzeug.exceptions: Werkzeug + werkzeug.formparser: Werkzeug + werkzeug.http: Werkzeug + werkzeug.local: Werkzeug + werkzeug.middleware: Werkzeug + werkzeug.middleware.dispatcher: Werkzeug + werkzeug.middleware.http_proxy: Werkzeug + werkzeug.middleware.lint: Werkzeug + werkzeug.middleware.profiler: Werkzeug + werkzeug.middleware.proxy_fix: Werkzeug + werkzeug.middleware.shared_data: Werkzeug + werkzeug.routing: Werkzeug + werkzeug.routing.converters: Werkzeug + werkzeug.routing.exceptions: Werkzeug + werkzeug.routing.map: Werkzeug + werkzeug.routing.matcher: Werkzeug + werkzeug.routing.rules: Werkzeug + werkzeug.sansio: Werkzeug + werkzeug.sansio.http: Werkzeug + werkzeug.sansio.multipart: Werkzeug + werkzeug.sansio.request: Werkzeug + werkzeug.sansio.response: Werkzeug + werkzeug.sansio.utils: Werkzeug + werkzeug.security: Werkzeug + werkzeug.serving: Werkzeug + werkzeug.test: Werkzeug + werkzeug.testapp: Werkzeug + werkzeug.urls: Werkzeug + werkzeug.user_agent: Werkzeug + werkzeug.utils: Werkzeug + werkzeug.wrappers: Werkzeug + werkzeug.wrappers.request: Werkzeug + werkzeug.wrappers.response: Werkzeug + werkzeug.wsgi: Werkzeug + zipp: zipp + zipp.py310compat: zipp pip_repository: name: pip - incremental: true -integrity: c47bf2ca0a185cf6b8815d4a61e26e7457564e931de76c70653277e4eccfadc8 + use_pip_repository_aliases: true +integrity: 030d6d99b56c32d6577e616b617260d0a93588af791269162e43391a5a4fa576 diff --git a/examples/build_file_generation/random_number_generator/BUILD.bazel b/examples/build_file_generation/random_number_generator/BUILD.bazel new file mode 100644 index 0000000000..95e16fd301 --- /dev/null +++ b/examples/build_file_generation/random_number_generator/BUILD.bazel @@ -0,0 +1,19 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +py_library( + name = "random_number_generator", + srcs = [ + "__init__.py", + "generate_random_number.py", + ], + imports = [".."], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "random_number_generator_test", + srcs = ["__test__.py"], + imports = [".."], + main = "__test__.py", + deps = [":random_number_generator"], +) diff --git a/examples/build_file_generation/random_number_generator/__init__.py b/examples/build_file_generation/random_number_generator/__init__.py new file mode 100644 index 0000000000..bbdfb4c588 --- /dev/null +++ b/examples/build_file_generation/random_number_generator/__init__.py @@ -0,0 +1,14 @@ +# 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. + diff --git a/examples/build_file_generation/random_number_generator/__test__.py b/examples/build_file_generation/random_number_generator/__test__.py new file mode 100644 index 0000000000..8cfb235d57 --- /dev/null +++ b/examples/build_file_generation/random_number_generator/__test__.py @@ -0,0 +1,25 @@ +# Copyright 2022 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 +import random_number_generator.generate_random_number as generate_random_number + +class TestRandomNumberGenerator(unittest.TestCase): + def test_generate_random_number(self): + number = generate_random_number.generate_random_number() + self.assertGreaterEqual(number, 1) + self.assertLessEqual(number, 10) + +if __name__ == '__main__': + unittest.main() diff --git a/examples/build_file_generation/random_number_generator/generate_random_number.py b/examples/build_file_generation/random_number_generator/generate_random_number.py new file mode 100644 index 0000000000..e198b5bbcd --- /dev/null +++ b/examples/build_file_generation/random_number_generator/generate_random_number.py @@ -0,0 +1,19 @@ +# Copyright 2022 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 random + +"""Generate a random number""" +def generate_random_number(): + return random.randint(1, 10) diff --git a/examples/build_file_generation/requirements.in b/examples/build_file_generation/requirements.in new file mode 100644 index 0000000000..7e1060246f --- /dev/null +++ b/examples/build_file_generation/requirements.in @@ -0,0 +1 @@ +flask diff --git a/examples/build_file_generation/requirements.txt b/examples/build_file_generation/requirements.txt deleted file mode 100644 index 9d84d35885..0000000000 --- a/examples/build_file_generation/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -requests==2.25.1 diff --git a/examples/build_file_generation/requirements_lock.txt b/examples/build_file_generation/requirements_lock.txt index b66c41fef9..443db71ddc 100644 --- a/examples/build_file_generation/requirements_lock.txt +++ b/examples/build_file_generation/requirements_lock.txt @@ -1,26 +1,78 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # -# pip-compile --generate-hashes --output-file=requirements_lock.txt requirements.txt +# bazel run //:requirements.update # -certifi==2020.12.5 \ - --hash=sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c \ - --hash=sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830 - # via requests -chardet==3.0.4 \ - --hash=sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae \ - --hash=sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691 - # via requests -idna==2.10 \ - --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \ - --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 - # via requests -requests==2.25.1 \ - --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 \ - --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e - # via -r requirements.txt -urllib3==1.26.5 \ - --hash=sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c \ - --hash=sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098 - # via requests +click==8.1.3 \ + --hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \ + --hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48 + # via flask +flask==2.2.2 \ + --hash=sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b \ + --hash=sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526 + # via -r requirements.in +importlib-metadata==5.2.0 \ + --hash=sha256:0eafa39ba42bf225fc00e67f701d71f85aead9f878569caf13c3724f704b970f \ + --hash=sha256:404d48d62bba0b7a77ff9d405efd91501bef2e67ff4ace0bed40a0cf28c3c7cd + # via flask +itsdangerous==2.1.2 \ + --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \ + --hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a + # via flask +jinja2==3.1.2 \ + --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ + --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 + # via flask +markupsafe==2.1.1 \ + --hash=sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003 \ + --hash=sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88 \ + --hash=sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5 \ + --hash=sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7 \ + --hash=sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a \ + --hash=sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603 \ + --hash=sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1 \ + --hash=sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135 \ + --hash=sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247 \ + --hash=sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6 \ + --hash=sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601 \ + --hash=sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77 \ + --hash=sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02 \ + --hash=sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e \ + --hash=sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63 \ + --hash=sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f \ + --hash=sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980 \ + --hash=sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b \ + --hash=sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812 \ + --hash=sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff \ + --hash=sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96 \ + --hash=sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1 \ + --hash=sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925 \ + --hash=sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a \ + --hash=sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6 \ + --hash=sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e \ + --hash=sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f \ + --hash=sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4 \ + --hash=sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f \ + --hash=sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3 \ + --hash=sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c \ + --hash=sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a \ + --hash=sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417 \ + --hash=sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a \ + --hash=sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a \ + --hash=sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37 \ + --hash=sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452 \ + --hash=sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933 \ + --hash=sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a \ + --hash=sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7 + # via + # jinja2 + # werkzeug +werkzeug==2.2.2 \ + --hash=sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f \ + --hash=sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5 + # via flask +zipp==3.11.0 \ + --hash=sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa \ + --hash=sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766 + # via importlib-metadata diff --git a/examples/build_file_generation/requirements_windows.txt b/examples/build_file_generation/requirements_windows.txt new file mode 100644 index 0000000000..bdd536fdcf --- /dev/null +++ b/examples/build_file_generation/requirements_windows.txt @@ -0,0 +1,82 @@ +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# bazel run //:requirements.update +# +click==8.1.3 \ + --hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \ + --hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48 + # via flask +colorama==0.4.6 \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via click +flask==2.2.2 \ + --hash=sha256:642c450d19c4ad482f96729bd2a8f6d32554aa1e231f4f6b4e7e5264b16cca2b \ + --hash=sha256:b9c46cc36662a7949f34b52d8ec7bb59c0d74ba08ba6cb9ce9adc1d8676d9526 + # via -r requirements.in +importlib-metadata==5.2.0 \ + --hash=sha256:0eafa39ba42bf225fc00e67f701d71f85aead9f878569caf13c3724f704b970f \ + --hash=sha256:404d48d62bba0b7a77ff9d405efd91501bef2e67ff4ace0bed40a0cf28c3c7cd + # via flask +itsdangerous==2.1.2 \ + --hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \ + --hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a + # via flask +jinja2==3.1.2 \ + --hash=sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852 \ + --hash=sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61 + # via flask +markupsafe==2.1.1 \ + --hash=sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003 \ + --hash=sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88 \ + --hash=sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5 \ + --hash=sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7 \ + --hash=sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a \ + --hash=sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603 \ + --hash=sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1 \ + --hash=sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135 \ + --hash=sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247 \ + --hash=sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6 \ + --hash=sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601 \ + --hash=sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77 \ + --hash=sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02 \ + --hash=sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e \ + --hash=sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63 \ + --hash=sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f \ + --hash=sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980 \ + --hash=sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b \ + --hash=sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812 \ + --hash=sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff \ + --hash=sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96 \ + --hash=sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1 \ + --hash=sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925 \ + --hash=sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a \ + --hash=sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6 \ + --hash=sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e \ + --hash=sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f \ + --hash=sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4 \ + --hash=sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f \ + --hash=sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3 \ + --hash=sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c \ + --hash=sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a \ + --hash=sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417 \ + --hash=sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a \ + --hash=sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a \ + --hash=sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37 \ + --hash=sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452 \ + --hash=sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933 \ + --hash=sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a \ + --hash=sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7 + # via + # jinja2 + # werkzeug +werkzeug==2.2.2 \ + --hash=sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f \ + --hash=sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5 + # via flask +zipp==3.11.0 \ + --hash=sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa \ + --hash=sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766 + # via importlib-metadata diff --git a/examples/bzlmod/.bazelignore b/examples/bzlmod/.bazelignore new file mode 100644 index 0000000000..ab3eb1635c --- /dev/null +++ b/examples/bzlmod/.bazelignore @@ -0,0 +1 @@ +other_module diff --git a/examples/bzlmod/.bazelrc b/examples/bzlmod/.bazelrc index b3a24e8605..b8c233f98c 100644 --- a/examples/bzlmod/.bazelrc +++ b/examples/bzlmod/.bazelrc @@ -1 +1,3 @@ common --experimental_enable_bzlmod + +coverage --java_runtime_version=remotejdk_11 diff --git a/examples/bzlmod/.bazelversion b/examples/bzlmod/.bazelversion new file mode 100644 index 0000000000..09b254e90c --- /dev/null +++ b/examples/bzlmod/.bazelversion @@ -0,0 +1 @@ +6.0.0 diff --git a/examples/bzlmod/BUILD.bazel b/examples/bzlmod/BUILD.bazel index 3e0349bf4f..7ecc035853 100644 --- a/examples/bzlmod/BUILD.bazel +++ b/examples/bzlmod/BUILD.bazel @@ -1,8 +1,24 @@ +load("@pip//:requirements.bzl", "requirement") +load("@python3_9//: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") + +compile_pip_requirements( + name = "requirements", + extra_args = ["--allow-unsafe"], + requirements_in = "requirements.in", + requirements_txt = "requirements_lock.txt", + requirements_windows = "requirements_windows.txt", +) py_library( name = "lib", - srcs = ["__init__.py"], + srcs = ["lib.py"], + deps = [ + requirement("pylint"), + requirement("tabulate"), + requirement("python-dateutil"), + ], ) py_binary( @@ -10,7 +26,9 @@ py_binary( srcs = ["__main__.py"], main = "__main__.py", visibility = ["//:__subpackages__"], - deps = [":lib"], + deps = [ + ":lib", + ], ) py_test( @@ -18,3 +36,10 @@ py_test( srcs = ["test.py"], deps = [":lib"], ) + +py_test_with_transition( + name = "test_with_transition", + srcs = ["test.py"], + main = "test.py", + deps = [":lib"], +) diff --git a/examples/bzlmod/MODULE.bazel b/examples/bzlmod/MODULE.bazel index e3fc51a115..ce9122810c 100644 --- a/examples/bzlmod/MODULE.bazel +++ b/examples/bzlmod/MODULE.bazel @@ -1,12 +1,38 @@ module( name = "example_bzlmod", - compatibility_level = 1, version = "0.0.0", + compatibility_level = 1, ) bazel_dep(name = "rules_python", version = "0.0.0") - local_path_override( module_name = "rules_python", path = "../..", ) + +python = use_extension("@rules_python//python:extensions.bzl", "python") +python.toolchain( + name = "python3_9", + configure_coverage_tool = True, + python_version = "3.9", +) +use_repo(python, "python3_9") +use_repo(python, "python3_9_toolchains") + +register_toolchains( + "@python3_9_toolchains//:all", +) + +pip = use_extension("@rules_python//python:extensions.bzl", "pip") +pip.parse( + name = "pip", + requirements_lock = "//:requirements_lock.txt", + requirements_windows = "//:requirements_windows.txt", +) +use_repo(pip, "pip") + +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/__init__.py b/examples/bzlmod/__init__.py deleted file mode 100644 index da9768f838..0000000000 --- a/examples/bzlmod/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# TODO: bzlmod should grant access to pip_install dependencies as well -# import requests - - -def main(url): - # r = requests.get(url) - # return r.text - return url diff --git a/examples/bzlmod/__main__.py b/examples/bzlmod/__main__.py index 04bcfb0b1f..099493b3c8 100644 --- a/examples/bzlmod/__main__.py +++ b/examples/bzlmod/__main__.py @@ -1,4 +1,18 @@ -from __init__ import main +# 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("https://example.com")) + print(main([["A", 1], ["B", 2]])) diff --git a/examples/bzlmod/gazelle_python.yaml b/examples/bzlmod/gazelle_python.yaml new file mode 100644 index 0000000000..12096e5837 --- /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/lib.py b/examples/bzlmod/lib.py new file mode 100644 index 0000000000..646c6e890f --- /dev/null +++ b/examples/bzlmod/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/other_module/MODULE.bazel b/examples/bzlmod/other_module/MODULE.bazel new file mode 100644 index 0000000000..992e120760 --- /dev/null +++ b/examples/bzlmod/other_module/MODULE.bazel @@ -0,0 +1,5 @@ +module( + name = "other_module", +) + +bazel_dep(name = "rules_python", version = "") diff --git a/gazelle/testdata/python_target_with_test_in_name/WORKSPACE b/examples/bzlmod/other_module/WORKSPACE similarity index 100% rename from gazelle/testdata/python_target_with_test_in_name/WORKSPACE rename to examples/bzlmod/other_module/WORKSPACE diff --git a/examples/bzlmod/other_module/other_module/pkg/BUILD.bazel b/examples/bzlmod/other_module/other_module/pkg/BUILD.bazel new file mode 100644 index 0000000000..9a130e3554 --- /dev/null +++ b/examples/bzlmod/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/other_module/other_module/pkg/data/data.txt b/examples/bzlmod/other_module/other_module/pkg/data/data.txt new file mode 100644 index 0000000000..e975eaf640 --- /dev/null +++ b/examples/bzlmod/other_module/other_module/pkg/data/data.txt @@ -0,0 +1 @@ +Hello, other_module! diff --git a/examples/bzlmod/other_module/other_module/pkg/lib.py b/examples/bzlmod/other_module/other_module/pkg/lib.py new file mode 100644 index 0000000000..eaf65fb46a --- /dev/null +++ b/examples/bzlmod/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/requirements.in b/examples/bzlmod/requirements.in new file mode 100644 index 0000000000..a709195442 --- /dev/null +++ b/examples/bzlmod/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/requirements_lock.txt b/examples/bzlmod/requirements_lock.txt new file mode 100644 index 0000000000..2160fe1163 --- /dev/null +++ b/examples/bzlmod/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/requirements_windows.txt b/examples/bzlmod/requirements_windows.txt new file mode 100644 index 0000000000..06cfdc332c --- /dev/null +++ b/examples/bzlmod/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/runfiles/BUILD.bazel b/examples/bzlmod/runfiles/BUILD.bazel new file mode 100644 index 0000000000..3503ac3017 --- /dev/null +++ b/examples/bzlmod/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/runfiles/data/data.txt b/examples/bzlmod/runfiles/data/data.txt new file mode 100644 index 0000000000..fb17e0df66 --- /dev/null +++ b/examples/bzlmod/runfiles/data/data.txt @@ -0,0 +1 @@ +Hello, example_bzlmod! diff --git a/examples/bzlmod/runfiles/runfiles_test.py b/examples/bzlmod/runfiles/runfiles_test.py new file mode 100644 index 0000000000..e1ba14e569 --- /dev/null +++ b/examples/bzlmod/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/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/examples/bzlmod/test.py b/examples/bzlmod/test.py index 5d725a862c..cdc1c89680 100644 --- a/examples/bzlmod/test.py +++ b/examples/bzlmod/test.py @@ -1,11 +1,32 @@ +# 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 __init__ import main +from lib import main class ExampleTest(unittest.TestCase): def test_main(self): - self.assertEquals("http://google.com", main("http://google.com")) + self.assertEquals( + """\ +- - +A 1 +B 2 +- -""", + main([["A", 1], ["B", 2]]), + ) if __name__ == "__main__": diff --git a/examples/bzlmod_build_file_generation/.bazelignore b/examples/bzlmod_build_file_generation/.bazelignore new file mode 100644 index 0000000000..ab3eb1635c --- /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 0000000000..1fbada7ec4 --- /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 0000000000..09b254e90c --- /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 0000000000..ac51a054d2 --- /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 0000000000..c667f1e49b --- /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 0000000000..781b0cba39 --- /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 0000000000..703fd38ebe --- /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 0000000000..78cc252e57 --- /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 0000000000..099493b3c8 --- /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 0000000000..cdc1c89680 --- /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 0000000000..12096e5837 --- /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 0000000000..646c6e890f --- /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 0000000000..992e120760 --- /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/gazelle/testdata/dependency_resolution_order/bar/BUILD.in b/examples/bzlmod_build_file_generation/other_module/WORKSPACE similarity index 100% rename from gazelle/testdata/dependency_resolution_order/bar/BUILD.in rename to 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 0000000000..9a130e3554 --- /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 0000000000..e975eaf640 --- /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 0000000000..eaf65fb46a --- /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 0000000000..a709195442 --- /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 0000000000..2160fe1163 --- /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 0000000000..06cfdc332c --- /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 0000000000..3503ac3017 --- /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 0000000000..fb17e0df66 --- /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 0000000000..a588040cfd --- /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/examples/multi_python_versions/.bazelrc b/examples/multi_python_versions/.bazelrc new file mode 100644 index 0000000000..3fd6365ba9 --- /dev/null +++ b/examples/multi_python_versions/.bazelrc @@ -0,0 +1,7 @@ +test --test_output=errors + +# Windows requires these for multi-python support: +build --enable_runfiles +startup --windows_enable_symlinks + +coverage --java_runtime_version=remotejdk_11 diff --git a/examples/multi_python_versions/.gitignore b/examples/multi_python_versions/.gitignore new file mode 100644 index 0000000000..ac51a054d2 --- /dev/null +++ b/examples/multi_python_versions/.gitignore @@ -0,0 +1 @@ +bazel-* diff --git a/examples/multi_python_versions/WORKSPACE b/examples/multi_python_versions/WORKSPACE new file mode 100644 index 0000000000..35855ca1e1 --- /dev/null +++ b/examples/multi_python_versions/WORKSPACE @@ -0,0 +1,55 @@ +workspace(name = "rules_python_multi_python_versions") + +local_repository( + name = "rules_python", + path = "../..", +) + +load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_multi_toolchains") + +py_repositories() + +load("@rules_python//python/pip_install:repositories.bzl", "pip_install_dependencies") + +pip_install_dependencies() + +default_python_version = "3.9" + +python_register_multi_toolchains( + name = "python", + default_version = default_python_version, + python_versions = [ + "3.8", + "3.9", + "3.10", + "3.11", + ], + register_coverage_tool = True, +) + +load("@python//:pip.bzl", "multi_pip_parse") +load("@python//3.10:defs.bzl", interpreter_3_10 = "interpreter") +load("@python//3.11:defs.bzl", interpreter_3_11 = "interpreter") +load("@python//3.8:defs.bzl", interpreter_3_8 = "interpreter") +load("@python//3.9:defs.bzl", interpreter_3_9 = "interpreter") + +multi_pip_parse( + name = "pypi", + default_version = default_python_version, + python_interpreter_target = { + "3.10": interpreter_3_10, + "3.11": interpreter_3_11, + "3.8": interpreter_3_8, + "3.9": interpreter_3_9, + }, + requirements_lock = { + "3.10": "//requirements:requirements_lock_3_10.txt", + "3.11": "//requirements:requirements_lock_3_11.txt", + "3.8": "//requirements:requirements_lock_3_8.txt", + "3.9": "//requirements:requirements_lock_3_9.txt", + }, +) + +load("@pypi//:requirements.bzl", "install_deps") + +install_deps() diff --git a/examples/multi_python_versions/libs/my_lib/BUILD.bazel b/examples/multi_python_versions/libs/my_lib/BUILD.bazel new file mode 100644 index 0000000000..8c29f6083c --- /dev/null +++ b/examples/multi_python_versions/libs/my_lib/BUILD.bazel @@ -0,0 +1,9 @@ +load("@pypi//:requirements.bzl", "requirement") +load("@rules_python//python:defs.bzl", "py_library") + +py_library( + name = "my_lib", + srcs = ["__init__.py"], + visibility = ["@//tests:__pkg__"], + deps = [requirement("websockets")], +) diff --git a/examples/multi_python_versions/libs/my_lib/__init__.py b/examples/multi_python_versions/libs/my_lib/__init__.py new file mode 100644 index 0000000000..33cfb414f5 --- /dev/null +++ b/examples/multi_python_versions/libs/my_lib/__init__.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. + +import websockets + + +def websockets_is_for_python_version(sanitized_version_check): + return f"pypi_{sanitized_version_check}_websockets" in websockets.__file__ diff --git a/examples/multi_python_versions/requirements/BUILD.bazel b/examples/multi_python_versions/requirements/BUILD.bazel new file mode 100644 index 0000000000..e3184c8ac5 --- /dev/null +++ b/examples/multi_python_versions/requirements/BUILD.bazel @@ -0,0 +1,32 @@ +load("@python//3.10:defs.bzl", compile_pip_requirements_3_10 = "compile_pip_requirements") +load("@python//3.11:defs.bzl", compile_pip_requirements_3_11 = "compile_pip_requirements") +load("@python//3.8:defs.bzl", compile_pip_requirements_3_8 = "compile_pip_requirements") +load("@python//3.9:defs.bzl", compile_pip_requirements_3_9 = "compile_pip_requirements") + +compile_pip_requirements_3_8( + name = "requirements_3_8", + extra_args = ["--allow-unsafe"], + requirements_in = "requirements.in", + requirements_txt = "requirements_lock_3_8.txt", +) + +compile_pip_requirements_3_9( + name = "requirements_3_9", + extra_args = ["--allow-unsafe"], + requirements_in = "requirements.in", + requirements_txt = "requirements_lock_3_9.txt", +) + +compile_pip_requirements_3_10( + name = "requirements_3_10", + extra_args = ["--allow-unsafe"], + requirements_in = "requirements.in", + requirements_txt = "requirements_lock_3_10.txt", +) + +compile_pip_requirements_3_11( + name = "requirements_3_11", + extra_args = ["--allow-unsafe"], + requirements_in = "requirements.in", + requirements_txt = "requirements_lock_3_11.txt", +) diff --git a/examples/multi_python_versions/requirements/requirements.in b/examples/multi_python_versions/requirements/requirements.in new file mode 100644 index 0000000000..14774b465e --- /dev/null +++ b/examples/multi_python_versions/requirements/requirements.in @@ -0,0 +1 @@ +websockets diff --git a/examples/multi_python_versions/requirements/requirements_lock_3_10.txt b/examples/multi_python_versions/requirements/requirements_lock_3_10.txt new file mode 100644 index 0000000000..6bee4e0030 --- /dev/null +++ b/examples/multi_python_versions/requirements/requirements_lock_3_10.txt @@ -0,0 +1,56 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# bazel run //requirements:requirements_3_10.update +# +websockets==10.3 \ + --hash=sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af \ + --hash=sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c \ + --hash=sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76 \ + --hash=sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47 \ + --hash=sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69 \ + --hash=sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079 \ + --hash=sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c \ + --hash=sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55 \ + --hash=sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02 \ + --hash=sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559 \ + --hash=sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3 \ + --hash=sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e \ + --hash=sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978 \ + --hash=sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98 \ + --hash=sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae \ + --hash=sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755 \ + --hash=sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d \ + --hash=sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991 \ + --hash=sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1 \ + --hash=sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680 \ + --hash=sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247 \ + --hash=sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f \ + --hash=sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2 \ + --hash=sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7 \ + --hash=sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4 \ + --hash=sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667 \ + --hash=sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb \ + --hash=sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094 \ + --hash=sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36 \ + --hash=sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79 \ + --hash=sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500 \ + --hash=sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e \ + --hash=sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582 \ + --hash=sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442 \ + --hash=sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd \ + --hash=sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6 \ + --hash=sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731 \ + --hash=sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4 \ + --hash=sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d \ + --hash=sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8 \ + --hash=sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f \ + --hash=sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677 \ + --hash=sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8 \ + --hash=sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9 \ + --hash=sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e \ + --hash=sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b \ + --hash=sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916 \ + --hash=sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4 + # via -r requirements/requirements.in diff --git a/examples/multi_python_versions/requirements/requirements_lock_3_11.txt b/examples/multi_python_versions/requirements/requirements_lock_3_11.txt new file mode 100644 index 0000000000..a437a397d0 --- /dev/null +++ b/examples/multi_python_versions/requirements/requirements_lock_3_11.txt @@ -0,0 +1,56 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# bazel run //requirements:requirements_3_11.update +# +websockets==10.3 \ + --hash=sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af \ + --hash=sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c \ + --hash=sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76 \ + --hash=sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47 \ + --hash=sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69 \ + --hash=sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079 \ + --hash=sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c \ + --hash=sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55 \ + --hash=sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02 \ + --hash=sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559 \ + --hash=sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3 \ + --hash=sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e \ + --hash=sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978 \ + --hash=sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98 \ + --hash=sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae \ + --hash=sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755 \ + --hash=sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d \ + --hash=sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991 \ + --hash=sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1 \ + --hash=sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680 \ + --hash=sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247 \ + --hash=sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f \ + --hash=sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2 \ + --hash=sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7 \ + --hash=sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4 \ + --hash=sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667 \ + --hash=sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb \ + --hash=sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094 \ + --hash=sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36 \ + --hash=sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79 \ + --hash=sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500 \ + --hash=sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e \ + --hash=sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582 \ + --hash=sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442 \ + --hash=sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd \ + --hash=sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6 \ + --hash=sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731 \ + --hash=sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4 \ + --hash=sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d \ + --hash=sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8 \ + --hash=sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f \ + --hash=sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677 \ + --hash=sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8 \ + --hash=sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9 \ + --hash=sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e \ + --hash=sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b \ + --hash=sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916 \ + --hash=sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4 + # via -r requirements/requirements.in diff --git a/examples/multi_python_versions/requirements/requirements_lock_3_8.txt b/examples/multi_python_versions/requirements/requirements_lock_3_8.txt new file mode 100644 index 0000000000..19303f8eff --- /dev/null +++ b/examples/multi_python_versions/requirements/requirements_lock_3_8.txt @@ -0,0 +1,56 @@ +# +# This file is autogenerated by pip-compile with Python 3.8 +# by the following command: +# +# bazel run //requirements:requirements_3_8.update +# +websockets==10.3 \ + --hash=sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af \ + --hash=sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c \ + --hash=sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76 \ + --hash=sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47 \ + --hash=sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69 \ + --hash=sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079 \ + --hash=sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c \ + --hash=sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55 \ + --hash=sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02 \ + --hash=sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559 \ + --hash=sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3 \ + --hash=sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e \ + --hash=sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978 \ + --hash=sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98 \ + --hash=sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae \ + --hash=sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755 \ + --hash=sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d \ + --hash=sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991 \ + --hash=sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1 \ + --hash=sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680 \ + --hash=sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247 \ + --hash=sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f \ + --hash=sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2 \ + --hash=sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7 \ + --hash=sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4 \ + --hash=sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667 \ + --hash=sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb \ + --hash=sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094 \ + --hash=sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36 \ + --hash=sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79 \ + --hash=sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500 \ + --hash=sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e \ + --hash=sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582 \ + --hash=sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442 \ + --hash=sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd \ + --hash=sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6 \ + --hash=sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731 \ + --hash=sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4 \ + --hash=sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d \ + --hash=sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8 \ + --hash=sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f \ + --hash=sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677 \ + --hash=sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8 \ + --hash=sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9 \ + --hash=sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e \ + --hash=sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b \ + --hash=sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916 \ + --hash=sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4 + # via -r requirements/requirements.in diff --git a/examples/multi_python_versions/requirements/requirements_lock_3_9.txt b/examples/multi_python_versions/requirements/requirements_lock_3_9.txt new file mode 100644 index 0000000000..4af42ca277 --- /dev/null +++ b/examples/multi_python_versions/requirements/requirements_lock_3_9.txt @@ -0,0 +1,56 @@ +# +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: +# +# bazel run //requirements:requirements_3_9.update +# +websockets==10.3 \ + --hash=sha256:07cdc0a5b2549bcfbadb585ad8471ebdc7bdf91e32e34ae3889001c1c106a6af \ + --hash=sha256:210aad7fdd381c52e58777560860c7e6110b6174488ef1d4b681c08b68bf7f8c \ + --hash=sha256:28dd20b938a57c3124028680dc1600c197294da5db4292c76a0b48efb3ed7f76 \ + --hash=sha256:2f94fa3ae454a63ea3a19f73b95deeebc9f02ba2d5617ca16f0bbdae375cda47 \ + --hash=sha256:31564a67c3e4005f27815634343df688b25705cccb22bc1db621c781ddc64c69 \ + --hash=sha256:347974105bbd4ea068106ec65e8e8ebd86f28c19e529d115d89bd8cc5cda3079 \ + --hash=sha256:379e03422178436af4f3abe0aa8f401aa77ae2487843738542a75faf44a31f0c \ + --hash=sha256:3eda1cb7e9da1b22588cefff09f0951771d6ee9fa8dbe66f5ae04cc5f26b2b55 \ + --hash=sha256:51695d3b199cd03098ae5b42833006a0f43dc5418d3102972addc593a783bc02 \ + --hash=sha256:54c000abeaff6d8771a4e2cef40900919908ea7b6b6a30eae72752607c6db559 \ + --hash=sha256:5b936bf552e4f6357f5727579072ff1e1324717902127ffe60c92d29b67b7be3 \ + --hash=sha256:6075fd24df23133c1b078e08a9b04a3bc40b31a8def4ee0b9f2c8865acce913e \ + --hash=sha256:661f641b44ed315556a2fa630239adfd77bd1b11cb0b9d96ed8ad90b0b1e4978 \ + --hash=sha256:6ea6b300a6bdd782e49922d690e11c3669828fe36fc2471408c58b93b5535a98 \ + --hash=sha256:6ed1d6f791eabfd9808afea1e068f5e59418e55721db8b7f3bfc39dc831c42ae \ + --hash=sha256:7934e055fd5cd9dee60f11d16c8d79c4567315824bacb1246d0208a47eca9755 \ + --hash=sha256:7ab36e17af592eec5747c68ef2722a74c1a4a70f3772bc661079baf4ae30e40d \ + --hash=sha256:7f6d96fdb0975044fdd7953b35d003b03f9e2bcf85f2d2cf86285ece53e9f991 \ + --hash=sha256:83e5ca0d5b743cde3d29fda74ccab37bdd0911f25bd4cdf09ff8b51b7b4f2fa1 \ + --hash=sha256:85506b3328a9e083cc0a0fb3ba27e33c8db78341b3eb12eb72e8afd166c36680 \ + --hash=sha256:8af75085b4bc0b5c40c4a3c0e113fa95e84c60f4ed6786cbb675aeb1ee128247 \ + --hash=sha256:8b1359aba0ff810d5830d5ab8e2c4a02bebf98a60aa0124fb29aa78cfdb8031f \ + --hash=sha256:8fbd7d77f8aba46d43245e86dd91a8970eac4fb74c473f8e30e9c07581f852b2 \ + --hash=sha256:907e8247480f287aa9bbc9391bd6de23c906d48af54c8c421df84655eef66af7 \ + --hash=sha256:93d5ea0b5da8d66d868b32c614d2b52d14304444e39e13a59566d4acb8d6e2e4 \ + --hash=sha256:97bc9d41e69a7521a358f9b8e44871f6cdeb42af31815c17aed36372d4eec667 \ + --hash=sha256:994cdb1942a7a4c2e10098d9162948c9e7b235df755de91ca33f6e0481366fdb \ + --hash=sha256:a141de3d5a92188234afa61653ed0bbd2dde46ad47b15c3042ffb89548e77094 \ + --hash=sha256:a1e15b230c3613e8ea82c9fc6941b2093e8eb939dd794c02754d33980ba81e36 \ + --hash=sha256:aad5e300ab32036eb3fdc350ad30877210e2f51bceaca83fb7fef4d2b6c72b79 \ + --hash=sha256:b529fdfa881b69fe563dbd98acce84f3e5a67df13de415e143ef053ff006d500 \ + --hash=sha256:b9c77f0d1436ea4b4dc089ed8335fa141e6a251a92f75f675056dac4ab47a71e \ + --hash=sha256:bb621ec2dbbbe8df78a27dbd9dd7919f9b7d32a73fafcb4d9252fc4637343582 \ + --hash=sha256:c7250848ce69559756ad0086a37b82c986cd33c2d344ab87fea596c5ac6d9442 \ + --hash=sha256:c8d1d14aa0f600b5be363077b621b1b4d1eb3fbf90af83f9281cda668e6ff7fd \ + --hash=sha256:d1655a6fc7aecd333b079d00fb3c8132d18988e47f19740c69303bf02e9883c6 \ + --hash=sha256:d6353ba89cfc657a3f5beabb3b69be226adbb5c6c7a66398e17809b0ce3c4731 \ + --hash=sha256:da4377904a3379f0c1b75a965fff23b28315bcd516d27f99a803720dfebd94d4 \ + --hash=sha256:e49ea4c1a9543d2bd8a747ff24411509c29e4bdcde05b5b0895e2120cb1a761d \ + --hash=sha256:e4e08305bfd76ba8edab08dcc6496f40674f44eb9d5e23153efa0a35750337e8 \ + --hash=sha256:e6fa05a680e35d0fcc1470cb070b10e6fe247af54768f488ed93542e71339d6f \ + --hash=sha256:e7e6f2d6fd48422071cc8a6f8542016f350b79cc782752de531577d35e9bd677 \ + --hash=sha256:e904c0381c014b914136c492c8fa711ca4cced4e9b3d110e5e7d436d0fc289e8 \ + --hash=sha256:ec2b0ab7edc8cd4b0eb428b38ed89079bdc20c6bdb5f889d353011038caac2f9 \ + --hash=sha256:ef5ce841e102278c1c2e98f043db99d6755b1c58bde475516aef3a008ed7f28e \ + --hash=sha256:f351c7d7d92f67c0609329ab2735eee0426a03022771b00102816a72715bb00b \ + --hash=sha256:fab7c640815812ed5f10fbee7abbf58788d602046b7bb3af9b1ac753a6d5e916 \ + --hash=sha256:fc06cc8073c8e87072138ba1e431300e2d408f054b27047d047b549455066ff4 + # via -r requirements/requirements.in diff --git a/examples/multi_python_versions/tests/BUILD.bazel b/examples/multi_python_versions/tests/BUILD.bazel new file mode 100644 index 0000000000..2292d53e40 --- /dev/null +++ b/examples/multi_python_versions/tests/BUILD.bazel @@ -0,0 +1,169 @@ +load("@python//3.10:defs.bzl", py_binary_3_10 = "py_binary", py_test_3_10 = "py_test") +load("@python//3.11:defs.bzl", py_binary_3_11 = "py_binary", py_test_3_11 = "py_test") +load("@python//3.8:defs.bzl", py_binary_3_8 = "py_binary", py_test_3_8 = "py_test") +load("@python//3.9:defs.bzl", py_binary_3_9 = "py_binary", py_test_3_9 = "py_test") +load("@rules_python//python:defs.bzl", "py_binary", "py_test") + +py_binary( + name = "version_default", + srcs = ["version.py"], + main = "version.py", +) + +py_binary_3_8( + name = "version_3_8", + srcs = ["version.py"], + main = "version.py", +) + +py_binary_3_9( + name = "version_3_9", + srcs = ["version.py"], + main = "version.py", +) + +py_binary_3_10( + name = "version_3_10", + srcs = ["version.py"], + main = "version.py", +) + +py_binary_3_11( + name = "version_3_11", + srcs = ["version.py"], + main = "version.py", +) + +py_test( + name = "my_lib_default_test", + srcs = ["my_lib_test.py"], + main = "my_lib_test.py", + deps = ["//libs/my_lib"], +) + +py_test_3_8( + name = "my_lib_3_8_test", + srcs = ["my_lib_test.py"], + main = "my_lib_test.py", + deps = ["//libs/my_lib"], +) + +py_test_3_9( + name = "my_lib_3_9_test", + srcs = ["my_lib_test.py"], + main = "my_lib_test.py", + deps = ["//libs/my_lib"], +) + +py_test_3_10( + name = "my_lib_3_10_test", + srcs = ["my_lib_test.py"], + main = "my_lib_test.py", + deps = ["//libs/my_lib"], +) + +py_test_3_11( + name = "my_lib_3_11_test", + srcs = ["my_lib_test.py"], + main = "my_lib_test.py", + deps = ["//libs/my_lib"], +) + +py_test( + name = "version_default_test", + srcs = ["version_test.py"], + env = {"VERSION_CHECK": "3.9"}, # The default defined in the WORKSPACE. + main = "version_test.py", +) + +py_test_3_8( + name = "version_3_8_test", + srcs = ["version_test.py"], + env = {"VERSION_CHECK": "3.8"}, + main = "version_test.py", +) + +py_test_3_9( + name = "version_3_9_test", + srcs = ["version_test.py"], + env = {"VERSION_CHECK": "3.9"}, + main = "version_test.py", +) + +py_test_3_10( + name = "version_3_10_test", + srcs = ["version_test.py"], + env = {"VERSION_CHECK": "3.10"}, + main = "version_test.py", +) + +py_test_3_11( + name = "version_3_11_test", + srcs = ["version_test.py"], + env = {"VERSION_CHECK": "3.11"}, + main = "version_test.py", +) + +py_test( + name = "version_default_takes_3_10_subprocess_test", + srcs = ["cross_version_test.py"], + data = [":version_3_10"], + env = { + "SUBPROCESS_VERSION_CHECK": "3.10", + "SUBPROCESS_VERSION_PY_BINARY": "$(rootpath :version_3_10)", + "VERSION_CHECK": "3.9", + }, + main = "cross_version_test.py", +) + +py_test_3_10( + name = "version_3_10_takes_3_9_subprocess_test", + srcs = ["cross_version_test.py"], + data = [":version_3_9"], + env = { + "SUBPROCESS_VERSION_CHECK": "3.9", + "SUBPROCESS_VERSION_PY_BINARY": "$(rootpath :version_3_9)", + "VERSION_CHECK": "3.10", + }, + main = "cross_version_test.py", +) + +sh_test( + name = "version_test_binary_default", + srcs = ["version_test.sh"], + data = [":version_default"], + env = { + "VERSION_CHECK": "3.9", # The default defined in the WORKSPACE. + "VERSION_PY_BINARY": "$(rootpath :version_default)", + }, +) + +sh_test( + name = "version_test_binary_3_8", + srcs = ["version_test.sh"], + data = [":version_3_8"], + env = { + "VERSION_CHECK": "3.8", + "VERSION_PY_BINARY": "$(rootpath :version_3_8)", + }, +) + +sh_test( + name = "version_test_binary_3_9", + srcs = ["version_test.sh"], + data = [":version_3_9"], + env = { + "VERSION_CHECK": "3.9", + "VERSION_PY_BINARY": "$(rootpath :version_3_9)", + }, +) + +sh_test( + name = "version_test_binary_3_10", + srcs = ["version_test.sh"], + data = [":version_3_10"], + env = { + "VERSION_CHECK": "3.10", + "VERSION_PY_BINARY": "$(rootpath :version_3_10)", + }, +) diff --git a/examples/multi_python_versions/tests/cross_version_test.py b/examples/multi_python_versions/tests/cross_version_test.py new file mode 100644 index 0000000000..437be2ed5a --- /dev/null +++ b/examples/multi_python_versions/tests/cross_version_test.py @@ -0,0 +1,39 @@ +# 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 subprocess +import sys + +process = subprocess.run( + [os.getenv("SUBPROCESS_VERSION_PY_BINARY")], + stdout=subprocess.PIPE, + universal_newlines=True, +) + +subprocess_current = process.stdout.strip() +subprocess_expected = os.getenv("SUBPROCESS_VERSION_CHECK") + +if subprocess_current != subprocess_expected: + print( + f"expected subprocess version '{subprocess_expected}' is different than returned '{subprocess_current}'" + ) + sys.exit(1) + +expected = os.getenv("VERSION_CHECK") +current = f"{sys.version_info.major}.{sys.version_info.minor}" + +if current != expected: + print(f"expected version '{expected}' is different than returned '{current}'") + sys.exit(1) diff --git a/examples/multi_python_versions/tests/my_lib_test.py b/examples/multi_python_versions/tests/my_lib_test.py new file mode 100644 index 0000000000..e0a97dbf2b --- /dev/null +++ b/examples/multi_python_versions/tests/my_lib_test.py @@ -0,0 +1,24 @@ +# 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 sys + +import libs.my_lib as my_lib + +sanitized_version_check = f"{sys.version_info.major}_{sys.version_info.minor}" + +if not my_lib.websockets_is_for_python_version(sanitized_version_check): + print("expected package for Python version is different than returned") + sys.exit(1) diff --git a/examples/multi_python_versions/tests/version.py b/examples/multi_python_versions/tests/version.py new file mode 100644 index 0000000000..2d293c1571 --- /dev/null +++ b/examples/multi_python_versions/tests/version.py @@ -0,0 +1,17 @@ +# 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 sys + +print(f"{sys.version_info.major}.{sys.version_info.minor}") diff --git a/examples/multi_python_versions/tests/version_test.py b/examples/multi_python_versions/tests/version_test.py new file mode 100644 index 0000000000..444f5e4321 --- /dev/null +++ b/examples/multi_python_versions/tests/version_test.py @@ -0,0 +1,23 @@ +# 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 sys + +expected = os.getenv("VERSION_CHECK") +current = f"{sys.version_info.major}.{sys.version_info.minor}" + +if current != expected: + print(f"expected version '{expected}' is different than returned '{current}'") + sys.exit(1) diff --git a/examples/multi_python_versions/tests/version_test.sh b/examples/multi_python_versions/tests/version_test.sh new file mode 100755 index 0000000000..3bedb95ef9 --- /dev/null +++ b/examples/multi_python_versions/tests/version_test.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# 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. + + +set -o errexit -o nounset -o pipefail + +version_py_binary=$("${VERSION_PY_BINARY}") + +if [[ "${version_py_binary}" != "${VERSION_CHECK}" ]]; then + echo >&2 "expected version '${VERSION_CHECK}' is different than returned '${version_py_binary}'" + exit 1 +fi diff --git a/examples/pip_install/BUILD b/examples/pip_install/BUILD.bazel similarity index 93% rename from examples/pip_install/BUILD rename to examples/pip_install/BUILD.bazel index ad983b2f54..35f5a9338a 100644 --- a/examples/pip_install/BUILD +++ b/examples/pip_install/BUILD.bazel @@ -88,9 +88,9 @@ py_test( genquery( name = "yamllint_lib_by_version", expression = """ - attr("tags", "\\bpypi_version=1.26.3\\b", "@pip//pypi__yamllint") + attr("tags", "\\bpypi_version=1.26.3\\b", "@pip_yamllint//:pkg") intersect - attr("tags", "\\bpypi_name=yamllint\\b", "@pip//pypi__yamllint") + attr("tags", "\\bpypi_name=yamllint\\b", "@pip_yamllint//:pkg") """, scope = [requirement("yamllint")], ) @@ -99,7 +99,7 @@ write_file( name = "write_expected", out = "expected", content = [ - "@pip//pypi__yamllint:pypi__yamllint", + "@pip_yamllint//:pkg", "", ], ) diff --git a/examples/pip_install/WORKSPACE b/examples/pip_install/WORKSPACE index 0b33a2b390..b1744bfa7d 100644 --- a/examples/pip_install/WORKSPACE +++ b/examples/pip_install/WORKSPACE @@ -1,22 +1,13 @@ workspace(name = "rules_python_pip_install_example") -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - -http_archive( - name = "bazel_skylib", - sha256 = "c6966ec828da198c5d9adbaa94c05e3a1c7f21bd012a0b29ba8ddbccb2c93b0d", - urls = [ - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", - ], -) - local_repository( name = "rules_python", path = "../..", ) -load("@rules_python//python:repositories.bzl", "python_register_toolchains") +load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains") + +py_repositories() python_register_toolchains( name = "python39", @@ -57,6 +48,11 @@ pip_install( requirements = "//:requirements.txt", ) +load("@pip//:requirements.bzl", "install_deps") + +# Initialize repositories for all packages in requirements.txt. +install_deps() + # You could optionally use an in-build, compiled python interpreter as a toolchain, # and also use it to execute pip. # diff --git a/examples/pip_install/main.py b/examples/pip_install/main.py index 4440cdeb2e..1fb7249f76 100644 --- a/examples/pip_install/main.py +++ b/examples/pip_install/main.py @@ -1,5 +1,20 @@ +# 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 boto3 + def the_dir(): return dir(boto3) diff --git a/examples/pip_install/pip_install_test.py b/examples/pip_install/pip_install_test.py index eb4d7d8e95..3e1b085ed4 100644 --- a/examples/pip_install/pip_install_test.py +++ b/examples/pip_install/pip_install_test.py @@ -1,4 +1,18 @@ #!/usr/bin/env python3 +# 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 subprocess @@ -37,11 +51,11 @@ def test_data(self): self.assertListEqual( env.split(" "), [ - "external/pip/pypi__s3cmd/data/share/doc/packages/s3cmd/INSTALL.md", - "external/pip/pypi__s3cmd/data/share/doc/packages/s3cmd/LICENSE", - "external/pip/pypi__s3cmd/data/share/doc/packages/s3cmd/NEWS", - "external/pip/pypi__s3cmd/data/share/doc/packages/s3cmd/README.md", - "external/pip/pypi__s3cmd/data/share/man/man1/s3cmd.1", + "external/pip_s3cmd/data/share/doc/packages/s3cmd/INSTALL.md", + "external/pip_s3cmd/data/share/doc/packages/s3cmd/LICENSE", + "external/pip_s3cmd/data/share/doc/packages/s3cmd/NEWS", + "external/pip_s3cmd/data/share/doc/packages/s3cmd/README.md", + "external/pip_s3cmd/data/share/man/man1/s3cmd.1", ], ) @@ -51,13 +65,13 @@ def test_dist_info(self): self.assertListEqual( env.split(" "), [ - "external/pip/pypi__boto3/site-packages/boto3-1.14.51.dist-info/DESCRIPTION.rst", - 'external/pip/pypi__boto3/site-packages/boto3-1.14.51.dist-info/INSTALLER', - "external/pip/pypi__boto3/site-packages/boto3-1.14.51.dist-info/METADATA", - "external/pip/pypi__boto3/site-packages/boto3-1.14.51.dist-info/RECORD", - "external/pip/pypi__boto3/site-packages/boto3-1.14.51.dist-info/WHEEL", - "external/pip/pypi__boto3/site-packages/boto3-1.14.51.dist-info/metadata.json", - "external/pip/pypi__boto3/site-packages/boto3-1.14.51.dist-info/top_level.txt", + "external/pip_boto3/site-packages/boto3-1.14.63.dist-info/DESCRIPTION.rst", + "external/pip_boto3/site-packages/boto3-1.14.63.dist-info/INSTALLER", + "external/pip_boto3/site-packages/boto3-1.14.63.dist-info/METADATA", + "external/pip_boto3/site-packages/boto3-1.14.63.dist-info/RECORD", + "external/pip_boto3/site-packages/boto3-1.14.63.dist-info/WHEEL", + "external/pip_boto3/site-packages/boto3-1.14.63.dist-info/metadata.json", + "external/pip_boto3/site-packages/boto3-1.14.63.dist-info/top_level.txt", ], ) diff --git a/examples/pip_install/requirements.txt b/examples/pip_install/requirements.txt index db76801f39..495a32a637 100644 --- a/examples/pip_install/requirements.txt +++ b/examples/pip_install/requirements.txt @@ -1,12 +1,12 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # # bazel run //:requirements.update # -boto3==1.14.51 \ - --hash=sha256:a6bdb808e948bd264af135af50efb76253e85732c451fa605b7a287faf022432 \ - --hash=sha256:f9dbccbcec916051c6588adbccae86547308ac4cd154f1eb7cf6422f0e391a71 +boto3==1.14.63 \ + --hash=sha256:25c716b7c01d4664027afc6a6418a06459e311a610c7fd39a030a1ced1b72ce4 \ + --hash=sha256:37158c37a151eab5b9080968305621a40168171fda9584d50a309ceb4e5e6964 # via -r requirements.in botocore==1.17.63 \ --hash=sha256:40f13f6c9c29c307a9dc5982739e537ddce55b29787b90c3447b507e3283bcd6 \ @@ -25,9 +25,9 @@ jmespath==0.10.0 \ # via # boto3 # botocore -pathspec==0.9.0 \ - --hash=sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a \ - --hash=sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1 +pathspec==0.10.3 \ + --hash=sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6 \ + --hash=sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6 # via yamllint python-dateutil==2.8.2 \ --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ @@ -35,11 +35,12 @@ python-dateutil==2.8.2 \ # via # botocore # s3cmd -python-magic==0.4.24 \ - --hash=sha256:4fec8ee805fea30c07afccd1592c0f17977089895bdfaae5fec870a84e997626 \ - --hash=sha256:de800df9fb50f8ec5974761054a708af6e4246b03b4bdaee993f948947b0ebcf +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 \ @@ -51,26 +52,32 @@ pyyaml==6.0 \ --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 @@ -82,6 +89,10 @@ s3transfer==0.3.7 \ --hash=sha256:35627b86af8ff97e7ac27975fe0a98a312814b46c6333d8a6b889627bcd80994 \ --hash=sha256:efa5bd92a897b6a8d5c1383828dca3d52d0790e0756d49740563a3fb6ed03246 # via boto3 +setuptools==65.6.3 \ + --hash=sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54 \ + --hash=sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75 + # via yamllint six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 @@ -97,9 +108,3 @@ urllib3==1.25.11 \ yamllint==1.26.3 \ --hash=sha256:3934dcde484374596d6b52d8db412929a169f6d9e52e20f9ade5bf3523d9b96e # via -r requirements.in - -# The following packages are considered to be unsafe in a requirements file: -setuptools==59.6.0 \ - --hash=sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373 \ - --hash=sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e - # via yamllint diff --git a/examples/pip_install/requirements_windows.txt b/examples/pip_install/requirements_windows.txt index 26de1adaac..b87192f9d0 100644 --- a/examples/pip_install/requirements_windows.txt +++ b/examples/pip_install/requirements_windows.txt @@ -1,12 +1,12 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # # bazel run //:requirements.update # -boto3==1.14.51 \ - --hash=sha256:a6bdb808e948bd264af135af50efb76253e85732c451fa605b7a287faf022432 \ - --hash=sha256:f9dbccbcec916051c6588adbccae86547308ac4cd154f1eb7cf6422f0e391a71 +boto3==1.14.63 \ + --hash=sha256:25c716b7c01d4664027afc6a6418a06459e311a610c7fd39a030a1ced1b72ce4 \ + --hash=sha256:37158c37a151eab5b9080968305621a40168171fda9584d50a309ceb4e5e6964 # via -r requirements.in botocore==1.17.63 \ --hash=sha256:40f13f6c9c29c307a9dc5982739e537ddce55b29787b90c3447b507e3283bcd6 \ @@ -25,9 +25,9 @@ jmespath==0.10.0 \ # via # boto3 # botocore -pathspec==0.9.0 \ - --hash=sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a \ - --hash=sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1 +pathspec==0.10.3 \ + --hash=sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6 \ + --hash=sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6 # via yamllint python-dateutil==2.8.2 \ --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ @@ -35,11 +35,12 @@ python-dateutil==2.8.2 \ # via # botocore # s3cmd -python-magic==0.4.24 \ - --hash=sha256:4fec8ee805fea30c07afccd1592c0f17977089895bdfaae5fec870a84e997626 \ - --hash=sha256:de800df9fb50f8ec5974761054a708af6e4246b03b4bdaee993f948947b0ebcf +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 \ @@ -51,26 +52,32 @@ pyyaml==6.0 \ --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 @@ -82,6 +89,10 @@ s3transfer==0.3.7 \ --hash=sha256:35627b86af8ff97e7ac27975fe0a98a312814b46c6333d8a6b889627bcd80994 \ --hash=sha256:efa5bd92a897b6a8d5c1383828dca3d52d0790e0756d49740563a3fb6ed03246 # via boto3 +setuptools==65.6.3 \ + --hash=sha256:57f6f22bde4e042978bcd50176fdb381d7c21a9efa4041202288d3737a0c6a54 \ + --hash=sha256:a7620757bf984b58deaf32fc8a4577a9bbc0850cf92c20e1ce41c38c19e5fb75 + # via yamllint six==1.16.0 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 @@ -93,9 +104,3 @@ urllib3==1.25.11 \ yamllint==1.26.3 \ --hash=sha256:3934dcde484374596d6b52d8db412929a169f6d9e52e20f9ade5bf3523d9b96e # via -r requirements.in - -# The following packages are considered to be unsafe in a requirements file: -setuptools==59.6.0 \ - --hash=sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373 \ - --hash=sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e - # via yamllint diff --git a/examples/pip_install/test.py b/examples/pip_install/test.py index 0859a2831a..0f5b7c905e 100644 --- a/examples/pip_install/test.py +++ b/examples/pip_install/test.py @@ -1,3 +1,17 @@ +# 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 import main diff --git a/examples/pip_parse/BUILD b/examples/pip_parse/BUILD.bazel similarity index 100% rename from examples/pip_parse/BUILD rename to examples/pip_parse/BUILD.bazel diff --git a/examples/pip_parse/WORKSPACE b/examples/pip_parse/WORKSPACE index e96db9f844..79aca14b8c 100644 --- a/examples/pip_parse/WORKSPACE +++ b/examples/pip_parse/WORKSPACE @@ -5,7 +5,9 @@ local_repository( path = "../..", ) -load("@rules_python//python:repositories.bzl", "python_register_toolchains") +load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains") + +py_repositories() python_register_toolchains( name = "python39", diff --git a/examples/pip_parse/main.py b/examples/pip_parse/main.py index 79e1c1219b..80610f42a1 100644 --- a/examples/pip_parse/main.py +++ b/examples/pip_parse/main.py @@ -1,3 +1,17 @@ +# 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 requests diff --git a/examples/pip_parse/pip_parse_test.py b/examples/pip_parse/pip_parse_test.py index 030e38c077..f319cb898f 100644 --- a/examples/pip_parse/pip_parse_test.py +++ b/examples/pip_parse/pip_parse_test.py @@ -1,4 +1,18 @@ #!/usr/bin/env python3 +# 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 subprocess @@ -49,7 +63,7 @@ def test_dist_info(self): self.assertListEqual( env.split(" "), [ - 'external/pypi_requests/site-packages/requests-2.25.1.dist-info/INSTALLER', + "external/pypi_requests/site-packages/requests-2.25.1.dist-info/INSTALLER", "external/pypi_requests/site-packages/requests-2.25.1.dist-info/LICENSE", "external/pypi_requests/site-packages/requests-2.25.1.dist-info/METADATA", "external/pypi_requests/site-packages/requests-2.25.1.dist-info/RECORD", diff --git a/examples/pip_parse/requirements_lock.txt b/examples/pip_parse/requirements_lock.txt index d3cb1f5bc9..3cbe57f28c 100644 --- a/examples/pip_parse/requirements_lock.txt +++ b/examples/pip_parse/requirements_lock.txt @@ -1,12 +1,12 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # # bazel run //:requirements.update # -certifi==2021.10.8 \ - --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ - --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 +certifi==2022.12.7 \ + --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ + --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 # via requests chardet==4.0.0 \ --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \ @@ -16,19 +16,20 @@ idna==2.10 \ --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \ --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 # via requests -pathspec==0.9.0 \ - --hash=sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a \ - --hash=sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1 +pathspec==0.10.3 \ + --hash=sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6 \ + --hash=sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6 # via yamllint python-dateutil==2.8.2 \ --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 # via s3cmd -python-magic==0.4.24 \ - --hash=sha256:4fec8ee805fea30c07afccd1592c0f17977089895bdfaae5fec870a84e997626 \ - --hash=sha256:de800df9fb50f8ec5974761054a708af6e4246b03b4bdaee993f948947b0ebcf +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 \ @@ -40,26 +41,32 @@ pyyaml==6.0 \ --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 @@ -71,20 +78,18 @@ 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 -urllib3==1.26.7 \ - --hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece \ - --hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844 +urllib3==1.26.13 \ + --hash=sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc \ + --hash=sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8 # via requests yamllint==1.26.3 \ --hash=sha256:3934dcde484374596d6b52d8db412929a169f6d9e52e20f9ade5bf3523d9b96e # via -r requirements.in - -# The following packages are considered to be unsafe in a requirements file: -setuptools==59.6.0 \ - --hash=sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373 \ - --hash=sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e - # via yamllint diff --git a/examples/pip_parse/test.py b/examples/pip_parse/test.py index e1f97f167b..2dc3046319 100644 --- a/examples/pip_parse/test.py +++ b/examples/pip_parse/test.py @@ -1,3 +1,17 @@ +# 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 import main diff --git a/examples/pip_parse_vendored/.bazelrc b/examples/pip_parse_vendored/.bazelrc new file mode 100644 index 0000000000..f23315a7a1 --- /dev/null +++ b/examples/pip_parse_vendored/.bazelrc @@ -0,0 +1,5 @@ +test --test_output=errors + +# Windows requires these for multi-python support: +build --enable_runfiles +startup --windows_enable_symlinks diff --git a/examples/pip_parse_vendored/.gitignore b/examples/pip_parse_vendored/.gitignore new file mode 100644 index 0000000000..ac51a054d2 --- /dev/null +++ b/examples/pip_parse_vendored/.gitignore @@ -0,0 +1 @@ +bazel-* diff --git a/examples/pip_parse_vendored/BUILD b/examples/pip_parse_vendored/BUILD.bazel similarity index 89% rename from examples/pip_parse_vendored/BUILD rename to examples/pip_parse_vendored/BUILD.bazel index b5a85295e3..56630e513d 100644 --- a/examples/pip_parse_vendored/BUILD +++ b/examples/pip_parse_vendored/BUILD.bazel @@ -17,7 +17,9 @@ genrule( "cat $<", # Insert our load statement after the existing one so we don't produce a file with buildifier warnings """sed -e '/^load.*/i\\'$$'\\n''load("@python39//:defs.bzl", "interpreter")'""", - """tr "'" '"' """, + # Replace the bazel 6.0.0 specific comment with something that bazel 5.4.0 would produce. + # This enables this example to be run as a test under bazel 5.4.0. + """sed -e 's#@//#//#'""", """sed 's#"@python39_.*//:bin/python3"#interpreter#' >$@""", ]), ) diff --git a/examples/pip_parse_vendored/README.md b/examples/pip_parse_vendored/README.md index 616e291409..f53260a175 100644 --- a/examples/pip_parse_vendored/README.md +++ b/examples/pip_parse_vendored/README.md @@ -9,3 +9,23 @@ The requirements now form a triple: - requirements.in - human editable, expresses only direct dependencies and load-bearing version constraints - requirements.txt - lockfile produced by pip-compile or other means - requirements.bzl - the "parsed" version of the lockfile readable by Bazel downloader + +The `requirements.bzl` file contains baked-in attributes such as `python_interpreter_target` as they were specified in the original `pip_parse` rule. These can be overridden at install time by passing arguments to `install_deps`. For example: + +```python +# Register a hermetic toolchain +load("@rules_python//python:repositories.bzl", "python_register_toolchains") + +python_register_toolchains( + name = "python39", + python_version = "3.9", +) +load("@python39//:defs.bzl", "interpreter") + +# Load dependencies vendored by some other ruleset. +load("@some_rules//:py_deps.bzl", "install_deps") + +install_deps( + python_interpreter_target = interpreter, +) +``` diff --git a/examples/pip_parse_vendored/WORKSPACE b/examples/pip_parse_vendored/WORKSPACE index 2f0bfb183a..157f70aeb6 100644 --- a/examples/pip_parse_vendored/WORKSPACE +++ b/examples/pip_parse_vendored/WORKSPACE @@ -1,22 +1,13 @@ workspace(name = "pip_repository_annotations_example") -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - local_repository( name = "rules_python", path = "../..", ) -http_archive( - name = "bazel_skylib", - sha256 = "c6966ec828da198c5d9adbaa94c05e3a1c7f21bd012a0b29ba8ddbccb2c93b0d", - urls = [ - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", - ], -) +load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains") -load("@rules_python//python:repositories.bzl", "python_register_toolchains") +py_repositories() python_register_toolchains( name = "python39", diff --git a/examples/pip_parse_vendored/requirements.bzl b/examples/pip_parse_vendored/requirements.bzl index 038b612309..015df9340a 100644 --- a/examples/pip_parse_vendored/requirements.bzl +++ b/examples/pip_parse_vendored/requirements.bzl @@ -11,8 +11,8 @@ all_requirements = ["@pip_certifi//:pkg", "@pip_charset_normalizer//:pkg", "@pip all_whl_requirements = ["@pip_certifi//:whl", "@pip_charset_normalizer//:whl", "@pip_idna//:whl", "@pip_requests//:whl", "@pip_urllib3//:whl"] -_packages = [("pip_certifi", "certifi==2021.10.8 --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"), ("pip_charset_normalizer", "charset-normalizer==2.0.12 --hash=sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597 --hash=sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"), ("pip_idna", "idna==3.3 --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"), ("pip_requests", "requests==2.27.1 --hash=sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61 --hash=sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"), ("pip_urllib3", "urllib3==1.26.9 --hash=sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14 --hash=sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e")] -_config = {"enable_implicit_namespace_pkgs": False, "environment": {}, "extra_pip_args": [], "isolated": True, "pip_data_exclude": [], "python_interpreter": "python3", "python_interpreter_target": interpreter, "quiet": True, "repo": "pip", "repo_prefix": "pip_", "timeout": 600} +_packages = [("pip_certifi", "certifi==2022.12.7 --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"), ("pip_charset_normalizer", "charset-normalizer==2.1.1 --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 --hash=sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"), ("pip_idna", "idna==3.4 --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"), ("pip_requests", "requests==2.28.1 --hash=sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983 --hash=sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"), ("pip_urllib3", "urllib3==1.26.13 --hash=sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc --hash=sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8")] +_config = {"download_only": False, "enable_implicit_namespace_pkgs": False, "environment": {}, "extra_pip_args": [], "isolated": True, "pip_data_exclude": [], "python_interpreter": "python3", "python_interpreter_target": interpreter, "quiet": True, "repo": "pip", "repo_prefix": "pip_", "timeout": 600} _annotations = {} def _clean_name(name): @@ -37,15 +37,17 @@ def entry_point(pkg, script = None): def _get_annotation(requirement): # This expects to parse `setuptools==58.2.0 --hash=sha256:2551203ae6955b9876741a26ab3e767bb3242dafe86a32a749ea0d78b6792f11` - # down wo `setuptools`. - name = requirement.split(" ")[0].split("=")[0] + # down to `setuptools`. + name = requirement.split(" ")[0].split("=")[0].split("[")[0] return _annotations.get(name) -def install_deps(): +def install_deps(**whl_library_kwargs): + whl_config = dict(_config) + whl_config.update(whl_library_kwargs) for name, requirement in _packages: whl_library( name = name, requirement = requirement, annotation = _get_annotation(requirement), - **_config + **whl_config ) diff --git a/examples/pip_parse_vendored/requirements.txt b/examples/pip_parse_vendored/requirements.txt index 81b56154c2..ff1a3633a2 100644 --- a/examples/pip_parse_vendored/requirements.txt +++ b/examples/pip_parse_vendored/requirements.txt @@ -1,26 +1,26 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # # bazel run //:requirements.update # -certifi==2021.10.8 \ - --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ - --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 +certifi==2022.12.7 \ + --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ + --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 # via requests -charset-normalizer==2.0.12 \ - --hash=sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597 \ - --hash=sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df +charset-normalizer==2.1.1 \ + --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 \ + --hash=sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f # via requests -idna==3.3 \ - --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ - --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d +idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 # via requests -requests==2.27.1 \ - --hash=sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61 \ - --hash=sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d +requests==2.28.1 \ + --hash=sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983 \ + --hash=sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349 # via -r requirements.in -urllib3==1.26.9 \ - --hash=sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14 \ - --hash=sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e +urllib3==1.26.13 \ + --hash=sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc \ + --hash=sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8 # via requests diff --git a/examples/pip_repository_annotations/.gitignore b/examples/pip_repository_annotations/.gitignore new file mode 100644 index 0000000000..a6ef824c1f --- /dev/null +++ b/examples/pip_repository_annotations/.gitignore @@ -0,0 +1 @@ +/bazel-* diff --git a/examples/pip_repository_annotations/BUILD b/examples/pip_repository_annotations/BUILD.bazel similarity index 74% rename from examples/pip_repository_annotations/BUILD rename to examples/pip_repository_annotations/BUILD.bazel index 8c69c40aff..84089f77d0 100644 --- a/examples/pip_repository_annotations/BUILD +++ b/examples/pip_repository_annotations/BUILD.bazel @@ -16,9 +16,13 @@ compile_pip_requirements( py_test( name = "pip_parse_annotations_test", srcs = ["pip_repository_annotations_test.py"], - env = {"WHEEL_PKG_DIR": "pip_parsed_wheel"}, + env = { + "REQUESTS_PKG_DIR": "pip_parsed_requests", + "WHEEL_PKG_DIR": "pip_parsed_wheel", + }, main = "pip_repository_annotations_test.py", deps = [ + "@pip_parsed_requests//:pkg", "@pip_parsed_wheel//:pkg", "@rules_python//python/runfiles", ], @@ -27,10 +31,14 @@ py_test( py_test( name = "pip_install_annotations_test", srcs = ["pip_repository_annotations_test.py"], - env = {"WHEEL_PKG_DIR": "pip_installed/pypi__wheel"}, + env = { + "REQUESTS_PKG_DIR": "pip_installed_requests", + "WHEEL_PKG_DIR": "pip_installed_wheel", + }, main = "pip_repository_annotations_test.py", deps = [ requirement("wheel"), + requirement("requests"), "@rules_python//python/runfiles", ], ) diff --git a/examples/pip_repository_annotations/WORKSPACE b/examples/pip_repository_annotations/WORKSPACE index 8ee885d468..3deea0329c 100644 --- a/examples/pip_repository_annotations/WORKSPACE +++ b/examples/pip_repository_annotations/WORKSPACE @@ -1,22 +1,13 @@ workspace(name = "pip_repository_annotations_example") -load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") - local_repository( name = "rules_python", path = "../..", ) -http_archive( - name = "bazel_skylib", - sha256 = "c6966ec828da198c5d9adbaa94c05e3a1c7f21bd012a0b29ba8ddbccb2c93b0d", - urls = [ - "https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", - "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", - ], -) +load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains") -load("@rules_python//python:repositories.bzl", "python_register_toolchains") +py_repositories() python_register_toolchains( name = "python39", @@ -30,6 +21,19 @@ load("@rules_python//python:pip.bzl", "package_annotation", "pip_install", "pip_ # package. For details on `package_annotation` and it's uses, see the # docs at @rules_python//docs:pip.md`. ANNOTATIONS = { + # This annotation verifies that annotations work correctly for pip packages with extras + # specified, in this case requests[security]. + "requests": package_annotation( + additive_build_content = """\ +load("@bazel_skylib//rules:write_file.bzl", "write_file") +write_file( + name = "generated_file", + out = "generated_file.txt", + content = ["Hello world from requests"], +) +""", + data = [":generated_file"], + ), "wheel": package_annotation( additive_build_content = """\ load("@bazel_skylib//rules:write_file.bzl", "write_file") @@ -54,9 +58,9 @@ pip_parse( requirements_lock = "//:requirements.txt", ) -load("@pip_parsed//:requirements.bzl", "install_deps") +load("@pip_parsed//:requirements.bzl", install_pip_parse_deps = "install_deps") -install_deps() +install_pip_parse_deps() # For a more thorough example of `pip_install`. See `@rules_python//examples/pip_install` pip_install( @@ -65,3 +69,7 @@ pip_install( python_interpreter_target = interpreter, requirements = "//:requirements.txt", ) + +load("@pip_installed//:requirements.bzl", install_pip_install_deps = "install_deps") + +install_pip_install_deps() diff --git a/examples/pip_repository_annotations/data/copy_executable.py b/examples/pip_repository_annotations/data/copy_executable.py index 20c6651e5b..5cb1af7fdb 100755 --- a/examples/pip_repository_annotations/data/copy_executable.py +++ b/examples/pip_repository_annotations/data/copy_executable.py @@ -1,4 +1,18 @@ #!/usr/bin/env python +# 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. + if __name__ == "__main__": print("Hello world from copied executable") diff --git a/examples/pip_repository_annotations/pip_repository_annotations_test.py b/examples/pip_repository_annotations/pip_repository_annotations_test.py index 468788f50b..e41dd4f0f6 100644 --- a/examples/pip_repository_annotations/pip_repository_annotations_test.py +++ b/examples/pip_repository_annotations/pip_repository_annotations_test.py @@ -1,4 +1,18 @@ #!/usr/bin/env python3 +# 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 platform @@ -65,14 +79,12 @@ def test_copy_executables(self): self.assertEqual(stdout, "Hello world from copied executable") def test_data_exclude_glob(self): - current_wheel_version = "0.37.1" + current_wheel_version = "0.38.4" r = runfiles.Create() - dist_info_dir = ( - "pip_repository_annotations_example/external/{}/site-packages/wheel-{}.dist-info".format( - self.wheel_pkg_dir(), - current_wheel_version, - ) + dist_info_dir = "pip_repository_annotations_example/external/{}/site-packages/wheel-{}.dist-info".format( + self.wheel_pkg_dir(), + current_wheel_version, ) # Note: `METADATA` is important as it's consumed by https://docs.python.org/3/library/importlib.metadata.html @@ -92,6 +104,26 @@ def test_data_exclude_glob(self): self.assertTrue(Path(metadata_path).exists()) self.assertFalse(Path(wheel_path).exists()) + def requests_pkg_dir(self) -> str: + env = os.environ.get("REQUESTS_PKG_DIR") + self.assertIsNotNone(env) + return env + + def test_extra(self): + # This test verifies that annotations work correctly for pip packages with extras + # specified, in this case requests[security]. + r = runfiles.Create() + rpath = r.Rlocation( + "pip_repository_annotations_example/external/{}/generated_file.txt".format( + self.requests_pkg_dir() + ) + ) + generated_file = Path(rpath) + self.assertTrue(generated_file.exists()) + + content = generated_file.read_text().rstrip() + self.assertEqual(content, "Hello world from requests") + if __name__ == "__main__": unittest.main() diff --git a/examples/pip_repository_annotations/requirements.in b/examples/pip_repository_annotations/requirements.in index a955311f63..fd3f75c888 100644 --- a/examples/pip_repository_annotations/requirements.in +++ b/examples/pip_repository_annotations/requirements.in @@ -3,3 +3,4 @@ --extra-index-url https://pypi.python.org/simple/ wheel +requests[security]>=2.8.1 diff --git a/examples/pip_repository_annotations/requirements.txt b/examples/pip_repository_annotations/requirements.txt index e1c53c27e3..9fde0a922f 100644 --- a/examples/pip_repository_annotations/requirements.txt +++ b/examples/pip_repository_annotations/requirements.txt @@ -1,12 +1,32 @@ # -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.9 +# by the following command: # # bazel run //:requirements.update # --extra-index-url https://pypi.python.org/simple/ -wheel==0.37.1 \ - --hash=sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a \ - --hash=sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4 +certifi==2022.12.7 \ + --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ + --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 + # via requests +charset-normalizer==2.1.1 \ + --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 \ + --hash=sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f + # via requests +idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 + # via requests +requests[security]==2.28.1 \ + --hash=sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983 \ + --hash=sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349 + # via -r requirements.in +urllib3==1.26.13 \ + --hash=sha256:47cc05d99aaa09c9e72ed5809b60e7ba354e64b59c9c173ac3018642d8bb41fc \ + --hash=sha256:c083dd0dce68dbfbe1129d5271cb90f9447dea7d52097c6e0126120c521ddea8 + # via requests +wheel==0.38.4 \ + --hash=sha256:965f5259b566725405b05e7cf774052044b1ed30119b5d586b2703aafe8719ac \ + --hash=sha256:b60533f3f5d530e971d6737ca6d58681ee434818fab630c83a734bb10c083ce8 # via -r requirements.in diff --git a/gazelle/testdata/dependency_resolution_order/baz/BUILD.in b/examples/py_proto_library/.bazelrc similarity index 100% rename from gazelle/testdata/dependency_resolution_order/baz/BUILD.in rename to examples/py_proto_library/.bazelrc diff --git a/examples/py_proto_library/.gitignore b/examples/py_proto_library/.gitignore new file mode 100644 index 0000000000..e5ae073b3c --- /dev/null +++ b/examples/py_proto_library/.gitignore @@ -0,0 +1,4 @@ +# git ignore patterns + +/bazel-* +user.bazelrc diff --git a/examples/py_proto_library/BUILD.bazel b/examples/py_proto_library/BUILD.bazel new file mode 100644 index 0000000000..7a18a5e4e1 --- /dev/null +++ b/examples/py_proto_library/BUILD.bazel @@ -0,0 +1,22 @@ +load("@rules_proto//proto:defs.bzl", "proto_library") +load("@rules_python//python:defs.bzl", "py_test") +load("@rules_python//python:proto.bzl", "py_proto_library") + +py_proto_library( + name = "pricetag_proto_py_pb2", + deps = [":pricetag_proto"], +) + +proto_library( + name = "pricetag_proto", + srcs = ["pricetag.proto"], +) + +py_test( + name = "pricetag_test", + srcs = ["test.py"], + main = "test.py", + deps = [ + ":pricetag_proto_py_pb2", + ], +) diff --git a/examples/py_proto_library/MODULE.bazel b/examples/py_proto_library/MODULE.bazel new file mode 100644 index 0000000000..5ce0924a99 --- /dev/null +++ b/examples/py_proto_library/MODULE.bazel @@ -0,0 +1,28 @@ +module( + name = "rules_python_py_proto_library_example", + version = "0.0.0", + compatibility_level = 1, +) + +bazel_dep(name = "rules_python", version = "0.17.3") + +# The following local_path_override is only needed to run this example as part of our CI. +local_path_override( + module_name = "rules_python", + path = "../..", +) + +python = use_extension("@rules_python//python:extensions.bzl", "python") +python.toolchain( + name = "python3_9", + configure_coverage_tool = True, + python_version = "3.9", +) +use_repo(python, "python3_9_toolchains") + +register_toolchains( + "@python3_9_toolchains//:all", +) + +# We are using rules_proto to define rules_proto targets to be consumed by py_proto_library. +bazel_dep(name = "rules_proto", version = "5.3.0-21.7") diff --git a/examples/py_proto_library/WORKSPACE b/examples/py_proto_library/WORKSPACE new file mode 100644 index 0000000000..bf38112f98 --- /dev/null +++ b/examples/py_proto_library/WORKSPACE @@ -0,0 +1,48 @@ +workspace(name = "rules_python_py_proto_library_example") + +# The following local_path_override is only needed to run this example as part of our CI. +local_repository( + name = "rules_python", + path = "../..", +) + +# When not using this example in the rules_python git repo you would load the python +# rules using http_archive(), as documented in the release notes. + +load("@rules_python//python:repositories.bzl", "py_repositories", "python_register_toolchains") + +# We install the rules_python dependencies using the function below. +py_repositories() + +python_register_toolchains( + name = "python39", + python_version = "3.9", +) + +# Then we need to setup dependencies in order to use py_proto_library +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "rules_proto", + sha256 = "dc3fb206a2cb3441b485eb1e423165b231235a1ea9b031b4433cf7bc1fa460dd", + strip_prefix = "rules_proto-5.3.0-21.7", + urls = [ + "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz", + ], +) + +http_archive( + name = "com_google_protobuf", + sha256 = "75be42bd736f4df6d702a0e4e4d30de9ee40eac024c4b845d17ae4cc831fe4ae", + strip_prefix = "protobuf-21.7", + urls = [ + "https://mirror.bazel.build/github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz", + "https://github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz", + ], +) + +load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") + +rules_proto_dependencies() + +rules_proto_toolchains() diff --git a/gazelle/testdata/dependency_resolution_order/foo/BUILD.in b/examples/py_proto_library/WORKSPACE.bzlmod similarity index 100% rename from gazelle/testdata/dependency_resolution_order/foo/BUILD.in rename to examples/py_proto_library/WORKSPACE.bzlmod diff --git a/examples/py_proto_library/pricetag.proto b/examples/py_proto_library/pricetag.proto new file mode 100644 index 0000000000..c952248846 --- /dev/null +++ b/examples/py_proto_library/pricetag.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package rules_python; + +message PriceTag { + string name = 2; + double cost = 1; +} diff --git a/examples/py_proto_library/test.py b/examples/py_proto_library/test.py new file mode 100644 index 0000000000..9f09702f8c --- /dev/null +++ b/examples/py_proto_library/test.py @@ -0,0 +1,17 @@ +import sys +import unittest + +import pricetag_pb2 + + +class TestCase(unittest.TestCase): + def test_pricetag(self): + got = pricetag_pb2.PriceTag( + name="dollar", + cost=5.00, + ) + self.assertIsNotNone(got) + + +if __name__ == "__main__": + unittest.main() diff --git a/examples/relative_requirements/BUILD b/examples/relative_requirements/BUILD deleted file mode 100644 index d24ee5f72b..0000000000 --- a/examples/relative_requirements/BUILD +++ /dev/null @@ -1,10 +0,0 @@ -load("@pip//:requirements.bzl", "requirement") -load("@rules_python//python:defs.bzl", "py_test") - -py_test( - name = "main", - srcs = ["main.py"], - deps = [ - requirement("relative_package_name"), - ], -) diff --git a/examples/relative_requirements/README.md b/examples/relative_requirements/README.md deleted file mode 100644 index 4b9258e370..0000000000 --- a/examples/relative_requirements/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# relative_requirements example - -This example shows how to use pip to fetch relative dependencies from a requirements.txt file, -then use them in BUILD files as dependencies of Bazel targets. diff --git a/examples/relative_requirements/WORKSPACE b/examples/relative_requirements/WORKSPACE deleted file mode 100644 index 4ae91c39d8..0000000000 --- a/examples/relative_requirements/WORKSPACE +++ /dev/null @@ -1,21 +0,0 @@ -workspace(name = "example_repo") - -local_repository( - name = "rules_python", - path = "../..", -) - -load("@rules_python//python:repositories.bzl", "python_register_toolchains") - -python_register_toolchains( - name = "python39", - python_version = "3.9", -) - -load("@python39//:defs.bzl", "interpreter") -load("@rules_python//python:pip.bzl", "pip_install") - -pip_install( - python_interpreter_target = interpreter, - requirements = "//:requirements.txt", -) diff --git a/examples/relative_requirements/main.py b/examples/relative_requirements/main.py deleted file mode 100644 index b8ac021e90..0000000000 --- a/examples/relative_requirements/main.py +++ /dev/null @@ -1,5 +0,0 @@ -import relative_package_name - -if __name__ == "__main__": - # Run a function from the relative package - print(relative_package_name.test()) diff --git a/examples/relative_requirements/relative_package/relative_package_name/__init__.py b/examples/relative_requirements/relative_package/relative_package_name/__init__.py deleted file mode 100644 index c031192907..0000000000 --- a/examples/relative_requirements/relative_package/relative_package_name/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -def test(): - return True diff --git a/examples/relative_requirements/relative_package/setup.py b/examples/relative_requirements/relative_package/setup.py deleted file mode 100644 index 052b519345..0000000000 --- a/examples/relative_requirements/relative_package/setup.py +++ /dev/null @@ -1,7 +0,0 @@ -from setuptools import setup - -setup( - name="relative_package_name", - version="1.0.0", - packages=["relative_package_name"], -) diff --git a/examples/relative_requirements/requirements.txt b/examples/relative_requirements/requirements.txt deleted file mode 100644 index 9a81317e1e..0000000000 --- a/examples/relative_requirements/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -./relative_package diff --git a/examples/wheel/BUILD b/examples/wheel/BUILD.bazel similarity index 82% rename from examples/wheel/BUILD rename to examples/wheel/BUILD.bazel index f745dc31ca..61a43ae6cf 100644 --- a/examples/wheel/BUILD +++ b/examples/wheel/BUILD.bazel @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@bazel_skylib//rules:build_test.bzl", "build_test") +load("//examples/wheel/private:wheel_utils.bzl", "directory_writer", "make_variable_tags") load("//python:defs.bzl", "py_library", "py_test") load("//python:packaging.bzl", "py_package", "py_wheel") load("//python:versions.bzl", "gen_python_config_settings") @@ -40,15 +42,16 @@ py_library( ], ) -genrule( +directory_writer( name = "gen_dir", - outs = ["someDir"], - cmd = "mkdir -p $@ && touch $@/foo.py", + out = "someDir", + files = {"foo.py": ""}, ) # Package just a specific py_libraries, without their dependencies py_wheel( name = "minimal_with_py_library", + testonly = True, # Set this to verify the generated .dist target doesn't break things # Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl" distribution = "example_minimal_library", python_tag = "py3", @@ -59,6 +62,34 @@ py_wheel( ], ) +# Populate a rule with "Make Variable" arguments for +# abi, python_tag and version. You might want to do this +# for the following use cases: +# - abi, python_tag: introspect a toolchain to map to appropriate cpython tags +# - version: populate given this or a dependent module's version +make_variable_tags( + name = "make_variable_tags", +) + +py_wheel( + name = "minimal_with_py_library_with_make_variables", + testonly = True, + abi = "$(ABI)", + distribution = "example_minimal_library", + python_tag = "$(PYTHON_TAG)", + toolchains = ["//examples/wheel:make_variable_tags"], + version = "$(VERSION)", + deps = [ + "//examples/wheel/lib:module_with_data", + "//examples/wheel/lib:simple_module", + ], +) + +build_test( + name = "dist_build_tests", + targets = [":minimal_with_py_library.dist"], +) + # Package just a specific py_libraries, without their dependencies py_wheel( name = "minimal_with_py_library_with_stamp", @@ -119,6 +150,11 @@ py_wheel( "first = first.main:f", ], }, + extra_distinfo_files = { + "//examples/wheel:NOTICE": "NOTICE", + # Rename the file when packaging to show we can. + "//examples/wheel:README.md": "README", + }, homepage = "www.example.com", license = "Apache 2.0", python_tag = "py3", @@ -188,8 +224,8 @@ py_wheel( ) py_wheel( - name = "use_genrule_with_dir_in_outs", - distribution = "use_genrule_with_dir_in_outs", + name = "use_rule_with_dir_in_outs", + distribution = "use_rule_with_dir_in_outs", python_tag = "py3", version = "0.0.1", deps = [ @@ -237,9 +273,10 @@ py_test( ":customized", ":filename_escaping", ":minimal_with_py_library", + ":minimal_with_py_library_with_stamp", ":minimal_with_py_package", ":python_abi3_binary_wheel", ":python_requires_in_a_package", - ":use_genrule_with_dir_in_outs", + ":use_rule_with_dir_in_outs", ], ) diff --git a/examples/wheel/NOTICE b/examples/wheel/NOTICE new file mode 100644 index 0000000000..700336b8cf --- /dev/null +++ b/examples/wheel/NOTICE @@ -0,0 +1 @@ +This is a test "NOTICE" file to be packaged into distribtion dist-info dir. diff --git a/examples/wheel/lib/BUILD b/examples/wheel/lib/BUILD.bazel similarity index 100% rename from examples/wheel/lib/BUILD rename to examples/wheel/lib/BUILD.bazel diff --git a/examples/wheel/private/BUILD.bazel b/examples/wheel/private/BUILD.bazel new file mode 100644 index 0000000000..3462d354d4 --- /dev/null +++ b/examples/wheel/private/BUILD.bazel @@ -0,0 +1,7 @@ +load("@rules_python//python:defs.bzl", "py_binary") + +py_binary( + name = "directory_writer", + srcs = ["directory_writer.py"], + visibility = ["//:__subpackages__"], +) diff --git a/examples/wheel/private/directory_writer.py b/examples/wheel/private/directory_writer.py new file mode 100644 index 0000000000..7d9a93ed3f --- /dev/null +++ b/examples/wheel/private/directory_writer.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# 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. + +"""The action executable of the `@rules_python//examples/wheel/private:wheel_utils.bzl%directory_writer` rule.""" + +import argparse +import json +from pathlib import Path +from typing import Tuple + + +def _file_input(value) -> Tuple[Path, str]: + path, content = value.split("=", maxsplit=1) + return (Path(path), json.loads(content)) + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser() + + parser.add_argument( + "--output", type=Path, required=True, help="The output directory to create." + ) + parser.add_argument( + "--file", + dest="files", + type=_file_input, + action="append", + help="Files to create within the `output` directory.", + ) + + return parser.parse_args() + + +def main() -> None: + args = parse_args() + + args.output.mkdir(parents=True, exist_ok=True) + + for (path, content) in args.files: + new_file = args.output / path + new_file.parent.mkdir(parents=True, exist_ok=True) + new_file.write_text(content) + + +if __name__ == "__main__": + main() diff --git a/examples/wheel/private/wheel_utils.bzl b/examples/wheel/private/wheel_utils.bzl new file mode 100644 index 0000000000..037fed0175 --- /dev/null +++ b/examples/wheel/private/wheel_utils.bzl @@ -0,0 +1,73 @@ +# 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. + +"""Helper rules for demonstrating `py_wheel` examples""" + +def _directory_writer_impl(ctx): + output = ctx.actions.declare_directory(ctx.attr.out) + + args = ctx.actions.args() + args.add("--output", output.path) + + for path, content in ctx.attr.files.items(): + args.add("--file={}={}".format( + path, + json.encode(content), + )) + + ctx.actions.run( + outputs = [output], + arguments = [args], + executable = ctx.executable._writer, + ) + + return [DefaultInfo( + files = depset([output]), + runfiles = ctx.runfiles(files = [output]), + )] + +directory_writer = rule( + implementation = _directory_writer_impl, + doc = "A rule for generating a directory with the requested content.", + attrs = { + "files": attr.string_dict( + doc = "A mapping of file name to content to create relative to the generated `out` directory.", + ), + "out": attr.string( + doc = "The name of the directory to create", + ), + "_writer": attr.label( + executable = True, + cfg = "exec", + default = Label("//examples/wheel/private:directory_writer"), + ), + }, +) + +def _make_variable_tags_impl(ctx): # buildifier: disable=unused-variable + # This example is contrived. In a real usage, this rule would + # look at flags or dependencies to determine what values to use. + # If all you're doing is setting constant values, then you can simply + # set them in the py_wheel() call. + vars = {} + vars["ABI"] = "cp38" + vars["PYTHON_TAG"] = "cp38" + vars["VERSION"] = "0.99.0" + return [platform_common.TemplateVariableInfo(vars)] + +make_variable_tags = rule( + attrs = {}, + doc = """Make variable tags to pass to a py_wheel rule.""", + implementation = _make_variable_tags_impl, +) diff --git a/examples/wheel/wheel_test.py b/examples/wheel/wheel_test.py index f326af20c6..c292c87132 100644 --- a/examples/wheel/wheel_test.py +++ b/examples/wheel/wheel_test.py @@ -20,6 +20,8 @@ class WheelTest(unittest.TestCase): + maxDiff = None + def test_py_library_wheel(self): filename = os.path.join( os.environ["TEST_SRCDIR"], @@ -29,7 +31,7 @@ def test_py_library_wheel(self): "example_minimal_library-0.0.1-py3-none-any.whl", ) with zipfile.ZipFile(filename) as zf: - self.assertEquals( + self.assertEqual( zf.namelist(), [ "examples/wheel/lib/module_with_data.py", @@ -49,7 +51,7 @@ def test_py_package_wheel(self): "example_minimal_package-0.0.1-py3-none-any.whl", ) with zipfile.ZipFile(filename) as zf: - self.assertEquals( + self.assertEqual( zf.namelist(), [ "examples/wheel/lib/data.txt", @@ -71,7 +73,7 @@ def test_customized_wheel(self): "example_customized-0.0.1-py3-none-any.whl", ) with zipfile.ZipFile(filename) as zf: - self.assertEquals( + self.assertEqual( zf.namelist(), [ "examples/wheel/lib/data.txt", @@ -81,6 +83,8 @@ def test_customized_wheel(self): "example_customized-0.0.1.dist-info/WHEEL", "example_customized-0.0.1.dist-info/METADATA", "example_customized-0.0.1.dist-info/entry_points.txt", + "example_customized-0.0.1.dist-info/NOTICE", + "example_customized-0.0.1.dist-info/README", "example_customized-0.0.1.dist-info/RECORD", ], ) @@ -90,26 +94,14 @@ def test_customized_wheel(self): entry_point_contents = zf.read( "example_customized-0.0.1.dist-info/entry_points.txt" ) - # The entries are guaranteed to be sorted. - if platform.system() == "Windows": - self.assertEquals( - record_contents, - b"""\ -example_customized-0.0.1.dist-info/METADATA,sha256=pzE96o3Sp63TDzxAZgl0F42EFevm8x15vpDLqDVp_EQ,378 -example_customized-0.0.1.dist-info/RECORD,, -example_customized-0.0.1.dist-info/WHEEL,sha256=sobxWSyDDkdg_rinUth-jxhXHqoNqlmNMJY3aTZn2Us,91 -example_customized-0.0.1.dist-info/entry_points.txt,sha256=pqzpbQ8MMorrJ3Jp0ntmpZcuvfByyqzMXXi2UujuXD0,137 -examples/wheel/lib/data.txt,sha256=9vJKEdfLu8bZRArKLroPZJh1XKkK3qFMXiM79MBL2Sg,12 -examples/wheel/lib/module_with_data.py,sha256=8s0Khhcqz3yVsBKv2IB5u4l4TMKh7-c_V6p65WVHPms,637 -examples/wheel/lib/simple_module.py,sha256=z2hwciab_XPNIBNH8B1Q5fYgnJvQTeYf0ZQJpY8yLLY,637 -examples/wheel/main.py,sha256=sgg5iWN_9inYBjm6_Zw27hYdmo-l24fA-2rfphT-IlY,909 -""", - ) - else: - self.assertEquals( - record_contents, - b"""\ -example_customized-0.0.1.dist-info/METADATA,sha256=TeeEmokHE2NWjkaMcVJuSAq4_AXUoIad2-SLuquRmbg,372 + + self.assertEqual( + record_contents, + # The entries are guaranteed to be sorted. + b"""\ +example_customized-0.0.1.dist-info/METADATA,sha256=YUnzQ9gTMXspIBURe90Ct3aL_CCn8fwC3SiZe6MMTs8,372 +example_customized-0.0.1.dist-info/NOTICE,sha256=Xpdw-FXET1IRgZ_wTkx1YQfo1-alET0FVf6V1LXO4js,76 +example_customized-0.0.1.dist-info/README,sha256=WmOFwZ3Jga1bHG3JiGRsUheb4UbLffUxyTdHczS27-o,40 example_customized-0.0.1.dist-info/RECORD,, example_customized-0.0.1.dist-info/WHEEL,sha256=sobxWSyDDkdg_rinUth-jxhXHqoNqlmNMJY3aTZn2Us,91 example_customized-0.0.1.dist-info/entry_points.txt,sha256=pqzpbQ8MMorrJ3Jp0ntmpZcuvfByyqzMXXi2UujuXD0,137 @@ -118,8 +110,8 @@ def test_customized_wheel(self): examples/wheel/lib/simple_module.py,sha256=z2hwciab_XPNIBNH8B1Q5fYgnJvQTeYf0ZQJpY8yLLY,637 examples/wheel/main.py,sha256=sgg5iWN_9inYBjm6_Zw27hYdmo-l24fA-2rfphT-IlY,909 """, - ) - self.assertEquals( + ) + self.assertEqual( wheel_contents, b"""\ Wheel-Version: 1.0 @@ -128,31 +120,11 @@ def test_customized_wheel(self): Tag: py3-none-any """, ) - if platform.system() == "Windows": - self.assertEquals( - metadata_contents, - b"""\ -Metadata-Version: 2.1 -Name: example_customized -Version: 0.0.1 -Author: Example Author with non-ascii characters: \xc3\x85\xc2\xbc\xc3\x83\xc2\xb3\xc3\x85\xc2\x82w -Author-email: example@example.com -Home-page: www.example.com -License: Apache 2.0 -Classifier: License :: OSI Approved :: Apache Software License -Classifier: Intended Audience :: Developers -Requires-Dist: pytest - -This is a sample description of a wheel. -""", - ) - else: - self.assertEquals( - metadata_contents, - b"""\ + self.assertEqual( + metadata_contents, + b"""\ Metadata-Version: 2.1 Name: example_customized -Version: 0.0.1 Author: Example Author with non-ascii characters: \xc5\xbc\xc3\xb3\xc5\x82w Author-email: example@example.com Home-page: www.example.com @@ -160,11 +132,12 @@ def test_customized_wheel(self): Classifier: License :: OSI Approved :: Apache Software License Classifier: Intended Audience :: Developers Requires-Dist: pytest +Version: 0.0.1 This is a sample description of a wheel. """, - ) - self.assertEquals( + ) + self.assertEqual( entry_point_contents, b"""\ [console_scripts] @@ -185,7 +158,7 @@ def test_filename_escaping(self): "file_name_escaping-0.0.1_r7-py3-none-any.whl", ) with zipfile.ZipFile(filename) as zf: - self.assertEquals( + self.assertEqual( zf.namelist(), [ "examples/wheel/lib/data.txt", @@ -203,7 +176,7 @@ def test_filename_escaping(self): metadata_contents = zf.read( "file_name_escaping-0.0.1_r7.dist-info/METADATA" ) - self.assertEquals( + self.assertEqual( metadata_contents, b"""\ Metadata-Version: 2.1 @@ -224,7 +197,7 @@ def test_custom_package_root_wheel(self): ) with zipfile.ZipFile(filename) as zf: - self.assertEquals( + self.assertEqual( zf.namelist(), [ "wheel/lib/data.txt", @@ -238,6 +211,14 @@ def test_custom_package_root_wheel(self): ], ) + record_contents = zf.read( + "examples_custom_package_root-0.0.1.dist-info/RECORD" + ).decode("utf-8") + + # Ensure RECORD files do not have leading forward slashes + for line in record_contents.splitlines(): + self.assertFalse(line.startswith("/")) + def test_custom_package_root_multi_prefix_wheel(self): filename = os.path.join( os.environ["TEST_SRCDIR"], @@ -248,7 +229,7 @@ def test_custom_package_root_multi_prefix_wheel(self): ) with zipfile.ZipFile(filename) as zf: - self.assertEquals( + self.assertEqual( zf.namelist(), [ "data.txt", @@ -261,6 +242,14 @@ def test_custom_package_root_multi_prefix_wheel(self): ], ) + record_contents = zf.read( + "example_custom_package_root_multi_prefix-0.0.1.dist-info/RECORD" + ).decode("utf-8") + + # Ensure RECORD files do not have leading forward slashes + for line in record_contents.splitlines(): + self.assertFalse(line.startswith("/")) + def test_custom_package_root_multi_prefix_reverse_order_wheel(self): filename = os.path.join( os.environ["TEST_SRCDIR"], @@ -271,7 +260,7 @@ def test_custom_package_root_multi_prefix_reverse_order_wheel(self): ) with zipfile.ZipFile(filename) as zf: - self.assertEquals( + self.assertEqual( zf.namelist(), [ "lib/data.txt", @@ -284,6 +273,14 @@ def test_custom_package_root_multi_prefix_reverse_order_wheel(self): ], ) + record_contents = zf.read( + "example_custom_package_root_multi_prefix_reverse_order-0.0.1.dist-info/RECORD" + ).decode("utf-8") + + # Ensure RECORD files do not have leading forward slashes + for line in record_contents.splitlines(): + self.assertFalse(line.startswith("/")) + def test_python_requires_wheel(self): filename = os.path.join( os.environ["TEST_SRCDIR"], @@ -297,13 +294,13 @@ def test_python_requires_wheel(self): "example_python_requires_in_a_package-0.0.1.dist-info/METADATA" ) # The entries are guaranteed to be sorted. - self.assertEquals( + self.assertEqual( metadata_contents, b"""\ Metadata-Version: 2.1 Name: example_python_requires_in_a_package -Version: 0.0.1 Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Version: 0.0.1 UNKNOWN """, @@ -337,8 +334,8 @@ def test_python_abi3_binary_wheel(self): b"""\ Metadata-Version: 2.1 Name: example_python_abi3_binary_wheel -Version: 0.0.1 Requires-Python: >=3.8 +Version: 0.0.1 UNKNOWN """, @@ -356,27 +353,52 @@ def test_python_abi3_binary_wheel(self): """, ) - def test_genrule_creates_directory_and_is_included_in_wheel(self): + def test_rule_creates_directory_and_is_included_in_wheel(self): filename = os.path.join( os.environ["TEST_SRCDIR"], "rules_python", "examples", "wheel", - "use_genrule_with_dir_in_outs-0.0.1-py3-none-any.whl", + "use_rule_with_dir_in_outs-0.0.1-py3-none-any.whl", ) with zipfile.ZipFile(filename) as zf: - self.assertEquals( + self.assertEqual( zf.namelist(), [ "examples/wheel/main.py", "examples/wheel/someDir/foo.py", - "use_genrule_with_dir_in_outs-0.0.1.dist-info/WHEEL", - "use_genrule_with_dir_in_outs-0.0.1.dist-info/METADATA", - "use_genrule_with_dir_in_outs-0.0.1.dist-info/RECORD", + "use_rule_with_dir_in_outs-0.0.1.dist-info/WHEEL", + "use_rule_with_dir_in_outs-0.0.1.dist-info/METADATA", + "use_rule_with_dir_in_outs-0.0.1.dist-info/RECORD", ], ) + def test_rule_sets_stamped_version_in_wheel_metadata(self): + filename = os.path.join( + os.environ["TEST_SRCDIR"], + "rules_python", + "examples", + "wheel", + "example_minimal_library-0.1._BUILD_TIMESTAMP_-py3-none-any.whl", + ) + + with zipfile.ZipFile(filename) as zf: + metadata_file = None + for f in zf.namelist(): + self.assertNotIn("_BUILD_TIMESTAMP_", f) + if os.path.basename(f) == "METADATA": + metadata_file = f + self.assertIsNotNone(metadata_file) + + version = None + with zf.open(metadata_file) as fp: + for line in fp: + if line.startswith(b'Version:'): + version = line.decode().split()[-1] + self.assertIsNotNone(version) + self.assertNotIn("{BUILD_TIMESTAMP}", version) + if __name__ == "__main__": unittest.main() diff --git a/gazelle/.bazelrc b/gazelle/.bazelrc new file mode 100644 index 0000000000..f48d0a97ee --- /dev/null +++ b/gazelle/.bazelrc @@ -0,0 +1,13 @@ +test --test_output=errors + +# Do NOT implicitly create empty __init__.py files in the runfiles tree. +# By default, these are created in every directory containing Python source code +# or shared libraries, and every parent directory of those directories, +# excluding the repo root directory. With this flag set, we are responsible for +# creating (possibly empty) __init__.py files and adding them to the srcs of +# Python targets as required. +build --incompatible_default_to_explicit_init_py + +# Windows makes use of runfiles for some rules +build --enable_runfiles +startup --windows_enable_symlinks diff --git a/gazelle/.gitignore b/gazelle/.gitignore new file mode 100644 index 0000000000..8481c9668c --- /dev/null +++ b/gazelle/.gitignore @@ -0,0 +1,12 @@ +# Bazel directories +/bazel-* +/bazel-bin +/bazel-genfiles +/bazel-out +/bazel-testlogs +user.bazelrc + +# Go/Gazelle files +# These otherwise match patterns above +!go.mod +!BUILD.out diff --git a/gazelle/BUILD.bazel b/gazelle/BUILD.bazel index c24a086a50..6016145516 100644 --- a/gazelle/BUILD.bazel +++ b/gazelle/BUILD.bazel @@ -1,71 +1,35 @@ -load("@bazel_gazelle//:def.bzl", "gazelle_binary") -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") -load("@rules_python//python:defs.bzl", "py_binary") +load("@bazel_gazelle//:def.bzl", "gazelle") -go_library( - name = "gazelle", - srcs = [ - "configure.go", - "fix.go", - "generate.go", - "kinds.go", - "language.go", - "parser.go", - "resolve.go", - "std_modules.go", - "target.go", - ], - importpath = "github.com/bazelbuild/rules_python/gazelle", - visibility = ["//visibility:public"], - deps = [ - "//gazelle/manifest", - "//gazelle/pythonconfig", - "@bazel_gazelle//config:go_default_library", - "@bazel_gazelle//label:go_default_library", - "@bazel_gazelle//language:go_default_library", - "@bazel_gazelle//repo:go_default_library", - "@bazel_gazelle//resolve:go_default_library", - "@bazel_gazelle//rule:go_default_library", - "@com_github_bazelbuild_buildtools//build:go_default_library", - "@com_github_bmatcuk_doublestar//:doublestar", - "@com_github_emirpasic_gods//lists/singlylinkedlist", - "@com_github_emirpasic_gods//sets/treeset", - "@com_github_emirpasic_gods//utils", - "@com_github_google_uuid//:uuid", - "@io_bazel_rules_go//go/tools/bazel:go_default_library", - ], -) - -py_binary( - name = "parse", - srcs = ["parse.py"], - visibility = ["//visibility:public"], -) - -py_binary( - name = "std_modules", - srcs = ["std_modules.py"], - visibility = ["//visibility:public"], -) +# Gazelle configuration options. +# See https://github.com/bazelbuild/bazel-gazelle#running-gazelle-with-bazel +# gazelle:prefix github.com/bazelbuild/rules_python/gazelle +# gazelle:exclude bazel-out +gazelle(name = "gazelle") -go_test( - name = "gazelle_test", - srcs = ["python_test.go"], - data = [ - ":gazelle_python_binary", - ":parse", - ":std_modules", - ] + glob(["testdata/**"]), - deps = [ - "@bazel_gazelle//testtools:go_default_library", - "@com_github_emirpasic_gods//lists/singlylinkedlist", - "@com_github_ghodss_yaml//:yaml", - "@io_bazel_rules_go//go/tools/bazel:go_default_library", +gazelle( + name = "gazelle_update_repos", + args = [ + "-from_file=go.mod", + "-to_macro=deps.bzl%gazelle_deps", + "-prune", ], + command = "update-repos", ) -gazelle_binary( - name = "gazelle_python_binary", - languages = ["//gazelle"], - visibility = ["//visibility:public"], +filegroup( + name = "distribution", + srcs = [ + ":BUILD.bazel", + ":README.md", + ":WORKSPACE", + ":def.bzl", + ":deps.bzl", + ":go.mod", + ":go.sum", + "//manifest:distribution", + "//modules_mapping:distribution", + "//python:distribution", + "//pythonconfig:distribution", + ], + visibility = ["@rules_python//:__pkg__"], ) diff --git a/gazelle/MODULE.bazel b/gazelle/MODULE.bazel new file mode 100644 index 0000000000..bd634020f3 --- /dev/null +++ b/gazelle/MODULE.bazel @@ -0,0 +1,20 @@ +module( + name = "rules_python_gazelle_plugin", + version = "0.0.0", + compatibility_level = 1, +) + +bazel_dep(name = "rules_python", version = "0.18.0") +bazel_dep(name = "rules_go", version = "0.38.1", repo_name = "io_bazel_rules_go") +bazel_dep(name = "gazelle", version = "0.29.0", repo_name = "bazel_gazelle") + +go_deps = use_extension("@bazel_gazelle//:extensions.bzl", "go_deps") +go_deps.from_file(go_mod = "//:go.mod") +use_repo( + go_deps, + "com_github_bazelbuild_buildtools", + "com_github_bmatcuk_doublestar", + "com_github_emirpasic_gods", + "com_github_ghodss_yaml", + "in_gopkg_yaml_v2", +) diff --git a/gazelle/README.md b/gazelle/README.md index 51055cb953..e36f3a303a 100644 --- a/gazelle/README.md +++ b/gazelle/README.md @@ -1,27 +1,79 @@ # Python Gazelle plugin +[Gazelle](https://github.com/bazelbuild/bazel-gazelle) +is a build file generator for Bazel projects. It can create new BUILD.bazel files for a project that follows language conventions, and it can update existing build files to include new sources, dependencies, and options. + +Gazelle may be run by Bazel using the gazelle rule, or it may be installed and run as a command line tool. + This directory contains a plugin for [Gazelle](https://github.com/bazelbuild/bazel-gazelle) -that generates BUILD file content for Python code. +that generates BUILD files content for Python code. + +The following instructions are for when you use [bzlmod](https://docs.bazel.build/versions/5.0.0/bzlmod.html). +Please refer to older documentation that includes instructions on how to use Gazelle +without using bzlmod as your dependency manager. + +## Example + +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. -## Installation +## Adding Gazelle to your project -First, you'll need to add Gazelle to your `WORKSPACE` file. -Follow the instructions at https://github.com/bazelbuild/bazel-gazelle#running-gazelle-with-bazel +First, you'll need to add Gazelle to your `MODULES.bazel` file. +Get the current version of Gazelle from there releases here: https://github.com/bazelbuild/bazel-gazelle/releases/. -Next, we need to fetch the third-party Go libraries that the python extension -depends on. -Add this to your `WORKSPACE`: +See the installation `MODULE.bazel` snippet on the Releases page: +https://github.com/bazelbuild/rules_python/releases in order to configure rules_python. + +You will also need to add the `bazel_dep` for configuration for `rules_python_gazelle_plugin`. + +Here is a snippet of a `MODULE.bazel` file. ```starlark -# To compile the rules_python gazelle extension from source, -# we must fetch some third-party go dependencies that it uses. -load("@rules_python//gazelle:deps.bzl", _py_gazelle_deps = "gazelle_deps") +# The following stanza defines the dependency rules_python. +bazel_dep(name = "rules_python", version = "0.20.0") -_py_gazelle_deps() -``` +# The following stanza defines the dependency rules_python. +# For typical setups you set the version. +bazel_dep(name = "rules_python_gazelle_plugin", version = "0.20.0") +# The following stanza defines the dependency rules_python. +bazel_dep(name = "gazelle", version = "0.30.0", repo_name = "bazel_gazelle") + +# 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 @@ -29,15 +81,17 @@ by the `modules_mapping` rule. We'll make a target for consuming this This is checked into the repo for speed, as it takes some time to calculate in a large monorepo. -Create a file `gazelle_python.yaml` next to your `requirements.txt` -file. (You can just use `touch` at this point, it just needs to exist.) +Gazelle will walk up the filesystem from a Python file to find this metadata, +looking for a file called `gazelle_python.yaml` in an ancestor folder of the Python code. +Create an empty file with this name. It might be next to your `requirements.txt` file. +(You can just use `touch` at this point, it just needs to exist.) -Then put this in your `BUILD.bazel` file next to the `requirements.txt`: +To keep the metadata updated, put this in your `BUILD.bazel` file next to `gazelle_python.yaml`: ```starlark load("@pip//:requirements.bzl", "all_whl_requirements") -load("@rules_python//gazelle/manifest:defs.bzl", "gazelle_python_manifest") -load("@rules_python//gazelle/modules_mapping:def.bzl", "modules_mapping") +load("@rules_python_gazelle_plugin//manifest:defs.bzl", "gazelle_python_manifest") +load("@rules_python_gazelle_plugin//modules_mapping:def.bzl", "modules_mapping") # This 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. @@ -59,11 +113,12 @@ gazelle_python_manifest( # This is what we called our `pip_install` rule, where third-party # python libraries are loaded in BUILD files. pip_repository_name = "pip", - # When using pip_parse instead of pip_install, set the following. - # pip_repository_incremental = True, # 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, ) ``` @@ -71,9 +126,9 @@ Finally, you create a target that you'll invoke to run the Gazelle tool with the rules_python extension included. This typically goes in your root `/BUILD.bazel` file: -``` +```starlark load("@bazel_gazelle//:def.bzl", "gazelle") -load("@rules_python//gazelle:def.bzl", "GAZELLE_PYTHON_RUNTIME_DEPS") +load("@rules_python_gazelle_plugin//:def.bzl", "GAZELLE_PYTHON_RUNTIME_DEPS") # Our gazelle target points to the python gazelle binary. # This is the simple case where we only need one language supported. @@ -83,15 +138,13 @@ load("@rules_python//gazelle:def.bzl", "GAZELLE_PYTHON_RUNTIME_DEPS") gazelle( name = "gazelle", data = GAZELLE_PYTHON_RUNTIME_DEPS, - gazelle = "@rules_python//gazelle:gazelle_python_binary", + gazelle = "@rules_python_gazelle_plugin//python:gazelle_binary", ) ``` 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. @@ -162,11 +215,29 @@ Next, all source files are collected into the `srcs` of the `py_library`. Finally, the `import` statements in the source files are parsed, and dependencies are added to the `deps` attribute. -### Tests +### Unit Tests + +A `py_test` target is added to the BUILD file when gazelle encounters +a file named `__test__.py`. +Often, Python unit test files are named with the suffix `_test`. +For example, if we had a folder that is a package named "foo" we could have a Python file named `foo_test.py` +and gazelle would create a `py_test` block for the file. + +The following is an example of a `py_test` target that gazelle would add when +it encounters a file named `__test__.py`. -Python test files are those ending in `_test.py`. +```starlark +py_test( + name = "build_file_generation_test", + srcs = ["__test__.py"], + main = "__test__.py", + deps = [":build_file_generation"], +) +``` -A `py_test` target is added containing all test files as `srcs`. +You can control the naming convention for test targets by adding a gazelle directive named +`# gazelle:python_test_naming_convention`. See the instructions in the section above that +covers directives. ### Binaries @@ -175,16 +246,18 @@ of a Python program. A `py_binary` target will be created, named `[package]_bin`. -## Developing on the extension +## Developer Notes -Gazelle extensions are written in Go. Ours is a hybrid, which also spawns -a Python interpreter as a subprocess to parse python files. +Gazelle extensions are written in Go. This gazelle plugin is a hybrid, as it uses Go to execute a +Python interpreter as a subprocess to parse Python source files. +See the gazelle documentation https://github.com/bazelbuild/bazel-gazelle/blob/master/extend.md +for more information on extending Gazelle. -The Go dependencies are managed by the go.mod file. -After changing that file, run `go mod tidy` to get a `go.sum` file, -then run `bazel run //:update_go_deps` to convert that to the `gazelle/deps.bzl` file. -The latter is loaded in our `/WORKSPACE` to define the external repos -that we can load Go dependencies from. +If you add new Go dependencies to the plugin source code, you need to "tidy" the go.mod file. +After changing that file, run `go mod tidy` or `bazel run @go_sdk//:bin/go -- mod tidy` +to update the go.mod and go.sum files. Then run `bazel run //:update_go_deps` to have gazelle +add the new dependenies to the deps.bzl file. The deps.bzl file is used as defined in our /WORKSPACE +to include the external repos Bazel loads Go dependencies from. -Then after editing Go code, run `bazel run //:gazelle` to generate/update -go_* rules in the BUILD.bazel files in our repo. +Then after editing Go code, run `bazel run //:gazelle` to generate/update the rules in the +BUILD.bazel files in our repo. diff --git a/gazelle/WORKSPACE b/gazelle/WORKSPACE new file mode 100644 index 0000000000..55cf1b0d40 --- /dev/null +++ b/gazelle/WORKSPACE @@ -0,0 +1,47 @@ +workspace(name = "rules_python_gazelle_plugin") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "io_bazel_rules_go", + sha256 = "099a9fb96a376ccbbb7d291ed4ecbdfd42f6bc822ab77ae6f1b5cb9e914e94fa", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.35.0/rules_go-v0.35.0.zip", + "https://github.com/bazelbuild/rules_go/releases/download/v0.35.0/rules_go-v0.35.0.zip", + ], +) + +http_archive( + name = "bazel_gazelle", + sha256 = "448e37e0dbf61d6fa8f00aaa12d191745e14f07c31cabfa731f0c8e8a4f41b97", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.28.0/bazel-gazelle-v0.28.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.28.0/bazel-gazelle-v0.28.0.tar.gz", + ], +) + +load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") +load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") + +go_rules_dependencies() + +go_register_toolchains(version = "1.19.4") + +gazelle_dependencies() + +local_repository( + name = "rules_python", + path = "..", +) + +load("@rules_python//python:repositories.bzl", "python_register_toolchains") + +python_register_toolchains( + name = "python39", + python_version = "3.9", +) + +load("//:deps.bzl", _py_gazelle_deps = "gazelle_deps") + +# gazelle:repository_macro deps.bzl%gazelle_deps +_py_gazelle_deps() diff --git a/gazelle/bazel_gazelle.pr1095.patch b/gazelle/bazel_gazelle.pr1095.patch deleted file mode 100644 index a417c94944..0000000000 --- a/gazelle/bazel_gazelle.pr1095.patch +++ /dev/null @@ -1,19 +0,0 @@ -commit b1c61c0b77648f7345a7c42cce941e32d87c84bf -Author: Alex Eagle -Date: Wed Aug 18 17:55:13 2021 -0700 - - Merge the private attribute - -diff --git a/rule/merge.go b/rule/merge.go -index d5fbe94..e13e547 100644 ---- a/rule/merge.go -+++ b/rule/merge.go -@@ -79,6 +79,8 @@ func MergeRules(src, dst *Rule, mergeable map[string]bool, filename string) { - } - } - } -+ -+ dst.private = src.private - } - - // mergeExprs combines information from src and dst and returns a merged diff --git a/gazelle/def.bzl b/gazelle/def.bzl index a402fc74c4..80b11576e6 100644 --- a/gazelle/def.bzl +++ b/gazelle/def.bzl @@ -1,7 +1,21 @@ +# 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. + """This module contains the Gazelle runtime dependencies for the Python extension. """ GAZELLE_PYTHON_RUNTIME_DEPS = [ - "@rules_python//gazelle:parse", - "@rules_python//gazelle:std_modules", + "@rules_python_gazelle_plugin//python:parse", + "@rules_python_gazelle_plugin//python:std_modules", ] diff --git a/gazelle/deps.bzl b/gazelle/deps.bzl index 1d53fdd99f..26f8c66aec 100644 --- a/gazelle/deps.bzl +++ b/gazelle/deps.bzl @@ -1,4 +1,18 @@ -"This file managed by `bazel run //:update_go_deps`" +# 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. + +"This file managed by `bazel run //:gazelle_update_repos`" load("@bazel_gazelle//:deps.bzl", _go_repository = "go_repository") @@ -9,30 +23,27 @@ def go_repository(name, **kwargs): def gazelle_deps(): "Fetch go dependencies" go_repository( - name = "com_github_bazelbuild_bazel_gazelle", - importpath = "github.com/bazelbuild/bazel-gazelle", - sum = "h1:Ks6YN+WkOv2lYWlvf7ksxUpLvrDbBHPBXXUrBFQ3BZM=", - version = "v0.23.0", + name = "co_honnef_go_tools", + importpath = "honnef.co/go/tools", + sum = "h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=", + version = "v0.0.0-20190523083050-ea95bdfd59fc", ) + go_repository( name = "com_github_bazelbuild_buildtools", build_naming_convention = "go_default_library", importpath = "github.com/bazelbuild/buildtools", - sum = "h1:Et1IIXrXwhpDvR5wH9REPEZ0sUtzUoJSq19nfmBqzBY=", - version = "v0.0.0-20200718160251-b1667ff58f71", - ) - go_repository( - name = "com_github_bazelbuild_rules_go", - importpath = "github.com/bazelbuild/rules_go", - sum = "h1:wzbawlkLtl2ze9w/312NHZ84c7kpUCtlkD8HgFY27sw=", - version = "v0.0.0-20190719190356-6dae44dc5cab", + sum = "h1:jhiMzJ+8unnLRtV8rpbWBFE9pFNzIqgUTyZU5aA++w8=", + version = "v0.0.0-20221004120235-7186f635531b", ) + go_repository( name = "com_github_bmatcuk_doublestar", importpath = "github.com/bmatcuk/doublestar", - sum = "h1:oC24CykoSAB8zd7XgruHo33E0cHJf/WhQA/7BeXj+x0=", - version = "v1.2.2", + sum = "h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0=", + version = "v1.3.4", ) + go_repository( name = "com_github_burntsushi_toml", importpath = "github.com/BurntSushi/toml", @@ -40,23 +51,54 @@ def gazelle_deps(): version = "v0.3.1", ) go_repository( - name = "com_github_davecgh_go_spew", - importpath = "github.com/davecgh/go-spew", - sum = "h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=", - version = "v1.1.1", + name = "com_github_census_instrumentation_opencensus_proto", + importpath = "github.com/census-instrumentation/opencensus-proto", + sum = "h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=", + version = "v0.2.1", + ) + go_repository( + name = "com_github_chzyer_logex", + importpath = "github.com/chzyer/logex", + sum = "h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=", + version = "v1.1.10", + ) + go_repository( + name = "com_github_chzyer_readline", + importpath = "github.com/chzyer/readline", + sum = "h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=", + version = "v0.0.0-20180603132655-2972be24d48e", + ) + go_repository( + name = "com_github_chzyer_test", + importpath = "github.com/chzyer/test", + sum = "h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=", + version = "v0.0.0-20180213035817-a1ea475d72b1", + ) + go_repository( + name = "com_github_client9_misspell", + importpath = "github.com/client9/misspell", + sum = "h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=", + version = "v0.3.4", ) go_repository( name = "com_github_emirpasic_gods", importpath = "github.com/emirpasic/gods", - sum = "h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=", - version = "v1.12.0", + sum = "h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=", + version = "v1.18.1", ) go_repository( - name = "com_github_fsnotify_fsnotify", - importpath = "github.com/fsnotify/fsnotify", - sum = "h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=", - version = "v1.4.7", + name = "com_github_envoyproxy_go_control_plane", + importpath = "github.com/envoyproxy/go-control-plane", + sum = "h1:4cmBvAEBNJaGARUEs3/suWRyfyBfhf7I60WBZq+bv2w=", + version = "v0.9.1-0.20191026205805-5f8ba28d4473", + ) + go_repository( + name = "com_github_envoyproxy_protoc_gen_validate", + importpath = "github.com/envoyproxy/protoc-gen-validate", + sum = "h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=", + version = "v0.1.0", ) + go_repository( name = "com_github_ghodss_yaml", importpath = "github.com/ghodss/yaml", @@ -64,96 +106,143 @@ def gazelle_deps(): version = "v1.0.0", ) go_repository( - name = "com_github_google_go_cmp", - importpath = "github.com/google/go-cmp", - sum = "h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M=", - version = "v0.5.4", + name = "com_github_golang_glog", + importpath = "github.com/golang/glog", + sum = "h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=", + version = "v0.0.0-20160126235308-23def4e6c14b", ) go_repository( - name = "com_github_google_uuid", - importpath = "github.com/google/uuid", - sum = "h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=", - version = "v1.3.0", + name = "com_github_golang_mock", + importpath = "github.com/golang/mock", + sum = "h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=", + version = "v1.1.1", ) - go_repository( - name = "com_github_kr_pretty", - importpath = "github.com/kr/pretty", - sum = "h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=", - version = "v0.1.0", + name = "com_github_golang_protobuf", + importpath = "github.com/golang/protobuf", + sum = "h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=", + version = "v1.4.3", ) go_repository( - name = "com_github_kr_pty", - importpath = "github.com/kr/pty", - sum = "h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=", - version = "v1.1.1", + name = "com_github_google_go_cmp", + importpath = "github.com/google/go-cmp", + sum = "h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=", + version = "v0.5.9", ) + go_repository( - name = "com_github_kr_text", - importpath = "github.com/kr/text", - sum = "h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=", - version = "v0.1.0", + name = "com_github_prometheus_client_model", + importpath = "github.com/prometheus/client_model", + sum = "h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=", + version = "v0.0.0-20190812154241-14fe0d1b01d4", ) go_repository( - name = "com_github_pelletier_go_toml", - importpath = "github.com/pelletier/go-toml", - sum = "h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=", - version = "v1.2.0", + name = "com_github_yuin_goldmark", + importpath = "github.com/yuin/goldmark", + sum = "h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=", + version = "v1.4.13", ) go_repository( - name = "com_github_pmezard_go_difflib", - importpath = "github.com/pmezard/go-difflib", - sum = "h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=", - version = "v1.0.0", + name = "com_google_cloud_go", + importpath = "cloud.google.com/go", + sum = "h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=", + version = "v0.26.0", ) - go_repository( name = "in_gopkg_check_v1", importpath = "gopkg.in/check.v1", - sum = "h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=", - version = "v1.0.0-20180628173108-788fd7840127", + sum = "h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=", + version = "v0.0.0-20161208181325-20d25e280405", ) go_repository( name = "in_gopkg_yaml_v2", importpath = "gopkg.in/yaml.v2", - sum = "h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=", - version = "v2.2.2", + sum = "h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=", + version = "v2.4.0", + ) + go_repository( + name = "net_starlark_go", + importpath = "go.starlark.net", + sum = "h1:xwwDQW5We85NaTk2APgoN9202w/l0DVGp+GZMfsrh7s=", + version = "v0.0.0-20210223155950-e043a3d3c984", + ) + go_repository( + name = "org_golang_google_appengine", + importpath = "google.golang.org/appengine", + sum = "h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=", + version = "v1.4.0", + ) + go_repository( + name = "org_golang_google_genproto", + importpath = "google.golang.org/genproto", + sum = "h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=", + version = "v0.0.0-20200526211855-cb27e3aa2013", + ) + go_repository( + name = "org_golang_google_grpc", + importpath = "google.golang.org/grpc", + sum = "h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=", + version = "v1.27.0", + ) + go_repository( + name = "org_golang_google_protobuf", + importpath = "google.golang.org/protobuf", + sum = "h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=", + version = "v1.25.0", ) go_repository( name = "org_golang_x_crypto", importpath = "golang.org/x/crypto", - sum = "h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=", - version = "v0.0.0-20191011191535-87dc89f01550", + sum = "h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=", + version = "v0.0.0-20190308221718-c2843e01d9a2", + ) + go_repository( + name = "org_golang_x_exp", + importpath = "golang.org/x/exp", + sum = "h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=", + version = "v0.0.0-20190121172915-509febef88a4", + ) + go_repository( + name = "org_golang_x_lint", + importpath = "golang.org/x/lint", + sum = "h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=", + version = "v0.0.0-20190313153728-d0100b6bd8b3", ) go_repository( name = "org_golang_x_mod", importpath = "golang.org/x/mod", - sum = "h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY=", - version = "v0.4.1", + sum = "h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=", + version = "v0.6.0-dev.0.20220419223038-86c51ed26bb4", ) go_repository( name = "org_golang_x_net", importpath = "golang.org/x/net", - sum = "h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=", - version = "v0.0.0-20190620200207-3b0461eec859", + sum = "h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0=", + version = "v0.0.0-20220722155237-a158d28d115b", + ) + go_repository( + name = "org_golang_x_oauth2", + importpath = "golang.org/x/oauth2", + sum = "h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=", + version = "v0.0.0-20180821212333-d2e6202438be", ) go_repository( name = "org_golang_x_sync", importpath = "golang.org/x/sync", - sum = "h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=", - version = "v0.0.0-20190911185100-cd5d95a43a6e", + sum = "h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=", + version = "v0.0.0-20220722155255-886fb9371eb4", ) go_repository( name = "org_golang_x_sys", importpath = "golang.org/x/sys", - sum = "h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=", - version = "v0.0.0-20190412213103-97732733099d", + sum = "h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc=", + version = "v0.0.0-20221010170243-090e33056c14", ) go_repository( name = "org_golang_x_text", importpath = "golang.org/x/text", - sum = "h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=", - version = "v0.3.0", + sum = "h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=", + version = "v0.3.7", ) go_repository( name = "org_golang_x_tools", @@ -161,12 +250,12 @@ def gazelle_deps(): "gazelle:exclude **/testdata/**/*", ], importpath = "golang.org/x/tools", - sum = "h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ=", - version = "v0.0.0-20191119224855-298f0cb1881e", + sum = "h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=", + version = "v0.1.12", ) go_repository( name = "org_golang_x_xerrors", importpath = "golang.org/x/xerrors", - sum = "h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=", - version = "v0.0.0-20191204190536-9bdfabe68543", + sum = "h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=", + version = "v0.0.0-20200804184101-5ec99f83aff1", ) diff --git a/gazelle/fix.go b/gazelle/fix.go deleted file mode 100644 index c669f21d27..0000000000 --- a/gazelle/fix.go +++ /dev/null @@ -1,13 +0,0 @@ -package python - -import ( - "github.com/bazelbuild/bazel-gazelle/config" - "github.com/bazelbuild/bazel-gazelle/rule" -) - -// Fix repairs deprecated usage of language-specific rules in f. This is -// called before the file is indexed. Unless c.ShouldFix is true, fixes -// that delete or rename rules should not be performed. -func (py *Python) Fix(c *config.Config, f *rule.File) { - // TODO(f0rmiga): implement. -} diff --git a/gazelle/go.mod b/gazelle/go.mod new file mode 100644 index 0000000000..94f19e801f --- /dev/null +++ b/gazelle/go.mod @@ -0,0 +1,17 @@ +module github.com/bazelbuild/rules_python/gazelle + +go 1.19 + +require ( + github.com/bazelbuild/buildtools v0.0.0-20221004120235-7186f635531b + github.com/bmatcuk/doublestar v1.3.4 + github.com/emirpasic/gods v1.18.1 + github.com/ghodss/yaml v1.0.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/google/go-cmp v0.5.9 // indirect + golang.org/x/sys v0.0.0-20221010170243-090e33056c14 // indirect + golang.org/x/tools v0.1.12 // indirect +) diff --git a/gazelle/go.sum b/gazelle/go.sum new file mode 100644 index 0000000000..ed8ceae5ec --- /dev/null +++ b/gazelle/go.sum @@ -0,0 +1,92 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/bazelbuild/bazel-gazelle v0.27.0 h1:+/ZhUxlDy4XnyMIGeKkbRZoIGssy1eO51GijwIvvuwE= +github.com/bazelbuild/bazel-gazelle v0.27.0/go.mod h1:2K6B42/loq8ext4JObmam4gTYx4En1MUSzHFKQF8hPM= +github.com/bazelbuild/buildtools v0.0.0-20221004120235-7186f635531b h1:jhiMzJ+8unnLRtV8rpbWBFE9pFNzIqgUTyZU5aA++w8= +github.com/bazelbuild/buildtools v0.0.0-20221004120235-7186f635531b/go.mod h1:689QdV3hBP7Vo9dJMmzhoYIyo/9iMhEmHkJcnaPRCbo= +github.com/bazelbuild/rules_go v0.35.0 h1:ViPR65vOrg74JKntAUFY6qZkheBKGB6to7wFd8gCRU4= +github.com/bazelbuild/rules_go v0.35.0/go.mod h1:ahciH68Viyxtm/gvCQplaAiu8buhf/b+gWswcPjFixI= +github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= +github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +go.starlark.net v0.0.0-20210223155950-e043a3d3c984/go.mod h1:t3mmBBPzAVvK0L0n1drDmrQsJ8FoIx4INCqVMTr/Zo0= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14 h1:k5II8e6QD8mITdi+okbbmR/cIyEbeXLBhy5Ha4nevyc= +golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/gazelle/language.go b/gazelle/language.go deleted file mode 100644 index 877ac6d065..0000000000 --- a/gazelle/language.go +++ /dev/null @@ -1,18 +0,0 @@ -package python - -import ( - "github.com/bazelbuild/bazel-gazelle/language" -) - -// Python satisfies the language.Language interface. It is the Gazelle extension -// for Python rules. -type Python struct { - Configurer - Resolver -} - -// NewLanguage initializes a new Python that satisfies the language.Language -// interface. This is the entrypoint for the extension initialization. -func NewLanguage() language.Language { - return &Python{} -} diff --git a/gazelle/manifest/BUILD.bazel b/gazelle/manifest/BUILD.bazel index 2e5b6b8e99..fc7fa09632 100644 --- a/gazelle/manifest/BUILD.bazel +++ b/gazelle/manifest/BUILD.bazel @@ -5,7 +5,10 @@ go_library( srcs = ["manifest.go"], importpath = "github.com/bazelbuild/rules_python/gazelle/manifest", visibility = ["//visibility:public"], - deps = ["@in_gopkg_yaml_v2//:yaml_v2"], + deps = [ + "@com_github_emirpasic_gods//sets/treeset", + "@in_gopkg_yaml_v2//:yaml_v2", + ], ) go_test( @@ -14,3 +17,13 @@ go_test( data = glob(["testdata/**"]), deps = [":manifest"], ) + +filegroup( + name = "distribution", + srcs = glob(["**"]) + [ + "//manifest/generate:distribution", + "//manifest/hasher:distribution", + "//manifest/test:distribution", + ], + visibility = ["//:__pkg__"], +) diff --git a/gazelle/manifest/defs.bzl b/gazelle/manifest/defs.bzl index a675e25465..05562a1583 100644 --- a/gazelle/manifest/defs.bzl +++ b/gazelle/manifest/defs.bzl @@ -1,24 +1,39 @@ +# 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. + """This module provides the gazelle_python_manifest macro that contains targets for updating and testing the Gazelle manifest file. """ -load("@io_bazel_rules_go//go:def.bzl", "go_binary") +load("@io_bazel_rules_go//go:def.bzl", "GoSource", "go_binary", "go_test") def gazelle_python_manifest( name, requirements, modules_mapping, pip_repository_name = "", - pip_repository_incremental = False, pip_deps_repository_name = "", - manifest = ":gazelle_python.yaml"): + manifest = ":gazelle_python.yaml", + use_pip_repository_aliases = False): """A macro for defining the updating and testing targets for the Gazelle manifest file. Args: name: the name used as a base for the targets. requirements: the target for the requirements.txt file. pip_repository_name: the name of the pip_install or pip_repository target. - pip_repository_incremental: the incremental property of pip_repository. + use_pip_repository_aliases: boolean flag to enable using user-friendly + python package aliases. pip_deps_repository_name: deprecated - the old pip_install target name. modules_mapping: the target for the generated modules_mapping.json file. manifest: the target for the Gazelle manifest file. @@ -38,7 +53,11 @@ def gazelle_python_manifest( update_target = "{}.update".format(name) update_target_label = "//{}:{}".format(native.package_name(), update_target) + manifest_generator_hash = Label("//manifest/generate:generate_lib_sources_hash") + update_args = [ + "--manifest-generator-hash", + "$(rootpath {})".format(manifest_generator_hash), "--requirements", "$(rootpath {})".format(requirements), "--pip-repository-name", @@ -50,42 +69,101 @@ def gazelle_python_manifest( "--update-target", update_target_label, ] - if pip_repository_incremental: - update_args.append("--pip-repository-incremental") + + if use_pip_repository_aliases: + update_args += [ + "--use-pip-repository-aliases", + "true", + ] go_binary( name = update_target, - embed = ["@rules_python//gazelle/manifest/generate:generate_lib"], + embed = [Label("//manifest/generate:generate_lib")], data = [ manifest, modules_mapping, requirements, + manifest_generator_hash, ], args = update_args, visibility = ["//visibility:private"], tags = ["manual"], ) - test_binary = "_{}_test_bin".format(name) - - go_binary( - name = test_binary, - embed = ["@rules_python//gazelle/manifest/test:test_lib"], - visibility = ["//visibility:private"], - ) - - native.sh_test( + go_test( name = "{}.test".format(name), - srcs = ["@rules_python//gazelle/manifest/test:run.sh"], + srcs = [Label("//manifest/test:test.go")], data = [ - ":{}".format(test_binary), manifest, requirements, + manifest_generator_hash, ], env = { - "_TEST_BINARY": "$(rootpath :{})".format(test_binary), "_TEST_MANIFEST": "$(rootpath {})".format(manifest), + "_TEST_MANIFEST_GENERATOR_HASH": "$(rootpath {})".format(manifest_generator_hash), "_TEST_REQUIREMENTS": "$(rootpath {})".format(requirements), }, - visibility = ["//visibility:private"], + rundir = ".", + deps = [Label("//manifest")], + size = "small", + ) + + native.filegroup( + name = name, + srcs = [manifest], + tags = ["manual"], + visibility = ["//visibility:public"], + ) + +# buildifier: disable=provider-params +AllSourcesInfo = provider(fields = {"all_srcs": "All sources collected from the target and dependencies."}) + +_rules_python_workspace = Label("@rules_python//:WORKSPACE") + +def _get_all_sources_impl(target, ctx): + is_rules_python = target.label.workspace_name == _rules_python_workspace.workspace_name + if not is_rules_python: + # Avoid adding third-party dependency files to the checksum of the srcs. + return AllSourcesInfo(all_srcs = depset()) + srcs = depset( + target[GoSource].orig_srcs, + transitive = [dep[AllSourcesInfo].all_srcs for dep in ctx.rule.attr.deps], ) + return [AllSourcesInfo(all_srcs = srcs)] + +_get_all_sources = aspect( + implementation = _get_all_sources_impl, + attr_aspects = ["deps"], +) + +def _sources_hash_impl(ctx): + all_srcs = ctx.attr.go_library[AllSourcesInfo].all_srcs + hash_file = ctx.actions.declare_file(ctx.attr.name + ".hash") + args = ctx.actions.args() + args.add(hash_file) + args.add_all(all_srcs) + ctx.actions.run( + outputs = [hash_file], + inputs = all_srcs, + arguments = [args], + executable = ctx.executable._hasher, + ) + return [DefaultInfo( + files = depset([hash_file]), + runfiles = ctx.runfiles([hash_file]), + )] + +sources_hash = rule( + _sources_hash_impl, + attrs = { + "go_library": attr.label( + aspects = [_get_all_sources], + providers = [GoSource], + ), + "_hasher": attr.label( + cfg = "exec", + default = Label("//manifest/hasher"), + executable = True, + ), + }, +) diff --git a/gazelle/manifest/generate/BUILD.bazel b/gazelle/manifest/generate/BUILD.bazel index 29b9f15628..96248f4e08 100644 --- a/gazelle/manifest/generate/BUILD.bazel +++ b/gazelle/manifest/generate/BUILD.bazel @@ -1,11 +1,18 @@ load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") +load("//manifest:defs.bzl", "sources_hash") go_library( name = "generate_lib", srcs = ["generate.go"], importpath = "github.com/bazelbuild/rules_python/gazelle/manifest/generate", visibility = ["//visibility:public"], - deps = ["//gazelle/manifest"], + deps = ["//manifest"], +) + +sources_hash( + name = "generate_lib_sources_hash", + go_library = ":generate_lib", + visibility = ["//visibility:public"], ) go_binary( @@ -13,3 +20,9 @@ go_binary( embed = [":generate_lib"], visibility = ["//visibility:public"], ) + +filegroup( + name = "distribution", + srcs = glob(["**"]), + visibility = ["//manifest:__pkg__"], +) diff --git a/gazelle/manifest/generate/generate.go b/gazelle/manifest/generate/generate.go index 04d7441fd2..1f56e630cc 100644 --- a/gazelle/manifest/generate/generate.go +++ b/gazelle/manifest/generate/generate.go @@ -1,3 +1,17 @@ +// 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. + /* generate.go is a program that generates the Gazelle YAML manifest. @@ -24,12 +38,21 @@ func init() { } func main() { - var requirementsPath string - var pipRepositoryName string - var pipRepositoryIncremental bool - var modulesMappingPath string - var outputPath string - var updateTarget string + var ( + manifestGeneratorHashPath string + requirementsPath string + pipRepositoryName string + usePipRepositoryAliases bool + modulesMappingPath string + outputPath string + updateTarget string + ) + flag.StringVar( + &manifestGeneratorHashPath, + "manifest-generator-hash", + "", + "The file containing the hash for the source code of the manifest generator."+ + "This is important to force manifest updates when the generator logic changes.") flag.StringVar( &requirementsPath, "requirements", @@ -41,10 +64,10 @@ func main() { "", "The name of the pip_install or pip_repository target.") flag.BoolVar( - &pipRepositoryIncremental, - "pip-repository-incremental", + &usePipRepositoryAliases, + "use-pip-repository-aliases", false, - "The value for the incremental option in pip_repository.") + "Whether to use the pip-repository aliases, which are generated when passing 'incompatible_generate_aliases = True'.") flag.StringVar( &modulesMappingPath, "modules-mapping", @@ -88,11 +111,17 @@ func main() { manifestFile := manifest.NewFile(&manifest.Manifest{ ModulesMapping: modulesMapping, PipRepository: &manifest.PipRepository{ - Name: pipRepositoryName, - Incremental: pipRepositoryIncremental, + Name: pipRepositoryName, + UsePipRepositoryAliases: usePipRepositoryAliases, }, }) - if err := writeOutput(outputPath, header, manifestFile, requirementsPath); err != nil { + if err := writeOutput( + outputPath, + header, + manifestFile, + manifestGeneratorHashPath, + requirementsPath, + ); err != nil { log.Fatalf("ERROR: %v\n", err) } } @@ -129,6 +158,7 @@ func writeOutput( outputPath string, header string, manifestFile *manifest.File, + manifestGeneratorHashPath string, requirementsPath string, ) error { stat, err := os.Stat(outputPath) @@ -146,7 +176,19 @@ func writeOutput( return fmt.Errorf("failed to write output: %w", err) } - if err := manifestFile.Encode(outputFile, requirementsPath); err != nil { + manifestGeneratorHash, err := os.Open(manifestGeneratorHashPath) + if err != nil { + return fmt.Errorf("failed to write output: %w", err) + } + defer manifestGeneratorHash.Close() + + requirements, err := os.Open(requirementsPath) + if err != nil { + return fmt.Errorf("failed to write output: %w", err) + } + defer requirements.Close() + + if err := manifestFile.Encode(outputFile, manifestGeneratorHash, requirements); err != nil { return fmt.Errorf("failed to write output: %w", err) } diff --git a/gazelle/manifest/hasher/BUILD.bazel b/gazelle/manifest/hasher/BUILD.bazel new file mode 100644 index 0000000000..2e7b125cc0 --- /dev/null +++ b/gazelle/manifest/hasher/BUILD.bazel @@ -0,0 +1,20 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "hasher_lib", + srcs = ["main.go"], + importpath = "github.com/bazelbuild/rules_python/gazelle/manifest/hasher", + visibility = ["//visibility:private"], +) + +go_binary( + name = "hasher", + embed = [":hasher_lib"], + visibility = ["//visibility:public"], +) + +filegroup( + name = "distribution", + srcs = glob(["**"]), + visibility = ["//manifest:__pkg__"], +) diff --git a/gazelle/manifest/hasher/main.go b/gazelle/manifest/hasher/main.go new file mode 100644 index 0000000000..61f8952904 --- /dev/null +++ b/gazelle/manifest/hasher/main.go @@ -0,0 +1,44 @@ +// 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. + +package main + +import ( + "crypto/sha256" + "io" + "log" + "os" +) + +func main() { + h := sha256.New() + out, err := os.Create(os.Args[1]) + if err != nil { + log.Fatal(err) + } + defer out.Close() + for _, filename := range os.Args[2:] { + f, err := os.Open(filename) + if err != nil { + log.Fatal(err) + } + defer f.Close() + if _, err := io.Copy(h, f); err != nil { + log.Fatal(err) + } + } + if _, err := out.Write(h.Sum(nil)); err != nil { + log.Fatal(err) + } +} diff --git a/gazelle/manifest/manifest.go b/gazelle/manifest/manifest.go index b92706a230..c49951dd3e 100644 --- a/gazelle/manifest/manifest.go +++ b/gazelle/manifest/manifest.go @@ -1,3 +1,17 @@ +// 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. + package manifest import ( @@ -6,6 +20,8 @@ import ( "io" "os" + "github.com/emirpasic/gods/sets/treeset" + yaml "gopkg.in/yaml.v2" ) @@ -24,12 +40,8 @@ func NewFile(manifest *Manifest) *File { } // Encode encodes the manifest file to the given writer. -func (f *File) Encode(w io.Writer, requirementsPath string) error { - requirementsChecksum, err := sha256File(requirementsPath) - if err != nil { - return fmt.Errorf("failed to encode manifest file: %w", err) - } - integrityBytes, err := f.calculateIntegrity(requirementsChecksum) +func (f *File) Encode(w io.Writer, manifestGeneratorHashFile, requirements io.Reader) error { + integrityBytes, err := f.calculateIntegrity(manifestGeneratorHashFile, requirements) if err != nil { return fmt.Errorf("failed to encode manifest file: %w", err) } @@ -43,12 +55,8 @@ func (f *File) Encode(w io.Writer, requirementsPath string) error { } // VerifyIntegrity verifies if the integrity set in the File is valid. -func (f *File) VerifyIntegrity(requirementsPath string) (bool, error) { - requirementsChecksum, err := sha256File(requirementsPath) - if err != nil { - return false, fmt.Errorf("failed to verify integrity: %w", err) - } - integrityBytes, err := f.calculateIntegrity(requirementsChecksum) +func (f *File) VerifyIntegrity(manifestGeneratorHashFile, requirements io.Reader) (bool, error) { + integrityBytes, err := f.calculateIntegrity(manifestGeneratorHashFile, requirements) if err != nil { return false, fmt.Errorf("failed to verify integrity: %w", err) } @@ -60,7 +68,9 @@ func (f *File) VerifyIntegrity(requirementsPath string) (bool, error) { // provided checksum for the requirements.txt file used as input to the modules // mapping, plus the manifest structure in the manifest file. This integrity // calculation ensures the manifest files are kept up-to-date. -func (f *File) calculateIntegrity(requirementsChecksum []byte) ([]byte, error) { +func (f *File) calculateIntegrity( + manifestGeneratorHash, requirements io.Reader, +) ([]byte, error) { hash := sha256.New() // Sum the manifest part of the file. @@ -70,8 +80,13 @@ func (f *File) calculateIntegrity(requirementsChecksum []byte) ([]byte, error) { return nil, fmt.Errorf("failed to calculate integrity: %w", err) } + // Sum the manifest generator checksum bytes. + if _, err := io.Copy(hash, manifestGeneratorHash); err != nil { + return nil, fmt.Errorf("failed to calculate integrity: %w", err) + } + // Sum the requirements.txt checksum bytes. - if _, err := hash.Write(requirementsChecksum); err != nil { + if _, err := io.Copy(hash, requirements); err != nil { return nil, fmt.Errorf("failed to calculate integrity: %w", err) } @@ -94,11 +109,30 @@ func (f *File) Decode(manifestPath string) error { return nil } +// ModulesMapping is the type used to map from importable Python modules to +// the wheel names that provide these modules. +type ModulesMapping map[string]string + +// MarshalYAML makes sure that we sort the module names before marshaling +// the contents of `ModulesMapping` to a YAML file. This ensures that the +// file is deterministically generated from the map. +func (m ModulesMapping) MarshalYAML() (interface{}, error) { + var mapslice yaml.MapSlice + keySet := treeset.NewWithStringComparator() + for key := range m { + keySet.Add(key) + } + for _, key := range keySet.Values() { + mapslice = append(mapslice, yaml.MapItem{Key: key, Value: m[key.(string)]}) + } + return mapslice, nil +} + // Manifest represents the structure of the Gazelle manifest file. type Manifest struct { // ModulesMapping is the mapping from importable modules to which Python // wheel name provides these modules. - ModulesMapping map[string]string `yaml:"modules_mapping"` + ModulesMapping ModulesMapping `yaml:"modules_mapping"` // PipDepsRepositoryName is the name of the pip_install repository target. // DEPRECATED PipDepsRepositoryName string `yaml:"pip_deps_repository_name,omitempty"` @@ -110,22 +144,7 @@ type Manifest struct { type PipRepository struct { // The name of the pip_install or pip_repository target. Name string - // The incremental property of pip_repository. - Incremental bool -} - -// sha256File calculates the checksum of a given file path. -func sha256File(filePath string) ([]byte, error) { - file, err := os.Open(filePath) - if err != nil { - return nil, fmt.Errorf("failed to calculate sha256 sum for file: %w", err) - } - defer file.Close() - - hash := sha256.New() - if _, err := io.Copy(hash, file); err != nil { - return nil, fmt.Errorf("failed to calculate sha256 sum for file: %w", err) - } - - return hash.Sum(nil), nil + // UsePipRepositoryAliases allows to use aliases generated pip_repository + // when passing incompatible_generate_aliases = True. + UsePipRepositoryAliases bool `yaml:"use_pip_repository_aliases,omitempty"` } diff --git a/gazelle/manifest/manifest_test.go b/gazelle/manifest/manifest_test.go index 40a231f2bd..43c4099aa1 100644 --- a/gazelle/manifest/manifest_test.go +++ b/gazelle/manifest/manifest_test.go @@ -1,16 +1,31 @@ +// 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. + package manifest_test import ( "bytes" - "io/ioutil" "log" + "os" "reflect" + "strings" "testing" "github.com/bazelbuild/rules_python/gazelle/manifest" ) -var modulesMapping = map[string]string{ +var modulesMapping = manifest.ModulesMapping{ "arrow": "arrow", "arrow.__init__": "arrow", "arrow.api": "arrow", @@ -31,11 +46,18 @@ func TestFile(t *testing.T) { PipDepsRepositoryName: pipDepsRepositoryName, }) var b bytes.Buffer - if err := f.Encode(&b, "testdata/requirements.txt"); err != nil { + manifestGeneratorHashFile := strings.NewReader("") + requirements, err := os.Open("testdata/requirements.txt") + if err != nil { + log.Println(err) + t.FailNow() + } + defer requirements.Close() + if err := f.Encode(&b, manifestGeneratorHashFile, requirements); err != nil { log.Println(err) t.FailNow() } - expected, err := ioutil.ReadFile("testdata/gazelle_python.yaml") + expected, err := os.ReadFile("testdata/gazelle_python.yaml") if err != nil { log.Println(err) t.FailNow() @@ -66,7 +88,14 @@ func TestFile(t *testing.T) { log.Println(err) t.FailNow() } - valid, err := f.VerifyIntegrity("testdata/requirements.txt") + manifestGeneratorHashFile := strings.NewReader("") + requirements, err := os.Open("testdata/requirements.txt") + if err != nil { + log.Println(err) + t.FailNow() + } + defer requirements.Close() + valid, err := f.VerifyIntegrity(manifestGeneratorHashFile, requirements) if err != nil { log.Println(err) t.FailNow() @@ -76,4 +105,4 @@ func TestFile(t *testing.T) { t.FailNow() } }) -} \ No newline at end of file +} diff --git a/gazelle/manifest/test/BUILD.bazel b/gazelle/manifest/test/BUILD.bazel index f14845f756..28c6c548d9 100644 --- a/gazelle/manifest/test/BUILD.bazel +++ b/gazelle/manifest/test/BUILD.bazel @@ -1,17 +1,9 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") +# gazelle:ignore -go_library( - name = "test_lib", - srcs = ["test.go"], - importpath = "github.com/bazelbuild/rules_python/gazelle/manifest/test", - visibility = ["//visibility:public"], - deps = ["//gazelle/manifest"], -) +exports_files(["test.go"]) -go_binary( - name = "test", - embed = [":test_lib"], - visibility = ["//visibility:public"], +filegroup( + name = "distribution", + srcs = glob(["**"]), + visibility = ["//manifest:__pkg__"], ) - -exports_files(["run.sh"]) diff --git a/gazelle/manifest/test/run.sh b/gazelle/manifest/test/run.sh deleted file mode 100755 index 4b24b51ae4..0000000000 --- a/gazelle/manifest/test/run.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -# This file exists to allow passing the runfile paths to the Go program via -# environment variables. - -set -o errexit -o nounset - -"${_TEST_BINARY}" --requirements "${_TEST_REQUIREMENTS}" --manifest "${_TEST_MANIFEST}" \ No newline at end of file diff --git a/gazelle/manifest/test/test.go b/gazelle/manifest/test/test.go index 518fe06eb6..72cb260d4d 100644 --- a/gazelle/manifest/test/test.go +++ b/gazelle/manifest/test/test.go @@ -1,63 +1,78 @@ +// 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. + /* -test.go is a program that asserts the Gazelle YAML manifest is up-to-date in +test.go is a unit test that asserts the Gazelle YAML manifest is up-to-date in regards to the requirements.txt. It re-hashes the requirements.txt and compares it to the recorded one in the existing generated Gazelle manifest. */ -package main +package test import ( - "flag" - "log" + "os" "path/filepath" + "testing" "github.com/bazelbuild/rules_python/gazelle/manifest" ) -func main() { - var requirementsPath string - var manifestPath string - flag.StringVar( - &requirementsPath, - "requirements", - "", - "The requirements.txt file.") - flag.StringVar( - &manifestPath, - "manifest", - "", - "The manifest YAML file.") - flag.Parse() - +func TestGazelleManifestIsUpdated(t *testing.T) { + requirementsPath := os.Getenv("_TEST_REQUIREMENTS") if requirementsPath == "" { - log.Fatalln("ERROR: --requirements must be set") + t.Fatalf("_TEST_REQUIREMENTS must be set") } + manifestPath := os.Getenv("_TEST_MANIFEST") if manifestPath == "" { - log.Fatalln("ERROR: --manifest must be set") + t.Fatalf("_TEST_MANIFEST must be set") } manifestFile := new(manifest.File) if err := manifestFile.Decode(manifestPath); err != nil { - log.Fatalf("ERROR: %v\n", err) + t.Fatalf("decoding manifest file: %v", err) } if manifestFile.Integrity == "" { - log.Fatalln("ERROR: failed to find the Gazelle manifest file integrity") + t.Fatal("failed to find the Gazelle manifest file integrity") + } + + manifestGeneratorHashPath := os.Getenv("_TEST_MANIFEST_GENERATOR_HASH") + manifestGeneratorHash, err := os.Open(manifestGeneratorHashPath) + if err != nil { + t.Fatalf("opening %q: %v", manifestGeneratorHashPath, err) + } + defer manifestGeneratorHash.Close() + + requirements, err := os.Open(requirementsPath) + if err != nil { + t.Fatalf("opening %q: %v", requirementsPath, err) } + defer requirements.Close() - valid, err := manifestFile.VerifyIntegrity(requirementsPath) + valid, err := manifestFile.VerifyIntegrity(manifestGeneratorHash, requirements) if err != nil { - log.Fatalf("ERROR: %v\n", err) + t.Fatalf("verifying integrity: %v", err) } if !valid { manifestRealpath, err := filepath.EvalSymlinks(manifestPath) if err != nil { - log.Fatalf("ERROR: %v\n", err) + t.Fatalf("evaluating symlink %q: %v", manifestPath, err) } - log.Fatalf( - "ERROR: %q is out-of-date, follow the intructions on this file for updating.\n", + t.Errorf( + "%q is out-of-date. Follow the update instructions in that file to resolve this", manifestRealpath) } -} \ No newline at end of file +} diff --git a/gazelle/manifest/testdata/gazelle_python.yaml b/gazelle/manifest/testdata/gazelle_python.yaml index 4dc1f2c545..70f7aff19a 100644 --- a/gazelle/manifest/testdata/gazelle_python.yaml +++ b/gazelle/manifest/testdata/gazelle_python.yaml @@ -10,4 +10,4 @@ manifest: arrow.parser: arrow arrow.util: arrow pip_deps_repository_name: test_repository_name -integrity: 624f5f6c078eb44b907efd5a64e308354ac3620c568232b815668bcdf3e3366a +integrity: eedf187f8b7ec27cdfc682feee4206e063b51d13d78f77c05d3a30ec11bd7411 diff --git a/gazelle/modules_mapping/BUILD.bazel b/gazelle/modules_mapping/BUILD.bazel index d1cd42e7d9..1855551a80 100644 --- a/gazelle/modules_mapping/BUILD.bazel +++ b/gazelle/modules_mapping/BUILD.bazel @@ -5,3 +5,9 @@ py_binary( srcs = ["generator.py"], visibility = ["//visibility:public"], ) + +filegroup( + name = "distribution", + srcs = glob(["**"]), + visibility = ["//:__pkg__"], +) diff --git a/gazelle/modules_mapping/def.bzl b/gazelle/modules_mapping/def.bzl index 04ea50facd..54fc8add80 100644 --- a/gazelle/modules_mapping/def.bzl +++ b/gazelle/modules_mapping/def.bzl @@ -1,3 +1,17 @@ +# 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. + """Definitions for the modules_mapping.json generation. The modules_mapping.json file is a mapping from Python modules to the wheel @@ -12,8 +26,9 @@ module name doesn't match the wheel distribution name. def _modules_mapping_impl(ctx): modules_mapping = ctx.actions.declare_file(ctx.attr.modules_mapping_name) args = ctx.actions.args() - args.add(modules_mapping.path) - args.add_all([whl.path for whl in ctx.files.wheels]) + args.add("--output_file", modules_mapping.path) + args.add_all("--exclude_patterns", ctx.attr.exclude_patterns) + args.add_all("--wheels", [whl.path for whl in ctx.files.wheels]) ctx.actions.run( inputs = ctx.files.wheels, outputs = [modules_mapping], @@ -26,6 +41,11 @@ def _modules_mapping_impl(ctx): modules_mapping = rule( _modules_mapping_impl, attrs = { + "exclude_patterns": attr.string_list( + default = ["^_|(\\._)+"], + doc = "A set of regex patterns to match against each calculated module path. By default, exclude the modules starting with underscores.", + mandatory = False, + ), "modules_mapping_name": attr.string( default = "modules_mapping.json", doc = "The name for the output JSON file.", @@ -38,7 +58,7 @@ modules_mapping = rule( ), "_generator": attr.label( cfg = "exec", - default = "//gazelle/modules_mapping:generator", + default = "//modules_mapping:generator", executable = True, ), }, diff --git a/gazelle/modules_mapping/generator.py b/gazelle/modules_mapping/generator.py index b93f9689ec..be57eac3bc 100644 --- a/gazelle/modules_mapping/generator.py +++ b/gazelle/modules_mapping/generator.py @@ -1,5 +1,21 @@ +# 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 argparse import json import pathlib +import re import sys import zipfile @@ -8,47 +24,69 @@ class Generator: stderr = None output_file = None + excluded_patterns = None + mapping = {} - def __init__(self, stderr, output_file): + def __init__(self, stderr, output_file, excluded_patterns): self.stderr = stderr self.output_file = output_file + self.excluded_patterns = [re.compile(pattern) for pattern in excluded_patterns] # dig_wheel analyses the wheel .whl file determining the modules it provides # by looking at the directory structure. def dig_wheel(self, whl): - mapping = {} - wheel_name = get_wheel_name(whl) with zipfile.ZipFile(whl, "r") as zip_file: for path in zip_file.namelist(): if is_metadata(path): - continue - ext = pathlib.Path(path).suffix - if ext == ".py" or ext == ".so": - # Note the '/' here means that the __init__.py is not in the - # root of the wheel, therefore we can index the directory - # where this file is as an importable package. - if path.endswith("/__init__.py"): - module = path[: -len("/__init__.py")].replace("/", ".") - mapping[module] = wheel_name - # Always index the module file. - if ext == ".so": - # Also remove extra metadata that is embeded as part of - # the file name as an extra extension. - ext = "".join(pathlib.Path(path).suffixes) - module = path[: -len(ext)].replace("/", ".") - mapping[module] = wheel_name - return mapping + if data_has_purelib_or_platlib(path): + self.module_for_path(path, whl) + else: + continue + else: + self.module_for_path(path, whl) + + def module_for_path(self, path, whl): + ext = pathlib.Path(path).suffix + if ext == ".py" or ext == ".so": + if "purelib" in path or "platlib" in path: + root = "/".join(path.split("/")[2:]) + else: + root = path + + wheel_name = get_wheel_name(whl) + + if root.endswith("/__init__.py"): + # Note the '/' here means that the __init__.py is not in the + # root of the wheel, therefore we can index the directory + # where this file is as an importable package. + module = root[: -len("/__init__.py")].replace("/", ".") + if not self.is_excluded(module): + self.mapping[module] = wheel_name + + # Always index the module file. + if ext == ".so": + # Also remove extra metadata that is embeded as part of + # the file name as an extra extension. + ext = "".join(pathlib.Path(root).suffixes) + module = root[: -len(ext)].replace("/", ".") + if not self.is_excluded(module): + self.mapping[module] = wheel_name + + def is_excluded(self, module): + for pattern in self.excluded_patterns: + if pattern.search(module): + return True + return False # run is the entrypoint for the generator. def run(self, wheels): - mapping = {} for whl in wheels: try: - mapping.update(self.dig_wheel(whl)) + self.dig_wheel(whl) except AssertionError as error: print(error, file=self.stderr) return 1 - mapping_json = json.dumps(mapping) + mapping_json = json.dumps(self.mapping) with open(self.output_file, "w") as f: f.write(mapping_json) return 0 @@ -73,8 +111,23 @@ def is_metadata(path): return top_level.endswith(".dist-info") or top_level.endswith(".data") +# The .data is allowed to contain a full purelib or platlib directory +# These get unpacked into site-packages, so require indexing too. +# This is the same if "Root-Is-Purelib: true" is set and the files are at the root. +# Ref: https://peps.python.org/pep-0427/#what-s-the-deal-with-purelib-vs-platlib +def data_has_purelib_or_platlib(path): + maybe_lib = path.split("/")[1].lower() + return is_metadata(path) and (maybe_lib == "purelib" or maybe_lib == "platlib") + + if __name__ == "__main__": - output_file = sys.argv[1] - wheels = sys.argv[2:] - generator = Generator(sys.stderr, output_file) - exit(generator.run(wheels)) + parser = argparse.ArgumentParser( + prog="generator", + description="Generates the modules mapping used by the Gazelle manifest.", + ) + parser.add_argument("--output_file", type=str) + parser.add_argument("--exclude_patterns", nargs="+", default=[]) + parser.add_argument("--wheels", nargs="+", default=[]) + args = parser.parse_args() + generator = Generator(sys.stderr, args.output_file, args.exclude_patterns) + exit(generator.run(args.wheels)) diff --git a/gazelle/python/BUILD.bazel b/gazelle/python/BUILD.bazel new file mode 100644 index 0000000000..ddcad2785d --- /dev/null +++ b/gazelle/python/BUILD.bazel @@ -0,0 +1,79 @@ +load("@bazel_gazelle//:def.bzl", "gazelle_binary") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") +load("@rules_python//python:defs.bzl", "py_binary") + +go_library( + name = "python", + srcs = [ + "configure.go", + "fix.go", + "generate.go", + "kinds.go", + "language.go", + "parser.go", + "resolve.go", + "std_modules.go", + "target.go", + ], + data = [ + ":parse", + ":std_modules", + ], + importpath = "github.com/bazelbuild/rules_python/gazelle/python", + visibility = ["//visibility:public"], + deps = [ + "//manifest", + "//pythonconfig", + "@bazel_gazelle//config:go_default_library", + "@bazel_gazelle//label:go_default_library", + "@bazel_gazelle//language:go_default_library", + "@bazel_gazelle//repo:go_default_library", + "@bazel_gazelle//resolve:go_default_library", + "@bazel_gazelle//rule:go_default_library", + "@com_github_bazelbuild_buildtools//build:go_default_library", + "@com_github_bmatcuk_doublestar//:doublestar", + "@com_github_emirpasic_gods//lists/singlylinkedlist", + "@com_github_emirpasic_gods//sets/treeset", + "@com_github_emirpasic_gods//utils", + "@io_bazel_rules_go//go/tools/bazel:go_default_library", + ], +) + +py_binary( + name = "parse", + srcs = ["parse.py"], + visibility = ["//visibility:public"], +) + +py_binary( + name = "std_modules", + srcs = ["std_modules.py"], + visibility = ["//visibility:public"], +) + +go_test( + name = "python_test", + srcs = ["python_test.go"], + data = [ + ":gazelle_binary", + ":parse", + ":std_modules", + ] + glob(["testdata/**"]), + deps = [ + "@bazel_gazelle//testtools:go_default_library", + "@com_github_ghodss_yaml//:yaml", + "@io_bazel_rules_go//go/tools/bazel:go_default_library", + ], +) + +gazelle_binary( + name = "gazelle_binary", + languages = [":python"], + visibility = ["//visibility:public"], +) + +filegroup( + name = "distribution", + srcs = glob(["**"]), + visibility = ["//:__pkg__"], +) diff --git a/gazelle/configure.go b/gazelle/python/configure.go similarity index 89% rename from gazelle/configure.go rename to gazelle/python/configure.go index 8e71110d0a..32f9ab0a11 100644 --- a/gazelle/configure.go +++ b/gazelle/python/configure.go @@ -1,3 +1,17 @@ +// 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. + package python import ( @@ -89,7 +103,7 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) { case "exclude": // We record the exclude directive for coarse-grained packages // since we do manual tree traversal in this mode. - config.AddExcludedPattern(strings.TrimSpace(d.Value)) + config.AddExcludedPattern(filepath.Join(rel, strings.TrimSpace(d.Value))) case pythonconfig.PythonExtensionDirective: switch d.Value { case "enabled": diff --git a/gazelle/python/fix.go b/gazelle/python/fix.go new file mode 100644 index 0000000000..1ca42571ab --- /dev/null +++ b/gazelle/python/fix.go @@ -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. + +package python + +import ( + "github.com/bazelbuild/bazel-gazelle/config" + "github.com/bazelbuild/bazel-gazelle/rule" +) + +// Fix repairs deprecated usage of language-specific rules in f. This is +// called before the file is indexed. Unless c.ShouldFix is true, fixes +// that delete or rename rules should not be performed. +func (py *Python) Fix(c *config.Config, f *rule.File) { + // TODO(f0rmiga): implement. +} diff --git a/gazelle/generate.go b/gazelle/python/generate.go similarity index 62% rename from gazelle/generate.go rename to gazelle/python/generate.go index 685068d7f5..fb41324fd6 100644 --- a/gazelle/generate.go +++ b/gazelle/python/generate.go @@ -1,7 +1,22 @@ +// 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. + package python import ( "fmt" + "io/fs" "log" "os" "path/filepath" @@ -11,13 +26,11 @@ import ( "github.com/bazelbuild/bazel-gazelle/label" "github.com/bazelbuild/bazel-gazelle/language" "github.com/bazelbuild/bazel-gazelle/rule" + "github.com/bazelbuild/rules_python/gazelle/pythonconfig" "github.com/bmatcuk/doublestar" "github.com/emirpasic/gods/lists/singlylinkedlist" "github.com/emirpasic/gods/sets/treeset" godsutils "github.com/emirpasic/gods/utils" - "github.com/google/uuid" - - "github.com/bazelbuild/rules_python/gazelle/pythonconfig" ) const ( @@ -25,15 +38,21 @@ const ( pyBinaryEntrypointFilename = "__main__.py" pyTestEntrypointFilename = "__test__.py" pyTestEntrypointTargetname = "__test__" + conftestFilename = "conftest.py" + conftestTargetname = "conftest" ) var ( buildFilenames = []string{"BUILD", "BUILD.bazel"} - // errHaltDigging is an error that signals whether the generator should halt - // digging the source tree searching for modules in subdirectories. - errHaltDigging = fmt.Errorf("halt digging") ) +func GetActualKindName(kind string, args language.GenerateArgs) string { + if kindOverride, ok := args.Config.KindMap[kind]; ok { + return kindOverride.KindName + } + return kind +} + // GenerateRules extracts build metadata from source files in a directory. // GenerateRules is called in each directory where an update is requested // in depth-first post-order. @@ -58,44 +77,55 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes } } + actualPyBinaryKind := GetActualKindName(pyBinaryKind, args) + actualPyLibraryKind := GetActualKindName(pyLibraryKind, args) + actualPyTestKind := GetActualKindName(pyTestKind, args) + pythonProjectRoot := cfg.PythonProjectRoot() packageName := filepath.Base(args.Dir) pyLibraryFilenames := treeset.NewWith(godsutils.StringComparator) pyTestFilenames := treeset.NewWith(godsutils.StringComparator) + pyFileNames := treeset.NewWith(godsutils.StringComparator) // hasPyBinary controls whether a py_binary target should be generated for // this package or not. hasPyBinary := false - // hasPyTestFile and hasPyTestTarget control whether a py_test target should + // hasPyTestEntryPointFile and hasPyTestEntryPointTarget control whether a py_test target should // be generated for this package or not. - hasPyTestFile := false - hasPyTestTarget := false + hasPyTestEntryPointFile := false + hasPyTestEntryPointTarget := false + hasConftestFile := false for _, f := range args.RegularFiles { if cfg.IgnoresFile(filepath.Base(f)) { continue } ext := filepath.Ext(f) - if !hasPyBinary && f == pyBinaryEntrypointFilename { - hasPyBinary = true - } else if !hasPyTestFile && f == pyTestEntrypointFilename { - hasPyTestFile = true - } else if strings.HasSuffix(f, "_test.py") || (strings.HasPrefix(f, "test_") && ext == ".py") { - pyTestFilenames.Add(f) - } else if ext == ".py" { - pyLibraryFilenames.Add(f) + if ext == ".py" { + pyFileNames.Add(f) + if !hasPyBinary && f == pyBinaryEntrypointFilename { + hasPyBinary = true + } else if !hasPyTestEntryPointFile && f == pyTestEntrypointFilename { + hasPyTestEntryPointFile = true + } else if f == conftestFilename { + hasConftestFile = true + } else if strings.HasSuffix(f, "_test.py") || strings.HasPrefix(f, "test_") { + pyTestFilenames.Add(f) + } else { + pyLibraryFilenames.Add(f) + } } } // If a __test__.py file was not found on disk, search for targets that are // named __test__. - if !hasPyTestFile && args.File != nil { + if !hasPyTestEntryPointFile && args.File != nil { for _, rule := range args.File.Rules { if rule.Name() == pyTestEntrypointTargetname { - hasPyTestTarget = true + hasPyTestEntryPointTarget = true break } } @@ -106,9 +136,9 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes // boundaryPackages represents child Bazel packages that are used as a // boundary to stop processing under that tree. boundaryPackages := make(map[string]struct{}) - err := filepath.Walk( + err := filepath.WalkDir( filepath.Join(args.Dir, d), - func(path string, info os.FileInfo, err error) error { + func(path string, entry fs.DirEntry, err error) error { if err != nil { return err } @@ -120,7 +150,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes return nil } } - if info.IsDir() { + if entry.IsDir() { // If we are visiting a directory, we determine if we should // halt digging the tree based on a few criterias: // 1. The directory has a BUILD or BUILD.bazel files. Then @@ -135,20 +165,21 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes } if !cfg.CoarseGrainedGeneration() && hasEntrypointFile(path) { - return errHaltDigging + return fs.SkipDir } return nil } if filepath.Ext(path) == ".py" { if cfg.CoarseGrainedGeneration() || !isEntrypointFile(path) { - f, _ := filepath.Rel(args.Dir, path) + srcPath, _ := filepath.Rel(args.Dir, path) + repoPath := filepath.Join(args.Rel, srcPath) excludedPatterns := cfg.ExcludedPatterns() if excludedPatterns != nil { it := excludedPatterns.Iterator() for it.Next() { excludedPattern := it.Value().(string) - isExcluded, err := doublestar.Match(excludedPattern, f) + isExcluded, err := doublestar.Match(excludedPattern, repoPath) if err != nil { return err } @@ -159,16 +190,16 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes } baseName := filepath.Base(path) if strings.HasSuffix(baseName, "_test.py") || strings.HasPrefix(baseName, "test_") { - pyTestFilenames.Add(f) + pyTestFilenames.Add(srcPath) } else { - pyLibraryFilenames.Add(f) + pyLibraryFilenames.Add(srcPath) } } } return nil }, ) - if err != nil && err != errHaltDigging { + if err != nil { log.Printf("ERROR: %v\n", err) return language.GenerateResult{} } @@ -182,13 +213,6 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes collisionErrors := singlylinkedlist.New() - if !hasPyTestFile && !hasPyTestTarget { - it := pyTestFilenames.Iterator() - for it.Next() { - pyLibraryFilenames.Add(it.Value()) - } - } - var pyLibrary *rule.Rule if !pyLibraryFilenames.Empty() { deps, err := parser.parse(pyLibraryFilenames) @@ -198,25 +222,24 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes pyLibraryTargetName := cfg.RenderLibraryName(packageName) - // Check if a target with the same name we are generating alredy exists, - // and if it is of a different kind from the one we are generating. If - // so, we have to throw an error since Gazelle won't generate it - // correctly. + // Check if a target with the same name we are generating already + // exists, and if it is of a different kind from the one we are + // generating. If so, we have to throw an error since Gazelle won't + // generate it correctly. if args.File != nil { for _, t := range args.File.Rules { - if t.Name() == pyLibraryTargetName && t.Kind() != pyLibraryKind { + if t.Name() == pyLibraryTargetName && t.Kind() != actualPyLibraryKind { fqTarget := label.New("", args.Rel, pyLibraryTargetName) err := fmt.Errorf("failed to generate target %q of kind %q: "+ "a target of kind %q with the same name already exists. "+ "Use the '# gazelle:%s' directive to change the naming convention.", - fqTarget.String(), pyLibraryKind, t.Kind(), pythonconfig.LibraryNamingConvention) + fqTarget.String(), actualPyLibraryKind, t.Kind(), pythonconfig.LibraryNamingConvention) collisionErrors.Add(err) } } } - pyLibrary = newTargetBuilder(pyLibraryKind, pyLibraryTargetName, pythonProjectRoot, args.Rel). - setUUID(uuid.Must(uuid.NewUUID()).String()). + pyLibrary = newTargetBuilder(pyLibraryKind, pyLibraryTargetName, pythonProjectRoot, args.Rel, pyFileNames). addVisibility(visibility). addSrcs(pyLibraryFilenames). addModuleDependencies(deps). @@ -235,76 +258,109 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes pyBinaryTargetName := cfg.RenderBinaryName(packageName) - // Check if a target with the same name we are generating alredy exists, - // and if it is of a different kind from the one we are generating. If - // so, we have to throw an error since Gazelle won't generate it - // correctly. + // Check if a target with the same name we are generating already + // exists, and if it is of a different kind from the one we are + // generating. If so, we have to throw an error since Gazelle won't + // generate it correctly. if args.File != nil { for _, t := range args.File.Rules { - if t.Name() == pyBinaryTargetName && t.Kind() != pyBinaryKind { + if t.Name() == pyBinaryTargetName && t.Kind() != actualPyBinaryKind { fqTarget := label.New("", args.Rel, pyBinaryTargetName) err := fmt.Errorf("failed to generate target %q of kind %q: "+ "a target of kind %q with the same name already exists. "+ "Use the '# gazelle:%s' directive to change the naming convention.", - fqTarget.String(), pyBinaryKind, t.Kind(), pythonconfig.BinaryNamingConvention) + fqTarget.String(), actualPyBinaryKind, t.Kind(), pythonconfig.BinaryNamingConvention) collisionErrors.Add(err) } } } - pyBinaryTarget := newTargetBuilder(pyBinaryKind, pyBinaryTargetName, pythonProjectRoot, args.Rel). + pyBinaryTarget := newTargetBuilder(pyBinaryKind, pyBinaryTargetName, pythonProjectRoot, args.Rel, pyFileNames). setMain(pyBinaryEntrypointFilename). addVisibility(visibility). addSrc(pyBinaryEntrypointFilename). addModuleDependencies(deps). generateImportsAttribute() - if pyLibrary != nil { - pyBinaryTarget.addModuleDependency(module{Name: pyLibrary.PrivateAttr(uuidKey).(string)}) - } - pyBinary := pyBinaryTarget.build() result.Gen = append(result.Gen, pyBinary) result.Imports = append(result.Imports, pyBinary.PrivateAttr(config.GazelleImportsKey)) } - if hasPyTestFile || hasPyTestTarget { - if hasPyTestFile { - // Only add the pyTestEntrypointFilename to the pyTestFilenames if - // the file exists on disk. - pyTestFilenames.Add(pyTestEntrypointFilename) - } - deps, err := parser.parse(pyTestFilenames) + var conftest *rule.Rule + if hasConftestFile { + deps, err := parser.parseSingle(conftestFilename) if err != nil { log.Fatalf("ERROR: %v\n", err) } - pyTestTargetName := cfg.RenderTestName(packageName) + // Check if a target with the same name we are generating already + // exists, and if it is of a different kind from the one we are + // generating. If so, we have to throw an error since Gazelle won't + // generate it correctly. + if args.File != nil { + for _, t := range args.File.Rules { + if t.Name() == conftestTargetname && t.Kind() != actualPyLibraryKind { + fqTarget := label.New("", args.Rel, conftestTargetname) + err := fmt.Errorf("failed to generate target %q of kind %q: "+ + "a target of kind %q with the same name already exists.", + fqTarget.String(), actualPyLibraryKind, t.Kind()) + collisionErrors.Add(err) + } + } + } + + conftestTarget := newTargetBuilder(pyLibraryKind, conftestTargetname, pythonProjectRoot, args.Rel, pyFileNames). + addSrc(conftestFilename). + addModuleDependencies(deps). + addVisibility(visibility). + setTestonly(). + generateImportsAttribute() + + conftest = conftestTarget.build() - // Check if a target with the same name we are generating alredy exists, - // and if it is of a different kind from the one we are generating. If - // so, we have to throw an error since Gazelle won't generate it - // correctly. + result.Gen = append(result.Gen, conftest) + result.Imports = append(result.Imports, conftest.PrivateAttr(config.GazelleImportsKey)) + } + + var pyTestTargets []*targetBuilder + newPyTestTargetBuilder := func(srcs *treeset.Set, pyTestTargetName string) *targetBuilder { + deps, err := parser.parse(srcs) + if err != nil { + log.Fatalf("ERROR: %v\n", err) + } + // Check if a target with the same name we are generating already + // exists, and if it is of a different kind from the one we are + // generating. If so, we have to throw an error since Gazelle won't + // generate it correctly. if args.File != nil { for _, t := range args.File.Rules { - if t.Name() == pyTestTargetName && t.Kind() != pyTestKind { + if t.Name() == pyTestTargetName && t.Kind() != actualPyTestKind { fqTarget := label.New("", args.Rel, pyTestTargetName) err := fmt.Errorf("failed to generate target %q of kind %q: "+ "a target of kind %q with the same name already exists. "+ "Use the '# gazelle:%s' directive to change the naming convention.", - fqTarget.String(), pyTestKind, t.Kind(), pythonconfig.TestNamingConvention) + fqTarget.String(), actualPyTestKind, t.Kind(), pythonconfig.TestNamingConvention) collisionErrors.Add(err) } } } - - pyTestTarget := newTargetBuilder(pyTestKind, pyTestTargetName, pythonProjectRoot, args.Rel). - addSrcs(pyTestFilenames). + return newTargetBuilder(pyTestKind, pyTestTargetName, pythonProjectRoot, args.Rel, pyFileNames). + addSrcs(srcs). addModuleDependencies(deps). generateImportsAttribute() + } + if hasPyTestEntryPointFile || hasPyTestEntryPointTarget { + if hasPyTestEntryPointFile { + // Only add the pyTestEntrypointFilename to the pyTestFilenames if + // the file exists on disk. + pyTestFilenames.Add(pyTestEntrypointFilename) + } + pyTestTargetName := cfg.RenderTestName(packageName) + pyTestTarget := newPyTestTargetBuilder(pyTestFilenames, pyTestTargetName) - if hasPyTestTarget { + if hasPyTestEntryPointTarget { entrypointTarget := fmt.Sprintf(":%s", pyTestEntrypointTargetname) main := fmt.Sprintf(":%s", pyTestEntrypointFilename) pyTestTarget. @@ -314,11 +370,20 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes } else { pyTestTarget.setMain(pyTestEntrypointFilename) } + pyTestTargets = append(pyTestTargets, pyTestTarget) + } else { + // Create one py_test target per file + pyTestFilenames.Each(func(index int, testFile interface{}) { + srcs := treeset.NewWith(godsutils.StringComparator, testFile) + pyTestTargetName := strings.TrimSuffix(filepath.Base(testFile.(string)), ".py") + pyTestTargets = append(pyTestTargets, newPyTestTargetBuilder(srcs, pyTestTargetName)) + }) + } - if pyLibrary != nil { - pyTestTarget.addModuleDependency(module{Name: pyLibrary.PrivateAttr(uuidKey).(string)}) + for _, pyTestTarget := range pyTestTargets { + if conftest != nil { + pyTestTarget.addModuleDependency(module{Name: strings.TrimSuffix(conftestFilename, ".py")}) } - pyTest := pyTestTarget.build() result.Gen = append(result.Gen, pyTest) diff --git a/gazelle/kinds.go b/gazelle/python/kinds.go similarity index 73% rename from gazelle/kinds.go rename to gazelle/python/kinds.go index fa0f4ed98a..ab1afb7d55 100644 --- a/gazelle/kinds.go +++ b/gazelle/python/kinds.go @@ -1,3 +1,17 @@ +// 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. + package python import ( @@ -51,7 +65,7 @@ var pyKinds = map[string]rule.KindInfo{ }, }, pyTestKind: { - MatchAny: true, + MatchAny: false, NonEmptyAttrs: map[string]bool{ "deps": true, "main": true, diff --git a/gazelle/python/language.go b/gazelle/python/language.go new file mode 100644 index 0000000000..56eb97b043 --- /dev/null +++ b/gazelle/python/language.go @@ -0,0 +1,32 @@ +// 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. + +package python + +import ( + "github.com/bazelbuild/bazel-gazelle/language" +) + +// Python satisfies the language.Language interface. It is the Gazelle extension +// for Python rules. +type Python struct { + Configurer + Resolver +} + +// NewLanguage initializes a new Python that satisfies the language.Language +// interface. This is the entrypoint for the extension initialization. +func NewLanguage() language.Language { + return &Python{} +} diff --git a/gazelle/parse.py b/gazelle/python/parse.py similarity index 69% rename from gazelle/parse.py rename to gazelle/python/parse.py index dec3a16cc9..6c0ef69598 100644 --- a/gazelle/parse.py +++ b/gazelle/python/parse.py @@ -1,3 +1,17 @@ +# 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. + # parse.py is a long-living program that communicates over STDIN and STDOUT. # STDIN receives parse requests, one per line. It outputs the parsed modules and # comments from all the files from each request. @@ -13,7 +27,7 @@ def parse_import_statements(content, filepath): modules = list() - tree = ast.parse(content) + tree = ast.parse(content, filename=filepath) for node in ast.walk(tree): if isinstance(node, ast.Import): for subnode in node.names: @@ -21,15 +35,18 @@ def parse_import_statements(content, filepath): "name": subnode.name, "lineno": node.lineno, "filepath": filepath, + "from": "", } modules.append(module) elif isinstance(node, ast.ImportFrom) and node.level == 0: - module = { - "name": node.module, - "lineno": node.lineno, - "filepath": filepath, - } - modules.append(module) + for subnode in node.names: + module = { + "name": f"{node.module}.{subnode.name}", + "lineno": node.lineno, + "filepath": filepath, + "from": node.module, + } + modules.append(module) return modules @@ -47,9 +64,11 @@ def parse(repo_root, rel_package_path, filename): abs_filepath = os.path.join(repo_root, rel_filepath) with open(abs_filepath, "r") as file: content = file.read() - # From simple benchmarks, 2 workers gave the best performance here. + # From simple benchmarks, 2 workers gave the best performance here. with concurrent.futures.ThreadPoolExecutor(max_workers=2) as executor: - modules_future = executor.submit(parse_import_statements, content, rel_filepath) + modules_future = executor.submit( + parse_import_statements, content, rel_filepath + ) comments_future = executor.submit(parse_comments, content) modules = modules_future.result() comments = comments_future.result() diff --git a/gazelle/parser.go b/gazelle/python/parser.go similarity index 80% rename from gazelle/parser.go rename to gazelle/python/parser.go index 2a82c946e1..33eb6f4b33 100644 --- a/gazelle/parser.go +++ b/gazelle/python/parser.go @@ -1,3 +1,17 @@ +// 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. + package python import ( @@ -25,14 +39,14 @@ var ( ) func init() { - parseScriptRunfile, err := bazel.Runfile("gazelle/parse") + parseScriptRunfile, err := bazel.Runfile("python/parse") if err != nil { log.Printf("failed to initialize parser: %v\n", err) os.Exit(1) } ctx := context.Background() - ctx, parserCancel := context.WithTimeout(ctx, time.Minute*5) + ctx, parserCancel := context.WithTimeout(ctx, time.Minute*10) cmd := exec.CommandContext(ctx, parseScriptRunfile) cmd.Stderr = os.Stderr @@ -128,18 +142,21 @@ func (p *python3Parser) parse(pyFilenames *treeset.Set) (*treeset.Set, error) { } for _, res := range allRes { - annotations := annotationsFromComments(res.Comments) + annotations, err := annotationsFromComments(res.Comments) + if err != nil { + return nil, fmt.Errorf("failed to parse annotations: %w", err) + } for _, m := range res.Modules { // Check for ignored dependencies set via an annotation to the Python // module. - if annotations.ignores(m.Name) { + if annotations.ignores(m.Name) || annotations.ignores(m.From) { continue } // Check for ignored dependencies set via a Gazelle directive in a BUILD // file. - if p.ignoresDependency(m.Name) { + if p.ignoresDependency(m.Name) || p.ignoresDependency(m.From) { continue } @@ -170,6 +187,9 @@ type module struct { LineNumber uint32 `json:"lineno"` // The path to the module file relative to the Bazel workspace root. Filepath string `json:"filepath"` + // If this was a from import, e.g. from foo import bar, From indicates the module + // from which it is imported. + From string `json:"from"` } // moduleComparator compares modules by name. @@ -192,17 +212,20 @@ type comment string // asAnnotation returns an annotation object if the comment has the // annotationPrefix. -func (c *comment) asAnnotation() *annotation { +func (c *comment) asAnnotation() (*annotation, error) { uncomment := strings.TrimLeft(string(*c), "# ") if !strings.HasPrefix(uncomment, annotationPrefix) { - return nil + return nil, nil } withoutPrefix := strings.TrimPrefix(uncomment, annotationPrefix) annotationParts := strings.SplitN(withoutPrefix, " ", 2) + if len(annotationParts) < 2 { + return nil, fmt.Errorf("`%s` requires a value", *c) + } return &annotation{ kind: annotationKind(annotationParts[0]), value: annotationParts[1], - } + }, nil } // annotation represents a single Gazelle annotation parsed from a Python @@ -221,10 +244,13 @@ type annotations struct { // annotationsFromComments returns all the annotations parsed out of the // comments of a Python module. -func annotationsFromComments(comments []comment) *annotations { +func annotationsFromComments(comments []comment) (*annotations, error) { ignore := make(map[string]struct{}) for _, comment := range comments { - annotation := comment.asAnnotation() + annotation, err := comment.asAnnotation() + if err != nil { + return nil, err + } if annotation != nil { if annotation.kind == annotationKindIgnore { modules := strings.Split(annotation.value, ",") @@ -240,7 +266,7 @@ func annotationsFromComments(comments []comment) *annotations { } return &annotations{ ignore: ignore, - } + }, nil } // ignored returns true if the given module was ignored via the ignore diff --git a/gazelle/python_test.go b/gazelle/python/python_test.go similarity index 72% rename from gazelle/python_test.go rename to gazelle/python/python_test.go index 99656552dd..79450ad584 100644 --- a/gazelle/python_test.go +++ b/gazelle/python/python_test.go @@ -23,8 +23,6 @@ import ( "bytes" "context" "errors" - "fmt" - "io/ioutil" "os" "os/exec" "path/filepath" @@ -34,14 +32,13 @@ import ( "github.com/bazelbuild/bazel-gazelle/testtools" "github.com/bazelbuild/rules_go/go/tools/bazel" - "github.com/emirpasic/gods/lists/singlylinkedlist" "github.com/ghodss/yaml" ) const ( - extensionDir = "gazelle/" - testDataPath = extensionDir + "testdata/" - gazelleBinaryName = "gazelle_python_binary" + extensionDir = "python" + string(os.PathSeparator) + testDataPath = extensionDir + "testdata" + string(os.PathSeparator) + gazelleBinaryName = "gazelle_binary" ) var gazellePath = mustFindGazelle() @@ -56,7 +53,7 @@ func TestGazelleBinary(t *testing.T) { for _, f := range runfiles { if strings.HasPrefix(f.ShortPath, testDataPath) { relativePath := strings.TrimPrefix(f.ShortPath, testDataPath) - parts := strings.SplitN(relativePath, "/", 2) + parts := strings.SplitN(relativePath, string(os.PathSeparator), 2) if len(parts) < 2 { // This file is not a part of a testcase since it must be in a dir that // is the test case and then have a path inside of that. @@ -77,13 +74,13 @@ func TestGazelleBinary(t *testing.T) { func testPath(t *testing.T, name string, files []bazel.RunfileEntry) { t.Run(name, func(t *testing.T) { - var inputs []testtools.FileSpec - var goldens []testtools.FileSpec + t.Parallel() + var inputs, goldens []testtools.FileSpec var config *testYAML for _, f := range files { path := f.Path - trim := testDataPath + name + "/" + trim := filepath.Join(testDataPath, name) + string(os.PathSeparator) shortPath := strings.TrimPrefix(f.ShortPath, trim) info, err := os.Stat(path) if err != nil { @@ -94,9 +91,9 @@ func testPath(t *testing.T, name string, files []bazel.RunfileEntry) { continue } - content, err := ioutil.ReadFile(path) + content, err := os.ReadFile(path) if err != nil { - t.Errorf("ioutil.ReadFile(%q) error: %v", path, err) + t.Errorf("os.ReadFile(%q) error: %v", path, err) } if filepath.Base(shortPath) == "test.yaml" { @@ -114,43 +111,49 @@ func testPath(t *testing.T, name string, files []bazel.RunfileEntry) { Path: filepath.Join(name, strings.TrimSuffix(shortPath, ".in")), Content: string(content), }) - } else if strings.HasSuffix(shortPath, ".out") { + continue + } + + if strings.HasSuffix(shortPath, ".out") { goldens = append(goldens, testtools.FileSpec{ Path: filepath.Join(name, strings.TrimSuffix(shortPath, ".out")), Content: string(content), }) - } else { - inputs = append(inputs, testtools.FileSpec{ - Path: filepath.Join(name, shortPath), - Content: string(content), - }) - goldens = append(goldens, testtools.FileSpec{ - Path: filepath.Join(name, shortPath), - Content: string(content), - }) + continue } + + inputs = append(inputs, testtools.FileSpec{ + Path: filepath.Join(name, shortPath), + Content: string(content), + }) + goldens = append(goldens, testtools.FileSpec{ + Path: filepath.Join(name, shortPath), + Content: string(content), + }) } testdataDir, cleanup := testtools.CreateFiles(t, inputs) - defer cleanup() - defer func() { - if t.Failed() { - filepath.Walk(testdataDir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - t.Logf("%q exists", strings.TrimPrefix(path, testdataDir)) - return nil - }) + t.Cleanup(cleanup) + t.Cleanup(func() { + if !t.Failed() { + return } - }() + + filepath.Walk(testdataDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + t.Logf("%q exists", strings.TrimPrefix(path, testdataDir)) + return nil + }) + }) workspaceRoot := filepath.Join(testdataDir, name) args := []string{"-build_file_name=BUILD,BUILD.bazel"} ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() + t.Cleanup(cancel) cmd := exec.CommandContext(ctx, gazellePath, args...) var stdout, stderr bytes.Buffer cmd.Stdout = &stdout @@ -162,31 +165,23 @@ func testPath(t *testing.T, name string, files []bazel.RunfileEntry) { t.Fatal(err) } } - errs := singlylinkedlist.New() + actualExitCode := cmd.ProcessState.ExitCode() if config.Expect.ExitCode != actualExitCode { - errs.Add(fmt.Errorf("expected gazelle exit code: %d\ngot: %d", - config.Expect.ExitCode, actualExitCode, - )) + t.Errorf("expected gazelle exit code: %d\ngot: %d", + config.Expect.ExitCode, actualExitCode) } actualStdout := stdout.String() if strings.TrimSpace(config.Expect.Stdout) != strings.TrimSpace(actualStdout) { - errs.Add(fmt.Errorf("expected gazelle stdout: %s\ngot: %s", - config.Expect.Stdout, actualStdout, - )) + t.Errorf("expected gazelle stdout: %s\ngot: %s", + config.Expect.Stdout, actualStdout) } actualStderr := stderr.String() if strings.TrimSpace(config.Expect.Stderr) != strings.TrimSpace(actualStderr) { - errs.Add(fmt.Errorf("expected gazelle stderr: %s\ngot: %s", - config.Expect.Stderr, actualStderr, - )) + t.Errorf("expected gazelle stderr: %s\ngot: %s", + config.Expect.Stderr, actualStderr) } - if !errs.Empty() { - errsIt := errs.Iterator() - for errsIt.Next() { - err := errsIt.Value().(error) - t.Log(err) - } + if t.Failed() { t.FailNow() } diff --git a/gazelle/python/resolve.go b/gazelle/python/resolve.go new file mode 100644 index 0000000000..46014e50ec --- /dev/null +++ b/gazelle/python/resolve.go @@ -0,0 +1,304 @@ +// 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. + +package python + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strings" + + "github.com/bazelbuild/bazel-gazelle/config" + "github.com/bazelbuild/bazel-gazelle/label" + "github.com/bazelbuild/bazel-gazelle/repo" + "github.com/bazelbuild/bazel-gazelle/resolve" + "github.com/bazelbuild/bazel-gazelle/rule" + bzl "github.com/bazelbuild/buildtools/build" + "github.com/emirpasic/gods/sets/treeset" + godsutils "github.com/emirpasic/gods/utils" + + "github.com/bazelbuild/rules_python/gazelle/pythonconfig" +) + +const languageName = "py" + +const ( + // resolvedDepsKey is the attribute key used to pass dependencies that don't + // need to be resolved by the dependency resolver in the Resolver step. + resolvedDepsKey = "_gazelle_python_resolved_deps" +) + +// Resolver satisfies the resolve.Resolver interface. It resolves dependencies +// in rules generated by this extension. +type Resolver struct{} + +// Name returns the name of the language. This is the prefix of the kinds of +// rules generated. E.g. py_library and py_binary. +func (*Resolver) Name() string { return languageName } + +// Imports returns a list of ImportSpecs that can be used to import the rule +// r. This is used to populate RuleIndex. +// +// If nil is returned, the rule will not be indexed. If any non-nil slice is +// returned, including an empty slice, the rule will be indexed. +func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec { + cfgs := c.Exts[languageName].(pythonconfig.Configs) + cfg := cfgs[f.Pkg] + srcs := r.AttrStrings("srcs") + provides := make([]resolve.ImportSpec, 0, len(srcs)+1) + for _, src := range srcs { + ext := filepath.Ext(src) + if ext == ".py" { + pythonProjectRoot := cfg.PythonProjectRoot() + provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src) + provides = append(provides, provide) + } + } + if len(provides) == 0 { + return nil + } + return provides +} + +// importSpecFromSrc determines the ImportSpec based on the target that contains the src so that +// the target can be indexed for import statements that match the calculated src relative to the its +// Python project root. +func importSpecFromSrc(pythonProjectRoot, bzlPkg, src string) resolve.ImportSpec { + pythonPkgDir := filepath.Join(bzlPkg, filepath.Dir(src)) + relPythonPkgDir, err := filepath.Rel(pythonProjectRoot, pythonPkgDir) + if err != nil { + panic(fmt.Errorf("unexpected failure: %v", err)) + } + if relPythonPkgDir == "." { + relPythonPkgDir = "" + } + pythonPkg := strings.ReplaceAll(relPythonPkgDir, "/", ".") + filename := filepath.Base(src) + if filename == pyLibraryEntrypointFilename { + if pythonPkg != "" { + return resolve.ImportSpec{ + Lang: languageName, + Imp: pythonPkg, + } + } + } + moduleName := strings.TrimSuffix(filename, ".py") + var imp string + if pythonPkg == "" { + imp = moduleName + } else { + imp = fmt.Sprintf("%s.%s", pythonPkg, moduleName) + } + return resolve.ImportSpec{ + Lang: languageName, + Imp: imp, + } +} + +// Embeds returns a list of labels of rules that the given rule embeds. If +// a rule is embedded by another importable rule of the same language, only +// the embedding rule will be indexed. The embedding rule will inherit +// the imports of the embedded rule. +func (py *Resolver) Embeds(r *rule.Rule, from label.Label) []label.Label { + // TODO(f0rmiga): implement. + return make([]label.Label, 0) +} + +// Resolve translates imported libraries for a given rule into Bazel +// dependencies. Information about imported libraries is returned for each +// rule generated by language.GenerateRules in +// language.GenerateResult.Imports. Resolve generates a "deps" attribute (or +// the appropriate language-specific equivalent) for each import according to +// language-specific rules and heuristics. +func (py *Resolver) Resolve( + c *config.Config, + ix *resolve.RuleIndex, + rc *repo.RemoteCache, + r *rule.Rule, + modulesRaw interface{}, + from label.Label, +) { + // TODO(f0rmiga): may need to be defensive here once this Gazelle extension + // join with the main Gazelle binary with other rules. It may conflict with + // other generators that generate py_* targets. + deps := treeset.NewWith(godsutils.StringComparator) + if modulesRaw != nil { + cfgs := c.Exts[languageName].(pythonconfig.Configs) + cfg := cfgs[from.Pkg] + pythonProjectRoot := cfg.PythonProjectRoot() + modules := modulesRaw.(*treeset.Set) + it := modules.Iterator() + explainDependency := os.Getenv("EXPLAIN_DEPENDENCY") + hasFatalError := false + MODULES_LOOP: + for it.Next() { + mod := it.Value().(module) + moduleParts := strings.Split(mod.Name, ".") + possibleModules := []string{mod.Name} + for len(moduleParts) > 1 { + // Iterate back through the possible imports until + // a match is found. + // For example, "from foo.bar import baz" where bar is a variable, we should try + // `foo.bar.baz` first, then `foo.bar`, then `foo`. In the first case, the import could be file `baz.py` + // in the directory `foo/bar`. + // Or, the import could be variable `bar` in file `foo/bar.py`. + // The import could also be from a standard module, e.g. `six.moves`, where + // the dependency is actually `six`. + moduleParts = moduleParts[:len(moduleParts)-1] + possibleModules = append(possibleModules, strings.Join(moduleParts, ".")) + } + errs := []error{} + POSSIBLE_MODULE_LOOP: + for _, moduleName := range possibleModules { + imp := resolve.ImportSpec{Lang: languageName, Imp: moduleName} + if override, ok := resolve.FindRuleWithOverride(c, imp, languageName); ok { + if override.Repo == "" { + override.Repo = from.Repo + } + if !override.Equal(from) { + if override.Repo == from.Repo { + override.Repo = "" + } + dep := override.String() + deps.Add(dep) + if explainDependency == dep { + log.Printf("Explaining dependency (%s): "+ + "in the target %q, the file %q imports %q at line %d, "+ + "which resolves using the \"gazelle:resolve\" directive.\n", + explainDependency, from.String(), mod.Filepath, moduleName, mod.LineNumber) + } + continue MODULES_LOOP + } + } else { + if dep, ok := cfg.FindThirdPartyDependency(moduleName); ok { + deps.Add(dep) + if explainDependency == dep { + log.Printf("Explaining dependency (%s): "+ + "in the target %q, the file %q imports %q at line %d, "+ + "which resolves from the third-party module %q from the wheel %q.\n", + explainDependency, from.String(), mod.Filepath, moduleName, mod.LineNumber, mod.Name, dep) + } + continue MODULES_LOOP + } else { + matches := ix.FindRulesByImportWithConfig(c, imp, languageName) + if len(matches) == 0 { + // Check if the imported module is part of the standard library. + if isStd, err := isStdModule(module{Name: moduleName}); err != nil { + log.Println("Error checking if standard module: ", err) + hasFatalError = true + continue POSSIBLE_MODULE_LOOP + } else if isStd { + continue MODULES_LOOP + } else if cfg.ValidateImportStatements() { + err := fmt.Errorf( + "%[1]q at line %[2]d from %[3]q is an invalid dependency: possible solutions:\n"+ + "\t1. Add it as a dependency in the requirements.txt file.\n"+ + "\t2. Instruct Gazelle to resolve to a known dependency using the gazelle:resolve directive.\n"+ + "\t3. Ignore it with a comment '# gazelle:ignore %[1]s' in the Python file.\n", + moduleName, mod.LineNumber, mod.Filepath, + ) + errs = append(errs, err) + continue POSSIBLE_MODULE_LOOP + } + } + filteredMatches := make([]resolve.FindResult, 0, len(matches)) + for _, match := range matches { + if match.IsSelfImport(from) { + // Prevent from adding itself as a dependency. + continue MODULES_LOOP + } + filteredMatches = append(filteredMatches, match) + } + if len(filteredMatches) == 0 { + continue POSSIBLE_MODULE_LOOP + } + if len(filteredMatches) > 1 { + sameRootMatches := make([]resolve.FindResult, 0, len(filteredMatches)) + for _, match := range filteredMatches { + if strings.HasPrefix(match.Label.Pkg, pythonProjectRoot) { + sameRootMatches = append(sameRootMatches, match) + } + } + if len(sameRootMatches) != 1 { + err := fmt.Errorf( + "multiple targets (%s) may be imported with %q at line %d in %q "+ + "- this must be fixed using the \"gazelle:resolve\" directive", + targetListFromResults(filteredMatches), moduleName, mod.LineNumber, mod.Filepath) + errs = append(errs, err) + continue POSSIBLE_MODULE_LOOP + } + filteredMatches = sameRootMatches + } + matchLabel := filteredMatches[0].Label.Rel(from.Repo, from.Pkg) + dep := matchLabel.String() + deps.Add(dep) + if explainDependency == dep { + log.Printf("Explaining dependency (%s): "+ + "in the target %q, the file %q imports %q at line %d, "+ + "which resolves from the first-party indexed labels.\n", + explainDependency, from.String(), mod.Filepath, moduleName, mod.LineNumber) + } + continue MODULES_LOOP + } + } + } // End possible modules loop. + if len(errs) > 0 { + // If, after trying all possible modules, we still haven't found anything, error out. + joinedErrs := "" + for _, err := range errs { + joinedErrs = fmt.Sprintf("%s%s\n", joinedErrs, err) + } + log.Printf("ERROR: failed to validate dependencies for target %q: %v\n", from.String(), joinedErrs) + hasFatalError = true + } + } + if hasFatalError { + os.Exit(1) + } + } + resolvedDeps := r.PrivateAttr(resolvedDepsKey).(*treeset.Set) + if !resolvedDeps.Empty() { + it := resolvedDeps.Iterator() + for it.Next() { + deps.Add(it.Value()) + } + } + if !deps.Empty() { + r.SetAttr("deps", convertDependencySetToExpr(deps)) + } +} + +// targetListFromResults returns a string with the human-readable list of +// targets contained in the given results. +func targetListFromResults(results []resolve.FindResult) string { + list := make([]string, len(results)) + for i, result := range results { + list[i] = result.Label.String() + } + return strings.Join(list, ", ") +} + +// convertDependencySetToExpr converts the given set of dependencies to an +// expression to be used in the deps attribute. +func convertDependencySetToExpr(set *treeset.Set) bzl.Expr { + deps := make([]bzl.Expr, set.Size()) + it := set.Iterator() + for it.Next() { + dep := it.Value().(string) + deps[it.Index()] = &bzl.StringExpr{Value: dep} + } + return &bzl.ListExpr{List: deps} +} diff --git a/gazelle/std_modules.go b/gazelle/python/std_modules.go similarity index 67% rename from gazelle/std_modules.go rename to gazelle/python/std_modules.go index f7d0c243d5..94ef45666e 100644 --- a/gazelle/std_modules.go +++ b/gazelle/python/std_modules.go @@ -1,3 +1,17 @@ +// 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. + package python import ( @@ -26,19 +40,19 @@ var ( func init() { stdModulesSeen = make(map[string]struct{}) - stdModulesScriptRunfile, err := bazel.Runfile("gazelle/std_modules") + stdModulesScriptRunfile, err := bazel.Runfile("python/std_modules") if err != nil { log.Printf("failed to initialize std_modules: %v\n", err) os.Exit(1) } ctx := context.Background() - ctx, stdModulesCancel := context.WithTimeout(ctx, time.Minute*5) + ctx, stdModulesCancel := context.WithTimeout(ctx, time.Minute*10) cmd := exec.CommandContext(ctx, stdModulesScriptRunfile) cmd.Stderr = os.Stderr - cmd.Env = []string{} - + // All userland site-packages should be ignored. + cmd.Env = []string{"PYTHONNOUSERSITE=1"} stdin, err := cmd.StdinPipe() if err != nil { log.Printf("failed to initialize std_modules: %v\n", err) diff --git a/gazelle/python/std_modules.py b/gazelle/python/std_modules.py new file mode 100644 index 0000000000..779a325508 --- /dev/null +++ b/gazelle/python/std_modules.py @@ -0,0 +1,51 @@ +# 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. + +# std_modules.py is a long-living program that communicates over STDIN and +# STDOUT. STDIN receives module names, one per line. For each module statement +# it evaluates, it outputs true/false for whether the module is part of the +# standard library or not. + +import os +import sys +from contextlib import redirect_stdout + + +def is_std_modules(module): + # If for some reason a module (such as pygame, see https://github.com/pygame/pygame/issues/542) + # prints to stdout upon import, + # the output of this script should still be parseable by golang. + # Therefore, redirect stdout while running the import. + with redirect_stdout(os.devnull): + try: + __import__(module, globals(), locals(), [], 0) + return True + except Exception: + return False + + +def main(stdin, stdout): + for module in stdin: + module = module.strip() + # Don't print the boolean directly as it is capitalized in Python. + print( + "true" if is_std_modules(module) else "false", + end="\n", + file=stdout, + ) + stdout.flush() + + +if __name__ == "__main__": + exit(main(sys.stdin, sys.stdout)) diff --git a/gazelle/target.go b/gazelle/python/target.go similarity index 71% rename from gazelle/target.go rename to gazelle/python/target.go index 2b260679b6..fdc99fc68c 100644 --- a/gazelle/target.go +++ b/gazelle/python/target.go @@ -1,12 +1,25 @@ +// 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. + package python import ( - "path/filepath" - "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/rule" "github.com/emirpasic/gods/sets/treeset" godsutils "github.com/emirpasic/gods/utils" + "path/filepath" ) // targetBuilder builds targets to be generated by Gazelle. @@ -15,38 +28,31 @@ type targetBuilder struct { name string pythonProjectRoot string bzlPackage string - uuid string srcs *treeset.Set + siblingSrcs *treeset.Set deps *treeset.Set resolvedDeps *treeset.Set visibility *treeset.Set main *string imports []string + testonly bool } // newTargetBuilder constructs a new targetBuilder. -func newTargetBuilder(kind, name, pythonProjectRoot, bzlPackage string) *targetBuilder { +func newTargetBuilder(kind, name, pythonProjectRoot, bzlPackage string, siblingSrcs *treeset.Set) *targetBuilder { return &targetBuilder{ kind: kind, name: name, pythonProjectRoot: pythonProjectRoot, bzlPackage: bzlPackage, srcs: treeset.NewWith(godsutils.StringComparator), + siblingSrcs: siblingSrcs, deps: treeset.NewWith(moduleComparator), resolvedDeps: treeset.NewWith(godsutils.StringComparator), visibility: treeset.NewWith(godsutils.StringComparator), } } -// setUUID sets the given UUID for the target. It's used to index the generated -// target based on this value in addition to the other ways the targets can be -// imported. py_{binary,test} targets in the same Bazel package can add a -// virtual dependency to this UUID that gets resolved in the Resolver interface. -func (t *targetBuilder) setUUID(uuid string) *targetBuilder { - t.uuid = uuid - return t -} - // addSrc adds a single src to the target. func (t *targetBuilder) addSrc(src string) *targetBuilder { t.srcs.Add(src) @@ -64,6 +70,15 @@ func (t *targetBuilder) addSrcs(srcs *treeset.Set) *targetBuilder { // addModuleDependency adds a single module dep to the target. func (t *targetBuilder) addModuleDependency(dep module) *targetBuilder { + fileName := dep.Name + ".py" + if dep.From != "" { + fileName = dep.From + ".py" + } + if t.siblingSrcs.Contains(fileName) && fileName != filepath.Base(dep.Filepath) { + // importing another module from the same package, converting to absolute imports to make + // dependency resolution easier + dep.Name = importSpecFromSrc(t.pythonProjectRoot, t.bzlPackage, fileName).Imp + } t.deps.Add(dep) return t } @@ -72,7 +87,7 @@ func (t *targetBuilder) addModuleDependency(dep module) *targetBuilder { func (t *targetBuilder) addModuleDependencies(deps *treeset.Set) *targetBuilder { it := deps.Iterator() for it.Next() { - t.deps.Add(it.Value().(module)) + t.addModuleDependency(it.Value().(module)) } return t } @@ -96,6 +111,12 @@ func (t *targetBuilder) setMain(main string) *targetBuilder { return t } +// setTestonly sets the testonly attribute to true. +func (t *targetBuilder) setTestonly() *targetBuilder { + t.testonly = true + return t +} + // generateImportsAttribute generates the imports attribute. // These are a list of import directories to be added to the PYTHONPATH. In our // case, the value we add is on Bazel sub-packages to be able to perform imports @@ -113,9 +134,6 @@ func (t *targetBuilder) generateImportsAttribute() *targetBuilder { // build returns the assembled *rule.Rule for the target. func (t *targetBuilder) build() *rule.Rule { r := rule.NewRule(t.kind, t.name) - if t.uuid != "" { - r.SetPrivateAttr(uuidKey, t.uuid) - } if !t.srcs.Empty() { r.SetAttr("srcs", t.srcs.Values()) } @@ -131,6 +149,9 @@ func (t *targetBuilder) build() *rule.Rule { if !t.deps.Empty() { r.SetPrivateAttr(config.GazelleImportsKey, t.deps) } + if t.testonly { + r.SetAttr("testonly", true) + } r.SetPrivateAttr(resolvedDepsKey, t.resolvedDeps) return r } diff --git a/gazelle/testdata/README.md b/gazelle/python/testdata/README.md similarity index 100% rename from gazelle/testdata/README.md rename to gazelle/python/testdata/README.md diff --git a/gazelle/testdata/dependency_resolution_order/BUILD.in b/gazelle/python/testdata/dependency_resolution_order/BUILD.in similarity index 100% rename from gazelle/testdata/dependency_resolution_order/BUILD.in rename to gazelle/python/testdata/dependency_resolution_order/BUILD.in diff --git a/gazelle/testdata/dependency_resolution_order/BUILD.out b/gazelle/python/testdata/dependency_resolution_order/BUILD.out similarity index 85% rename from gazelle/testdata/dependency_resolution_order/BUILD.out rename to gazelle/python/testdata/dependency_resolution_order/BUILD.out index 2ba2c84c9a..3ea83eb5f1 100644 --- a/gazelle/testdata/dependency_resolution_order/BUILD.out +++ b/gazelle/python/testdata/dependency_resolution_order/BUILD.out @@ -9,6 +9,6 @@ py_library( deps = [ "//baz", "//somewhere/bar", - "@gazelle_python_test//pypi__some_foo", + "@gazelle_python_test_some_foo//:pkg", ], ) diff --git a/gazelle/testdata/dependency_resolution_order/README.md b/gazelle/python/testdata/dependency_resolution_order/README.md similarity index 100% rename from gazelle/testdata/dependency_resolution_order/README.md rename to gazelle/python/testdata/dependency_resolution_order/README.md diff --git a/gazelle/testdata/dependency_resolution_order/WORKSPACE b/gazelle/python/testdata/dependency_resolution_order/WORKSPACE similarity index 100% rename from gazelle/testdata/dependency_resolution_order/WORKSPACE rename to gazelle/python/testdata/dependency_resolution_order/WORKSPACE diff --git a/gazelle/python/testdata/dependency_resolution_order/__init__.py b/gazelle/python/testdata/dependency_resolution_order/__init__.py new file mode 100644 index 0000000000..d9c6504deb --- /dev/null +++ b/gazelle/python/testdata/dependency_resolution_order/__init__.py @@ -0,0 +1,24 @@ +# 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 sys + +import bar +import baz +import foo + +_ = sys +_ = bar +_ = baz +_ = foo diff --git a/gazelle/testdata/dependency_resolution_order/somewhere/bar/BUILD.in b/gazelle/python/testdata/dependency_resolution_order/bar/BUILD.in similarity index 100% rename from gazelle/testdata/dependency_resolution_order/somewhere/bar/BUILD.in rename to gazelle/python/testdata/dependency_resolution_order/bar/BUILD.in diff --git a/gazelle/testdata/dependency_resolution_order/bar/BUILD.out b/gazelle/python/testdata/dependency_resolution_order/bar/BUILD.out similarity index 100% rename from gazelle/testdata/dependency_resolution_order/bar/BUILD.out rename to gazelle/python/testdata/dependency_resolution_order/bar/BUILD.out diff --git a/gazelle/python/testdata/dependency_resolution_order/bar/__init__.py b/gazelle/python/testdata/dependency_resolution_order/bar/__init__.py new file mode 100644 index 0000000000..1c0275c070 --- /dev/null +++ b/gazelle/python/testdata/dependency_resolution_order/bar/__init__.py @@ -0,0 +1,17 @@ +# 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 + +_ = os diff --git a/gazelle/testdata/file_name_matches_import_statement/BUILD.in b/gazelle/python/testdata/dependency_resolution_order/baz/BUILD.in similarity index 100% rename from gazelle/testdata/file_name_matches_import_statement/BUILD.in rename to gazelle/python/testdata/dependency_resolution_order/baz/BUILD.in diff --git a/gazelle/testdata/dependency_resolution_order/baz/BUILD.out b/gazelle/python/testdata/dependency_resolution_order/baz/BUILD.out similarity index 100% rename from gazelle/testdata/dependency_resolution_order/baz/BUILD.out rename to gazelle/python/testdata/dependency_resolution_order/baz/BUILD.out diff --git a/gazelle/python/testdata/dependency_resolution_order/baz/__init__.py b/gazelle/python/testdata/dependency_resolution_order/baz/__init__.py new file mode 100644 index 0000000000..1c0275c070 --- /dev/null +++ b/gazelle/python/testdata/dependency_resolution_order/baz/__init__.py @@ -0,0 +1,17 @@ +# 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 + +_ = os diff --git a/gazelle/testdata/first_party_dependencies/BUILD.in b/gazelle/python/testdata/dependency_resolution_order/foo/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_dependencies/BUILD.in rename to gazelle/python/testdata/dependency_resolution_order/foo/BUILD.in diff --git a/gazelle/testdata/dependency_resolution_order/foo/BUILD.out b/gazelle/python/testdata/dependency_resolution_order/foo/BUILD.out similarity index 100% rename from gazelle/testdata/dependency_resolution_order/foo/BUILD.out rename to gazelle/python/testdata/dependency_resolution_order/foo/BUILD.out diff --git a/gazelle/python/testdata/dependency_resolution_order/foo/__init__.py b/gazelle/python/testdata/dependency_resolution_order/foo/__init__.py new file mode 100644 index 0000000000..1c0275c070 --- /dev/null +++ b/gazelle/python/testdata/dependency_resolution_order/foo/__init__.py @@ -0,0 +1,17 @@ +# 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 + +_ = os diff --git a/gazelle/python/testdata/dependency_resolution_order/gazelle_python.yaml b/gazelle/python/testdata/dependency_resolution_order/gazelle_python.yaml new file mode 100644 index 0000000000..8615181c91 --- /dev/null +++ b/gazelle/python/testdata/dependency_resolution_order/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + foo: some_foo + pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/testdata/first_party_file_and_directory_modules/foo/BUILD.in b/gazelle/python/testdata/dependency_resolution_order/somewhere/bar/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/foo/BUILD.in rename to gazelle/python/testdata/dependency_resolution_order/somewhere/bar/BUILD.in diff --git a/gazelle/testdata/dependency_resolution_order/somewhere/bar/BUILD.out b/gazelle/python/testdata/dependency_resolution_order/somewhere/bar/BUILD.out similarity index 100% rename from gazelle/testdata/dependency_resolution_order/somewhere/bar/BUILD.out rename to gazelle/python/testdata/dependency_resolution_order/somewhere/bar/BUILD.out diff --git a/gazelle/python/testdata/dependency_resolution_order/somewhere/bar/__init__.py b/gazelle/python/testdata/dependency_resolution_order/somewhere/bar/__init__.py new file mode 100644 index 0000000000..1c0275c070 --- /dev/null +++ b/gazelle/python/testdata/dependency_resolution_order/somewhere/bar/__init__.py @@ -0,0 +1,17 @@ +# 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 + +_ = os diff --git a/gazelle/python/testdata/dependency_resolution_order/test.yaml b/gazelle/python/testdata/dependency_resolution_order/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/dependency_resolution_order/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/disable_import_statements_validation/BUILD.in b/gazelle/python/testdata/disable_import_statements_validation/BUILD.in similarity index 100% rename from gazelle/testdata/disable_import_statements_validation/BUILD.in rename to gazelle/python/testdata/disable_import_statements_validation/BUILD.in diff --git a/gazelle/testdata/disable_import_statements_validation/BUILD.out b/gazelle/python/testdata/disable_import_statements_validation/BUILD.out similarity index 100% rename from gazelle/testdata/disable_import_statements_validation/BUILD.out rename to gazelle/python/testdata/disable_import_statements_validation/BUILD.out diff --git a/gazelle/testdata/disable_import_statements_validation/README.md b/gazelle/python/testdata/disable_import_statements_validation/README.md similarity index 100% rename from gazelle/testdata/disable_import_statements_validation/README.md rename to gazelle/python/testdata/disable_import_statements_validation/README.md diff --git a/gazelle/testdata/disable_import_statements_validation/WORKSPACE b/gazelle/python/testdata/disable_import_statements_validation/WORKSPACE similarity index 100% rename from gazelle/testdata/disable_import_statements_validation/WORKSPACE rename to gazelle/python/testdata/disable_import_statements_validation/WORKSPACE diff --git a/gazelle/python/testdata/disable_import_statements_validation/__init__.py b/gazelle/python/testdata/disable_import_statements_validation/__init__.py new file mode 100644 index 0000000000..fde6e50c27 --- /dev/null +++ b/gazelle/python/testdata/disable_import_statements_validation/__init__.py @@ -0,0 +1,17 @@ +# 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 abcdefg + +_ = abcdefg diff --git a/gazelle/python/testdata/disable_import_statements_validation/test.yaml b/gazelle/python/testdata/disable_import_statements_validation/test.yaml new file mode 100644 index 0000000000..2410223e59 --- /dev/null +++ b/gazelle/python/testdata/disable_import_statements_validation/test.yaml @@ -0,0 +1,17 @@ +# 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. + +--- +expect: + exit_code: 0 diff --git a/gazelle/testdata/dont_rename_target/BUILD.in b/gazelle/python/testdata/dont_rename_target/BUILD.in similarity index 100% rename from gazelle/testdata/dont_rename_target/BUILD.in rename to gazelle/python/testdata/dont_rename_target/BUILD.in diff --git a/gazelle/testdata/dont_rename_target/BUILD.out b/gazelle/python/testdata/dont_rename_target/BUILD.out similarity index 100% rename from gazelle/testdata/dont_rename_target/BUILD.out rename to gazelle/python/testdata/dont_rename_target/BUILD.out diff --git a/gazelle/testdata/dont_rename_target/README.md b/gazelle/python/testdata/dont_rename_target/README.md similarity index 100% rename from gazelle/testdata/dont_rename_target/README.md rename to gazelle/python/testdata/dont_rename_target/README.md diff --git a/gazelle/testdata/dont_rename_target/WORKSPACE b/gazelle/python/testdata/dont_rename_target/WORKSPACE similarity index 100% rename from gazelle/testdata/dont_rename_target/WORKSPACE rename to gazelle/python/testdata/dont_rename_target/WORKSPACE diff --git a/gazelle/python/testdata/dont_rename_target/__init__.py b/gazelle/python/testdata/dont_rename_target/__init__.py new file mode 100644 index 0000000000..bbdfb4c588 --- /dev/null +++ b/gazelle/python/testdata/dont_rename_target/__init__.py @@ -0,0 +1,14 @@ +# 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. + diff --git a/gazelle/python/testdata/dont_rename_target/test.yaml b/gazelle/python/testdata/dont_rename_target/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/dont_rename_target/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/first_party_file_and_directory_modules/one/BUILD.in b/gazelle/python/testdata/file_name_matches_import_statement/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/one/BUILD.in rename to gazelle/python/testdata/file_name_matches_import_statement/BUILD.in diff --git a/gazelle/testdata/file_name_matches_import_statement/BUILD.out b/gazelle/python/testdata/file_name_matches_import_statement/BUILD.out similarity index 78% rename from gazelle/testdata/file_name_matches_import_statement/BUILD.out rename to gazelle/python/testdata/file_name_matches_import_statement/BUILD.out index fd6c48559d..0216e4b2e3 100644 --- a/gazelle/testdata/file_name_matches_import_statement/BUILD.out +++ b/gazelle/python/testdata/file_name_matches_import_statement/BUILD.out @@ -7,5 +7,5 @@ py_library( "rest_framework.py", ], visibility = ["//:__subpackages__"], - deps = ["@gazelle_python_test//pypi__djangorestframework"], + deps = ["@gazelle_python_test_djangorestframework//:pkg"], ) diff --git a/gazelle/testdata/file_name_matches_import_statement/README.md b/gazelle/python/testdata/file_name_matches_import_statement/README.md similarity index 100% rename from gazelle/testdata/file_name_matches_import_statement/README.md rename to gazelle/python/testdata/file_name_matches_import_statement/README.md diff --git a/gazelle/testdata/file_name_matches_import_statement/WORKSPACE b/gazelle/python/testdata/file_name_matches_import_statement/WORKSPACE similarity index 100% rename from gazelle/testdata/file_name_matches_import_statement/WORKSPACE rename to gazelle/python/testdata/file_name_matches_import_statement/WORKSPACE diff --git a/gazelle/python/testdata/file_name_matches_import_statement/__init__.py b/gazelle/python/testdata/file_name_matches_import_statement/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/file_name_matches_import_statement/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/file_name_matches_import_statement/gazelle_python.yaml b/gazelle/python/testdata/file_name_matches_import_statement/gazelle_python.yaml new file mode 100644 index 0000000000..f50d3ae397 --- /dev/null +++ b/gazelle/python/testdata/file_name_matches_import_statement/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + rest_framework: djangorestframework + pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/python/testdata/file_name_matches_import_statement/rest_framework.py b/gazelle/python/testdata/file_name_matches_import_statement/rest_framework.py new file mode 100644 index 0000000000..43098d29e2 --- /dev/null +++ b/gazelle/python/testdata/file_name_matches_import_statement/rest_framework.py @@ -0,0 +1,17 @@ +# 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 rest_framework + +_ = rest_framework diff --git a/gazelle/python/testdata/file_name_matches_import_statement/test.yaml b/gazelle/python/testdata/file_name_matches_import_statement/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/file_name_matches_import_statement/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/ignored_invalid_imported_module/BUILD.in b/gazelle/python/testdata/first_party_dependencies/BUILD.in similarity index 100% rename from gazelle/testdata/ignored_invalid_imported_module/BUILD.in rename to gazelle/python/testdata/first_party_dependencies/BUILD.in diff --git a/gazelle/testdata/first_party_dependencies/BUILD.out b/gazelle/python/testdata/first_party_dependencies/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_dependencies/BUILD.out rename to gazelle/python/testdata/first_party_dependencies/BUILD.out diff --git a/gazelle/testdata/first_party_dependencies/README.md b/gazelle/python/testdata/first_party_dependencies/README.md similarity index 100% rename from gazelle/testdata/first_party_dependencies/README.md rename to gazelle/python/testdata/first_party_dependencies/README.md diff --git a/gazelle/testdata/first_party_dependencies/WORKSPACE b/gazelle/python/testdata/first_party_dependencies/WORKSPACE similarity index 100% rename from gazelle/testdata/first_party_dependencies/WORKSPACE rename to gazelle/python/testdata/first_party_dependencies/WORKSPACE diff --git a/gazelle/testdata/first_party_dependencies/one/BUILD.in b/gazelle/python/testdata/first_party_dependencies/one/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_dependencies/one/BUILD.in rename to gazelle/python/testdata/first_party_dependencies/one/BUILD.in diff --git a/gazelle/testdata/first_party_dependencies/one/BUILD.out b/gazelle/python/testdata/first_party_dependencies/one/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_dependencies/one/BUILD.out rename to gazelle/python/testdata/first_party_dependencies/one/BUILD.out diff --git a/gazelle/python/testdata/first_party_dependencies/one/__main__.py b/gazelle/python/testdata/first_party_dependencies/one/__main__.py new file mode 100644 index 0000000000..efc7900d53 --- /dev/null +++ b/gazelle/python/testdata/first_party_dependencies/one/__main__.py @@ -0,0 +1,26 @@ +# 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 + +from bar import bar +from bar.baz import baz +from foo import foo + +if __name__ == "__main__": + INIT_FILENAME = "__init__.py" + dirname = os.path.dirname(os.path.abspath(__file__)) + assert bar() == os.path.join(dirname, "bar", INIT_FILENAME) + assert baz() == os.path.join(dirname, "bar", "baz", INIT_FILENAME) + assert foo() == os.path.join(dirname, "foo", INIT_FILENAME) diff --git a/gazelle/testdata/first_party_dependencies/one/bar/BUILD.in b/gazelle/python/testdata/first_party_dependencies/one/bar/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_dependencies/one/bar/BUILD.in rename to gazelle/python/testdata/first_party_dependencies/one/bar/BUILD.in diff --git a/gazelle/testdata/first_party_dependencies/one/bar/BUILD.out b/gazelle/python/testdata/first_party_dependencies/one/bar/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_dependencies/one/bar/BUILD.out rename to gazelle/python/testdata/first_party_dependencies/one/bar/BUILD.out diff --git a/gazelle/python/testdata/first_party_dependencies/one/bar/__init__.py b/gazelle/python/testdata/first_party_dependencies/one/bar/__init__.py new file mode 100644 index 0000000000..d4b5fb84f1 --- /dev/null +++ b/gazelle/python/testdata/first_party_dependencies/one/bar/__init__.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. + +import os + + +def bar(): + return os.path.abspath(__file__) diff --git a/gazelle/testdata/first_party_dependencies/one/bar/baz/BUILD.in b/gazelle/python/testdata/first_party_dependencies/one/bar/baz/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_dependencies/one/bar/baz/BUILD.in rename to gazelle/python/testdata/first_party_dependencies/one/bar/baz/BUILD.in diff --git a/gazelle/testdata/first_party_dependencies/one/bar/baz/BUILD.out b/gazelle/python/testdata/first_party_dependencies/one/bar/baz/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_dependencies/one/bar/baz/BUILD.out rename to gazelle/python/testdata/first_party_dependencies/one/bar/baz/BUILD.out diff --git a/gazelle/python/testdata/first_party_dependencies/one/bar/baz/__init__.py b/gazelle/python/testdata/first_party_dependencies/one/bar/baz/__init__.py new file mode 100644 index 0000000000..5be74a7d3e --- /dev/null +++ b/gazelle/python/testdata/first_party_dependencies/one/bar/baz/__init__.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. + +import os + + +def baz(): + return os.path.abspath(__file__) diff --git a/gazelle/testdata/first_party_dependencies/one/foo/BUILD.in b/gazelle/python/testdata/first_party_dependencies/one/foo/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_dependencies/one/foo/BUILD.in rename to gazelle/python/testdata/first_party_dependencies/one/foo/BUILD.in diff --git a/gazelle/testdata/first_party_dependencies/one/foo/BUILD.out b/gazelle/python/testdata/first_party_dependencies/one/foo/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_dependencies/one/foo/BUILD.out rename to gazelle/python/testdata/first_party_dependencies/one/foo/BUILD.out diff --git a/gazelle/python/testdata/first_party_dependencies/one/foo/__init__.py b/gazelle/python/testdata/first_party_dependencies/one/foo/__init__.py new file mode 100644 index 0000000000..978fb74567 --- /dev/null +++ b/gazelle/python/testdata/first_party_dependencies/one/foo/__init__.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. + +import os + + +def foo(): + return os.path.abspath(__file__) diff --git a/gazelle/python/testdata/first_party_dependencies/test.yaml b/gazelle/python/testdata/first_party_dependencies/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/first_party_dependencies/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/first_party_dependencies/three/BUILD.in b/gazelle/python/testdata/first_party_dependencies/three/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_dependencies/three/BUILD.in rename to gazelle/python/testdata/first_party_dependencies/three/BUILD.in diff --git a/gazelle/testdata/first_party_dependencies/three/BUILD.out b/gazelle/python/testdata/first_party_dependencies/three/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_dependencies/three/BUILD.out rename to gazelle/python/testdata/first_party_dependencies/three/BUILD.out diff --git a/gazelle/python/testdata/first_party_dependencies/three/__init__.py b/gazelle/python/testdata/first_party_dependencies/three/__init__.py new file mode 100644 index 0000000000..9f7d123649 --- /dev/null +++ b/gazelle/python/testdata/first_party_dependencies/three/__init__.py @@ -0,0 +1,24 @@ +# 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 + +from bar import bar +from bar.baz import baz +from foo import foo + +_ = os +_ = bar +_ = baz +_ = foo diff --git a/gazelle/testdata/first_party_dependencies/two/BUILD.in b/gazelle/python/testdata/first_party_dependencies/two/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_dependencies/two/BUILD.in rename to gazelle/python/testdata/first_party_dependencies/two/BUILD.in diff --git a/gazelle/testdata/first_party_dependencies/two/BUILD.out b/gazelle/python/testdata/first_party_dependencies/two/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_dependencies/two/BUILD.out rename to gazelle/python/testdata/first_party_dependencies/two/BUILD.out diff --git a/gazelle/python/testdata/first_party_dependencies/two/__init__.py b/gazelle/python/testdata/first_party_dependencies/two/__init__.py new file mode 100644 index 0000000000..88ff57bf1b --- /dev/null +++ b/gazelle/python/testdata/first_party_dependencies/two/__init__.py @@ -0,0 +1,20 @@ +# 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 + +from foo import foo + +_ = os +_ = foo diff --git a/gazelle/testdata/first_party_file_and_directory_modules/BUILD.in b/gazelle/python/testdata/first_party_file_and_directory_modules/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/BUILD.in rename to gazelle/python/testdata/first_party_file_and_directory_modules/BUILD.in diff --git a/gazelle/testdata/first_party_file_and_directory_modules/BUILD.out b/gazelle/python/testdata/first_party_file_and_directory_modules/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/BUILD.out rename to gazelle/python/testdata/first_party_file_and_directory_modules/BUILD.out diff --git a/gazelle/testdata/first_party_file_and_directory_modules/README.md b/gazelle/python/testdata/first_party_file_and_directory_modules/README.md similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/README.md rename to gazelle/python/testdata/first_party_file_and_directory_modules/README.md diff --git a/gazelle/testdata/first_party_file_and_directory_modules/WORKSPACE b/gazelle/python/testdata/first_party_file_and_directory_modules/WORKSPACE similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/WORKSPACE rename to gazelle/python/testdata/first_party_file_and_directory_modules/WORKSPACE diff --git a/gazelle/python/testdata/first_party_file_and_directory_modules/__main__.py b/gazelle/python/testdata/first_party_file_and_directory_modules/__main__.py new file mode 100644 index 0000000000..242448d348 --- /dev/null +++ b/gazelle/python/testdata/first_party_file_and_directory_modules/__main__.py @@ -0,0 +1,25 @@ +# 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 foo +from baz import baz as another_baz +from foo.bar import baz +from one.two import two +from package1.subpackage1.module1 import find_me + +assert not hasattr(foo, "foo") +assert baz() == "baz from foo/bar.py" +assert another_baz() == "baz from baz.py" +assert two() == "two" +assert find_me() == "found" diff --git a/gazelle/python/testdata/first_party_file_and_directory_modules/baz.py b/gazelle/python/testdata/first_party_file_and_directory_modules/baz.py new file mode 100644 index 0000000000..e03a9ecb9d --- /dev/null +++ b/gazelle/python/testdata/first_party_file_and_directory_modules/baz.py @@ -0,0 +1,16 @@ +# 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. + +def baz(): + return "baz from baz.py" diff --git a/gazelle/python/testdata/first_party_file_and_directory_modules/foo.py b/gazelle/python/testdata/first_party_file_and_directory_modules/foo.py new file mode 100644 index 0000000000..04474d83a8 --- /dev/null +++ b/gazelle/python/testdata/first_party_file_and_directory_modules/foo.py @@ -0,0 +1,16 @@ +# 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. + +def foo(): + print("foo") diff --git a/gazelle/testdata/invalid_imported_module/BUILD.in b/gazelle/python/testdata/first_party_file_and_directory_modules/foo/BUILD.in similarity index 100% rename from gazelle/testdata/invalid_imported_module/BUILD.in rename to gazelle/python/testdata/first_party_file_and_directory_modules/foo/BUILD.in diff --git a/gazelle/testdata/first_party_file_and_directory_modules/foo/BUILD.out b/gazelle/python/testdata/first_party_file_and_directory_modules/foo/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/foo/BUILD.out rename to gazelle/python/testdata/first_party_file_and_directory_modules/foo/BUILD.out diff --git a/gazelle/python/testdata/first_party_file_and_directory_modules/foo/__init__.py b/gazelle/python/testdata/first_party_file_and_directory_modules/foo/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/first_party_file_and_directory_modules/foo/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/first_party_file_and_directory_modules/foo/bar.py b/gazelle/python/testdata/first_party_file_and_directory_modules/foo/bar.py new file mode 100644 index 0000000000..dacf2d42b2 --- /dev/null +++ b/gazelle/python/testdata/first_party_file_and_directory_modules/foo/bar.py @@ -0,0 +1,21 @@ +# 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 one.two as two + +_ = two + + +def baz(): + return "baz from foo/bar.py" diff --git a/gazelle/testdata/monorepo/wont_generate/BUILD.in b/gazelle/python/testdata/first_party_file_and_directory_modules/one/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/wont_generate/BUILD.in rename to gazelle/python/testdata/first_party_file_and_directory_modules/one/BUILD.in diff --git a/gazelle/testdata/first_party_file_and_directory_modules/one/BUILD.out b/gazelle/python/testdata/first_party_file_and_directory_modules/one/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/one/BUILD.out rename to gazelle/python/testdata/first_party_file_and_directory_modules/one/BUILD.out diff --git a/gazelle/python/testdata/first_party_file_and_directory_modules/one/__init__.py b/gazelle/python/testdata/first_party_file_and_directory_modules/one/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/first_party_file_and_directory_modules/one/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/first_party_file_and_directory_modules/one/two.py b/gazelle/python/testdata/first_party_file_and_directory_modules/one/two.py new file mode 100644 index 0000000000..94cca3d002 --- /dev/null +++ b/gazelle/python/testdata/first_party_file_and_directory_modules/one/two.py @@ -0,0 +1,16 @@ +# 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. + +def two(): + return "two" diff --git a/gazelle/python/testdata/first_party_file_and_directory_modules/test.yaml b/gazelle/python/testdata/first_party_file_and_directory_modules/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/first_party_file_and_directory_modules/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/BUILD.in b/gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/BUILD.in rename to gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/BUILD.in diff --git a/gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/BUILD.out b/gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/BUILD.out rename to gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/BUILD.out diff --git a/gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/BUILD.in b/gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/BUILD.in similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/BUILD.in rename to gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/BUILD.in diff --git a/gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/BUILD.out b/gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/BUILD.out similarity index 100% rename from gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/BUILD.out rename to gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/BUILD.out diff --git a/gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/__init__.py b/gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/module1.py b/gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/module1.py new file mode 100644 index 0000000000..76c72273fa --- /dev/null +++ b/gazelle/python/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/module1.py @@ -0,0 +1,16 @@ +# 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. + +def find_me(): + return "found" diff --git a/gazelle/python/testdata/from_imports/BUILD.in b/gazelle/python/testdata/from_imports/BUILD.in new file mode 100644 index 0000000000..93f2259140 --- /dev/null +++ b/gazelle/python/testdata/from_imports/BUILD.in @@ -0,0 +1 @@ +# gazelle:python_extension enabled diff --git a/gazelle/python/testdata/from_imports/BUILD.out b/gazelle/python/testdata/from_imports/BUILD.out new file mode 100644 index 0000000000..93f2259140 --- /dev/null +++ b/gazelle/python/testdata/from_imports/BUILD.out @@ -0,0 +1 @@ +# gazelle:python_extension enabled diff --git a/gazelle/python/testdata/from_imports/README.md b/gazelle/python/testdata/from_imports/README.md new file mode 100644 index 0000000000..161dd18e33 --- /dev/null +++ b/gazelle/python/testdata/from_imports/README.md @@ -0,0 +1,7 @@ +# From Imports + +This test case simulates imports of the form: + +```python +from foo import bar +``` diff --git a/gazelle/testdata/monorepo/WORKSPACE b/gazelle/python/testdata/from_imports/WORKSPACE similarity index 100% rename from gazelle/testdata/monorepo/WORKSPACE rename to gazelle/python/testdata/from_imports/WORKSPACE diff --git a/gazelle/python/testdata/from_imports/foo/BUILD.in b/gazelle/python/testdata/from_imports/foo/BUILD.in new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/gazelle/python/testdata/from_imports/foo/BUILD.in @@ -0,0 +1 @@ + diff --git a/gazelle/testdata/subdir_sources/foo/has_build_bazel/BUILD.bazel.out b/gazelle/python/testdata/from_imports/foo/BUILD.out similarity index 54% rename from gazelle/testdata/subdir_sources/foo/has_build_bazel/BUILD.bazel.out rename to gazelle/python/testdata/from_imports/foo/BUILD.out index 79bd70a258..4404d30461 100644 --- a/gazelle/testdata/subdir_sources/foo/has_build_bazel/BUILD.bazel.out +++ b/gazelle/python/testdata/from_imports/foo/BUILD.out @@ -1,8 +1,8 @@ load("@rules_python//python:defs.bzl", "py_library") py_library( - name = "has_build_bazel", - srcs = ["python/my_module.py"], - imports = ["../.."], + name = "foo", + srcs = ["__init__.py"], + imports = [".."], visibility = ["//:__subpackages__"], ) diff --git a/gazelle/python/testdata/from_imports/foo/__init__.py b/gazelle/python/testdata/from_imports/foo/__init__.py new file mode 100644 index 0000000000..d0f74a859a --- /dev/null +++ b/gazelle/python/testdata/from_imports/foo/__init__.py @@ -0,0 +1,15 @@ +# 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. + +foo = "foo" diff --git a/gazelle/python/testdata/from_imports/foo/bar/BUILD.in b/gazelle/python/testdata/from_imports/foo/bar/BUILD.in new file mode 100644 index 0000000000..fbbec0284b --- /dev/null +++ b/gazelle/python/testdata/from_imports/foo/bar/BUILD.in @@ -0,0 +1,21 @@ +load("@rules_python//python:defs.bzl", "py_library") + +# gazelle:python_ignore_files baz.py + +py_library( + name = "baz", + srcs = [ + "baz.py", + ], + imports = ["../.."], + visibility = ["//:__subpackages__"], +) + +py_library( + name = "bar", + srcs = [ + "__init__.py", + ], + imports = ["../.."], + visibility = ["//:__subpackages__"], +) \ No newline at end of file diff --git a/gazelle/python/testdata/from_imports/foo/bar/BUILD.out b/gazelle/python/testdata/from_imports/foo/bar/BUILD.out new file mode 100644 index 0000000000..fbbec0284b --- /dev/null +++ b/gazelle/python/testdata/from_imports/foo/bar/BUILD.out @@ -0,0 +1,21 @@ +load("@rules_python//python:defs.bzl", "py_library") + +# gazelle:python_ignore_files baz.py + +py_library( + name = "baz", + srcs = [ + "baz.py", + ], + imports = ["../.."], + visibility = ["//:__subpackages__"], +) + +py_library( + name = "bar", + srcs = [ + "__init__.py", + ], + imports = ["../.."], + visibility = ["//:__subpackages__"], +) \ No newline at end of file diff --git a/gazelle/python/testdata/from_imports/foo/bar/__init__.py b/gazelle/python/testdata/from_imports/foo/bar/__init__.py new file mode 100644 index 0000000000..240f382ac6 --- /dev/null +++ b/gazelle/python/testdata/from_imports/foo/bar/__init__.py @@ -0,0 +1,15 @@ +# 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. + +bar = "bar" diff --git a/gazelle/python/testdata/from_imports/foo/bar/baz.py b/gazelle/python/testdata/from_imports/foo/bar/baz.py new file mode 100644 index 0000000000..9aeae611db --- /dev/null +++ b/gazelle/python/testdata/from_imports/foo/bar/baz.py @@ -0,0 +1,15 @@ +# 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. + +baz = "baz" diff --git a/gazelle/python/testdata/from_imports/gazelle_python.yaml b/gazelle/python/testdata/from_imports/gazelle_python.yaml new file mode 100644 index 0000000000..132854e842 --- /dev/null +++ b/gazelle/python/testdata/from_imports/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + boto3: rootboto3 + boto4: rootboto4 + pip_deps_repository_name: root_pip_deps diff --git a/gazelle/testdata/monorepo/wont_generate/bar/BUILD.in b/gazelle/python/testdata/from_imports/import_from_init_py/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/wont_generate/bar/BUILD.in rename to gazelle/python/testdata/from_imports/import_from_init_py/BUILD.in diff --git a/gazelle/python/testdata/from_imports/import_from_init_py/BUILD.out b/gazelle/python/testdata/from_imports/import_from_init_py/BUILD.out new file mode 100644 index 0000000000..99b48610c2 --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_from_init_py/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_python//python:defs.bzl", "py_library") + +py_library( + name = "import_from_init_py", + srcs = ["__init__.py"], + imports = [".."], + visibility = ["//:__subpackages__"], + deps = ["//foo/bar"], +) \ No newline at end of file diff --git a/gazelle/python/testdata/from_imports/import_from_init_py/__init__.py b/gazelle/python/testdata/from_imports/import_from_init_py/__init__.py new file mode 100644 index 0000000000..bd6d8a550f --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_from_init_py/__init__.py @@ -0,0 +1,16 @@ +# 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. + +# bar is a variable inside foo/bar/__init__.py +from foo.bar import bar diff --git a/gazelle/testdata/monorepo/wont_generate/bar/baz/BUILD.in b/gazelle/python/testdata/from_imports/import_from_multiple/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/wont_generate/bar/baz/BUILD.in rename to gazelle/python/testdata/from_imports/import_from_multiple/BUILD.in diff --git a/gazelle/python/testdata/from_imports/import_from_multiple/BUILD.out b/gazelle/python/testdata/from_imports/import_from_multiple/BUILD.out new file mode 100644 index 0000000000..d8219bb4d1 --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_from_multiple/BUILD.out @@ -0,0 +1,12 @@ +load("@rules_python//python:defs.bzl", "py_library") + +py_library( + name = "import_from_multiple", + srcs = ["__init__.py"], + imports = [".."], + visibility = ["//:__subpackages__"], + deps = [ + "//foo/bar", + "//foo/bar:baz", + ], +) \ No newline at end of file diff --git a/gazelle/python/testdata/from_imports/import_from_multiple/__init__.py b/gazelle/python/testdata/from_imports/import_from_multiple/__init__.py new file mode 100644 index 0000000000..05cd10460a --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_from_multiple/__init__.py @@ -0,0 +1,16 @@ +# 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 multiple values from the same import. +from foo.bar import bar, baz diff --git a/gazelle/testdata/monorepo/wont_generate/foo/BUILD.in b/gazelle/python/testdata/from_imports/import_nested_file/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/wont_generate/foo/BUILD.in rename to gazelle/python/testdata/from_imports/import_nested_file/BUILD.in diff --git a/gazelle/python/testdata/from_imports/import_nested_file/BUILD.out b/gazelle/python/testdata/from_imports/import_nested_file/BUILD.out new file mode 100644 index 0000000000..662da9c9a0 --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_nested_file/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_python//python:defs.bzl", "py_library") + +py_library( + name = "import_nested_file", + srcs = ["__init__.py"], + imports = [".."], + visibility = ["//:__subpackages__"], + deps = ["//foo/bar:baz"], +) \ No newline at end of file diff --git a/gazelle/python/testdata/from_imports/import_nested_file/__init__.py b/gazelle/python/testdata/from_imports/import_nested_file/__init__.py new file mode 100644 index 0000000000..55a1621628 --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_nested_file/__init__.py @@ -0,0 +1,16 @@ +# 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. + +# baz.py is a file at foo/bar/baz.py +from foo.bar import baz diff --git a/gazelle/testdata/python_ignore_files_directive/bar/BUILD.in b/gazelle/python/testdata/from_imports/import_nested_module/BUILD.in similarity index 100% rename from gazelle/testdata/python_ignore_files_directive/bar/BUILD.in rename to gazelle/python/testdata/from_imports/import_nested_module/BUILD.in diff --git a/gazelle/python/testdata/from_imports/import_nested_module/BUILD.out b/gazelle/python/testdata/from_imports/import_nested_module/BUILD.out new file mode 100644 index 0000000000..ec6da507dd --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_nested_module/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_python//python:defs.bzl", "py_library") + +py_library( + name = "import_nested_module", + srcs = ["__init__.py"], + imports = [".."], + visibility = ["//:__subpackages__"], + deps = ["//foo/bar"], +) \ No newline at end of file diff --git a/gazelle/python/testdata/from_imports/import_nested_module/__init__.py b/gazelle/python/testdata/from_imports/import_nested_module/__init__.py new file mode 100644 index 0000000000..96fa0e5ecb --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_nested_module/__init__.py @@ -0,0 +1,16 @@ +# 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. + +# bar is a module at foo/bar/__init__.py +from foo import bar diff --git a/gazelle/testdata/python_target_with_test_in_name/BUILD.in b/gazelle/python/testdata/from_imports/import_nested_var/BUILD.in similarity index 100% rename from gazelle/testdata/python_target_with_test_in_name/BUILD.in rename to gazelle/python/testdata/from_imports/import_nested_var/BUILD.in diff --git a/gazelle/python/testdata/from_imports/import_nested_var/BUILD.out b/gazelle/python/testdata/from_imports/import_nested_var/BUILD.out new file mode 100644 index 0000000000..8ee527e17a --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_nested_var/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_python//python:defs.bzl", "py_library") + +py_library( + name = "import_nested_var", + srcs = ["__init__.py"], + imports = [".."], + visibility = ["//:__subpackages__"], + deps = ["//foo/bar:baz"], +) \ No newline at end of file diff --git a/gazelle/python/testdata/from_imports/import_nested_var/__init__.py b/gazelle/python/testdata/from_imports/import_nested_var/__init__.py new file mode 100644 index 0000000000..d0f51c443c --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_nested_var/__init__.py @@ -0,0 +1,16 @@ +# 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. + +# baz is a variable in foo/bar/baz.py +from foo.bar.baz import baz diff --git a/gazelle/testdata/relative_imports/BUILD.in b/gazelle/python/testdata/from_imports/import_top_level_var/BUILD.in similarity index 100% rename from gazelle/testdata/relative_imports/BUILD.in rename to gazelle/python/testdata/from_imports/import_top_level_var/BUILD.in diff --git a/gazelle/python/testdata/from_imports/import_top_level_var/BUILD.out b/gazelle/python/testdata/from_imports/import_top_level_var/BUILD.out new file mode 100644 index 0000000000..6b584d713b --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_top_level_var/BUILD.out @@ -0,0 +1,9 @@ +load("@rules_python//python:defs.bzl", "py_library") + +py_library( + name = "import_top_level_var", + srcs = ["__init__.py"], + imports = [".."], + visibility = ["//:__subpackages__"], + deps = ["//foo"], +) \ No newline at end of file diff --git a/gazelle/python/testdata/from_imports/import_top_level_var/__init__.py b/gazelle/python/testdata/from_imports/import_top_level_var/__init__.py new file mode 100644 index 0000000000..71dd7c482f --- /dev/null +++ b/gazelle/python/testdata/from_imports/import_top_level_var/__init__.py @@ -0,0 +1,16 @@ +# 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. + +# foo is a variable in foo/__init__.py +from foo import foo diff --git a/gazelle/testdata/relative_imports/package2/BUILD.in b/gazelle/python/testdata/from_imports/std_module/BUILD.in similarity index 100% rename from gazelle/testdata/relative_imports/package2/BUILD.in rename to gazelle/python/testdata/from_imports/std_module/BUILD.in diff --git a/gazelle/python/testdata/from_imports/std_module/BUILD.out b/gazelle/python/testdata/from_imports/std_module/BUILD.out new file mode 100644 index 0000000000..4903999afc --- /dev/null +++ b/gazelle/python/testdata/from_imports/std_module/BUILD.out @@ -0,0 +1,8 @@ +load("@rules_python//python:defs.bzl", "py_library") + +py_library( + name = "std_module", + srcs = ["__init__.py"], + imports = [".."], + visibility = ["//:__subpackages__"], +) \ No newline at end of file diff --git a/gazelle/python/testdata/from_imports/std_module/__init__.py b/gazelle/python/testdata/from_imports/std_module/__init__.py new file mode 100644 index 0000000000..5518cc0239 --- /dev/null +++ b/gazelle/python/testdata/from_imports/std_module/__init__.py @@ -0,0 +1,17 @@ +# 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. + +# Gazelle should recognize this from import +# as the standard module __future__. +from __future__ import print_function diff --git a/gazelle/python/testdata/from_imports/test.yaml b/gazelle/python/testdata/from_imports/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/from_imports/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/generated_test_entrypoint/BUILD.in b/gazelle/python/testdata/generated_test_entrypoint/BUILD.in similarity index 100% rename from gazelle/testdata/generated_test_entrypoint/BUILD.in rename to gazelle/python/testdata/generated_test_entrypoint/BUILD.in diff --git a/gazelle/testdata/generated_test_entrypoint/BUILD.out b/gazelle/python/testdata/generated_test_entrypoint/BUILD.out similarity index 82% rename from gazelle/testdata/generated_test_entrypoint/BUILD.out rename to gazelle/python/testdata/generated_test_entrypoint/BUILD.out index 48df0688a6..e8e304c72b 100644 --- a/gazelle/testdata/generated_test_entrypoint/BUILD.out +++ b/gazelle/python/testdata/generated_test_entrypoint/BUILD.out @@ -17,8 +17,5 @@ py_test( name = "generated_test_entrypoint_test", srcs = [":__test__"], main = ":__test__.py", - deps = [ - ":__test__", - ":generated_test_entrypoint", - ], + deps = [":__test__"], ) diff --git a/gazelle/testdata/generated_test_entrypoint/README.md b/gazelle/python/testdata/generated_test_entrypoint/README.md similarity index 100% rename from gazelle/testdata/generated_test_entrypoint/README.md rename to gazelle/python/testdata/generated_test_entrypoint/README.md diff --git a/gazelle/testdata/generated_test_entrypoint/WORKSPACE b/gazelle/python/testdata/generated_test_entrypoint/WORKSPACE similarity index 100% rename from gazelle/testdata/generated_test_entrypoint/WORKSPACE rename to gazelle/python/testdata/generated_test_entrypoint/WORKSPACE diff --git a/gazelle/python/testdata/generated_test_entrypoint/__init__.py b/gazelle/python/testdata/generated_test_entrypoint/__init__.py new file mode 100644 index 0000000000..b274b0d921 --- /dev/null +++ b/gazelle/python/testdata/generated_test_entrypoint/__init__.py @@ -0,0 +1,17 @@ +# 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 foo import foo + +_ = foo diff --git a/gazelle/python/testdata/generated_test_entrypoint/foo.py b/gazelle/python/testdata/generated_test_entrypoint/foo.py new file mode 100644 index 0000000000..932de45b74 --- /dev/null +++ b/gazelle/python/testdata/generated_test_entrypoint/foo.py @@ -0,0 +1,16 @@ +# 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. + +def foo(): + return "foo" diff --git a/gazelle/python/testdata/generated_test_entrypoint/test.yaml b/gazelle/python/testdata/generated_test_entrypoint/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/generated_test_entrypoint/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/simple_binary/BUILD.in b/gazelle/python/testdata/ignored_invalid_imported_module/BUILD.in similarity index 100% rename from gazelle/testdata/simple_binary/BUILD.in rename to gazelle/python/testdata/ignored_invalid_imported_module/BUILD.in diff --git a/gazelle/testdata/ignored_invalid_imported_module/BUILD.out b/gazelle/python/testdata/ignored_invalid_imported_module/BUILD.out similarity index 79% rename from gazelle/testdata/ignored_invalid_imported_module/BUILD.out rename to gazelle/python/testdata/ignored_invalid_imported_module/BUILD.out index 3cd47a6fe0..b8c936a7dd 100644 --- a/gazelle/testdata/ignored_invalid_imported_module/BUILD.out +++ b/gazelle/python/testdata/ignored_invalid_imported_module/BUILD.out @@ -4,5 +4,5 @@ py_library( name = "ignored_invalid_imported_module", srcs = ["__init__.py"], visibility = ["//:__subpackages__"], - deps = ["@gazelle_python_test//pypi__foo"], + deps = ["@gazelle_python_test_foo//:pkg"], ) diff --git a/gazelle/testdata/ignored_invalid_imported_module/README.md b/gazelle/python/testdata/ignored_invalid_imported_module/README.md similarity index 100% rename from gazelle/testdata/ignored_invalid_imported_module/README.md rename to gazelle/python/testdata/ignored_invalid_imported_module/README.md diff --git a/gazelle/testdata/ignored_invalid_imported_module/WORKSPACE b/gazelle/python/testdata/ignored_invalid_imported_module/WORKSPACE similarity index 100% rename from gazelle/testdata/ignored_invalid_imported_module/WORKSPACE rename to gazelle/python/testdata/ignored_invalid_imported_module/WORKSPACE diff --git a/gazelle/python/testdata/ignored_invalid_imported_module/__init__.py b/gazelle/python/testdata/ignored_invalid_imported_module/__init__.py new file mode 100644 index 0000000000..a094ed0332 --- /dev/null +++ b/gazelle/python/testdata/ignored_invalid_imported_module/__init__.py @@ -0,0 +1,36 @@ +# 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. + +# gazelle:ignore abcdefg1,abcdefg2 +# gazelle:ignore abcdefg3 + +import abcdefg1 +import abcdefg2 +import abcdefg3 +import foo + +_ = abcdefg1 +_ = abcdefg2 +_ = abcdefg3 +_ = foo + +try: + # gazelle:ignore grpc + import grpc + + grpc_available = True +except ImportError: + grpc_available = False + +_ = grpc diff --git a/gazelle/python/testdata/ignored_invalid_imported_module/gazelle_python.yaml b/gazelle/python/testdata/ignored_invalid_imported_module/gazelle_python.yaml new file mode 100644 index 0000000000..4b12372b4e --- /dev/null +++ b/gazelle/python/testdata/ignored_invalid_imported_module/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + foo: foo + pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/python/testdata/ignored_invalid_imported_module/test.yaml b/gazelle/python/testdata/ignored_invalid_imported_module/test.yaml new file mode 100644 index 0000000000..2410223e59 --- /dev/null +++ b/gazelle/python/testdata/ignored_invalid_imported_module/test.yaml @@ -0,0 +1,17 @@ +# 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. + +--- +expect: + exit_code: 0 diff --git a/gazelle/testdata/simple_library/BUILD.in b/gazelle/python/testdata/invalid_annotation/BUILD.in similarity index 100% rename from gazelle/testdata/simple_library/BUILD.in rename to gazelle/python/testdata/invalid_annotation/BUILD.in diff --git a/gazelle/testdata/invalid_imported_module/BUILD.out b/gazelle/python/testdata/invalid_annotation/BUILD.out similarity index 100% rename from gazelle/testdata/invalid_imported_module/BUILD.out rename to gazelle/python/testdata/invalid_annotation/BUILD.out diff --git a/gazelle/python/testdata/invalid_annotation/README.md b/gazelle/python/testdata/invalid_annotation/README.md new file mode 100644 index 0000000000..b2544b5bda --- /dev/null +++ b/gazelle/python/testdata/invalid_annotation/README.md @@ -0,0 +1,2 @@ +# Invalid annotation +This test case asserts that the parse step fails as expected due to invalid annotation format. diff --git a/gazelle/testdata/invalid_imported_module/WORKSPACE b/gazelle/python/testdata/invalid_annotation/WORKSPACE similarity index 100% rename from gazelle/testdata/invalid_imported_module/WORKSPACE rename to gazelle/python/testdata/invalid_annotation/WORKSPACE diff --git a/gazelle/python/testdata/invalid_annotation/__init__.py b/gazelle/python/testdata/invalid_annotation/__init__.py new file mode 100644 index 0000000000..7aee8768ad --- /dev/null +++ b/gazelle/python/testdata/invalid_annotation/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# gazelle:ignore diff --git a/gazelle/python/testdata/invalid_annotation/test.yaml b/gazelle/python/testdata/invalid_annotation/test.yaml new file mode 100644 index 0000000000..19924b1288 --- /dev/null +++ b/gazelle/python/testdata/invalid_annotation/test.yaml @@ -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. + +--- +expect: + exit_code: 1 + stderr: | + gazelle: ERROR: failed to parse annotations: `# gazelle:ignore` requires a value diff --git a/gazelle/testdata/simple_library_without_init/BUILD.in b/gazelle/python/testdata/invalid_imported_module/BUILD.in similarity index 100% rename from gazelle/testdata/simple_library_without_init/BUILD.in rename to gazelle/python/testdata/invalid_imported_module/BUILD.in diff --git a/gazelle/testdata/monorepo/wont_generate/BUILD.out b/gazelle/python/testdata/invalid_imported_module/BUILD.out similarity index 100% rename from gazelle/testdata/monorepo/wont_generate/BUILD.out rename to gazelle/python/testdata/invalid_imported_module/BUILD.out diff --git a/gazelle/testdata/invalid_imported_module/README.md b/gazelle/python/testdata/invalid_imported_module/README.md similarity index 100% rename from gazelle/testdata/invalid_imported_module/README.md rename to gazelle/python/testdata/invalid_imported_module/README.md diff --git a/gazelle/testdata/naming_convention/WORKSPACE b/gazelle/python/testdata/invalid_imported_module/WORKSPACE similarity index 100% rename from gazelle/testdata/naming_convention/WORKSPACE rename to gazelle/python/testdata/invalid_imported_module/WORKSPACE diff --git a/gazelle/python/testdata/invalid_imported_module/__init__.py b/gazelle/python/testdata/invalid_imported_module/__init__.py new file mode 100644 index 0000000000..dc6fb8519e --- /dev/null +++ b/gazelle/python/testdata/invalid_imported_module/__init__.py @@ -0,0 +1,22 @@ +# 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. + +try: + import grpc + + grpc_available = True +except ImportError: + grpc_available = False + +_ = grpc diff --git a/gazelle/python/testdata/invalid_imported_module/test.yaml b/gazelle/python/testdata/invalid_imported_module/test.yaml new file mode 100644 index 0000000000..6bcea39d2e --- /dev/null +++ b/gazelle/python/testdata/invalid_imported_module/test.yaml @@ -0,0 +1,22 @@ +# 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. + +--- +expect: + exit_code: 1 + stderr: | + gazelle: ERROR: failed to validate dependencies for target "//:invalid_imported_module": "grpc" at line 16 from "__init__.py" is an invalid dependency: possible solutions: + 1. Add it as a dependency in the requirements.txt file. + 2. Instruct Gazelle to resolve to a known dependency using the gazelle:resolve directive. + 3. Ignore it with a comment '# gazelle:ignore grpc' in the Python file. diff --git a/gazelle/testdata/monorepo/BUILD.in b/gazelle/python/testdata/monorepo/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/BUILD.in rename to gazelle/python/testdata/monorepo/BUILD.in diff --git a/gazelle/testdata/monorepo/BUILD.out b/gazelle/python/testdata/monorepo/BUILD.out similarity index 100% rename from gazelle/testdata/monorepo/BUILD.out rename to gazelle/python/testdata/monorepo/BUILD.out diff --git a/gazelle/testdata/monorepo/README.md b/gazelle/python/testdata/monorepo/README.md similarity index 100% rename from gazelle/testdata/monorepo/README.md rename to gazelle/python/testdata/monorepo/README.md diff --git a/gazelle/testdata/relative_imports/WORKSPACE b/gazelle/python/testdata/monorepo/WORKSPACE similarity index 100% rename from gazelle/testdata/relative_imports/WORKSPACE rename to gazelle/python/testdata/monorepo/WORKSPACE diff --git a/gazelle/python/testdata/monorepo/a/BUILD.in b/gazelle/python/testdata/monorepo/a/BUILD.in new file mode 100644 index 0000000000..265129ea56 --- /dev/null +++ b/gazelle/python/testdata/monorepo/a/BUILD.in @@ -0,0 +1 @@ +# gazelle:exclude bar/baz/hue.py \ No newline at end of file diff --git a/gazelle/python/testdata/monorepo/a/BUILD.out b/gazelle/python/testdata/monorepo/a/BUILD.out new file mode 100644 index 0000000000..265129ea56 --- /dev/null +++ b/gazelle/python/testdata/monorepo/a/BUILD.out @@ -0,0 +1 @@ +# gazelle:exclude bar/baz/hue.py \ No newline at end of file diff --git a/gazelle/python/testdata/monorepo/a/README.md b/gazelle/python/testdata/monorepo/a/README.md new file mode 100644 index 0000000000..84d3bff052 --- /dev/null +++ b/gazelle/python/testdata/monorepo/a/README.md @@ -0,0 +1,3 @@ +# Exclusions +* Intentionally make the directory "a" so Gazelle visit this before "coarse_grained" +* Making sure that the exclusion here doesn't affect coarse_grained/bar/baz/hue.py \ No newline at end of file diff --git a/gazelle/testdata/monorepo/coarse_grained/BUILD.in b/gazelle/python/testdata/monorepo/coarse_grained/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/coarse_grained/BUILD.in rename to gazelle/python/testdata/monorepo/coarse_grained/BUILD.in diff --git a/gazelle/testdata/monorepo/coarse_grained/BUILD.out b/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out similarity index 90% rename from gazelle/testdata/monorepo/coarse_grained/BUILD.out rename to gazelle/python/testdata/monorepo/coarse_grained/BUILD.out index 0fba9515a1..b11cbbdaad 100644 --- a/gazelle/testdata/monorepo/coarse_grained/BUILD.out +++ b/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out @@ -16,5 +16,5 @@ py_library( "foo/__init__.py", ], visibility = ["//:__subpackages__"], - deps = ["@root_pip_deps//pypi__rootboto3"], + deps = ["@root_pip_deps_rootboto3//:pkg"], ) diff --git a/gazelle/python/testdata/monorepo/coarse_grained/__init__.py b/gazelle/python/testdata/monorepo/coarse_grained/__init__.py new file mode 100644 index 0000000000..6e77327a42 --- /dev/null +++ b/gazelle/python/testdata/monorepo/coarse_grained/__init__.py @@ -0,0 +1,26 @@ +# 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 boto3 +from bar import bar +from bar.baz import baz +from foo import foo + +_ = os +_ = boto3 +_ = bar +_ = baz +_ = foo diff --git a/gazelle/testdata/monorepo/coarse_grained/_boundary/BUILD.in b/gazelle/python/testdata/monorepo/coarse_grained/_boundary/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/coarse_grained/_boundary/BUILD.in rename to gazelle/python/testdata/monorepo/coarse_grained/_boundary/BUILD.in diff --git a/gazelle/testdata/monorepo/coarse_grained/_boundary/BUILD.out b/gazelle/python/testdata/monorepo/coarse_grained/_boundary/BUILD.out similarity index 100% rename from gazelle/testdata/monorepo/coarse_grained/_boundary/BUILD.out rename to gazelle/python/testdata/monorepo/coarse_grained/_boundary/BUILD.out diff --git a/gazelle/testdata/monorepo/coarse_grained/_boundary/README.md b/gazelle/python/testdata/monorepo/coarse_grained/_boundary/README.md similarity index 100% rename from gazelle/testdata/monorepo/coarse_grained/_boundary/README.md rename to gazelle/python/testdata/monorepo/coarse_grained/_boundary/README.md diff --git a/gazelle/python/testdata/monorepo/coarse_grained/_boundary/__init__.py b/gazelle/python/testdata/monorepo/coarse_grained/_boundary/__init__.py new file mode 100644 index 0000000000..bbdfb4c588 --- /dev/null +++ b/gazelle/python/testdata/monorepo/coarse_grained/_boundary/__init__.py @@ -0,0 +1,14 @@ +# 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. + diff --git a/gazelle/python/testdata/monorepo/coarse_grained/bar/__init__.py b/gazelle/python/testdata/monorepo/coarse_grained/bar/__init__.py new file mode 100644 index 0000000000..499a0903cc --- /dev/null +++ b/gazelle/python/testdata/monorepo/coarse_grained/bar/__init__.py @@ -0,0 +1,23 @@ +# 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 boto3 + +_ = boto3 + + +def bar(): + return os.path.abspath(__file__) diff --git a/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/__init__.py b/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/__init__.py new file mode 100644 index 0000000000..5be74a7d3e --- /dev/null +++ b/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/__init__.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. + +import os + + +def baz(): + return os.path.abspath(__file__) diff --git a/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/first_excluded.py b/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/first_excluded.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/first_excluded.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/hue.py b/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/hue.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/hue.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/second_excluded.py b/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/second_excluded.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/monorepo/coarse_grained/bar/baz/second_excluded.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/monorepo/coarse_grained/foo/__init__.py b/gazelle/python/testdata/monorepo/coarse_grained/foo/__init__.py new file mode 100644 index 0000000000..978fb74567 --- /dev/null +++ b/gazelle/python/testdata/monorepo/coarse_grained/foo/__init__.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. + +import os + + +def foo(): + return os.path.abspath(__file__) diff --git a/gazelle/python/testdata/monorepo/gazelle_python.yaml b/gazelle/python/testdata/monorepo/gazelle_python.yaml new file mode 100644 index 0000000000..132854e842 --- /dev/null +++ b/gazelle/python/testdata/monorepo/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + boto3: rootboto3 + boto4: rootboto4 + pip_deps_repository_name: root_pip_deps diff --git a/gazelle/testdata/monorepo/one/BUILD.in b/gazelle/python/testdata/monorepo/one/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/one/BUILD.in rename to gazelle/python/testdata/monorepo/one/BUILD.in diff --git a/gazelle/testdata/monorepo/one/BUILD.out b/gazelle/python/testdata/monorepo/one/BUILD.out similarity index 89% rename from gazelle/testdata/monorepo/one/BUILD.out rename to gazelle/python/testdata/monorepo/one/BUILD.out index a957227a9a..5098cc9a08 100644 --- a/gazelle/testdata/monorepo/one/BUILD.out +++ b/gazelle/python/testdata/monorepo/one/BUILD.out @@ -12,6 +12,6 @@ py_binary( "//one/bar", "//one/bar/baz:modified_name_baz", "//one/foo", - "@one_pip_deps//pypi__oneboto3", + "@one_pip_deps_oneboto3//:pkg", ], ) diff --git a/gazelle/python/testdata/monorepo/one/__main__.py b/gazelle/python/testdata/monorepo/one/__main__.py new file mode 100644 index 0000000000..7ef50cc97b --- /dev/null +++ b/gazelle/python/testdata/monorepo/one/__main__.py @@ -0,0 +1,29 @@ +# 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 boto3 +from bar import bar +from bar.baz import baz +from foo import foo + +_ = boto3 + +if __name__ == "__main__": + INIT_FILENAME = "__init__.py" + dirname = os.path.dirname(os.path.abspath(__file__)) + assert bar() == os.path.join(dirname, "bar", INIT_FILENAME) + assert baz() == os.path.join(dirname, "bar", "baz", INIT_FILENAME) + assert foo() == os.path.join(dirname, "foo", INIT_FILENAME) diff --git a/gazelle/testdata/monorepo/one/bar/BUILD.in b/gazelle/python/testdata/monorepo/one/bar/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/one/bar/BUILD.in rename to gazelle/python/testdata/monorepo/one/bar/BUILD.in diff --git a/gazelle/testdata/monorepo/one/bar/BUILD.out b/gazelle/python/testdata/monorepo/one/bar/BUILD.out similarity index 83% rename from gazelle/testdata/monorepo/one/bar/BUILD.out rename to gazelle/python/testdata/monorepo/one/bar/BUILD.out index 0e85623394..6ee6515eec 100644 --- a/gazelle/testdata/monorepo/one/bar/BUILD.out +++ b/gazelle/python/testdata/monorepo/one/bar/BUILD.out @@ -8,5 +8,5 @@ py_library( "//one:__subpackages__", "//three:__subpackages__", ], - deps = ["@one_pip_deps//pypi__oneboto3"], + deps = ["@one_pip_deps_oneboto3//:pkg"], ) diff --git a/gazelle/python/testdata/monorepo/one/bar/__init__.py b/gazelle/python/testdata/monorepo/one/bar/__init__.py new file mode 100644 index 0000000000..499a0903cc --- /dev/null +++ b/gazelle/python/testdata/monorepo/one/bar/__init__.py @@ -0,0 +1,23 @@ +# 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 boto3 + +_ = boto3 + + +def bar(): + return os.path.abspath(__file__) diff --git a/gazelle/testdata/monorepo/one/bar/baz/BUILD.in b/gazelle/python/testdata/monorepo/one/bar/baz/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/one/bar/baz/BUILD.in rename to gazelle/python/testdata/monorepo/one/bar/baz/BUILD.in diff --git a/gazelle/testdata/monorepo/one/bar/baz/BUILD.out b/gazelle/python/testdata/monorepo/one/bar/baz/BUILD.out similarity index 100% rename from gazelle/testdata/monorepo/one/bar/baz/BUILD.out rename to gazelle/python/testdata/monorepo/one/bar/baz/BUILD.out diff --git a/gazelle/python/testdata/monorepo/one/bar/baz/__init__.py b/gazelle/python/testdata/monorepo/one/bar/baz/__init__.py new file mode 100644 index 0000000000..5be74a7d3e --- /dev/null +++ b/gazelle/python/testdata/monorepo/one/bar/baz/__init__.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. + +import os + + +def baz(): + return os.path.abspath(__file__) diff --git a/gazelle/testdata/monorepo/one/foo/BUILD.in b/gazelle/python/testdata/monorepo/one/foo/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/one/foo/BUILD.in rename to gazelle/python/testdata/monorepo/one/foo/BUILD.in diff --git a/gazelle/testdata/monorepo/one/foo/BUILD.out b/gazelle/python/testdata/monorepo/one/foo/BUILD.out similarity index 100% rename from gazelle/testdata/monorepo/one/foo/BUILD.out rename to gazelle/python/testdata/monorepo/one/foo/BUILD.out diff --git a/gazelle/python/testdata/monorepo/one/foo/__init__.py b/gazelle/python/testdata/monorepo/one/foo/__init__.py new file mode 100644 index 0000000000..978fb74567 --- /dev/null +++ b/gazelle/python/testdata/monorepo/one/foo/__init__.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. + +import os + + +def foo(): + return os.path.abspath(__file__) diff --git a/gazelle/python/testdata/monorepo/one/gazelle_python.yaml b/gazelle/python/testdata/monorepo/one/gazelle_python.yaml new file mode 100644 index 0000000000..6b323b73d2 --- /dev/null +++ b/gazelle/python/testdata/monorepo/one/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + boto3: oneboto3 + pip_deps_repository_name: one_pip_deps diff --git a/gazelle/python/testdata/monorepo/test.yaml b/gazelle/python/testdata/monorepo/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/monorepo/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/monorepo/three/BUILD.in b/gazelle/python/testdata/monorepo/three/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/three/BUILD.in rename to gazelle/python/testdata/monorepo/three/BUILD.in diff --git a/gazelle/testdata/monorepo/three/BUILD.out b/gazelle/python/testdata/monorepo/three/BUILD.out similarity index 92% rename from gazelle/testdata/monorepo/three/BUILD.out rename to gazelle/python/testdata/monorepo/three/BUILD.out index 0da269d644..78a3927db9 100644 --- a/gazelle/testdata/monorepo/three/BUILD.out +++ b/gazelle/python/testdata/monorepo/three/BUILD.out @@ -15,7 +15,7 @@ py_library( "//one/bar", "//one/bar/baz:modified_name_baz", "//one/foo", - "@root_pip_deps//pypi__rootboto4", + "@root_pip_deps_rootboto4//:pkg", "@three_pip_deps_threeboto3//:pkg", ], ) diff --git a/gazelle/python/testdata/monorepo/three/__init__.py b/gazelle/python/testdata/monorepo/three/__init__.py new file mode 100644 index 0000000000..b324b0c416 --- /dev/null +++ b/gazelle/python/testdata/monorepo/three/__init__.py @@ -0,0 +1,30 @@ +# 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 bar.baz.hue as hue +import boto3 +import boto4 +from bar import bar +from bar.baz import baz +from foo import foo + +_ = os +_ = boto3 +_ = boto4 +_ = bar +_ = baz +_ = foo +_ = hue diff --git a/gazelle/python/testdata/monorepo/three/gazelle_python.yaml b/gazelle/python/testdata/monorepo/three/gazelle_python.yaml new file mode 100644 index 0000000000..8280b38d16 --- /dev/null +++ b/gazelle/python/testdata/monorepo/three/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + boto3: threeboto3 + pip_repository: + name: three_pip_deps diff --git a/gazelle/testdata/monorepo/two/BUILD.in b/gazelle/python/testdata/monorepo/two/BUILD.in similarity index 100% rename from gazelle/testdata/monorepo/two/BUILD.in rename to gazelle/python/testdata/monorepo/two/BUILD.in diff --git a/gazelle/testdata/monorepo/two/BUILD.out b/gazelle/python/testdata/monorepo/two/BUILD.out similarity index 87% rename from gazelle/testdata/monorepo/two/BUILD.out rename to gazelle/python/testdata/monorepo/two/BUILD.out index 4b638edea2..9cda007e59 100644 --- a/gazelle/testdata/monorepo/two/BUILD.out +++ b/gazelle/python/testdata/monorepo/two/BUILD.out @@ -10,6 +10,6 @@ py_library( visibility = ["//two:__subpackages__"], deps = [ "//one/foo", - "@two_pip_deps//pypi__twoboto3", + "@two_pip_deps_twoboto3//:pkg", ], ) diff --git a/gazelle/python/testdata/monorepo/two/__init__.py b/gazelle/python/testdata/monorepo/two/__init__.py new file mode 100644 index 0000000000..d080c27de3 --- /dev/null +++ b/gazelle/python/testdata/monorepo/two/__init__.py @@ -0,0 +1,22 @@ +# 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 boto3 +from foo import foo + +_ = os +_ = boto3 +_ = foo diff --git a/gazelle/python/testdata/monorepo/two/gazelle_python.yaml b/gazelle/python/testdata/monorepo/two/gazelle_python.yaml new file mode 100644 index 0000000000..88c24d0147 --- /dev/null +++ b/gazelle/python/testdata/monorepo/two/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + boto3: twoboto3 + pip_deps_repository_name: two_pip_deps diff --git a/gazelle/testdata/simple_library_without_init/foo/BUILD.in b/gazelle/python/testdata/monorepo/wont_generate/BUILD.in similarity index 100% rename from gazelle/testdata/simple_library_without_init/foo/BUILD.in rename to gazelle/python/testdata/monorepo/wont_generate/BUILD.in diff --git a/gazelle/testdata/monorepo/wont_generate/bar/BUILD.out b/gazelle/python/testdata/monorepo/wont_generate/BUILD.out similarity index 100% rename from gazelle/testdata/monorepo/wont_generate/bar/BUILD.out rename to gazelle/python/testdata/monorepo/wont_generate/BUILD.out diff --git a/gazelle/python/testdata/monorepo/wont_generate/__main__.py b/gazelle/python/testdata/monorepo/wont_generate/__main__.py new file mode 100644 index 0000000000..efc7900d53 --- /dev/null +++ b/gazelle/python/testdata/monorepo/wont_generate/__main__.py @@ -0,0 +1,26 @@ +# 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 + +from bar import bar +from bar.baz import baz +from foo import foo + +if __name__ == "__main__": + INIT_FILENAME = "__init__.py" + dirname = os.path.dirname(os.path.abspath(__file__)) + assert bar() == os.path.join(dirname, "bar", INIT_FILENAME) + assert baz() == os.path.join(dirname, "bar", "baz", INIT_FILENAME) + assert foo() == os.path.join(dirname, "foo", INIT_FILENAME) diff --git a/gazelle/testdata/subdir_sources/BUILD.in b/gazelle/python/testdata/monorepo/wont_generate/bar/BUILD.in similarity index 100% rename from gazelle/testdata/subdir_sources/BUILD.in rename to gazelle/python/testdata/monorepo/wont_generate/bar/BUILD.in diff --git a/gazelle/testdata/monorepo/wont_generate/bar/baz/BUILD.out b/gazelle/python/testdata/monorepo/wont_generate/bar/BUILD.out similarity index 100% rename from gazelle/testdata/monorepo/wont_generate/bar/baz/BUILD.out rename to gazelle/python/testdata/monorepo/wont_generate/bar/BUILD.out diff --git a/gazelle/python/testdata/monorepo/wont_generate/bar/__init__.py b/gazelle/python/testdata/monorepo/wont_generate/bar/__init__.py new file mode 100644 index 0000000000..d4b5fb84f1 --- /dev/null +++ b/gazelle/python/testdata/monorepo/wont_generate/bar/__init__.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. + +import os + + +def bar(): + return os.path.abspath(__file__) diff --git a/gazelle/testdata/subdir_sources/foo/BUILD.in b/gazelle/python/testdata/monorepo/wont_generate/bar/baz/BUILD.in similarity index 100% rename from gazelle/testdata/subdir_sources/foo/BUILD.in rename to gazelle/python/testdata/monorepo/wont_generate/bar/baz/BUILD.in diff --git a/gazelle/testdata/monorepo/wont_generate/foo/BUILD.out b/gazelle/python/testdata/monorepo/wont_generate/bar/baz/BUILD.out similarity index 100% rename from gazelle/testdata/monorepo/wont_generate/foo/BUILD.out rename to gazelle/python/testdata/monorepo/wont_generate/bar/baz/BUILD.out diff --git a/gazelle/python/testdata/monorepo/wont_generate/bar/baz/__init__.py b/gazelle/python/testdata/monorepo/wont_generate/bar/baz/__init__.py new file mode 100644 index 0000000000..5be74a7d3e --- /dev/null +++ b/gazelle/python/testdata/monorepo/wont_generate/bar/baz/__init__.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. + +import os + + +def baz(): + return os.path.abspath(__file__) diff --git a/gazelle/testdata/subdir_sources/foo/has_build/BUILD.in b/gazelle/python/testdata/monorepo/wont_generate/foo/BUILD.in similarity index 100% rename from gazelle/testdata/subdir_sources/foo/has_build/BUILD.in rename to gazelle/python/testdata/monorepo/wont_generate/foo/BUILD.in diff --git a/gazelle/testdata/simple_library_without_init/BUILD.out b/gazelle/python/testdata/monorepo/wont_generate/foo/BUILD.out similarity index 100% rename from gazelle/testdata/simple_library_without_init/BUILD.out rename to gazelle/python/testdata/monorepo/wont_generate/foo/BUILD.out diff --git a/gazelle/python/testdata/monorepo/wont_generate/foo/__init__.py b/gazelle/python/testdata/monorepo/wont_generate/foo/__init__.py new file mode 100644 index 0000000000..978fb74567 --- /dev/null +++ b/gazelle/python/testdata/monorepo/wont_generate/foo/__init__.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. + +import os + + +def foo(): + return os.path.abspath(__file__) diff --git a/gazelle/python/testdata/multiple_tests/BUILD.in b/gazelle/python/testdata/multiple_tests/BUILD.in new file mode 100644 index 0000000000..9e84e5dc32 --- /dev/null +++ b/gazelle/python/testdata/multiple_tests/BUILD.in @@ -0,0 +1,12 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +py_library( + name = "multiple_tests", + srcs = ["__init__.py"], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "bar_test", + srcs = ["bar_test.py"], +) diff --git a/gazelle/python/testdata/multiple_tests/BUILD.out b/gazelle/python/testdata/multiple_tests/BUILD.out new file mode 100644 index 0000000000..fd67724e3b --- /dev/null +++ b/gazelle/python/testdata/multiple_tests/BUILD.out @@ -0,0 +1,17 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +py_library( + name = "multiple_tests", + srcs = ["__init__.py"], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "bar_test", + srcs = ["bar_test.py"], +) + +py_test( + name = "foo_test", + srcs = ["foo_test.py"], +) diff --git a/gazelle/python/testdata/multiple_tests/README.md b/gazelle/python/testdata/multiple_tests/README.md new file mode 100644 index 0000000000..8220f6112d --- /dev/null +++ b/gazelle/python/testdata/multiple_tests/README.md @@ -0,0 +1,3 @@ +# Multiple tests + +This test case asserts that a second `py_test` rule is correctly created when a second `*_test.py` file is added to a package with an existing `py_test` rule. diff --git a/gazelle/testdata/naming_convention_binary_fail/WORKSPACE b/gazelle/python/testdata/multiple_tests/WORKSPACE similarity index 100% rename from gazelle/testdata/naming_convention_binary_fail/WORKSPACE rename to gazelle/python/testdata/multiple_tests/WORKSPACE diff --git a/gazelle/testdata/dont_rename_target/__init__.py b/gazelle/python/testdata/multiple_tests/__init__.py similarity index 100% rename from gazelle/testdata/dont_rename_target/__init__.py rename to gazelle/python/testdata/multiple_tests/__init__.py diff --git a/gazelle/python/testdata/multiple_tests/bar_test.py b/gazelle/python/testdata/multiple_tests/bar_test.py new file mode 100644 index 0000000000..9948f1ccd4 --- /dev/null +++ b/gazelle/python/testdata/multiple_tests/bar_test.py @@ -0,0 +1,24 @@ +# 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 + + +class BarTest(unittest.TestCase): + def test_foo(self): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/gazelle/python/testdata/multiple_tests/foo_test.py b/gazelle/python/testdata/multiple_tests/foo_test.py new file mode 100644 index 0000000000..a128adf67f --- /dev/null +++ b/gazelle/python/testdata/multiple_tests/foo_test.py @@ -0,0 +1,24 @@ +# 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 + + +class FooTest(unittest.TestCase): + def test_foo(self): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/gazelle/python/testdata/multiple_tests/test.yaml b/gazelle/python/testdata/multiple_tests/test.yaml new file mode 100644 index 0000000000..2410223e59 --- /dev/null +++ b/gazelle/python/testdata/multiple_tests/test.yaml @@ -0,0 +1,17 @@ +# 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. + +--- +expect: + exit_code: 0 diff --git a/gazelle/testdata/naming_convention/BUILD.in b/gazelle/python/testdata/naming_convention/BUILD.in similarity index 100% rename from gazelle/testdata/naming_convention/BUILD.in rename to gazelle/python/testdata/naming_convention/BUILD.in diff --git a/gazelle/testdata/naming_convention/BUILD.out b/gazelle/python/testdata/naming_convention/BUILD.out similarity index 100% rename from gazelle/testdata/naming_convention/BUILD.out rename to gazelle/python/testdata/naming_convention/BUILD.out diff --git a/gazelle/testdata/naming_convention/README.md b/gazelle/python/testdata/naming_convention/README.md similarity index 100% rename from gazelle/testdata/naming_convention/README.md rename to gazelle/python/testdata/naming_convention/README.md diff --git a/gazelle/testdata/naming_convention_library_fail/WORKSPACE b/gazelle/python/testdata/naming_convention/WORKSPACE similarity index 100% rename from gazelle/testdata/naming_convention_library_fail/WORKSPACE rename to gazelle/python/testdata/naming_convention/WORKSPACE diff --git a/gazelle/python/testdata/naming_convention/__init__.py b/gazelle/python/testdata/naming_convention/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/naming_convention/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/naming_convention/__main__.py b/gazelle/python/testdata/naming_convention/__main__.py new file mode 100644 index 0000000000..a3afc79dcd --- /dev/null +++ b/gazelle/python/testdata/naming_convention/__main__.py @@ -0,0 +1,16 @@ +# 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. + +# For test purposes only. +import __init__ \ No newline at end of file diff --git a/gazelle/python/testdata/naming_convention/__test__.py b/gazelle/python/testdata/naming_convention/__test__.py new file mode 100644 index 0000000000..a3afc79dcd --- /dev/null +++ b/gazelle/python/testdata/naming_convention/__test__.py @@ -0,0 +1,16 @@ +# 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. + +# For test purposes only. +import __init__ \ No newline at end of file diff --git a/gazelle/testdata/naming_convention/dont_rename/BUILD.in b/gazelle/python/testdata/naming_convention/dont_rename/BUILD.in similarity index 100% rename from gazelle/testdata/naming_convention/dont_rename/BUILD.in rename to gazelle/python/testdata/naming_convention/dont_rename/BUILD.in diff --git a/gazelle/testdata/naming_convention/dont_rename/BUILD.out b/gazelle/python/testdata/naming_convention/dont_rename/BUILD.out similarity index 100% rename from gazelle/testdata/naming_convention/dont_rename/BUILD.out rename to gazelle/python/testdata/naming_convention/dont_rename/BUILD.out diff --git a/gazelle/python/testdata/naming_convention/dont_rename/__init__.py b/gazelle/python/testdata/naming_convention/dont_rename/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/naming_convention/dont_rename/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/naming_convention/dont_rename/__main__.py b/gazelle/python/testdata/naming_convention/dont_rename/__main__.py new file mode 100644 index 0000000000..a3afc79dcd --- /dev/null +++ b/gazelle/python/testdata/naming_convention/dont_rename/__main__.py @@ -0,0 +1,16 @@ +# 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. + +# For test purposes only. +import __init__ \ No newline at end of file diff --git a/gazelle/python/testdata/naming_convention/dont_rename/__test__.py b/gazelle/python/testdata/naming_convention/dont_rename/__test__.py new file mode 100644 index 0000000000..a3afc79dcd --- /dev/null +++ b/gazelle/python/testdata/naming_convention/dont_rename/__test__.py @@ -0,0 +1,16 @@ +# 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. + +# For test purposes only. +import __init__ \ No newline at end of file diff --git a/gazelle/testdata/naming_convention/resolve_conflict/BUILD.in b/gazelle/python/testdata/naming_convention/resolve_conflict/BUILD.in similarity index 100% rename from gazelle/testdata/naming_convention/resolve_conflict/BUILD.in rename to gazelle/python/testdata/naming_convention/resolve_conflict/BUILD.in diff --git a/gazelle/testdata/naming_convention/resolve_conflict/BUILD.out b/gazelle/python/testdata/naming_convention/resolve_conflict/BUILD.out similarity index 100% rename from gazelle/testdata/naming_convention/resolve_conflict/BUILD.out rename to gazelle/python/testdata/naming_convention/resolve_conflict/BUILD.out diff --git a/gazelle/python/testdata/naming_convention/resolve_conflict/__init__.py b/gazelle/python/testdata/naming_convention/resolve_conflict/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/naming_convention/resolve_conflict/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/naming_convention/resolve_conflict/__main__.py b/gazelle/python/testdata/naming_convention/resolve_conflict/__main__.py new file mode 100644 index 0000000000..a3afc79dcd --- /dev/null +++ b/gazelle/python/testdata/naming_convention/resolve_conflict/__main__.py @@ -0,0 +1,16 @@ +# 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. + +# For test purposes only. +import __init__ \ No newline at end of file diff --git a/gazelle/python/testdata/naming_convention/resolve_conflict/__test__.py b/gazelle/python/testdata/naming_convention/resolve_conflict/__test__.py new file mode 100644 index 0000000000..a3afc79dcd --- /dev/null +++ b/gazelle/python/testdata/naming_convention/resolve_conflict/__test__.py @@ -0,0 +1,16 @@ +# 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. + +# For test purposes only. +import __init__ \ No newline at end of file diff --git a/gazelle/python/testdata/naming_convention/test.yaml b/gazelle/python/testdata/naming_convention/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/naming_convention/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/naming_convention_binary_fail/BUILD.in b/gazelle/python/testdata/naming_convention_binary_fail/BUILD.in similarity index 100% rename from gazelle/testdata/naming_convention_binary_fail/BUILD.in rename to gazelle/python/testdata/naming_convention_binary_fail/BUILD.in diff --git a/gazelle/testdata/naming_convention_binary_fail/BUILD.out b/gazelle/python/testdata/naming_convention_binary_fail/BUILD.out similarity index 100% rename from gazelle/testdata/naming_convention_binary_fail/BUILD.out rename to gazelle/python/testdata/naming_convention_binary_fail/BUILD.out diff --git a/gazelle/testdata/naming_convention_binary_fail/README.md b/gazelle/python/testdata/naming_convention_binary_fail/README.md similarity index 100% rename from gazelle/testdata/naming_convention_binary_fail/README.md rename to gazelle/python/testdata/naming_convention_binary_fail/README.md diff --git a/gazelle/testdata/naming_convention_test_fail/WORKSPACE b/gazelle/python/testdata/naming_convention_binary_fail/WORKSPACE similarity index 100% rename from gazelle/testdata/naming_convention_test_fail/WORKSPACE rename to gazelle/python/testdata/naming_convention_binary_fail/WORKSPACE diff --git a/gazelle/python/testdata/naming_convention_binary_fail/__main__.py b/gazelle/python/testdata/naming_convention_binary_fail/__main__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/naming_convention_binary_fail/__main__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/naming_convention_binary_fail/test.yaml b/gazelle/python/testdata/naming_convention_binary_fail/test.yaml new file mode 100644 index 0000000000..41eabbfb11 --- /dev/null +++ b/gazelle/python/testdata/naming_convention_binary_fail/test.yaml @@ -0,0 +1,21 @@ +# 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. + +--- +expect: + exit_code: 1 + stderr: > + gazelle: ERROR: failed to generate target "//:naming_convention_binary_fail_bin" of kind "py_binary": + a target of kind "go_binary" with the same name already exists. + Use the '# gazelle:python_binary_naming_convention' directive to change the naming convention. diff --git a/gazelle/testdata/naming_convention_library_fail/BUILD.in b/gazelle/python/testdata/naming_convention_library_fail/BUILD.in similarity index 100% rename from gazelle/testdata/naming_convention_library_fail/BUILD.in rename to gazelle/python/testdata/naming_convention_library_fail/BUILD.in diff --git a/gazelle/testdata/naming_convention_library_fail/BUILD.out b/gazelle/python/testdata/naming_convention_library_fail/BUILD.out similarity index 100% rename from gazelle/testdata/naming_convention_library_fail/BUILD.out rename to gazelle/python/testdata/naming_convention_library_fail/BUILD.out diff --git a/gazelle/testdata/naming_convention_library_fail/README.md b/gazelle/python/testdata/naming_convention_library_fail/README.md similarity index 100% rename from gazelle/testdata/naming_convention_library_fail/README.md rename to gazelle/python/testdata/naming_convention_library_fail/README.md diff --git a/gazelle/testdata/python_ignore_dependencies_directive/WORKSPACE b/gazelle/python/testdata/naming_convention_library_fail/WORKSPACE similarity index 100% rename from gazelle/testdata/python_ignore_dependencies_directive/WORKSPACE rename to gazelle/python/testdata/naming_convention_library_fail/WORKSPACE diff --git a/gazelle/python/testdata/naming_convention_library_fail/__init__.py b/gazelle/python/testdata/naming_convention_library_fail/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/naming_convention_library_fail/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/naming_convention_library_fail/test.yaml b/gazelle/python/testdata/naming_convention_library_fail/test.yaml new file mode 100644 index 0000000000..f48aa397f1 --- /dev/null +++ b/gazelle/python/testdata/naming_convention_library_fail/test.yaml @@ -0,0 +1,21 @@ +# 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. + +--- +expect: + exit_code: 1 + stderr: > + gazelle: ERROR: failed to generate target "//:naming_convention_library_fail" of kind "py_library": + a target of kind "go_library" with the same name already exists. + Use the '# gazelle:python_library_naming_convention' directive to change the naming convention. diff --git a/gazelle/testdata/naming_convention_test_fail/BUILD.in b/gazelle/python/testdata/naming_convention_test_fail/BUILD.in similarity index 100% rename from gazelle/testdata/naming_convention_test_fail/BUILD.in rename to gazelle/python/testdata/naming_convention_test_fail/BUILD.in diff --git a/gazelle/testdata/naming_convention_test_fail/BUILD.out b/gazelle/python/testdata/naming_convention_test_fail/BUILD.out similarity index 100% rename from gazelle/testdata/naming_convention_test_fail/BUILD.out rename to gazelle/python/testdata/naming_convention_test_fail/BUILD.out diff --git a/gazelle/testdata/naming_convention_test_fail/README.md b/gazelle/python/testdata/naming_convention_test_fail/README.md similarity index 100% rename from gazelle/testdata/naming_convention_test_fail/README.md rename to gazelle/python/testdata/naming_convention_test_fail/README.md diff --git a/gazelle/testdata/python_ignore_files_directive/WORKSPACE b/gazelle/python/testdata/naming_convention_test_fail/WORKSPACE similarity index 100% rename from gazelle/testdata/python_ignore_files_directive/WORKSPACE rename to gazelle/python/testdata/naming_convention_test_fail/WORKSPACE diff --git a/gazelle/python/testdata/naming_convention_test_fail/__test__.py b/gazelle/python/testdata/naming_convention_test_fail/__test__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/naming_convention_test_fail/__test__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/naming_convention_test_fail/test.yaml b/gazelle/python/testdata/naming_convention_test_fail/test.yaml new file mode 100644 index 0000000000..a8867e567e --- /dev/null +++ b/gazelle/python/testdata/naming_convention_test_fail/test.yaml @@ -0,0 +1,21 @@ +# 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. + +--- +expect: + exit_code: 1 + stderr: > + gazelle: ERROR: failed to generate target "//:naming_convention_test_fail_test" of kind "py_test": + a target of kind "go_test" with the same name already exists. + Use the '# gazelle:python_test_naming_convention' directive to change the naming convention. diff --git a/gazelle/testdata/python_ignore_dependencies_directive/BUILD.in b/gazelle/python/testdata/python_ignore_dependencies_directive/BUILD.in similarity index 100% rename from gazelle/testdata/python_ignore_dependencies_directive/BUILD.in rename to gazelle/python/testdata/python_ignore_dependencies_directive/BUILD.in diff --git a/gazelle/testdata/python_ignore_dependencies_directive/BUILD.out b/gazelle/python/testdata/python_ignore_dependencies_directive/BUILD.out similarity index 85% rename from gazelle/testdata/python_ignore_dependencies_directive/BUILD.out rename to gazelle/python/testdata/python_ignore_dependencies_directive/BUILD.out index 37ae4f9aa1..3fb91f5964 100644 --- a/gazelle/testdata/python_ignore_dependencies_directive/BUILD.out +++ b/gazelle/python/testdata/python_ignore_dependencies_directive/BUILD.out @@ -7,5 +7,5 @@ py_library( name = "python_ignore_dependencies_directive", srcs = ["__init__.py"], visibility = ["//:__subpackages__"], - deps = ["@gazelle_python_test//pypi__boto3"], + deps = ["@gazelle_python_test_boto3//:pkg"], ) diff --git a/gazelle/testdata/python_ignore_dependencies_directive/README.md b/gazelle/python/testdata/python_ignore_dependencies_directive/README.md similarity index 100% rename from gazelle/testdata/python_ignore_dependencies_directive/README.md rename to gazelle/python/testdata/python_ignore_dependencies_directive/README.md diff --git a/gazelle/testdata/simple_binary/WORKSPACE b/gazelle/python/testdata/python_ignore_dependencies_directive/WORKSPACE similarity index 100% rename from gazelle/testdata/simple_binary/WORKSPACE rename to gazelle/python/testdata/python_ignore_dependencies_directive/WORKSPACE diff --git a/gazelle/python/testdata/python_ignore_dependencies_directive/__init__.py b/gazelle/python/testdata/python_ignore_dependencies_directive/__init__.py new file mode 100644 index 0000000000..9e6e25a891 --- /dev/null +++ b/gazelle/python/testdata/python_ignore_dependencies_directive/__init__.py @@ -0,0 +1,25 @@ +# 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 bar +import boto3 +import foo +import foo.bar.baz +from baz import baz as bazfn + +_ = foo +_ = bar +_ = bazfn +_ = baz +_ = boto3 diff --git a/gazelle/python/testdata/python_ignore_dependencies_directive/gazelle_python.yaml b/gazelle/python/testdata/python_ignore_dependencies_directive/gazelle_python.yaml new file mode 100644 index 0000000000..1bf594f9b4 --- /dev/null +++ b/gazelle/python/testdata/python_ignore_dependencies_directive/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + boto3: boto3 + pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/python/testdata/python_ignore_dependencies_directive/test.yaml b/gazelle/python/testdata/python_ignore_dependencies_directive/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/python_ignore_dependencies_directive/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/python_ignore_files_directive/BUILD.in b/gazelle/python/testdata/python_ignore_files_directive/BUILD.in similarity index 100% rename from gazelle/testdata/python_ignore_files_directive/BUILD.in rename to gazelle/python/testdata/python_ignore_files_directive/BUILD.in diff --git a/gazelle/testdata/python_ignore_files_directive/BUILD.out b/gazelle/python/testdata/python_ignore_files_directive/BUILD.out similarity index 100% rename from gazelle/testdata/python_ignore_files_directive/BUILD.out rename to gazelle/python/testdata/python_ignore_files_directive/BUILD.out diff --git a/gazelle/testdata/python_ignore_files_directive/README.md b/gazelle/python/testdata/python_ignore_files_directive/README.md similarity index 100% rename from gazelle/testdata/python_ignore_files_directive/README.md rename to gazelle/python/testdata/python_ignore_files_directive/README.md diff --git a/gazelle/testdata/simple_binary_with_library/WORKSPACE b/gazelle/python/testdata/python_ignore_files_directive/WORKSPACE similarity index 100% rename from gazelle/testdata/simple_binary_with_library/WORKSPACE rename to gazelle/python/testdata/python_ignore_files_directive/WORKSPACE diff --git a/gazelle/python/testdata/python_ignore_files_directive/__init__.py b/gazelle/python/testdata/python_ignore_files_directive/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/python_ignore_files_directive/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/has_init/BUILD.in b/gazelle/python/testdata/python_ignore_files_directive/bar/BUILD.in similarity index 100% rename from gazelle/testdata/subdir_sources/foo/has_init/BUILD.in rename to gazelle/python/testdata/python_ignore_files_directive/bar/BUILD.in diff --git a/gazelle/testdata/python_ignore_files_directive/bar/BUILD.out b/gazelle/python/testdata/python_ignore_files_directive/bar/BUILD.out similarity index 100% rename from gazelle/testdata/python_ignore_files_directive/bar/BUILD.out rename to gazelle/python/testdata/python_ignore_files_directive/bar/BUILD.out diff --git a/gazelle/python/testdata/python_ignore_files_directive/bar/baz.py b/gazelle/python/testdata/python_ignore_files_directive/bar/baz.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/python_ignore_files_directive/bar/baz.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/python_ignore_files_directive/bar/some_other.py b/gazelle/python/testdata/python_ignore_files_directive/bar/some_other.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/python_ignore_files_directive/bar/some_other.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/testdata/python_ignore_files_directive/foo/BUILD.in b/gazelle/python/testdata/python_ignore_files_directive/foo/BUILD.in similarity index 100% rename from gazelle/testdata/python_ignore_files_directive/foo/BUILD.in rename to gazelle/python/testdata/python_ignore_files_directive/foo/BUILD.in diff --git a/gazelle/testdata/python_ignore_files_directive/foo/BUILD.out b/gazelle/python/testdata/python_ignore_files_directive/foo/BUILD.out similarity index 100% rename from gazelle/testdata/python_ignore_files_directive/foo/BUILD.out rename to gazelle/python/testdata/python_ignore_files_directive/foo/BUILD.out diff --git a/gazelle/python/testdata/python_ignore_files_directive/foo/baz.py b/gazelle/python/testdata/python_ignore_files_directive/foo/baz.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/python_ignore_files_directive/foo/baz.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/python_ignore_files_directive/setup.py b/gazelle/python/testdata/python_ignore_files_directive/setup.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/python_ignore_files_directive/setup.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/python_ignore_files_directive/some_other.py b/gazelle/python/testdata/python_ignore_files_directive/some_other.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/python_ignore_files_directive/some_other.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/python_ignore_files_directive/test.yaml b/gazelle/python/testdata/python_ignore_files_directive/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/python_ignore_files_directive/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/subdir_sources/foo/has_main/BUILD.in b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.in similarity index 100% rename from gazelle/testdata/subdir_sources/foo/has_main/BUILD.in rename to gazelle/python/testdata/python_target_with_test_in_name/BUILD.in diff --git a/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out new file mode 100644 index 0000000000..a46f5c40b8 --- /dev/null +++ b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out @@ -0,0 +1,22 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +py_library( + name = "python_target_with_test_in_name", + srcs = ["__init__.py"], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "real_test", + srcs = ["real_test.py"], + deps = [ + ":python_target_with_test_in_name", + "@gazelle_python_test_boto3//:pkg", + ], +) + +py_test( + name = "test_reality", + srcs = ["test_reality.py"], + deps = [":python_target_with_test_in_name"], +) diff --git a/gazelle/testdata/python_target_with_test_in_name/README.md b/gazelle/python/testdata/python_target_with_test_in_name/README.md similarity index 100% rename from gazelle/testdata/python_target_with_test_in_name/README.md rename to gazelle/python/testdata/python_target_with_test_in_name/README.md diff --git a/gazelle/testdata/monorepo/coarse_grained/_boundary/__init__.py b/gazelle/python/testdata/python_target_with_test_in_name/WORKSPACE similarity index 100% rename from gazelle/testdata/monorepo/coarse_grained/_boundary/__init__.py rename to gazelle/python/testdata/python_target_with_test_in_name/WORKSPACE diff --git a/gazelle/python/testdata/python_target_with_test_in_name/__init__.py b/gazelle/python/testdata/python_target_with_test_in_name/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/python_target_with_test_in_name/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/python_target_with_test_in_name/gazelle_python.yaml b/gazelle/python/testdata/python_target_with_test_in_name/gazelle_python.yaml new file mode 100644 index 0000000000..1bf594f9b4 --- /dev/null +++ b/gazelle/python/testdata/python_target_with_test_in_name/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + boto3: boto3 + pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/python/testdata/python_target_with_test_in_name/real_test.py b/gazelle/python/testdata/python_target_with_test_in_name/real_test.py new file mode 100644 index 0000000000..e390866be3 --- /dev/null +++ b/gazelle/python/testdata/python_target_with_test_in_name/real_test.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. + +import boto3 +import __init__ + +_ = boto3 diff --git a/gazelle/python/testdata/python_target_with_test_in_name/test.yaml b/gazelle/python/testdata/python_target_with_test_in_name/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/python_target_with_test_in_name/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/python/testdata/python_target_with_test_in_name/test_reality.py b/gazelle/python/testdata/python_target_with_test_in_name/test_reality.py new file mode 100644 index 0000000000..a3afc79dcd --- /dev/null +++ b/gazelle/python/testdata/python_target_with_test_in_name/test_reality.py @@ -0,0 +1,16 @@ +# 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. + +# For test purposes only. +import __init__ \ No newline at end of file diff --git a/gazelle/testdata/subdir_sources/foo/has_test/BUILD.in b/gazelle/python/testdata/relative_imports/BUILD.in similarity index 100% rename from gazelle/testdata/subdir_sources/foo/has_test/BUILD.in rename to gazelle/python/testdata/relative_imports/BUILD.in diff --git a/gazelle/testdata/relative_imports/BUILD.out b/gazelle/python/testdata/relative_imports/BUILD.out similarity index 100% rename from gazelle/testdata/relative_imports/BUILD.out rename to gazelle/python/testdata/relative_imports/BUILD.out diff --git a/gazelle/testdata/relative_imports/README.md b/gazelle/python/testdata/relative_imports/README.md similarity index 100% rename from gazelle/testdata/relative_imports/README.md rename to gazelle/python/testdata/relative_imports/README.md diff --git a/gazelle/python/testdata/relative_imports/WORKSPACE b/gazelle/python/testdata/relative_imports/WORKSPACE new file mode 100644 index 0000000000..4959898cdd --- /dev/null +++ b/gazelle/python/testdata/relative_imports/WORKSPACE @@ -0,0 +1 @@ +# This is a test data Bazel workspace. diff --git a/gazelle/python/testdata/relative_imports/__main__.py b/gazelle/python/testdata/relative_imports/__main__.py new file mode 100644 index 0000000000..8d468bd643 --- /dev/null +++ b/gazelle/python/testdata/relative_imports/__main__.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 package1.module1 import function1 +from package2.module3 import function3 + +print(function1()) +print(function3()) diff --git a/gazelle/python/testdata/relative_imports/package1/module1.py b/gazelle/python/testdata/relative_imports/package1/module1.py new file mode 100644 index 0000000000..28502f1f84 --- /dev/null +++ b/gazelle/python/testdata/relative_imports/package1/module1.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 .module2 import function2 + + +def function1(): + return "function1 " + function2() diff --git a/gazelle/python/testdata/relative_imports/package1/module2.py b/gazelle/python/testdata/relative_imports/package1/module2.py new file mode 100644 index 0000000000..f8893b24e6 --- /dev/null +++ b/gazelle/python/testdata/relative_imports/package1/module2.py @@ -0,0 +1,16 @@ +# 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. + +def function2(): + return "function2" diff --git a/gazelle/testdata/subdir_sources/one/BUILD.in b/gazelle/python/testdata/relative_imports/package2/BUILD.in similarity index 100% rename from gazelle/testdata/subdir_sources/one/BUILD.in rename to gazelle/python/testdata/relative_imports/package2/BUILD.in diff --git a/gazelle/testdata/relative_imports/package2/BUILD.out b/gazelle/python/testdata/relative_imports/package2/BUILD.out similarity index 100% rename from gazelle/testdata/relative_imports/package2/BUILD.out rename to gazelle/python/testdata/relative_imports/package2/BUILD.out diff --git a/gazelle/python/testdata/relative_imports/package2/__init__.py b/gazelle/python/testdata/relative_imports/package2/__init__.py new file mode 100644 index 0000000000..0f5956835b --- /dev/null +++ b/gazelle/python/testdata/relative_imports/package2/__init__.py @@ -0,0 +1,17 @@ +# 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. + +class Class1: + def method1(self): + return "method1" diff --git a/gazelle/python/testdata/relative_imports/package2/module3.py b/gazelle/python/testdata/relative_imports/package2/module3.py new file mode 100644 index 0000000000..74978a08d9 --- /dev/null +++ b/gazelle/python/testdata/relative_imports/package2/module3.py @@ -0,0 +1,21 @@ +# 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 . import Class1 +from .subpackage1.module5 import function5 + + +def function3(): + c1 = Class1() + return "function3 " + c1.method1() + " " + function5() diff --git a/gazelle/python/testdata/relative_imports/package2/module4.py b/gazelle/python/testdata/relative_imports/package2/module4.py new file mode 100644 index 0000000000..b7509dc7cf --- /dev/null +++ b/gazelle/python/testdata/relative_imports/package2/module4.py @@ -0,0 +1,16 @@ +# 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. + +def function4(): + return "function4" diff --git a/gazelle/python/testdata/relative_imports/package2/subpackage1/module5.py b/gazelle/python/testdata/relative_imports/package2/subpackage1/module5.py new file mode 100644 index 0000000000..ea0b981fd0 --- /dev/null +++ b/gazelle/python/testdata/relative_imports/package2/subpackage1/module5.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 ..module4 import function4 + + +def function5(): + return "function5 " + function4() diff --git a/gazelle/python/testdata/relative_imports/test.yaml b/gazelle/python/testdata/relative_imports/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/relative_imports/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/python/testdata/respect_kind_mapping/BUILD.in b/gazelle/python/testdata/respect_kind_mapping/BUILD.in new file mode 100644 index 0000000000..6a06737623 --- /dev/null +++ b/gazelle/python/testdata/respect_kind_mapping/BUILD.in @@ -0,0 +1,15 @@ +load("@rules_python//python:defs.bzl", "py_library") + +# gazelle:map_kind py_test my_test :mytest.bzl + +py_library( + name = "respect_kind_mapping", + srcs = ["__init__.py"], +) + +my_test( + name = "respect_kind_mapping_test", + srcs = ["__test__.py"], + main = "__test__.py", + deps = [":respect_kind_mapping"], +) diff --git a/gazelle/python/testdata/respect_kind_mapping/BUILD.out b/gazelle/python/testdata/respect_kind_mapping/BUILD.out new file mode 100644 index 0000000000..7c5fb0bd20 --- /dev/null +++ b/gazelle/python/testdata/respect_kind_mapping/BUILD.out @@ -0,0 +1,20 @@ +load(":mytest.bzl", "my_test") +load("@rules_python//python:defs.bzl", "py_library") + +# gazelle:map_kind py_test my_test :mytest.bzl + +py_library( + name = "respect_kind_mapping", + srcs = [ + "__init__.py", + "foo.py", + ], + visibility = ["//:__subpackages__"], +) + +my_test( + name = "respect_kind_mapping_test", + srcs = ["__test__.py"], + main = "__test__.py", + deps = [":respect_kind_mapping"], +) diff --git a/gazelle/python/testdata/respect_kind_mapping/README.md b/gazelle/python/testdata/respect_kind_mapping/README.md new file mode 100644 index 0000000000..9f0fa6cf39 --- /dev/null +++ b/gazelle/python/testdata/respect_kind_mapping/README.md @@ -0,0 +1,3 @@ +# Respect Kind Mapping + +This test case asserts that when using a kind mapping, gazelle will respect that mapping when parsing a BUILD file containing a mapped kind. diff --git a/gazelle/testdata/simple_library/WORKSPACE b/gazelle/python/testdata/respect_kind_mapping/WORKSPACE similarity index 100% rename from gazelle/testdata/simple_library/WORKSPACE rename to gazelle/python/testdata/respect_kind_mapping/WORKSPACE diff --git a/gazelle/python/testdata/respect_kind_mapping/__init__.py b/gazelle/python/testdata/respect_kind_mapping/__init__.py new file mode 100644 index 0000000000..b274b0d921 --- /dev/null +++ b/gazelle/python/testdata/respect_kind_mapping/__init__.py @@ -0,0 +1,17 @@ +# 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 foo import foo + +_ = foo diff --git a/gazelle/python/testdata/respect_kind_mapping/__test__.py b/gazelle/python/testdata/respect_kind_mapping/__test__.py new file mode 100644 index 0000000000..2b180a5f53 --- /dev/null +++ b/gazelle/python/testdata/respect_kind_mapping/__test__.py @@ -0,0 +1,26 @@ +# 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 __init__ import foo + + +class FooTest(unittest.TestCase): + def test_foo(self): + self.assertEqual("foo", foo()) + + +if __name__ == "__main__": + unittest.main() diff --git a/gazelle/python/testdata/respect_kind_mapping/foo.py b/gazelle/python/testdata/respect_kind_mapping/foo.py new file mode 100644 index 0000000000..932de45b74 --- /dev/null +++ b/gazelle/python/testdata/respect_kind_mapping/foo.py @@ -0,0 +1,16 @@ +# 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. + +def foo(): + return "foo" diff --git a/gazelle/python/testdata/respect_kind_mapping/test.yaml b/gazelle/python/testdata/respect_kind_mapping/test.yaml new file mode 100644 index 0000000000..2410223e59 --- /dev/null +++ b/gazelle/python/testdata/respect_kind_mapping/test.yaml @@ -0,0 +1,17 @@ +# 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. + +--- +expect: + exit_code: 0 diff --git a/gazelle/python/testdata/sibling_imports/README.md b/gazelle/python/testdata/sibling_imports/README.md new file mode 100644 index 0000000000..e59be07634 --- /dev/null +++ b/gazelle/python/testdata/sibling_imports/README.md @@ -0,0 +1,3 @@ +# Sibling imports + +This test case asserts that imports from sibling modules are resolved correctly. It covers 3 different types of imports in `pkg/unit_test.py` \ No newline at end of file diff --git a/gazelle/testdata/simple_library_without_init/WORKSPACE b/gazelle/python/testdata/sibling_imports/WORKSPACE similarity index 100% rename from gazelle/testdata/simple_library_without_init/WORKSPACE rename to gazelle/python/testdata/sibling_imports/WORKSPACE diff --git a/gazelle/testdata/subdir_sources/one/two/BUILD.in b/gazelle/python/testdata/sibling_imports/pkg/BUILD.in similarity index 100% rename from gazelle/testdata/subdir_sources/one/two/BUILD.in rename to gazelle/python/testdata/sibling_imports/pkg/BUILD.in diff --git a/gazelle/python/testdata/sibling_imports/pkg/BUILD.out b/gazelle/python/testdata/sibling_imports/pkg/BUILD.out new file mode 100644 index 0000000000..edb40a8bcb --- /dev/null +++ b/gazelle/python/testdata/sibling_imports/pkg/BUILD.out @@ -0,0 +1,29 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +py_library( + name = "pkg", + srcs = [ + "__init__.py", + "a.py", + "b.py", + ], + imports = [".."], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "test_util", + srcs = ["test_util.py"], + imports = [".."], +) + +py_test( + name = "unit_test", + srcs = ["unit_test.py"], + imports = [".."], + deps = [ + ":pkg", + ":test_util", + ], +) + diff --git a/python/pip_install/extract_wheels/__init__.py b/gazelle/python/testdata/sibling_imports/pkg/__init__.py similarity index 100% rename from python/pip_install/extract_wheels/__init__.py rename to gazelle/python/testdata/sibling_imports/pkg/__init__.py diff --git a/gazelle/testdata/subdir_sources/foo/has_build_bazel/BUILD.bazel.in b/gazelle/python/testdata/sibling_imports/pkg/a.py similarity index 100% rename from gazelle/testdata/subdir_sources/foo/has_build_bazel/BUILD.bazel.in rename to gazelle/python/testdata/sibling_imports/pkg/a.py diff --git a/gazelle/python/testdata/sibling_imports/pkg/b.py b/gazelle/python/testdata/sibling_imports/pkg/b.py new file mode 100644 index 0000000000..7095bdcfb2 --- /dev/null +++ b/gazelle/python/testdata/sibling_imports/pkg/b.py @@ -0,0 +1,2 @@ +def run(): + pass \ No newline at end of file diff --git a/gazelle/testdata/with_nested_import_statements/BUILD.in b/gazelle/python/testdata/sibling_imports/pkg/test_util.py similarity index 100% rename from gazelle/testdata/with_nested_import_statements/BUILD.in rename to gazelle/python/testdata/sibling_imports/pkg/test_util.py diff --git a/gazelle/python/testdata/sibling_imports/pkg/unit_test.py b/gazelle/python/testdata/sibling_imports/pkg/unit_test.py new file mode 100644 index 0000000000..a3218e2ec2 --- /dev/null +++ b/gazelle/python/testdata/sibling_imports/pkg/unit_test.py @@ -0,0 +1,3 @@ +import a +from b import run +import test_util \ No newline at end of file diff --git a/gazelle/testdata/dependency_resolution_order/test.yaml b/gazelle/python/testdata/sibling_imports/test.yaml similarity index 100% rename from gazelle/testdata/dependency_resolution_order/test.yaml rename to gazelle/python/testdata/sibling_imports/test.yaml diff --git a/gazelle/testdata/with_std_requirements/BUILD.in b/gazelle/python/testdata/simple_binary/BUILD.in similarity index 100% rename from gazelle/testdata/with_std_requirements/BUILD.in rename to gazelle/python/testdata/simple_binary/BUILD.in diff --git a/gazelle/testdata/simple_binary/BUILD.out b/gazelle/python/testdata/simple_binary/BUILD.out similarity index 100% rename from gazelle/testdata/simple_binary/BUILD.out rename to gazelle/python/testdata/simple_binary/BUILD.out diff --git a/gazelle/testdata/simple_binary/README.md b/gazelle/python/testdata/simple_binary/README.md similarity index 100% rename from gazelle/testdata/simple_binary/README.md rename to gazelle/python/testdata/simple_binary/README.md diff --git a/gazelle/testdata/simple_test/WORKSPACE b/gazelle/python/testdata/simple_binary/WORKSPACE similarity index 100% rename from gazelle/testdata/simple_test/WORKSPACE rename to gazelle/python/testdata/simple_binary/WORKSPACE diff --git a/gazelle/python/testdata/simple_binary/__main__.py b/gazelle/python/testdata/simple_binary/__main__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/simple_binary/__main__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/simple_binary/test.yaml b/gazelle/python/testdata/simple_binary/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/simple_binary/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/simple_binary_with_library/BUILD.in b/gazelle/python/testdata/simple_binary_with_library/BUILD.in similarity index 100% rename from gazelle/testdata/simple_binary_with_library/BUILD.in rename to gazelle/python/testdata/simple_binary_with_library/BUILD.in diff --git a/gazelle/testdata/simple_binary_with_library/BUILD.out b/gazelle/python/testdata/simple_binary_with_library/BUILD.out similarity index 100% rename from gazelle/testdata/simple_binary_with_library/BUILD.out rename to gazelle/python/testdata/simple_binary_with_library/BUILD.out diff --git a/gazelle/testdata/simple_binary_with_library/README.md b/gazelle/python/testdata/simple_binary_with_library/README.md similarity index 100% rename from gazelle/testdata/simple_binary_with_library/README.md rename to gazelle/python/testdata/simple_binary_with_library/README.md diff --git a/gazelle/testdata/subdir_sources/WORKSPACE b/gazelle/python/testdata/simple_binary_with_library/WORKSPACE similarity index 100% rename from gazelle/testdata/subdir_sources/WORKSPACE rename to gazelle/python/testdata/simple_binary_with_library/WORKSPACE diff --git a/gazelle/python/testdata/simple_binary_with_library/__init__.py b/gazelle/python/testdata/simple_binary_with_library/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/simple_binary_with_library/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/simple_binary_with_library/__main__.py b/gazelle/python/testdata/simple_binary_with_library/__main__.py new file mode 100644 index 0000000000..bc7ddf0a71 --- /dev/null +++ b/gazelle/python/testdata/simple_binary_with_library/__main__.py @@ -0,0 +1,16 @@ +# 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. + +# For test purposes only. +import foo diff --git a/gazelle/python/testdata/simple_binary_with_library/bar.py b/gazelle/python/testdata/simple_binary_with_library/bar.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/simple_binary_with_library/bar.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/simple_binary_with_library/foo.py b/gazelle/python/testdata/simple_binary_with_library/foo.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/simple_binary_with_library/foo.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/simple_binary_with_library/test.yaml b/gazelle/python/testdata/simple_binary_with_library/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/simple_binary_with_library/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/with_third_party_requirements/BUILD.in b/gazelle/python/testdata/simple_library/BUILD.in similarity index 100% rename from gazelle/testdata/with_third_party_requirements/BUILD.in rename to gazelle/python/testdata/simple_library/BUILD.in diff --git a/gazelle/testdata/simple_library/BUILD.out b/gazelle/python/testdata/simple_library/BUILD.out similarity index 100% rename from gazelle/testdata/simple_library/BUILD.out rename to gazelle/python/testdata/simple_library/BUILD.out diff --git a/gazelle/testdata/simple_library/README.md b/gazelle/python/testdata/simple_library/README.md similarity index 100% rename from gazelle/testdata/simple_library/README.md rename to gazelle/python/testdata/simple_library/README.md diff --git a/gazelle/testdata/with_nested_import_statements/WORKSPACE b/gazelle/python/testdata/simple_library/WORKSPACE similarity index 100% rename from gazelle/testdata/with_nested_import_statements/WORKSPACE rename to gazelle/python/testdata/simple_library/WORKSPACE diff --git a/gazelle/python/testdata/simple_library/__init__.py b/gazelle/python/testdata/simple_library/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/simple_library/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/simple_library/test.yaml b/gazelle/python/testdata/simple_library/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/simple_library/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/python/testdata/simple_library_without_init/BUILD.in b/gazelle/python/testdata/simple_library_without_init/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/python/testdata/simple_library_without_init/BUILD.out b/gazelle/python/testdata/simple_library_without_init/BUILD.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/simple_library_without_init/README.md b/gazelle/python/testdata/simple_library_without_init/README.md similarity index 100% rename from gazelle/testdata/simple_library_without_init/README.md rename to gazelle/python/testdata/simple_library_without_init/README.md diff --git a/gazelle/testdata/with_std_requirements/WORKSPACE b/gazelle/python/testdata/simple_library_without_init/WORKSPACE similarity index 100% rename from gazelle/testdata/with_std_requirements/WORKSPACE rename to gazelle/python/testdata/simple_library_without_init/WORKSPACE diff --git a/gazelle/python/testdata/simple_library_without_init/foo/BUILD.in b/gazelle/python/testdata/simple_library_without_init/foo/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/simple_library_without_init/foo/BUILD.out b/gazelle/python/testdata/simple_library_without_init/foo/BUILD.out similarity index 100% rename from gazelle/testdata/simple_library_without_init/foo/BUILD.out rename to gazelle/python/testdata/simple_library_without_init/foo/BUILD.out diff --git a/gazelle/python/testdata/simple_library_without_init/foo/foo.py b/gazelle/python/testdata/simple_library_without_init/foo/foo.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/simple_library_without_init/foo/foo.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/simple_library_without_init/test.yaml b/gazelle/python/testdata/simple_library_without_init/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/simple_library_without_init/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/testdata/simple_test/BUILD.in b/gazelle/python/testdata/simple_test/BUILD.in similarity index 100% rename from gazelle/testdata/simple_test/BUILD.in rename to gazelle/python/testdata/simple_test/BUILD.in diff --git a/gazelle/testdata/simple_test/BUILD.out b/gazelle/python/testdata/simple_test/BUILD.out similarity index 100% rename from gazelle/testdata/simple_test/BUILD.out rename to gazelle/python/testdata/simple_test/BUILD.out diff --git a/gazelle/testdata/simple_test/README.md b/gazelle/python/testdata/simple_test/README.md similarity index 100% rename from gazelle/testdata/simple_test/README.md rename to gazelle/python/testdata/simple_test/README.md diff --git a/gazelle/testdata/with_third_party_requirements/WORKSPACE b/gazelle/python/testdata/simple_test/WORKSPACE similarity index 100% rename from gazelle/testdata/with_third_party_requirements/WORKSPACE rename to gazelle/python/testdata/simple_test/WORKSPACE diff --git a/gazelle/python/testdata/simple_test/__init__.py b/gazelle/python/testdata/simple_test/__init__.py new file mode 100644 index 0000000000..b274b0d921 --- /dev/null +++ b/gazelle/python/testdata/simple_test/__init__.py @@ -0,0 +1,17 @@ +# 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 foo import foo + +_ = foo diff --git a/gazelle/python/testdata/simple_test/__test__.py b/gazelle/python/testdata/simple_test/__test__.py new file mode 100644 index 0000000000..2b180a5f53 --- /dev/null +++ b/gazelle/python/testdata/simple_test/__test__.py @@ -0,0 +1,26 @@ +# 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 __init__ import foo + + +class FooTest(unittest.TestCase): + def test_foo(self): + self.assertEqual("foo", foo()) + + +if __name__ == "__main__": + unittest.main() diff --git a/gazelle/python/testdata/simple_test/foo.py b/gazelle/python/testdata/simple_test/foo.py new file mode 100644 index 0000000000..932de45b74 --- /dev/null +++ b/gazelle/python/testdata/simple_test/foo.py @@ -0,0 +1,16 @@ +# 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. + +def foo(): + return "foo" diff --git a/gazelle/python/testdata/simple_test/test.yaml b/gazelle/python/testdata/simple_test/test.yaml new file mode 100644 index 0000000000..2410223e59 --- /dev/null +++ b/gazelle/python/testdata/simple_test/test.yaml @@ -0,0 +1,17 @@ +# 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. + +--- +expect: + exit_code: 0 diff --git a/gazelle/python/testdata/simple_test_with_conftest/BUILD.in b/gazelle/python/testdata/simple_test_with_conftest/BUILD.in new file mode 100644 index 0000000000..3f2beb3147 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/BUILD.in @@ -0,0 +1 @@ +load("@rules_python//python:defs.bzl", "py_library") diff --git a/gazelle/python/testdata/simple_test_with_conftest/BUILD.out b/gazelle/python/testdata/simple_test_with_conftest/BUILD.out new file mode 100644 index 0000000000..18079bf2f4 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/BUILD.out @@ -0,0 +1,27 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +py_library( + name = "simple_test_with_conftest", + srcs = [ + "__init__.py", + "foo.py", + ], + visibility = ["//:__subpackages__"], +) + +py_library( + name = "conftest", + testonly = True, + srcs = ["conftest.py"], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "simple_test_with_conftest_test", + srcs = ["__test__.py"], + main = "__test__.py", + deps = [ + ":conftest", + ":simple_test_with_conftest", + ], +) diff --git a/gazelle/python/testdata/simple_test_with_conftest/README.md b/gazelle/python/testdata/simple_test_with_conftest/README.md new file mode 100644 index 0000000000..0ff245f808 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/README.md @@ -0,0 +1,4 @@ +# Simple test with conftest.py + +This test case asserts that a simple `py_test` is generated as expected when a +`conftest.py` is present. diff --git a/gazelle/python/testdata/simple_test_with_conftest/WORKSPACE b/gazelle/python/testdata/simple_test_with_conftest/WORKSPACE new file mode 100644 index 0000000000..faff6af87a --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/WORKSPACE @@ -0,0 +1 @@ +# This is a Bazel workspace for the Gazelle test data. diff --git a/gazelle/python/testdata/simple_test_with_conftest/__init__.py b/gazelle/python/testdata/simple_test_with_conftest/__init__.py new file mode 100644 index 0000000000..b274b0d921 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/__init__.py @@ -0,0 +1,17 @@ +# 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 foo import foo + +_ = foo diff --git a/gazelle/python/testdata/simple_test_with_conftest/__test__.py b/gazelle/python/testdata/simple_test_with_conftest/__test__.py new file mode 100644 index 0000000000..2b180a5f53 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/__test__.py @@ -0,0 +1,26 @@ +# 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 __init__ import foo + + +class FooTest(unittest.TestCase): + def test_foo(self): + self.assertEqual("foo", foo()) + + +if __name__ == "__main__": + unittest.main() diff --git a/gazelle/python/testdata/simple_test_with_conftest/bar/BUILD.in b/gazelle/python/testdata/simple_test_with_conftest/bar/BUILD.in new file mode 100644 index 0000000000..3f2beb3147 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/bar/BUILD.in @@ -0,0 +1 @@ +load("@rules_python//python:defs.bzl", "py_library") diff --git a/gazelle/python/testdata/simple_test_with_conftest/bar/BUILD.out b/gazelle/python/testdata/simple_test_with_conftest/bar/BUILD.out new file mode 100644 index 0000000000..e42c4998b1 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/bar/BUILD.out @@ -0,0 +1,30 @@ +load("@rules_python//python:defs.bzl", "py_library", "py_test") + +py_library( + name = "bar", + srcs = [ + "__init__.py", + "bar.py", + ], + imports = [".."], + visibility = ["//:__subpackages__"], +) + +py_library( + name = "conftest", + testonly = True, + srcs = ["conftest.py"], + imports = [".."], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "bar_test", + srcs = ["__test__.py"], + imports = [".."], + main = "__test__.py", + deps = [ + ":bar", + ":conftest", + ], +) diff --git a/gazelle/python/testdata/simple_test_with_conftest/bar/__init__.py b/gazelle/python/testdata/simple_test_with_conftest/bar/__init__.py new file mode 100644 index 0000000000..3f0275e179 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/bar/__init__.py @@ -0,0 +1,17 @@ +# 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 bar import bar + +_ = bar diff --git a/gazelle/python/testdata/simple_test_with_conftest/bar/__test__.py b/gazelle/python/testdata/simple_test_with_conftest/bar/__test__.py new file mode 100644 index 0000000000..00c4c28247 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/bar/__test__.py @@ -0,0 +1,26 @@ +# 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 __init__ import bar + + +class BarTest(unittest.TestCase): + def test_bar(self): + self.assertEqual("bar", bar()) + + +if __name__ == "__main__": + unittest.main() diff --git a/gazelle/python/testdata/simple_test_with_conftest/bar/bar.py b/gazelle/python/testdata/simple_test_with_conftest/bar/bar.py new file mode 100644 index 0000000000..ba6a62db30 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/bar/bar.py @@ -0,0 +1,17 @@ +# 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. + + +def bar(): + return "bar" diff --git a/gazelle/python/testdata/simple_test_with_conftest/bar/conftest.py b/gazelle/python/testdata/simple_test_with_conftest/bar/conftest.py new file mode 100644 index 0000000000..41010956cf --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/bar/conftest.py @@ -0,0 +1,13 @@ +# 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. diff --git a/gazelle/python/testdata/simple_test_with_conftest/conftest.py b/gazelle/python/testdata/simple_test_with_conftest/conftest.py new file mode 100644 index 0000000000..bbdfb4c588 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/conftest.py @@ -0,0 +1,14 @@ +# 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. + diff --git a/gazelle/python/testdata/simple_test_with_conftest/foo.py b/gazelle/python/testdata/simple_test_with_conftest/foo.py new file mode 100644 index 0000000000..932de45b74 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/foo.py @@ -0,0 +1,16 @@ +# 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. + +def foo(): + return "foo" diff --git a/gazelle/python/testdata/simple_test_with_conftest/test.yaml b/gazelle/python/testdata/simple_test_with_conftest/test.yaml new file mode 100644 index 0000000000..2410223e59 --- /dev/null +++ b/gazelle/python/testdata/simple_test_with_conftest/test.yaml @@ -0,0 +1,17 @@ +# 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. + +--- +expect: + exit_code: 0 diff --git a/gazelle/python/testdata/subdir_sources/BUILD.in b/gazelle/python/testdata/subdir_sources/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/subdir_sources/BUILD.out b/gazelle/python/testdata/subdir_sources/BUILD.out similarity index 100% rename from gazelle/testdata/subdir_sources/BUILD.out rename to gazelle/python/testdata/subdir_sources/BUILD.out diff --git a/gazelle/testdata/subdir_sources/README.md b/gazelle/python/testdata/subdir_sources/README.md similarity index 100% rename from gazelle/testdata/subdir_sources/README.md rename to gazelle/python/testdata/subdir_sources/README.md diff --git a/gazelle/python/testdata/subdir_sources/WORKSPACE b/gazelle/python/testdata/subdir_sources/WORKSPACE new file mode 100644 index 0000000000..faff6af87a --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/WORKSPACE @@ -0,0 +1 @@ +# This is a Bazel workspace for the Gazelle test data. diff --git a/gazelle/python/testdata/subdir_sources/__main__.py b/gazelle/python/testdata/subdir_sources/__main__.py new file mode 100644 index 0000000000..aacfc67bc5 --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/__main__.py @@ -0,0 +1,21 @@ +# 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 foo.bar.bar as bar +import foo.baz.baz as baz +import one.two.three as three + +_ = bar +_ = baz +_ = three diff --git a/gazelle/python/testdata/subdir_sources/foo/BUILD.in b/gazelle/python/testdata/subdir_sources/foo/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/subdir_sources/foo/BUILD.out b/gazelle/python/testdata/subdir_sources/foo/BUILD.out similarity index 100% rename from gazelle/testdata/subdir_sources/foo/BUILD.out rename to gazelle/python/testdata/subdir_sources/foo/BUILD.out diff --git a/gazelle/python/testdata/subdir_sources/foo/__init__.py b/gazelle/python/testdata/subdir_sources/foo/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/foo/bar/bar.py b/gazelle/python/testdata/subdir_sources/foo/bar/bar.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/bar/bar.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/foo/baz/baz.py b/gazelle/python/testdata/subdir_sources/foo/baz/baz.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/baz/baz.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/foo/foo.py b/gazelle/python/testdata/subdir_sources/foo/foo.py new file mode 100644 index 0000000000..a98c73d4eb --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/foo.py @@ -0,0 +1,17 @@ +# 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 foo.bar.bar as bar + +_ = bar diff --git a/gazelle/python/testdata/subdir_sources/foo/has_build/BUILD.in b/gazelle/python/testdata/subdir_sources/foo/has_build/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/subdir_sources/foo/has_build/BUILD.out b/gazelle/python/testdata/subdir_sources/foo/has_build/BUILD.out similarity index 100% rename from gazelle/testdata/subdir_sources/foo/has_build/BUILD.out rename to gazelle/python/testdata/subdir_sources/foo/has_build/BUILD.out diff --git a/gazelle/python/testdata/subdir_sources/foo/has_build/python/my_module.py b/gazelle/python/testdata/subdir_sources/foo/has_build/python/my_module.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/has_build/python/my_module.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/foo/has_build_bazel/BUILD.bazel.in b/gazelle/python/testdata/subdir_sources/foo/has_build_bazel/BUILD.bazel.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/python/testdata/subdir_sources/foo/has_build_bazel/python/my_module.py b/gazelle/python/testdata/subdir_sources/foo/has_build_bazel/python/my_module.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/has_build_bazel/python/my_module.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/foo/has_init/BUILD.in b/gazelle/python/testdata/subdir_sources/foo/has_init/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/subdir_sources/foo/has_init/BUILD.out b/gazelle/python/testdata/subdir_sources/foo/has_init/BUILD.out similarity index 100% rename from gazelle/testdata/subdir_sources/foo/has_init/BUILD.out rename to gazelle/python/testdata/subdir_sources/foo/has_init/BUILD.out diff --git a/gazelle/python/testdata/subdir_sources/foo/has_init/__init__.py b/gazelle/python/testdata/subdir_sources/foo/has_init/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/has_init/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/foo/has_init/python/my_module.py b/gazelle/python/testdata/subdir_sources/foo/has_init/python/my_module.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/has_init/python/my_module.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/foo/has_main/BUILD.in b/gazelle/python/testdata/subdir_sources/foo/has_main/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/subdir_sources/foo/has_main/BUILD.out b/gazelle/python/testdata/subdir_sources/foo/has_main/BUILD.out similarity index 100% rename from gazelle/testdata/subdir_sources/foo/has_main/BUILD.out rename to gazelle/python/testdata/subdir_sources/foo/has_main/BUILD.out diff --git a/gazelle/python/testdata/subdir_sources/foo/has_main/__main__.py b/gazelle/python/testdata/subdir_sources/foo/has_main/__main__.py new file mode 100644 index 0000000000..bd0fe61faa --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/has_main/__main__.py @@ -0,0 +1,16 @@ +# 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. + +# For test purposes only. +import foo.has_main.python.my_module \ No newline at end of file diff --git a/gazelle/python/testdata/subdir_sources/foo/has_main/python/my_module.py b/gazelle/python/testdata/subdir_sources/foo/has_main/python/my_module.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/has_main/python/my_module.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/foo/has_test/BUILD.in b/gazelle/python/testdata/subdir_sources/foo/has_test/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/subdir_sources/foo/has_test/BUILD.out b/gazelle/python/testdata/subdir_sources/foo/has_test/BUILD.out similarity index 100% rename from gazelle/testdata/subdir_sources/foo/has_test/BUILD.out rename to gazelle/python/testdata/subdir_sources/foo/has_test/BUILD.out diff --git a/gazelle/python/testdata/subdir_sources/foo/has_test/__test__.py b/gazelle/python/testdata/subdir_sources/foo/has_test/__test__.py new file mode 100644 index 0000000000..3c9ed1a1bd --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/has_test/__test__.py @@ -0,0 +1,16 @@ +# 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. + +# For test purposes only. +import foo.has_test.python.my_module \ No newline at end of file diff --git a/gazelle/python/testdata/subdir_sources/foo/has_test/python/my_module.py b/gazelle/python/testdata/subdir_sources/foo/has_test/python/my_module.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/foo/has_test/python/my_module.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/one/BUILD.in b/gazelle/python/testdata/subdir_sources/one/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/subdir_sources/one/BUILD.out b/gazelle/python/testdata/subdir_sources/one/BUILD.out similarity index 100% rename from gazelle/testdata/subdir_sources/one/BUILD.out rename to gazelle/python/testdata/subdir_sources/one/BUILD.out diff --git a/gazelle/python/testdata/subdir_sources/one/__init__.py b/gazelle/python/testdata/subdir_sources/one/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/one/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/one/two/BUILD.in b/gazelle/python/testdata/subdir_sources/one/two/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/subdir_sources/one/two/BUILD.out b/gazelle/python/testdata/subdir_sources/one/two/BUILD.out similarity index 100% rename from gazelle/testdata/subdir_sources/one/two/BUILD.out rename to gazelle/python/testdata/subdir_sources/one/two/BUILD.out diff --git a/gazelle/python/testdata/subdir_sources/one/two/README.md b/gazelle/python/testdata/subdir_sources/one/two/README.md new file mode 100644 index 0000000000..ec4c15ddaa --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/one/two/README.md @@ -0,0 +1,2 @@ +# Same package imports +This test case asserts that no `deps` is needed when a module imports another module in the same package \ No newline at end of file diff --git a/gazelle/python/testdata/subdir_sources/one/two/__init__.py b/gazelle/python/testdata/subdir_sources/one/two/__init__.py new file mode 100644 index 0000000000..72357b3c46 --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/one/two/__init__.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. + +import foo.baz.baz as baz +import three + +_ = baz diff --git a/gazelle/python/testdata/subdir_sources/one/two/three.py b/gazelle/python/testdata/subdir_sources/one/two/three.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/one/two/three.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/subdir_sources/test.yaml b/gazelle/python/testdata/subdir_sources/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/subdir_sources/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/python/testdata/with_nested_import_statements/BUILD.in b/gazelle/python/testdata/with_nested_import_statements/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/with_nested_import_statements/BUILD.out b/gazelle/python/testdata/with_nested_import_statements/BUILD.out similarity index 78% rename from gazelle/testdata/with_nested_import_statements/BUILD.out rename to gazelle/python/testdata/with_nested_import_statements/BUILD.out index bb2f34db55..45bf265180 100644 --- a/gazelle/testdata/with_nested_import_statements/BUILD.out +++ b/gazelle/python/testdata/with_nested_import_statements/BUILD.out @@ -4,5 +4,5 @@ py_library( name = "with_nested_import_statements", srcs = ["__init__.py"], visibility = ["//:__subpackages__"], - deps = ["@gazelle_python_test//pypi__boto3"], + deps = ["@gazelle_python_test_boto3//:pkg"], ) diff --git a/gazelle/testdata/with_nested_import_statements/README.md b/gazelle/python/testdata/with_nested_import_statements/README.md similarity index 100% rename from gazelle/testdata/with_nested_import_statements/README.md rename to gazelle/python/testdata/with_nested_import_statements/README.md diff --git a/gazelle/python/testdata/with_nested_import_statements/WORKSPACE b/gazelle/python/testdata/with_nested_import_statements/WORKSPACE new file mode 100644 index 0000000000..faff6af87a --- /dev/null +++ b/gazelle/python/testdata/with_nested_import_statements/WORKSPACE @@ -0,0 +1 @@ +# This is a Bazel workspace for the Gazelle test data. diff --git a/gazelle/python/testdata/with_nested_import_statements/__init__.py b/gazelle/python/testdata/with_nested_import_statements/__init__.py new file mode 100644 index 0000000000..733b51f974 --- /dev/null +++ b/gazelle/python/testdata/with_nested_import_statements/__init__.py @@ -0,0 +1,25 @@ +# 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 sys + +_ = os +_ = sys + + +def main(): + import boto3 + + _ = boto3 diff --git a/gazelle/python/testdata/with_nested_import_statements/gazelle_python.yaml b/gazelle/python/testdata/with_nested_import_statements/gazelle_python.yaml new file mode 100644 index 0000000000..1bf594f9b4 --- /dev/null +++ b/gazelle/python/testdata/with_nested_import_statements/gazelle_python.yaml @@ -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. + +manifest: + modules_mapping: + boto3: boto3 + pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/python/testdata/with_nested_import_statements/test.yaml b/gazelle/python/testdata/with_nested_import_statements/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/with_nested_import_statements/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/python/testdata/with_std_requirements/BUILD.in b/gazelle/python/testdata/with_std_requirements/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/with_std_requirements/BUILD.out b/gazelle/python/testdata/with_std_requirements/BUILD.out similarity index 100% rename from gazelle/testdata/with_std_requirements/BUILD.out rename to gazelle/python/testdata/with_std_requirements/BUILD.out diff --git a/gazelle/testdata/with_std_requirements/README.md b/gazelle/python/testdata/with_std_requirements/README.md similarity index 100% rename from gazelle/testdata/with_std_requirements/README.md rename to gazelle/python/testdata/with_std_requirements/README.md diff --git a/gazelle/python/testdata/with_std_requirements/WORKSPACE b/gazelle/python/testdata/with_std_requirements/WORKSPACE new file mode 100644 index 0000000000..faff6af87a --- /dev/null +++ b/gazelle/python/testdata/with_std_requirements/WORKSPACE @@ -0,0 +1 @@ +# This is a Bazel workspace for the Gazelle test data. diff --git a/gazelle/python/testdata/with_std_requirements/__init__.py b/gazelle/python/testdata/with_std_requirements/__init__.py new file mode 100644 index 0000000000..e51d320213 --- /dev/null +++ b/gazelle/python/testdata/with_std_requirements/__init__.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. + +import os +import sys + +_ = os +_ = sys diff --git a/gazelle/python/testdata/with_std_requirements/test.yaml b/gazelle/python/testdata/with_std_requirements/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/with_std_requirements/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/python/testdata/with_third_party_requirements/BUILD.in b/gazelle/python/testdata/with_third_party_requirements/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/testdata/with_third_party_requirements/BUILD.out b/gazelle/python/testdata/with_third_party_requirements/BUILD.out similarity index 62% rename from gazelle/testdata/with_third_party_requirements/BUILD.out rename to gazelle/python/testdata/with_third_party_requirements/BUILD.out index 9854730a2e..2a97d8bc1e 100644 --- a/gazelle/testdata/with_third_party_requirements/BUILD.out +++ b/gazelle/python/testdata/with_third_party_requirements/BUILD.out @@ -9,9 +9,9 @@ py_library( ], visibility = ["//:__subpackages__"], deps = [ - "@gazelle_python_test//pypi__baz", - "@gazelle_python_test//pypi__boto3", - "@gazelle_python_test//pypi__djangorestframework", + "@gazelle_python_test_baz//:pkg", + "@gazelle_python_test_boto3//:pkg", + "@gazelle_python_test_djangorestframework//:pkg", ], ) @@ -20,8 +20,5 @@ py_binary( srcs = ["__main__.py"], main = "__main__.py", visibility = ["//:__subpackages__"], - deps = [ - ":with_third_party_requirements", - "@gazelle_python_test//pypi__baz", - ], + deps = ["@gazelle_python_test_baz//:pkg"], ) diff --git a/gazelle/python/testdata/with_third_party_requirements/README.md b/gazelle/python/testdata/with_third_party_requirements/README.md new file mode 100644 index 0000000000..a7ef7a3ca7 --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements/README.md @@ -0,0 +1,7 @@ +# With third-party requirements + +This test case asserts that +* a `py_library` is generated with dependencies +extracted from its sources and a `py_binary` is generated embeding the +`py_library` and inherits its dependencies, without specifying the `deps` again. +* when a third-party library and a module in the same package having the same name, the one in the same package takes precedence. diff --git a/gazelle/python/testdata/with_third_party_requirements/WORKSPACE b/gazelle/python/testdata/with_third_party_requirements/WORKSPACE new file mode 100644 index 0000000000..faff6af87a --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements/WORKSPACE @@ -0,0 +1 @@ +# This is a Bazel workspace for the Gazelle test data. diff --git a/gazelle/python/testdata/with_third_party_requirements/__init__.py b/gazelle/python/testdata/with_third_party_requirements/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/with_third_party_requirements/__main__.py b/gazelle/python/testdata/with_third_party_requirements/__main__.py new file mode 100644 index 0000000000..38e9a55fb5 --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements/__main__.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. + +import bar +import foo + +_ = bar +_ = foo diff --git a/gazelle/python/testdata/with_third_party_requirements/bar.py b/gazelle/python/testdata/with_third_party_requirements/bar.py new file mode 100644 index 0000000000..08f2e7c289 --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements/bar.py @@ -0,0 +1,25 @@ +# 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 bar +import boto3 +import rest_framework + +_ = os + +_ = bar +_ = boto3 +_ = rest_framework diff --git a/gazelle/python/testdata/with_third_party_requirements/foo.py b/gazelle/python/testdata/with_third_party_requirements/foo.py new file mode 100644 index 0000000000..9bebbfcfc6 --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements/foo.py @@ -0,0 +1,25 @@ +# 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 sys + +import boto3 +import foo +import rest_framework + +_ = sys + +_ = boto3 +_ = foo +_ = rest_framework diff --git a/gazelle/python/testdata/with_third_party_requirements/gazelle_python.yaml b/gazelle/python/testdata/with_third_party_requirements/gazelle_python.yaml new file mode 100644 index 0000000000..7753cfff2c --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements/gazelle_python.yaml @@ -0,0 +1,21 @@ +# 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. + +manifest: + modules_mapping: + boto3: boto3 + rest_framework: djangorestframework + foo: baz + bar: baz + pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/python/testdata/with_third_party_requirements/test.yaml b/gazelle/python/testdata/with_third_party_requirements/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/python/testdata/with_third_party_requirements_from_imports/BUILD.in b/gazelle/python/testdata/with_third_party_requirements_from_imports/BUILD.in new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/python/testdata/with_third_party_requirements_from_imports/BUILD.out b/gazelle/python/testdata/with_third_party_requirements_from_imports/BUILD.out new file mode 100644 index 0000000000..577f167143 --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements_from_imports/BUILD.out @@ -0,0 +1,25 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_library") + +py_library( + name = "with_third_party_requirements_from_imports", + srcs = [ + "__init__.py", + "bar.py", + ], + visibility = ["//:__subpackages__"], + deps = [ + "@gazelle_python_test_google_cloud_aiplatform//:pkg", + "@gazelle_python_test_google_cloud_storage//:pkg", + ], +) + +py_binary( + name = "with_third_party_requirements_from_imports_bin", + srcs = ["__main__.py"], + main = "__main__.py", + visibility = ["//:__subpackages__"], + deps = [ + ":with_third_party_requirements_from_imports", + "@gazelle_python_test_google_cloud_aiplatform//:pkg", + ], +) diff --git a/gazelle/python/testdata/with_third_party_requirements_from_imports/README.md b/gazelle/python/testdata/with_third_party_requirements_from_imports/README.md new file mode 100644 index 0000000000..c50a1ca100 --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements_from_imports/README.md @@ -0,0 +1,15 @@ +# With third-party requirements (from imports) + +This test case covers imports of the form: + +```python +from my_pip_dep import foo +``` + +for example + +```python +from google.cloud import aiplatform, storage +``` + +See https://github.com/bazelbuild/rules_python/issues/709 and https://github.com/sramirezmartin/gazelle-toy-example. diff --git a/gazelle/python/testdata/with_third_party_requirements_from_imports/WORKSPACE b/gazelle/python/testdata/with_third_party_requirements_from_imports/WORKSPACE new file mode 100644 index 0000000000..faff6af87a --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements_from_imports/WORKSPACE @@ -0,0 +1 @@ +# This is a Bazel workspace for the Gazelle test data. diff --git a/gazelle/python/testdata/with_third_party_requirements_from_imports/__init__.py b/gazelle/python/testdata/with_third_party_requirements_from_imports/__init__.py new file mode 100644 index 0000000000..730755995d --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements_from_imports/__init__.py @@ -0,0 +1,15 @@ +# 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. + +# For test purposes only. diff --git a/gazelle/python/testdata/with_third_party_requirements_from_imports/__main__.py b/gazelle/python/testdata/with_third_party_requirements_from_imports/__main__.py new file mode 100644 index 0000000000..2062a9b04a --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements_from_imports/__main__.py @@ -0,0 +1,20 @@ +# 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 bar import main +from google.cloud import aiplatform + +if __name__ == "__main__": + print(aiplatform) + main() diff --git a/gazelle/python/testdata/with_third_party_requirements_from_imports/bar.py b/gazelle/python/testdata/with_third_party_requirements_from_imports/bar.py new file mode 100644 index 0000000000..6886b2b4e9 --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements_from_imports/bar.py @@ -0,0 +1,20 @@ +# 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 google.cloud import aiplatform, storage + + +def main(): + a = dir(aiplatform) + b = dir(storage) diff --git a/gazelle/python/testdata/with_third_party_requirements_from_imports/gazelle_python.yaml b/gazelle/python/testdata/with_third_party_requirements_from_imports/gazelle_python.yaml new file mode 100644 index 0000000000..8b5694b2d7 --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements_from_imports/gazelle_python.yaml @@ -0,0 +1,1678 @@ +# 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. + +manifest: + modules_mapping: + cachetools: cachetools + cachetools.__init__: cachetools + cachetools.func: cachetools + cachetools.keys: cachetools + certifi: certifi + certifi.__init__: certifi + certifi.__main__: certifi + certifi.core: certifi + charset_normalizer: charset_normalizer + charset_normalizer.__init__: charset_normalizer + charset_normalizer.api: charset_normalizer + charset_normalizer.assets: charset_normalizer + charset_normalizer.assets.__init__: charset_normalizer + charset_normalizer.cd: charset_normalizer + charset_normalizer.cli: charset_normalizer + charset_normalizer.cli.__init__: charset_normalizer + charset_normalizer.cli.normalizer: charset_normalizer + charset_normalizer.constant: charset_normalizer + charset_normalizer.legacy: charset_normalizer + charset_normalizer.md: charset_normalizer + charset_normalizer.models: charset_normalizer + charset_normalizer.utils: charset_normalizer + charset_normalizer.version: charset_normalizer + dateutil: python_dateutil + dateutil.__init__: python_dateutil + dateutil._common: python_dateutil + dateutil._version: python_dateutil + dateutil.easter: python_dateutil + dateutil.parser: python_dateutil + dateutil.parser.__init__: python_dateutil + dateutil.parser._parser: python_dateutil + dateutil.parser.isoparser: python_dateutil + dateutil.relativedelta: python_dateutil + dateutil.rrule: python_dateutil + dateutil.tz: python_dateutil + dateutil.tz.__init__: python_dateutil + dateutil.tz._common: python_dateutil + dateutil.tz._factories: 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.__init__: python_dateutil + dateutil.zoneinfo.rebuild: python_dateutil + docs.conf: google_cloud_resource_manager + google._async_resumable_media: google_resumable_media + google._async_resumable_media.__init__: google_resumable_media + google._async_resumable_media._download: google_resumable_media + google._async_resumable_media._helpers: google_resumable_media + google._async_resumable_media._upload: google_resumable_media + google._async_resumable_media.requests: google_resumable_media + google._async_resumable_media.requests.__init__: google_resumable_media + google._async_resumable_media.requests._request_helpers: google_resumable_media + google._async_resumable_media.requests.download: google_resumable_media + google._async_resumable_media.requests.upload: google_resumable_media + google.api: googleapis_common_protos + google.api.__init__: googleapis_common_protos + google.api.annotations_pb2: googleapis_common_protos + google.api.auth_pb2: googleapis_common_protos + google.api.backend_pb2: googleapis_common_protos + google.api.billing_pb2: googleapis_common_protos + google.api.client_pb2: googleapis_common_protos + google.api.config_change_pb2: googleapis_common_protos + google.api.consumer_pb2: googleapis_common_protos + google.api.context_pb2: googleapis_common_protos + google.api.control_pb2: googleapis_common_protos + google.api.distribution_pb2: googleapis_common_protos + google.api.documentation_pb2: googleapis_common_protos + google.api.endpoint_pb2: googleapis_common_protos + google.api.error_reason_pb2: googleapis_common_protos + google.api.field_behavior_pb2: googleapis_common_protos + google.api.http_pb2: googleapis_common_protos + google.api.httpbody_pb2: googleapis_common_protos + google.api.label_pb2: googleapis_common_protos + google.api.launch_stage_pb2: googleapis_common_protos + google.api.log_pb2: googleapis_common_protos + google.api.logging_pb2: googleapis_common_protos + google.api.metric_pb2: googleapis_common_protos + google.api.monitored_resource_pb2: googleapis_common_protos + google.api.monitoring_pb2: googleapis_common_protos + google.api.quota_pb2: googleapis_common_protos + google.api.resource_pb2: googleapis_common_protos + google.api.routing_pb2: googleapis_common_protos + google.api.service_pb2: googleapis_common_protos + google.api.source_info_pb2: googleapis_common_protos + google.api.system_parameter_pb2: googleapis_common_protos + google.api.usage_pb2: googleapis_common_protos + google.api.visibility_pb2: googleapis_common_protos + google.api_core: google_api_core + google.api_core.__init__: google_api_core + google.api_core.bidi: google_api_core + google.api_core.client_info: google_api_core + google.api_core.client_options: google_api_core + google.api_core.datetime_helpers: google_api_core + google.api_core.exceptions: google_api_core + google.api_core.extended_operation: google_api_core + google.api_core.future: google_api_core + google.api_core.future.__init__: google_api_core + google.api_core.future._helpers: google_api_core + google.api_core.future.async_future: google_api_core + google.api_core.future.base: google_api_core + google.api_core.future.polling: google_api_core + google.api_core.gapic_v1: google_api_core + google.api_core.gapic_v1.__init__: google_api_core + google.api_core.gapic_v1.client_info: google_api_core + google.api_core.gapic_v1.config: google_api_core + google.api_core.gapic_v1.config_async: google_api_core + google.api_core.gapic_v1.method: google_api_core + google.api_core.gapic_v1.method_async: google_api_core + google.api_core.gapic_v1.routing_header: google_api_core + google.api_core.general_helpers: google_api_core + google.api_core.grpc_helpers: google_api_core + google.api_core.grpc_helpers_async: google_api_core + google.api_core.iam: google_api_core + google.api_core.operation: google_api_core + google.api_core.operation_async: google_api_core + google.api_core.operations_v1: google_api_core + google.api_core.operations_v1.__init__: google_api_core + google.api_core.operations_v1.abstract_operations_client: google_api_core + google.api_core.operations_v1.operations_async_client: google_api_core + google.api_core.operations_v1.operations_client: google_api_core + google.api_core.operations_v1.operations_client_config: google_api_core + google.api_core.operations_v1.pagers: google_api_core + google.api_core.operations_v1.transports: google_api_core + google.api_core.operations_v1.transports.__init__: google_api_core + google.api_core.operations_v1.transports.base: google_api_core + google.api_core.operations_v1.transports.rest: google_api_core + google.api_core.page_iterator: google_api_core + google.api_core.page_iterator_async: google_api_core + google.api_core.path_template: google_api_core + google.api_core.protobuf_helpers: google_api_core + google.api_core.rest_helpers: google_api_core + google.api_core.rest_streaming: google_api_core + google.api_core.retry: google_api_core + google.api_core.retry_async: google_api_core + google.api_core.timeout: google_api_core + google.api_core.version: google_api_core + google.auth: google_auth + google.auth.__init__: google_auth + google.auth._cloud_sdk: google_auth + google.auth._credentials_async: google_auth + google.auth._default: google_auth + google.auth._default_async: google_auth + google.auth._helpers: google_auth + google.auth._jwt_async: google_auth + google.auth._oauth2client: google_auth + google.auth._service_account_info: google_auth + google.auth.app_engine: google_auth + google.auth.aws: google_auth + google.auth.compute_engine: google_auth + google.auth.compute_engine.__init__: google_auth + google.auth.compute_engine._metadata: google_auth + google.auth.compute_engine.credentials: google_auth + google.auth.credentials: google_auth + google.auth.crypt: google_auth + google.auth.crypt.__init__: google_auth + google.auth.crypt._cryptography_rsa: google_auth + google.auth.crypt._helpers: google_auth + google.auth.crypt._python_rsa: google_auth + google.auth.crypt.base: google_auth + google.auth.crypt.es256: google_auth + google.auth.crypt.rsa: google_auth + google.auth.downscoped: google_auth + google.auth.environment_vars: google_auth + google.auth.exceptions: google_auth + google.auth.external_account: google_auth + google.auth.iam: google_auth + google.auth.identity_pool: google_auth + google.auth.impersonated_credentials: google_auth + google.auth.jwt: google_auth + google.auth.transport: google_auth + google.auth.transport.__init__: google_auth + google.auth.transport._aiohttp_requests: google_auth + google.auth.transport._http_client: google_auth + google.auth.transport._mtls_helper: google_auth + google.auth.transport.grpc: google_auth + google.auth.transport.mtls: google_auth + google.auth.transport.requests: google_auth + google.auth.transport.urllib3: google_auth + google.auth.version: google_auth + google.cloud._helpers: google_cloud_core + google.cloud._helpers.__init__: google_cloud_core + google.cloud._http: google_cloud_core + google.cloud._http.__init__: google_cloud_core + google.cloud._testing: google_cloud_core + google.cloud._testing.__init__: google_cloud_core + google.cloud.aiplatform: google_cloud_aiplatform + google.cloud.aiplatform.__init__: google_cloud_aiplatform + google.cloud.aiplatform._matching_engine: google_cloud_aiplatform + google.cloud.aiplatform._matching_engine.__init__: google_cloud_aiplatform + google.cloud.aiplatform._matching_engine.match_service_pb2: google_cloud_aiplatform + google.cloud.aiplatform._matching_engine.match_service_pb2_grpc: google_cloud_aiplatform + google.cloud.aiplatform._matching_engine.matching_engine_index: google_cloud_aiplatform + google.cloud.aiplatform._matching_engine.matching_engine_index_config: google_cloud_aiplatform + google.cloud.aiplatform._matching_engine.matching_engine_index_endpoint: google_cloud_aiplatform + google.cloud.aiplatform.base: google_cloud_aiplatform + google.cloud.aiplatform.compat: google_cloud_aiplatform + google.cloud.aiplatform.compat.__init__: google_cloud_aiplatform + google.cloud.aiplatform.compat.services: google_cloud_aiplatform + google.cloud.aiplatform.compat.services.__init__: google_cloud_aiplatform + google.cloud.aiplatform.compat.types: google_cloud_aiplatform + google.cloud.aiplatform.compat.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform.constants: google_cloud_aiplatform + google.cloud.aiplatform.constants.__init__: google_cloud_aiplatform + google.cloud.aiplatform.constants.base: google_cloud_aiplatform + google.cloud.aiplatform.constants.prediction: google_cloud_aiplatform + google.cloud.aiplatform.datasets: google_cloud_aiplatform + google.cloud.aiplatform.datasets.__init__: google_cloud_aiplatform + google.cloud.aiplatform.datasets._datasources: google_cloud_aiplatform + google.cloud.aiplatform.datasets.column_names_dataset: google_cloud_aiplatform + google.cloud.aiplatform.datasets.dataset: google_cloud_aiplatform + google.cloud.aiplatform.datasets.image_dataset: google_cloud_aiplatform + google.cloud.aiplatform.datasets.tabular_dataset: google_cloud_aiplatform + google.cloud.aiplatform.datasets.text_dataset: google_cloud_aiplatform + google.cloud.aiplatform.datasets.time_series_dataset: google_cloud_aiplatform + google.cloud.aiplatform.datasets.video_dataset: google_cloud_aiplatform + google.cloud.aiplatform.explain: google_cloud_aiplatform + google.cloud.aiplatform.explain.__init__: google_cloud_aiplatform + google.cloud.aiplatform.explain.lit: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata.__init__: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata.metadata_builder: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata.tf: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata.tf.__init__: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata.tf.v1: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata.tf.v1.__init__: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata.tf.v1.saved_model_metadata_builder: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata.tf.v2: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata.tf.v2.__init__: google_cloud_aiplatform + google.cloud.aiplatform.explain.metadata.tf.v2.saved_model_metadata_builder: google_cloud_aiplatform + google.cloud.aiplatform.featurestore: google_cloud_aiplatform + google.cloud.aiplatform.featurestore.__init__: google_cloud_aiplatform + google.cloud.aiplatform.featurestore.entity_type: google_cloud_aiplatform + google.cloud.aiplatform.featurestore.feature: google_cloud_aiplatform + google.cloud.aiplatform.featurestore.featurestore: google_cloud_aiplatform + google.cloud.aiplatform.gapic: google_cloud_aiplatform + google.cloud.aiplatform.gapic.__init__: google_cloud_aiplatform + google.cloud.aiplatform.gapic.schema: google_cloud_aiplatform + google.cloud.aiplatform.gapic.schema.__init__: google_cloud_aiplatform + google.cloud.aiplatform.helpers: google_cloud_aiplatform + google.cloud.aiplatform.helpers.__init__: google_cloud_aiplatform + google.cloud.aiplatform.helpers.container_uri_builders: google_cloud_aiplatform + google.cloud.aiplatform.hyperparameter_tuning: google_cloud_aiplatform + google.cloud.aiplatform.initializer: google_cloud_aiplatform + google.cloud.aiplatform.jobs: google_cloud_aiplatform + google.cloud.aiplatform.metadata: google_cloud_aiplatform + google.cloud.aiplatform.metadata.__init__: google_cloud_aiplatform + google.cloud.aiplatform.metadata.artifact: google_cloud_aiplatform + google.cloud.aiplatform.metadata.constants: google_cloud_aiplatform + google.cloud.aiplatform.metadata.context: google_cloud_aiplatform + google.cloud.aiplatform.metadata.execution: google_cloud_aiplatform + google.cloud.aiplatform.metadata.metadata: google_cloud_aiplatform + google.cloud.aiplatform.metadata.metadata_store: google_cloud_aiplatform + google.cloud.aiplatform.metadata.resource: google_cloud_aiplatform + google.cloud.aiplatform.model_evaluation: google_cloud_aiplatform + google.cloud.aiplatform.model_evaluation.__init__: google_cloud_aiplatform + google.cloud.aiplatform.model_evaluation.model_evaluation: google_cloud_aiplatform + google.cloud.aiplatform.models: google_cloud_aiplatform + google.cloud.aiplatform.pipeline_jobs: google_cloud_aiplatform + google.cloud.aiplatform.schema: google_cloud_aiplatform + google.cloud.aiplatform.tensorboard: google_cloud_aiplatform + google.cloud.aiplatform.tensorboard.__init__: google_cloud_aiplatform + google.cloud.aiplatform.tensorboard.plugins.tf_profiler.profile_uploader: google_cloud_aiplatform + google.cloud.aiplatform.tensorboard.tensorboard_resource: google_cloud_aiplatform + google.cloud.aiplatform.tensorboard.uploader: google_cloud_aiplatform + google.cloud.aiplatform.tensorboard.uploader_main: google_cloud_aiplatform + google.cloud.aiplatform.tensorboard.uploader_utils: google_cloud_aiplatform + google.cloud.aiplatform.training_jobs: google_cloud_aiplatform + google.cloud.aiplatform.training_utils: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.__init__: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.cloud_profiler: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.cloud_profiler.__init__: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.cloud_profiler.cloud_profiler_utils: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.cloud_profiler.initializer: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.cloud_profiler.plugins.base_plugin: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.cloud_profiler.plugins.tensorflow.tensorboard_api: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.cloud_profiler.plugins.tensorflow.tf_profiler: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.cloud_profiler.webserver: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.cloud_profiler.wsgi_types: google_cloud_aiplatform + google.cloud.aiplatform.training_utils.environment_variables: google_cloud_aiplatform + google.cloud.aiplatform.utils: google_cloud_aiplatform + google.cloud.aiplatform.utils.__init__: google_cloud_aiplatform + google.cloud.aiplatform.utils.column_transformations_utils: google_cloud_aiplatform + google.cloud.aiplatform.utils.console_utils: google_cloud_aiplatform + google.cloud.aiplatform.utils.enhanced_library: google_cloud_aiplatform + google.cloud.aiplatform.utils.enhanced_library.__init__: google_cloud_aiplatform + google.cloud.aiplatform.utils.enhanced_library._decorators: google_cloud_aiplatform + google.cloud.aiplatform.utils.enhanced_library.value_converter: google_cloud_aiplatform + google.cloud.aiplatform.utils.featurestore_utils: google_cloud_aiplatform + google.cloud.aiplatform.utils.gcs_utils: google_cloud_aiplatform + google.cloud.aiplatform.utils.pipeline_utils: google_cloud_aiplatform + google.cloud.aiplatform.utils.resource_manager_utils: google_cloud_aiplatform + google.cloud.aiplatform.utils.source_utils: google_cloud_aiplatform + google.cloud.aiplatform.utils.tensorboard_utils: google_cloud_aiplatform + google.cloud.aiplatform.utils.worker_spec_utils: google_cloud_aiplatform + google.cloud.aiplatform.utils.yaml_utils: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types.image_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types.image_object_detection: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types.image_segmentation: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types.text_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types.text_extraction: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types.text_sentiment: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types.video_action_recognition: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types.video_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.instance_v1.types.video_object_tracking: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params_v1: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params_v1.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params_v1.types: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params_v1.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params_v1.types.image_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params_v1.types.image_object_detection: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params_v1.types.image_segmentation: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params_v1.types.video_action_recognition: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params_v1.types.video_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.params_v1.types.video_object_tracking: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.image_object_detection: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.image_segmentation: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.tabular_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.tabular_regression: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.text_extraction: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.text_sentiment: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.video_action_recognition: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.video_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.predict.prediction_v1.types.video_object_tracking: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.automl_image_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.automl_image_object_detection: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.automl_image_segmentation: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.automl_tables: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.automl_text_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.automl_text_extraction: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.automl_text_sentiment: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.automl_video_action_recognition: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.automl_video_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.automl_video_object_tracking: google_cloud_aiplatform + google.cloud.aiplatform.v1.schema.trainingjob.definition_v1.types.export_evaluated_data_items_config: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types.image_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types.image_object_detection: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types.image_segmentation: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types.text_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types.text_extraction: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types.text_sentiment: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types.video_action_recognition: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types.video_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.instance_v1beta1.types.video_object_tracking: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params_v1beta1: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params_v1beta1.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params_v1beta1.types: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params_v1beta1.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params_v1beta1.types.image_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params_v1beta1.types.image_object_detection: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params_v1beta1.types.image_segmentation: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params_v1beta1.types.video_action_recognition: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params_v1beta1.types.video_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.params_v1beta1.types.video_object_tracking: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.image_object_detection: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.image_segmentation: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.tabular_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.tabular_regression: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.text_extraction: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.text_sentiment: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.time_series_forecasting: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.video_action_recognition: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.video_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.predict.prediction_v1beta1.types.video_object_tracking: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_forecasting: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_image_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_image_object_detection: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_image_segmentation: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_tables: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_text_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_text_extraction: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_text_sentiment: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_time_series_forecasting: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_video_action_recognition: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_video_classification: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.automl_video_object_tracking: google_cloud_aiplatform + google.cloud.aiplatform.v1beta1.schema.trainingjob.definition_v1beta1.types.export_evaluated_data_items_config: google_cloud_aiplatform + google.cloud.aiplatform.version: google_cloud_aiplatform + google.cloud.aiplatform_v1: google_cloud_aiplatform + google.cloud.aiplatform_v1.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.dataset_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.dataset_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.dataset_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.dataset_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.dataset_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.dataset_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.dataset_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.dataset_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.dataset_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.dataset_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.endpoint_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.endpoint_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.endpoint_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.endpoint_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.endpoint_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.endpoint_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.endpoint_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.endpoint_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.endpoint_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.endpoint_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_online_serving_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_online_serving_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_online_serving_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_online_serving_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_online_serving_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_online_serving_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_online_serving_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_online_serving_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_online_serving_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.featurestore_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_endpoint_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_endpoint_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_endpoint_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_endpoint_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_endpoint_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_endpoint_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_endpoint_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_endpoint_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_endpoint_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_endpoint_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.index_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.job_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.job_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.job_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.job_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.job_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.job_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.job_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.job_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.job_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.job_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.metadata_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.metadata_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.metadata_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.metadata_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.metadata_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.metadata_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.metadata_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.metadata_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.metadata_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.metadata_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.migration_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.migration_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.migration_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.migration_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.migration_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.migration_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.migration_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.migration_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.migration_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.migration_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.model_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.model_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.model_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.model_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.model_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.model_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.model_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.model_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.model_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.model_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.pipeline_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.pipeline_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.pipeline_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.pipeline_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.pipeline_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.pipeline_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.pipeline_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.pipeline_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.pipeline_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.pipeline_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.prediction_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.prediction_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.prediction_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.prediction_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.prediction_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.prediction_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.prediction_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.prediction_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.prediction_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.specialist_pool_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.specialist_pool_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.specialist_pool_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.specialist_pool_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.specialist_pool_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.specialist_pool_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.specialist_pool_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.specialist_pool_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.specialist_pool_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.specialist_pool_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.tensorboard_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.tensorboard_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.tensorboard_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.tensorboard_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.tensorboard_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.tensorboard_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.tensorboard_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.tensorboard_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.tensorboard_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.tensorboard_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.vizier_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.vizier_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.vizier_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.vizier_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.vizier_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.vizier_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.vizier_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.vizier_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.vizier_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1.services.vizier_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1.types: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.accelerator_type: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.annotation: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.annotation_spec: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.artifact: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.batch_prediction_job: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.completion_stats: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.context: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.custom_job: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.data_item: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.data_labeling_job: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.dataset: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.dataset_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.deployed_index_ref: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.deployed_model_ref: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.encryption_spec: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.endpoint: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.endpoint_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.entity_type: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.env_var: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.event: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.execution: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.explanation: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.explanation_metadata: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.feature: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.feature_monitoring_stats: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.feature_selector: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.featurestore: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.featurestore_monitoring: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.featurestore_online_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.featurestore_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.hyperparameter_tuning_job: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.index: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.index_endpoint: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.index_endpoint_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.index_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.io: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.job_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.job_state: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.lineage_subgraph: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.machine_resources: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.manual_batch_tuning_parameters: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.metadata_schema: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.metadata_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.metadata_store: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.migratable_resource: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.migration_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.model: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.model_deployment_monitoring_job: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.model_evaluation: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.model_evaluation_slice: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.model_monitoring: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.model_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.operation: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.pipeline_job: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.pipeline_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.pipeline_state: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.prediction_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.specialist_pool: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.specialist_pool_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.study: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.tensorboard: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.tensorboard_data: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.tensorboard_experiment: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.tensorboard_run: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.tensorboard_service: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.tensorboard_time_series: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.training_pipeline: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.types: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.unmanaged_container_model: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.user_action_reference: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.value: google_cloud_aiplatform + google.cloud.aiplatform_v1.types.vizier_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.dataset_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.dataset_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.dataset_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.dataset_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.dataset_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.dataset_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.dataset_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.dataset_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.dataset_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.dataset_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.endpoint_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.endpoint_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.endpoint_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.endpoint_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.endpoint_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.endpoint_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.endpoint_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.endpoint_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.endpoint_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.endpoint_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_online_serving_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_online_serving_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_online_serving_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_online_serving_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_online_serving_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_online_serving_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_online_serving_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_online_serving_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_online_serving_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.featurestore_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_endpoint_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_endpoint_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_endpoint_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_endpoint_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_endpoint_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_endpoint_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_endpoint_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_endpoint_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_endpoint_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_endpoint_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.index_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.job_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.job_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.job_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.job_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.job_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.job_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.job_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.job_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.job_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.job_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.metadata_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.metadata_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.metadata_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.metadata_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.metadata_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.metadata_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.metadata_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.metadata_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.metadata_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.metadata_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.migration_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.migration_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.migration_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.migration_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.migration_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.migration_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.migration_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.migration_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.migration_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.migration_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.model_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.model_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.model_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.model_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.model_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.model_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.model_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.model_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.model_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.model_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.pipeline_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.pipeline_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.pipeline_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.pipeline_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.pipeline_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.pipeline_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.pipeline_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.pipeline_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.pipeline_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.pipeline_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.prediction_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.prediction_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.prediction_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.prediction_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.prediction_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.prediction_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.prediction_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.prediction_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.prediction_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.specialist_pool_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.specialist_pool_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.specialist_pool_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.specialist_pool_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.specialist_pool_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.specialist_pool_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.specialist_pool_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.specialist_pool_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.specialist_pool_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.specialist_pool_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.tensorboard_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.tensorboard_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.tensorboard_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.tensorboard_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.tensorboard_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.tensorboard_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.tensorboard_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.tensorboard_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.tensorboard_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.tensorboard_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.vizier_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.vizier_service.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.vizier_service.async_client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.vizier_service.client: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.vizier_service.pagers: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.vizier_service.transports: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.vizier_service.transports.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.vizier_service.transports.base: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.vizier_service.transports.grpc: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.services.vizier_service.transports.grpc_asyncio: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.__init__: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.accelerator_type: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.annotation: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.annotation_spec: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.artifact: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.batch_prediction_job: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.completion_stats: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.context: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.custom_job: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.data_item: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.data_labeling_job: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.dataset: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.dataset_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.deployed_index_ref: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.deployed_model_ref: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.encryption_spec: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.endpoint: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.endpoint_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.entity_type: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.env_var: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.event: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.execution: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.explanation: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.explanation_metadata: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.feature: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.feature_monitoring_stats: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.feature_selector: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.featurestore: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.featurestore_monitoring: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.featurestore_online_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.featurestore_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.hyperparameter_tuning_job: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.index: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.index_endpoint: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.index_endpoint_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.index_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.io: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.job_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.job_state: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.lineage_subgraph: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.machine_resources: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.manual_batch_tuning_parameters: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.metadata_schema: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.metadata_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.metadata_store: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.migratable_resource: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.migration_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.model: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.model_deployment_monitoring_job: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.model_evaluation: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.model_evaluation_slice: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.model_monitoring: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.model_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.operation: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.pipeline_job: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.pipeline_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.pipeline_state: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.prediction_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.specialist_pool: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.specialist_pool_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.study: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.tensorboard: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.tensorboard_data: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.tensorboard_experiment: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.tensorboard_run: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.tensorboard_service: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.tensorboard_time_series: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.training_pipeline: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.types: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.unmanaged_container_model: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.user_action_reference: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.value: google_cloud_aiplatform + google.cloud.aiplatform_v1beta1.types.vizier_service: google_cloud_aiplatform + google.cloud.bigquery: google_cloud_bigquery + google.cloud.bigquery.__init__: google_cloud_bigquery + google.cloud.bigquery._helpers: google_cloud_bigquery + google.cloud.bigquery._http: google_cloud_bigquery + google.cloud.bigquery._pandas_helpers: google_cloud_bigquery + google.cloud.bigquery._tqdm_helpers: google_cloud_bigquery + google.cloud.bigquery.client: google_cloud_bigquery + google.cloud.bigquery.dataset: google_cloud_bigquery + google.cloud.bigquery.dbapi: google_cloud_bigquery + google.cloud.bigquery.dbapi.__init__: google_cloud_bigquery + google.cloud.bigquery.dbapi._helpers: google_cloud_bigquery + google.cloud.bigquery.dbapi.connection: google_cloud_bigquery + google.cloud.bigquery.dbapi.cursor: google_cloud_bigquery + google.cloud.bigquery.dbapi.exceptions: google_cloud_bigquery + google.cloud.bigquery.dbapi.types: google_cloud_bigquery + google.cloud.bigquery.encryption_configuration: google_cloud_bigquery + google.cloud.bigquery.enums: google_cloud_bigquery + google.cloud.bigquery.exceptions: google_cloud_bigquery + google.cloud.bigquery.external_config: google_cloud_bigquery + google.cloud.bigquery.format_options: google_cloud_bigquery + google.cloud.bigquery.iam: google_cloud_bigquery + google.cloud.bigquery.job: google_cloud_bigquery + google.cloud.bigquery.job.__init__: google_cloud_bigquery + google.cloud.bigquery.job.base: google_cloud_bigquery + google.cloud.bigquery.job.copy_: google_cloud_bigquery + google.cloud.bigquery.job.extract: google_cloud_bigquery + google.cloud.bigquery.job.load: google_cloud_bigquery + google.cloud.bigquery.job.query: google_cloud_bigquery + google.cloud.bigquery.magics: google_cloud_bigquery + google.cloud.bigquery.magics.__init__: google_cloud_bigquery + google.cloud.bigquery.magics.line_arg_parser: google_cloud_bigquery + google.cloud.bigquery.magics.line_arg_parser.__init__: google_cloud_bigquery + google.cloud.bigquery.magics.line_arg_parser.exceptions: google_cloud_bigquery + google.cloud.bigquery.magics.line_arg_parser.lexer: google_cloud_bigquery + google.cloud.bigquery.magics.line_arg_parser.parser: google_cloud_bigquery + google.cloud.bigquery.magics.line_arg_parser.visitors: google_cloud_bigquery + google.cloud.bigquery.magics.magics: google_cloud_bigquery + google.cloud.bigquery.model: google_cloud_bigquery + google.cloud.bigquery.opentelemetry_tracing: google_cloud_bigquery + google.cloud.bigquery.query: google_cloud_bigquery + google.cloud.bigquery.retry: google_cloud_bigquery + google.cloud.bigquery.routine: google_cloud_bigquery + google.cloud.bigquery.routine.__init__: google_cloud_bigquery + google.cloud.bigquery.routine.routine: google_cloud_bigquery + google.cloud.bigquery.schema: google_cloud_bigquery + google.cloud.bigquery.table: google_cloud_bigquery + google.cloud.bigquery.version: google_cloud_bigquery + google.cloud.bigquery_v2: google_cloud_bigquery + google.cloud.bigquery_v2.__init__: google_cloud_bigquery + google.cloud.bigquery_v2.types: google_cloud_bigquery + google.cloud.bigquery_v2.types.__init__: google_cloud_bigquery + google.cloud.bigquery_v2.types.encryption_config: google_cloud_bigquery + google.cloud.bigquery_v2.types.model: google_cloud_bigquery + google.cloud.bigquery_v2.types.model_reference: google_cloud_bigquery + google.cloud.bigquery_v2.types.standard_sql: google_cloud_bigquery + google.cloud.bigquery_v2.types.table_reference: google_cloud_bigquery + google.cloud.client: google_cloud_core + google.cloud.client.__init__: google_cloud_core + google.cloud.environment_vars: google_cloud_core + google.cloud.environment_vars.__init__: google_cloud_core + google.cloud.exceptions: google_cloud_core + google.cloud.exceptions.__init__: google_cloud_core + google.cloud.extended_operations_pb2: googleapis_common_protos + google.cloud.location.locations_pb2: googleapis_common_protos + google.cloud.obsolete: google_cloud_core + google.cloud.obsolete.__init__: google_cloud_core + google.cloud.operation: google_cloud_core + google.cloud.operation.__init__: google_cloud_core + google.cloud.resourcemanager: google_cloud_resource_manager + google.cloud.resourcemanager.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3: google_cloud_resource_manager + google.cloud.resourcemanager_v3.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.folders: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.folders.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.folders.async_client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.folders.client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.folders.pagers: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.folders.transports: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.folders.transports.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.folders.transports.base: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.folders.transports.grpc: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.folders.transports.grpc_asyncio: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.organizations: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.organizations.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.organizations.async_client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.organizations.client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.organizations.pagers: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.organizations.transports: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.organizations.transports.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.organizations.transports.base: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.organizations.transports.grpc: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.organizations.transports.grpc_asyncio: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.projects: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.projects.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.projects.async_client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.projects.client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.projects.pagers: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.projects.transports: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.projects.transports.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.projects.transports.base: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.projects.transports.grpc: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.projects.transports.grpc_asyncio: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_bindings: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_bindings.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_bindings.async_client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_bindings.client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_bindings.pagers: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_bindings.transports: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_bindings.transports.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_bindings.transports.base: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_bindings.transports.grpc: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_bindings.transports.grpc_asyncio: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_keys: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_keys.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_keys.async_client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_keys.client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_keys.pagers: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_keys.transports: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_keys.transports.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_keys.transports.base: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_keys.transports.grpc: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_keys.transports.grpc_asyncio: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_values: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_values.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_values.async_client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_values.client: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_values.pagers: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_values.transports: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_values.transports.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_values.transports.base: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_values.transports.grpc: google_cloud_resource_manager + google.cloud.resourcemanager_v3.services.tag_values.transports.grpc_asyncio: google_cloud_resource_manager + google.cloud.resourcemanager_v3.types: google_cloud_resource_manager + google.cloud.resourcemanager_v3.types.__init__: google_cloud_resource_manager + google.cloud.resourcemanager_v3.types.folders: google_cloud_resource_manager + google.cloud.resourcemanager_v3.types.organizations: google_cloud_resource_manager + google.cloud.resourcemanager_v3.types.projects: google_cloud_resource_manager + google.cloud.resourcemanager_v3.types.tag_bindings: google_cloud_resource_manager + google.cloud.resourcemanager_v3.types.tag_keys: google_cloud_resource_manager + google.cloud.resourcemanager_v3.types.tag_values: google_cloud_resource_manager + google.cloud.storage: google_cloud_storage + google.cloud.storage.__init__: google_cloud_storage + google.cloud.storage._helpers: google_cloud_storage + google.cloud.storage._http: google_cloud_storage + google.cloud.storage._signing: google_cloud_storage + google.cloud.storage.acl: google_cloud_storage + google.cloud.storage.batch: google_cloud_storage + google.cloud.storage.blob: google_cloud_storage + google.cloud.storage.bucket: google_cloud_storage + google.cloud.storage.client: google_cloud_storage + google.cloud.storage.constants: google_cloud_storage + google.cloud.storage.fileio: google_cloud_storage + google.cloud.storage.hmac_key: google_cloud_storage + google.cloud.storage.iam: google_cloud_storage + google.cloud.storage.notification: google_cloud_storage + google.cloud.storage.retry: google_cloud_storage + google.cloud.storage.version: google_cloud_storage + google.cloud.version: google_cloud_core + google.gapic.metadata: googleapis_common_protos + google.gapic.metadata.__init__: googleapis_common_protos + google.gapic.metadata.gapic_metadata_pb2: googleapis_common_protos + google.iam.v1: grpc_google_iam_v1 + google.iam.v1.__init__: grpc_google_iam_v1 + google.iam.v1.iam_policy_pb2: grpc_google_iam_v1 + google.iam.v1.iam_policy_pb2_grpc: grpc_google_iam_v1 + google.iam.v1.logging: grpc_google_iam_v1 + google.iam.v1.logging.__init__: grpc_google_iam_v1 + google.iam.v1.logging.audit_data_pb2: grpc_google_iam_v1 + google.iam.v1.options_pb2: grpc_google_iam_v1 + google.iam.v1.options_pb2_grpc: grpc_google_iam_v1 + google.iam.v1.policy_pb2: grpc_google_iam_v1 + google.iam.v1.policy_pb2_grpc: grpc_google_iam_v1 + google.logging.type: googleapis_common_protos + google.logging.type.__init__: googleapis_common_protos + google.logging.type.http_request_pb2: googleapis_common_protos + google.logging.type.log_severity_pb2: googleapis_common_protos + google.longrunning: googleapis_common_protos + google.longrunning.__init__: googleapis_common_protos + google.longrunning.operations_grpc: googleapis_common_protos + google.longrunning.operations_grpc_pb2: googleapis_common_protos + google.longrunning.operations_pb2: googleapis_common_protos + google.longrunning.operations_pb2_grpc: googleapis_common_protos + google.longrunning.operations_proto: googleapis_common_protos + google.longrunning.operations_proto_pb2: googleapis_common_protos + google.oauth2: google_auth + google.oauth2.__init__: google_auth + google.oauth2._client: google_auth + google.oauth2._client_async: google_auth + google.oauth2._credentials_async: google_auth + google.oauth2._id_token_async: google_auth + google.oauth2._reauth_async: google_auth + google.oauth2._service_account_async: google_auth + google.oauth2.challenges: google_auth + google.oauth2.credentials: google_auth + google.oauth2.id_token: google_auth + google.oauth2.reauth: google_auth + google.oauth2.service_account: google_auth + google.oauth2.sts: google_auth + google.oauth2.utils: google_auth + google.protobuf: protobuf + google.protobuf.__init__: protobuf + google.protobuf.any_pb2: protobuf + google.protobuf.api_pb2: protobuf + google.protobuf.compiler: protobuf + google.protobuf.compiler.__init__: protobuf + google.protobuf.compiler.plugin_pb2: protobuf + google.protobuf.descriptor: protobuf + google.protobuf.descriptor_database: protobuf + google.protobuf.descriptor_pb2: protobuf + google.protobuf.descriptor_pool: protobuf + google.protobuf.duration_pb2: protobuf + google.protobuf.empty_pb2: protobuf + google.protobuf.field_mask_pb2: protobuf + google.protobuf.internal: protobuf + google.protobuf.internal.__init__: protobuf + google.protobuf.internal._api_implementation: protobuf + google.protobuf.internal.api_implementation: protobuf + google.protobuf.internal.builder: protobuf + google.protobuf.internal.containers: protobuf + google.protobuf.internal.decoder: protobuf + google.protobuf.internal.encoder: protobuf + google.protobuf.internal.enum_type_wrapper: protobuf + google.protobuf.internal.extension_dict: protobuf + google.protobuf.internal.message_listener: protobuf + google.protobuf.internal.python_message: protobuf + google.protobuf.internal.type_checkers: protobuf + google.protobuf.internal.well_known_types: protobuf + google.protobuf.internal.wire_format: protobuf + google.protobuf.json_format: protobuf + google.protobuf.message: protobuf + google.protobuf.message_factory: protobuf + google.protobuf.proto_builder: protobuf + google.protobuf.pyext: protobuf + google.protobuf.pyext.__init__: protobuf + google.protobuf.pyext._message: protobuf + google.protobuf.pyext.cpp_message: protobuf + google.protobuf.reflection: protobuf + google.protobuf.service: protobuf + google.protobuf.service_reflection: protobuf + google.protobuf.source_context_pb2: protobuf + google.protobuf.struct_pb2: protobuf + google.protobuf.symbol_database: protobuf + google.protobuf.text_encoding: protobuf + google.protobuf.text_format: protobuf + google.protobuf.timestamp_pb2: protobuf + google.protobuf.type_pb2: protobuf + google.protobuf.util: protobuf + google.protobuf.util.__init__: protobuf + google.protobuf.util.json_format_pb2: protobuf + google.protobuf.util.json_format_proto3_pb2: protobuf + google.protobuf.wrappers_pb2: protobuf + google.resumable_media: google_resumable_media + google.resumable_media.__init__: google_resumable_media + google.resumable_media._download: google_resumable_media + google.resumable_media._helpers: google_resumable_media + google.resumable_media._upload: google_resumable_media + google.resumable_media.common: google_resumable_media + google.resumable_media.requests: google_resumable_media + google.resumable_media.requests.__init__: google_resumable_media + google.resumable_media.requests._request_helpers: google_resumable_media + google.resumable_media.requests.download: google_resumable_media + google.resumable_media.requests.upload: google_resumable_media + google.rpc: googleapis_common_protos + google.rpc.__init__: googleapis_common_protos + google.rpc.code_pb2: googleapis_common_protos + google.rpc.context: googleapis_common_protos + google.rpc.context.__init__: googleapis_common_protos + google.rpc.context.attribute_context_pb2: googleapis_common_protos + google.rpc.error_details_pb2: googleapis_common_protos + google.rpc.status_pb2: googleapis_common_protos + google.type: googleapis_common_protos + google.type.__init__: googleapis_common_protos + google.type.calendar_period_pb2: googleapis_common_protos + google.type.color_pb2: googleapis_common_protos + google.type.date_pb2: googleapis_common_protos + google.type.datetime_pb2: googleapis_common_protos + google.type.dayofweek_pb2: googleapis_common_protos + google.type.decimal_pb2: googleapis_common_protos + google.type.expr_pb2: googleapis_common_protos + google.type.fraction_pb2: googleapis_common_protos + google.type.interval_pb2: googleapis_common_protos + google.type.latlng_pb2: googleapis_common_protos + google.type.localized_text_pb2: googleapis_common_protos + google.type.money_pb2: googleapis_common_protos + google.type.month_pb2: googleapis_common_protos + google.type.phone_number_pb2: googleapis_common_protos + google.type.postal_address_pb2: googleapis_common_protos + google.type.quaternion_pb2: googleapis_common_protos + google.type.timeofday_pb2: googleapis_common_protos + google_crc32c: google_crc32c + google_crc32c.__config__: google_crc32c + google_crc32c.__init__: google_crc32c + google_crc32c._checksum: google_crc32c + google_crc32c._crc32c: google_crc32c + google_crc32c.cext: google_crc32c + google_crc32c.libs.libcrc32c-672e1704: google_crc32c + google_crc32c.python: google_crc32c + grpc: grpcio + grpc.__init__: grpcio + grpc._auth: grpcio + grpc._channel: grpcio + grpc._common: grpcio + grpc._compression: grpcio + grpc._cython: grpcio + grpc._cython.__init__: grpcio + grpc._cython._cygrpc: grpcio + grpc._cython._cygrpc.__init__: grpcio + grpc._cython.cygrpc: grpcio + grpc._grpcio_metadata: grpcio + grpc._interceptor: grpcio + grpc._plugin_wrapping: grpcio + grpc._runtime_protos: grpcio + grpc._server: grpcio + grpc._simple_stubs: grpcio + grpc._utilities: grpcio + grpc.aio: grpcio + grpc.aio.__init__: grpcio + grpc.aio._base_call: grpcio + grpc.aio._base_channel: grpcio + grpc.aio._base_server: grpcio + grpc.aio._call: grpcio + grpc.aio._channel: grpcio + grpc.aio._interceptor: grpcio + grpc.aio._metadata: grpcio + grpc.aio._server: grpcio + grpc.aio._typing: grpcio + grpc.aio._utils: grpcio + grpc.beta: grpcio + grpc.beta.__init__: grpcio + grpc.beta._client_adaptations: grpcio + grpc.beta._metadata: grpcio + grpc.beta._server_adaptations: grpcio + grpc.beta.implementations: grpcio + grpc.beta.interfaces: grpcio + grpc.beta.utilities: grpcio + grpc.experimental: grpcio + grpc.experimental.__init__: grpcio + grpc.experimental.aio: grpcio + grpc.experimental.aio.__init__: grpcio + grpc.experimental.gevent: grpcio + grpc.experimental.session_cache: grpcio + grpc.framework: grpcio + grpc.framework.__init__: grpcio + grpc.framework.common: grpcio + grpc.framework.common.__init__: grpcio + grpc.framework.common.cardinality: grpcio + grpc.framework.common.style: grpcio + grpc.framework.foundation: grpcio + grpc.framework.foundation.__init__: grpcio + grpc.framework.foundation.abandonment: grpcio + grpc.framework.foundation.callable_util: grpcio + grpc.framework.foundation.future: grpcio + grpc.framework.foundation.logging_pool: grpcio + grpc.framework.foundation.stream: grpcio + grpc.framework.foundation.stream_util: grpcio + grpc.framework.interfaces: grpcio + grpc.framework.interfaces.__init__: grpcio + grpc.framework.interfaces.base: grpcio + grpc.framework.interfaces.base.__init__: grpcio + grpc.framework.interfaces.base.base: grpcio + grpc.framework.interfaces.base.utilities: grpcio + grpc.framework.interfaces.face: grpcio + grpc.framework.interfaces.face.__init__: grpcio + grpc.framework.interfaces.face.face: grpcio + grpc.framework.interfaces.face.utilities: grpcio + grpc_status: grpcio_status + grpc_status.__init__: grpcio_status + grpc_status._async: grpcio_status + grpc_status._common: grpcio_status + grpc_status.rpc_status: grpcio_status + idna: idna + idna.__init__: idna + idna.codec: idna + idna.compat: idna + idna.core: idna + idna.idnadata: idna + idna.intranges: idna + idna.package_data: idna + idna.uts46data: idna + packaging: packaging + packaging.__about__: packaging + packaging.__init__: packaging + packaging._manylinux: packaging + packaging._musllinux: packaging + packaging._structures: packaging + packaging.markers: packaging + packaging.requirements: packaging + packaging.specifiers: packaging + packaging.tags: packaging + packaging.utils: packaging + packaging.version: packaging + proto: proto_plus + proto.__init__: proto_plus + proto._file_info: proto_plus + proto._package_info: proto_plus + proto.datetime_helpers: proto_plus + proto.enums: proto_plus + proto.fields: proto_plus + proto.marshal: proto_plus + proto.marshal.__init__: proto_plus + proto.marshal.collections: proto_plus + proto.marshal.collections.__init__: proto_plus + proto.marshal.collections.maps: proto_plus + proto.marshal.collections.repeated: proto_plus + proto.marshal.compat: proto_plus + proto.marshal.marshal: proto_plus + proto.marshal.rules: proto_plus + proto.marshal.rules.__init__: proto_plus + proto.marshal.rules.bytes: proto_plus + proto.marshal.rules.dates: proto_plus + proto.marshal.rules.enums: proto_plus + proto.marshal.rules.message: proto_plus + proto.marshal.rules.stringy_numbers: proto_plus + proto.marshal.rules.struct: proto_plus + proto.marshal.rules.wrappers: proto_plus + proto.message: proto_plus + proto.modules: proto_plus + proto.primitives: proto_plus + proto.utils: proto_plus + pyasn1: pyasn1 + pyasn1.__init__: pyasn1 + pyasn1.codec: pyasn1 + pyasn1.codec.__init__: pyasn1 + pyasn1.codec.ber: pyasn1 + pyasn1.codec.ber.__init__: pyasn1 + pyasn1.codec.ber.decoder: pyasn1 + pyasn1.codec.ber.encoder: pyasn1 + pyasn1.codec.ber.eoo: pyasn1 + pyasn1.codec.cer: pyasn1 + pyasn1.codec.cer.__init__: pyasn1 + pyasn1.codec.cer.decoder: pyasn1 + pyasn1.codec.cer.encoder: pyasn1 + pyasn1.codec.der: pyasn1 + pyasn1.codec.der.__init__: pyasn1 + pyasn1.codec.der.decoder: pyasn1 + pyasn1.codec.der.encoder: pyasn1 + pyasn1.codec.native: pyasn1 + pyasn1.codec.native.__init__: pyasn1 + pyasn1.codec.native.decoder: pyasn1 + pyasn1.codec.native.encoder: pyasn1 + pyasn1.compat: pyasn1 + pyasn1.compat.__init__: pyasn1 + pyasn1.compat.binary: pyasn1 + pyasn1.compat.calling: pyasn1 + pyasn1.compat.dateandtime: pyasn1 + pyasn1.compat.integer: pyasn1 + pyasn1.compat.octets: pyasn1 + pyasn1.compat.string: pyasn1 + pyasn1.debug: pyasn1 + pyasn1.error: pyasn1 + pyasn1.type: pyasn1 + pyasn1.type.__init__: pyasn1 + pyasn1.type.base: pyasn1 + pyasn1.type.char: pyasn1 + pyasn1.type.constraint: pyasn1 + pyasn1.type.error: pyasn1 + pyasn1.type.namedtype: pyasn1 + pyasn1.type.namedval: pyasn1 + pyasn1.type.opentype: pyasn1 + pyasn1.type.tag: pyasn1 + pyasn1.type.tagmap: pyasn1 + pyasn1.type.univ: pyasn1 + pyasn1.type.useful: pyasn1 + pyasn1_modules: pyasn1_modules + pyasn1_modules.__init__: pyasn1_modules + pyasn1_modules.pem: pyasn1_modules + pyasn1_modules.rfc1155: pyasn1_modules + pyasn1_modules.rfc1157: pyasn1_modules + pyasn1_modules.rfc1901: pyasn1_modules + pyasn1_modules.rfc1902: pyasn1_modules + pyasn1_modules.rfc1905: pyasn1_modules + pyasn1_modules.rfc2251: pyasn1_modules + pyasn1_modules.rfc2314: pyasn1_modules + pyasn1_modules.rfc2315: pyasn1_modules + pyasn1_modules.rfc2437: pyasn1_modules + pyasn1_modules.rfc2459: pyasn1_modules + pyasn1_modules.rfc2511: pyasn1_modules + pyasn1_modules.rfc2560: pyasn1_modules + pyasn1_modules.rfc2631: pyasn1_modules + pyasn1_modules.rfc2634: pyasn1_modules + pyasn1_modules.rfc2985: pyasn1_modules + pyasn1_modules.rfc2986: pyasn1_modules + pyasn1_modules.rfc3114: pyasn1_modules + pyasn1_modules.rfc3161: pyasn1_modules + pyasn1_modules.rfc3274: pyasn1_modules + pyasn1_modules.rfc3279: pyasn1_modules + pyasn1_modules.rfc3280: pyasn1_modules + pyasn1_modules.rfc3281: pyasn1_modules + pyasn1_modules.rfc3412: pyasn1_modules + pyasn1_modules.rfc3414: pyasn1_modules + pyasn1_modules.rfc3447: pyasn1_modules + pyasn1_modules.rfc3560: pyasn1_modules + pyasn1_modules.rfc3565: pyasn1_modules + pyasn1_modules.rfc3709: pyasn1_modules + pyasn1_modules.rfc3770: pyasn1_modules + pyasn1_modules.rfc3779: pyasn1_modules + pyasn1_modules.rfc3852: pyasn1_modules + pyasn1_modules.rfc4043: pyasn1_modules + pyasn1_modules.rfc4055: pyasn1_modules + pyasn1_modules.rfc4073: pyasn1_modules + pyasn1_modules.rfc4108: pyasn1_modules + pyasn1_modules.rfc4210: pyasn1_modules + pyasn1_modules.rfc4211: pyasn1_modules + pyasn1_modules.rfc4334: pyasn1_modules + pyasn1_modules.rfc4985: pyasn1_modules + pyasn1_modules.rfc5035: pyasn1_modules + pyasn1_modules.rfc5083: pyasn1_modules + pyasn1_modules.rfc5084: pyasn1_modules + pyasn1_modules.rfc5208: pyasn1_modules + pyasn1_modules.rfc5280: pyasn1_modules + pyasn1_modules.rfc5480: pyasn1_modules + pyasn1_modules.rfc5649: pyasn1_modules + pyasn1_modules.rfc5652: pyasn1_modules + pyasn1_modules.rfc5751: pyasn1_modules + pyasn1_modules.rfc5755: pyasn1_modules + pyasn1_modules.rfc5913: pyasn1_modules + pyasn1_modules.rfc5914: pyasn1_modules + pyasn1_modules.rfc5915: pyasn1_modules + pyasn1_modules.rfc5916: pyasn1_modules + pyasn1_modules.rfc5917: pyasn1_modules + pyasn1_modules.rfc5924: pyasn1_modules + pyasn1_modules.rfc5934: pyasn1_modules + pyasn1_modules.rfc5940: pyasn1_modules + pyasn1_modules.rfc5958: pyasn1_modules + pyasn1_modules.rfc5990: pyasn1_modules + pyasn1_modules.rfc6010: pyasn1_modules + pyasn1_modules.rfc6019: pyasn1_modules + pyasn1_modules.rfc6031: pyasn1_modules + pyasn1_modules.rfc6032: pyasn1_modules + pyasn1_modules.rfc6120: pyasn1_modules + pyasn1_modules.rfc6170: pyasn1_modules + pyasn1_modules.rfc6187: pyasn1_modules + pyasn1_modules.rfc6210: pyasn1_modules + pyasn1_modules.rfc6211: pyasn1_modules + pyasn1_modules.rfc6402: pyasn1_modules + pyasn1_modules.rfc6402-1: pyasn1_modules + pyasn1_modules.rfc6482: pyasn1_modules + pyasn1_modules.rfc6486: pyasn1_modules + pyasn1_modules.rfc6487: pyasn1_modules + pyasn1_modules.rfc6664: pyasn1_modules + pyasn1_modules.rfc6955: pyasn1_modules + pyasn1_modules.rfc6960: pyasn1_modules + pyasn1_modules.rfc7030: pyasn1_modules + pyasn1_modules.rfc7191: pyasn1_modules + pyasn1_modules.rfc7229: pyasn1_modules + pyasn1_modules.rfc7292: pyasn1_modules + pyasn1_modules.rfc7296: pyasn1_modules + pyasn1_modules.rfc7508: pyasn1_modules + pyasn1_modules.rfc7585: pyasn1_modules + pyasn1_modules.rfc7633: pyasn1_modules + pyasn1_modules.rfc7773: pyasn1_modules + pyasn1_modules.rfc7894: pyasn1_modules + pyasn1_modules.rfc7894-1: pyasn1_modules + pyasn1_modules.rfc7906: pyasn1_modules + pyasn1_modules.rfc7914: pyasn1_modules + pyasn1_modules.rfc8017: pyasn1_modules + pyasn1_modules.rfc8018: pyasn1_modules + pyasn1_modules.rfc8103: pyasn1_modules + pyasn1_modules.rfc8209: pyasn1_modules + pyasn1_modules.rfc8226: pyasn1_modules + pyasn1_modules.rfc8358: pyasn1_modules + pyasn1_modules.rfc8360: pyasn1_modules + pyasn1_modules.rfc8398: pyasn1_modules + pyasn1_modules.rfc8410: pyasn1_modules + pyasn1_modules.rfc8418: pyasn1_modules + pyasn1_modules.rfc8419: pyasn1_modules + pyasn1_modules.rfc8479: pyasn1_modules + pyasn1_modules.rfc8494: pyasn1_modules + pyasn1_modules.rfc8520: pyasn1_modules + pyasn1_modules.rfc8619: pyasn1_modules + pyasn1_modules.rfc8649: pyasn1_modules + pyparsing: pyparsing + pyparsing.__init__: pyparsing + pyparsing.actions: pyparsing + pyparsing.common: pyparsing + pyparsing.core: pyparsing + pyparsing.diagram: pyparsing + pyparsing.diagram.__init__: pyparsing + pyparsing.exceptions: pyparsing + pyparsing.helpers: pyparsing + pyparsing.results: pyparsing + pyparsing.testing: pyparsing + pyparsing.unicode: pyparsing + pyparsing.util: pyparsing + requests: requests + requests.__init__: requests + requests.__version__: requests + requests._internal_utils: 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 + rsa: rsa + rsa.__init__: rsa + rsa._compat: rsa + rsa.asn1: rsa + rsa.cli: rsa + rsa.common: rsa + rsa.core: rsa + rsa.key: rsa + rsa.parallel: rsa + rsa.pem: rsa + rsa.pkcs1: rsa + rsa.pkcs1_v2: rsa + rsa.prime: rsa + rsa.randnum: rsa + rsa.transform: rsa + rsa.util: rsa + samples.generated_samples.cloudresourcemanager_v3_generated_folders_create_folder_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_create_folder_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_delete_folder_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_delete_folder_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_get_folder_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_get_folder_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_get_iam_policy_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_get_iam_policy_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_list_folders_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_list_folders_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_move_folder_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_move_folder_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_search_folders_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_search_folders_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_set_iam_policy_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_set_iam_policy_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_test_iam_permissions_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_test_iam_permissions_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_undelete_folder_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_undelete_folder_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_update_folder_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_folders_update_folder_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_organizations_get_iam_policy_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_organizations_get_iam_policy_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_organizations_get_organization_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_organizations_get_organization_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_organizations_search_organizations_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_organizations_search_organizations_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_organizations_set_iam_policy_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_organizations_set_iam_policy_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_organizations_test_iam_permissions_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_organizations_test_iam_permissions_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_create_project_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_create_project_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_delete_project_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_delete_project_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_get_iam_policy_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_get_iam_policy_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_get_project_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_get_project_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_list_projects_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_list_projects_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_move_project_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_move_project_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_search_projects_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_search_projects_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_set_iam_policy_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_set_iam_policy_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_test_iam_permissions_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_test_iam_permissions_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_undelete_project_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_undelete_project_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_update_project_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_projects_update_project_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_bindings_create_tag_binding_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_bindings_create_tag_binding_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_bindings_delete_tag_binding_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_bindings_delete_tag_binding_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_bindings_list_tag_bindings_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_bindings_list_tag_bindings_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_create_tag_key_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_create_tag_key_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_delete_tag_key_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_delete_tag_key_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_get_iam_policy_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_get_iam_policy_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_get_tag_key_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_get_tag_key_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_list_tag_keys_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_list_tag_keys_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_set_iam_policy_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_set_iam_policy_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_test_iam_permissions_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_test_iam_permissions_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_update_tag_key_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_keys_update_tag_key_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_create_tag_value_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_create_tag_value_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_delete_tag_value_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_delete_tag_value_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_get_iam_policy_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_get_iam_policy_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_get_tag_value_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_get_tag_value_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_list_tag_values_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_list_tag_values_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_set_iam_policy_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_set_iam_policy_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_test_iam_permissions_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_test_iam_permissions_sync: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_update_tag_value_async: google_cloud_resource_manager + samples.generated_samples.cloudresourcemanager_v3_generated_tag_values_update_tag_value_sync: google_cloud_resource_manager + scripts.fixup_resourcemanager_v3_keywords: google_cloud_resource_manager + scripts.readme-gen.readme_gen: google_cloud_resource_manager + six: six + tests: google_cloud_resource_manager + tests.__init__: google_cloud_resource_manager + tests.unit: google_cloud_resource_manager + tests.unit.__init__: google_cloud_resource_manager + tests.unit.gapic: google_cloud_resource_manager + tests.unit.gapic.__init__: google_cloud_resource_manager + tests.unit.gapic.resourcemanager_v3: google_cloud_resource_manager + tests.unit.gapic.resourcemanager_v3.__init__: google_cloud_resource_manager + tests.unit.gapic.resourcemanager_v3.test_folders: google_cloud_resource_manager + tests.unit.gapic.resourcemanager_v3.test_organizations: google_cloud_resource_manager + tests.unit.gapic.resourcemanager_v3.test_projects: google_cloud_resource_manager + tests.unit.gapic.resourcemanager_v3.test_tag_bindings: google_cloud_resource_manager + tests.unit.gapic.resourcemanager_v3.test_tag_keys: google_cloud_resource_manager + tests.unit.gapic.resourcemanager_v3.test_tag_values: google_cloud_resource_manager + urllib3: urllib3 + urllib3.__init__: urllib3 + urllib3._collections: urllib3 + urllib3._version: urllib3 + urllib3.connection: urllib3 + urllib3.connectionpool: urllib3 + urllib3.contrib: urllib3 + urllib3.contrib.__init__: urllib3 + urllib3.contrib._appengine_environ: urllib3 + urllib3.contrib._securetransport: urllib3 + urllib3.contrib._securetransport.__init__: urllib3 + urllib3.contrib._securetransport.bindings: urllib3 + urllib3.contrib._securetransport.low_level: 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.__init__: urllib3 + urllib3.packages.backports: urllib3 + urllib3.packages.backports.__init__: urllib3 + urllib3.packages.backports.makefile: urllib3 + urllib3.packages.six: urllib3 + urllib3.poolmanager: urllib3 + urllib3.request: urllib3 + urllib3.response: urllib3 + urllib3.util: urllib3 + urllib3.util.__init__: 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 + pip_repository: + name: gazelle_python_test +integrity: 32e38932043eca090a64ca741758d8e4a5817c2cd7dc821fc927914c32fb3114 diff --git a/gazelle/python/testdata/with_third_party_requirements_from_imports/test.yaml b/gazelle/python/testdata/with_third_party_requirements_from_imports/test.yaml new file mode 100644 index 0000000000..fcea77710f --- /dev/null +++ b/gazelle/python/testdata/with_third_party_requirements_from_imports/test.yaml @@ -0,0 +1,15 @@ +# 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. + +--- diff --git a/gazelle/pythonconfig/BUILD.bazel b/gazelle/pythonconfig/BUILD.bazel index cff75d9ee3..d0f1690d94 100644 --- a/gazelle/pythonconfig/BUILD.bazel +++ b/gazelle/pythonconfig/BUILD.bazel @@ -1,4 +1,4 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library") +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") go_library( name = "pythonconfig", @@ -9,8 +9,20 @@ go_library( importpath = "github.com/bazelbuild/rules_python/gazelle/pythonconfig", visibility = ["//visibility:public"], deps = [ - "//gazelle/manifest", + "//manifest", "@bazel_gazelle//label:go_default_library", "@com_github_emirpasic_gods//lists/singlylinkedlist", ], ) + +go_test( + name = "pythonconfig_test", + srcs = ["pythonconfig_test.go"], + deps = [":pythonconfig"], +) + +filegroup( + name = "distribution", + srcs = glob(["**"]), + visibility = ["//:__pkg__"], +) diff --git a/gazelle/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go index 7e65fd98d7..c7cd7c1a28 100644 --- a/gazelle/pythonconfig/pythonconfig.go +++ b/gazelle/pythonconfig/pythonconfig.go @@ -1,3 +1,17 @@ +// 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. + package pythonconfig import ( @@ -76,6 +90,14 @@ var defaultIgnoreFiles = map[string]struct{}{ "setup.py": {}, } +func SanitizeDistribution(distributionName string) string { + sanitizedDistribution := strings.ToLower(distributionName) + sanitizedDistribution = strings.ReplaceAll(sanitizedDistribution, "-", "_") + sanitizedDistribution = strings.ReplaceAll(sanitizedDistribution, ".", "_") + + return sanitizedDistribution +} + // Configs is an extension of map[string]*Config. It provides finding methods // on top of the mapping. type Configs map[string]*Config @@ -204,18 +226,17 @@ func (c *Config) FindThirdPartyDependency(modName string) (string, bool) { } else if gazelleManifest.PipRepository != nil { distributionRepositoryName = gazelleManifest.PipRepository.Name } - sanitizedDistribution := strings.ToLower(distributionName) - sanitizedDistribution = strings.ReplaceAll(sanitizedDistribution, "-", "_") - var lbl label.Label - if gazelleManifest.PipRepository != nil && gazelleManifest.PipRepository.Incremental { - // @_//:pkg - distributionRepositoryName = distributionRepositoryName + "_" + sanitizedDistribution - lbl = label.New(distributionRepositoryName, "", "pkg") - } else { - // @//pypi__ - distributionPackage := "pypi__" + sanitizedDistribution - lbl = label.New(distributionRepositoryName, distributionPackage, distributionPackage) + sanitizedDistribution := SanitizeDistribution(distributionName) + + if gazelleManifest.PipRepository != nil && gazelleManifest.PipRepository.UsePipRepositoryAliases { + // @// + lbl := label.New(distributionRepositoryName, sanitizedDistribution, sanitizedDistribution) + return lbl.String(), true } + + // @_//:pkg + distributionRepositoryName = distributionRepositoryName + "_" + sanitizedDistribution + lbl := label.New(distributionRepositoryName, "", "pkg") return lbl.String(), true } } diff --git a/gazelle/pythonconfig/pythonconfig_test.go b/gazelle/pythonconfig/pythonconfig_test.go new file mode 100644 index 0000000000..1512eb97ae --- /dev/null +++ b/gazelle/pythonconfig/pythonconfig_test.go @@ -0,0 +1,28 @@ +package pythonconfig + +import ( + "testing" + + "github.com/bazelbuild/rules_python/gazelle/pythonconfig" +) + +func TestDistributionSanitizing(t *testing.T) { + tests := map[string]struct { + input string + want string + }{ + "upper case": {input: "DistWithUpperCase", want: "distwithuppercase"}, + "dashes": {input: "dist-with-dashes", want: "dist_with_dashes"}, + "dots": {input: "dist.with.dots", want: "dist_with_dots"}, + "mixed": {input: "To-be.sanitized", want: "to_be_sanitized"}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + got := pythonconfig.SanitizeDistribution(tc.input) + if tc.want != got { + t.Fatalf("expected %q, got %q", tc.want, got) + } + }) + } +} diff --git a/gazelle/pythonconfig/types.go b/gazelle/pythonconfig/types.go index bdb535bf6e..d83d35f015 100644 --- a/gazelle/pythonconfig/types.go +++ b/gazelle/pythonconfig/types.go @@ -1,3 +1,17 @@ +// 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. + package pythonconfig import ( diff --git a/gazelle/resolve.go b/gazelle/resolve.go deleted file mode 100644 index 8f68dfd036..0000000000 --- a/gazelle/resolve.go +++ /dev/null @@ -1,274 +0,0 @@ -package python - -import ( - "fmt" - "log" - "os" - "path/filepath" - "strings" - - "github.com/bazelbuild/bazel-gazelle/config" - "github.com/bazelbuild/bazel-gazelle/label" - "github.com/bazelbuild/bazel-gazelle/repo" - "github.com/bazelbuild/bazel-gazelle/resolve" - "github.com/bazelbuild/bazel-gazelle/rule" - bzl "github.com/bazelbuild/buildtools/build" - "github.com/emirpasic/gods/sets/treeset" - godsutils "github.com/emirpasic/gods/utils" - - "github.com/bazelbuild/rules_python/gazelle/pythonconfig" -) - -const languageName = "py" - -const ( - // resolvedDepsKey is the attribute key used to pass dependencies that don't - // need to be resolved by the dependency resolver in the Resolver step. - resolvedDepsKey = "_gazelle_python_resolved_deps" - // uuidKey is the attribute key used to uniquely identify a py_library - // target that should be imported by a py_test or py_binary in the same - // Bazel package. - uuidKey = "_gazelle_python_library_uuid" -) - -// Resolver satisfies the resolve.Resolver interface. It resolves dependencies -// in rules generated by this extension. -type Resolver struct{} - -// Name returns the name of the language. This is the prefix of the kinds of -// rules generated. E.g. py_library and py_binary. -func (*Resolver) Name() string { return languageName } - -// Imports returns a list of ImportSpecs that can be used to import the rule -// r. This is used to populate RuleIndex. -// -// If nil is returned, the rule will not be indexed. If any non-nil slice is -// returned, including an empty slice, the rule will be indexed. -func (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec { - cfgs := c.Exts[languageName].(pythonconfig.Configs) - cfg := cfgs[f.Pkg] - srcs := r.AttrStrings("srcs") - provides := make([]resolve.ImportSpec, 0, len(srcs)+1) - for _, src := range srcs { - ext := filepath.Ext(src) - if ext == ".py" { - pythonProjectRoot := cfg.PythonProjectRoot() - provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src) - provides = append(provides, provide) - } - } - if r.PrivateAttr(uuidKey) != nil { - provide := resolve.ImportSpec{ - Lang: languageName, - Imp: r.PrivateAttr(uuidKey).(string), - } - provides = append(provides, provide) - } - if len(provides) == 0 { - return nil - } - return provides -} - -// importSpecFromSrc determines the ImportSpec based on the target that contains the src so that -// the target can be indexed for import statements that match the calculated src relative to the its -// Python project root. -func importSpecFromSrc(pythonProjectRoot, bzlPkg, src string) resolve.ImportSpec { - pythonPkgDir := filepath.Join(bzlPkg, filepath.Dir(src)) - relPythonPkgDir, err := filepath.Rel(pythonProjectRoot, pythonPkgDir) - if err != nil { - panic(fmt.Errorf("unexpected failure: %v", err)) - } - if relPythonPkgDir == "." { - relPythonPkgDir = "" - } - pythonPkg := strings.ReplaceAll(relPythonPkgDir, "/", ".") - filename := filepath.Base(src) - if filename == pyLibraryEntrypointFilename { - if pythonPkg != "" { - return resolve.ImportSpec{ - Lang: languageName, - Imp: pythonPkg, - } - } - } - moduleName := strings.TrimSuffix(filename, ".py") - var imp string - if pythonPkg == "" { - imp = moduleName - } else { - imp = fmt.Sprintf("%s.%s", pythonPkg, moduleName) - } - return resolve.ImportSpec{ - Lang: languageName, - Imp: imp, - } -} - -// Embeds returns a list of labels of rules that the given rule embeds. If -// a rule is embedded by another importable rule of the same language, only -// the embedding rule will be indexed. The embedding rule will inherit -// the imports of the embedded rule. -func (py *Resolver) Embeds(r *rule.Rule, from label.Label) []label.Label { - // TODO(f0rmiga): implement. - return make([]label.Label, 0) -} - -// Resolve translates imported libraries for a given rule into Bazel -// dependencies. Information about imported libraries is returned for each -// rule generated by language.GenerateRules in -// language.GenerateResult.Imports. Resolve generates a "deps" attribute (or -// the appropriate language-specific equivalent) for each import according to -// language-specific rules and heuristics. -func (py *Resolver) Resolve( - c *config.Config, - ix *resolve.RuleIndex, - rc *repo.RemoteCache, - r *rule.Rule, - modulesRaw interface{}, - from label.Label, -) { - // TODO(f0rmiga): may need to be defensive here once this Gazelle extension - // join with the main Gazelle binary with other rules. It may conflict with - // other generators that generate py_* targets. - deps := treeset.NewWith(godsutils.StringComparator) - if modulesRaw != nil { - cfgs := c.Exts[languageName].(pythonconfig.Configs) - cfg := cfgs[from.Pkg] - pythonProjectRoot := cfg.PythonProjectRoot() - modules := modulesRaw.(*treeset.Set) - it := modules.Iterator() - explainDependency := os.Getenv("EXPLAIN_DEPENDENCY") - hasFatalError := false - MODULE_LOOP: - for it.Next() { - mod := it.Value().(module) - imp := resolve.ImportSpec{Lang: languageName, Imp: mod.Name} - if override, ok := resolve.FindRuleWithOverride(c, imp, languageName); ok { - if override.Repo == "" { - override.Repo = from.Repo - } - if !override.Equal(from) { - if override.Repo == from.Repo { - override.Repo = "" - } - dep := override.String() - deps.Add(dep) - if explainDependency == dep { - log.Printf("Explaining dependency (%s): "+ - "in the target %q, the file %q imports %q at line %d, "+ - "which resolves using the \"gazelle:resolve\" directive.\n", - explainDependency, from.String(), mod.Filepath, mod.Name, mod.LineNumber) - } - } - } else { - if dep, ok := cfg.FindThirdPartyDependency(mod.Name); ok { - deps.Add(dep) - if explainDependency == dep { - log.Printf("Explaining dependency (%s): "+ - "in the target %q, the file %q imports %q at line %d, "+ - "which resolves from the third-party module %q from the wheel %q.\n", - explainDependency, from.String(), mod.Filepath, mod.Name, mod.LineNumber, mod.Name, dep) - } - } else { - matches := ix.FindRulesByImportWithConfig(c, imp, languageName) - if len(matches) == 0 { - // Check if the imported module is part of the standard library. - if isStd, err := isStdModule(mod); err != nil { - log.Println("ERROR: ", err) - hasFatalError = true - continue MODULE_LOOP - } else if isStd { - continue MODULE_LOOP - } - if cfg.ValidateImportStatements() { - err := fmt.Errorf( - "%[1]q at line %[2]d from %[3]q is an invalid dependency: possible solutions:\n"+ - "\t1. Add it as a dependency in the requirements.txt file.\n"+ - "\t2. Instruct Gazelle to resolve to a known dependency using the gazelle:resolve directive.\n"+ - "\t3. Ignore it with a comment '# gazelle:ignore %[1]s' in the Python file.\n", - mod.Name, mod.LineNumber, mod.Filepath, - ) - log.Printf("ERROR: failed to validate dependencies for target %q: %v\n", from.String(), err) - hasFatalError = true - continue MODULE_LOOP - } - } - filteredMatches := make([]resolve.FindResult, 0, len(matches)) - for _, match := range matches { - if match.IsSelfImport(from) { - // Prevent from adding itself as a dependency. - continue MODULE_LOOP - } - filteredMatches = append(filteredMatches, match) - } - if len(filteredMatches) == 0 { - continue - } - if len(filteredMatches) > 1 { - sameRootMatches := make([]resolve.FindResult, 0, len(filteredMatches)) - for _, match := range filteredMatches { - if strings.HasPrefix(match.Label.Pkg, pythonProjectRoot) { - sameRootMatches = append(sameRootMatches, match) - } - } - if len(sameRootMatches) != 1 { - err := fmt.Errorf( - "multiple targets (%s) may be imported with %q at line %d in %q "+ - "- this must be fixed using the \"gazelle:resolve\" directive", - targetListFromResults(filteredMatches), mod.Name, mod.LineNumber, mod.Filepath) - log.Println("ERROR: ", err) - hasFatalError = true - continue MODULE_LOOP - } - filteredMatches = sameRootMatches - } - matchLabel := filteredMatches[0].Label.Rel(from.Repo, from.Pkg) - dep := matchLabel.String() - deps.Add(dep) - if explainDependency == dep { - log.Printf("Explaining dependency (%s): "+ - "in the target %q, the file %q imports %q at line %d, "+ - "which resolves from the first-party indexed labels.\n", - explainDependency, from.String(), mod.Filepath, mod.Name, mod.LineNumber) - } - } - } - } - if hasFatalError { - os.Exit(1) - } - } - resolvedDeps := r.PrivateAttr(resolvedDepsKey).(*treeset.Set) - if !resolvedDeps.Empty() { - it := resolvedDeps.Iterator() - for it.Next() { - deps.Add(it.Value()) - } - } - if !deps.Empty() { - r.SetAttr("deps", convertDependencySetToExpr(deps)) - } -} - -// targetListFromResults returns a string with the human-readable list of -// targets contained in the given results. -func targetListFromResults(results []resolve.FindResult) string { - list := make([]string, len(results)) - for i, result := range results { - list[i] = result.Label.String() - } - return strings.Join(list, ", ") -} - -// convertDependencySetToExpr converts the given set of dependencies to an -// expression to be used in the deps attribute. -func convertDependencySetToExpr(set *treeset.Set) bzl.Expr { - deps := make([]bzl.Expr, set.Size()) - it := set.Iterator() - for it.Next() { - dep := it.Value().(string) - deps[it.Index()] = &bzl.StringExpr{Value: dep} - } - return &bzl.ListExpr{List: deps} -} diff --git a/gazelle/std_modules.py b/gazelle/std_modules.py deleted file mode 100644 index ccd1dcd3aa..0000000000 --- a/gazelle/std_modules.py +++ /dev/null @@ -1,39 +0,0 @@ -# std_modules.py is a long-living program that communicates over STDIN and -# STDOUT. STDIN receives module names, one per line. For each module statement -# it evaluates, it outputs true/false for whether the module is part of the -# standard library or not. - -import site -import sys - - -# Don't return any paths, all userland site-packages should be ignored. -def __override_getusersitepackages__(): - return "" - - -site.getusersitepackages = __override_getusersitepackages__ - - -def is_std_modules(module): - try: - __import__(module, globals(), locals(), [], 0) - return True - except Exception: - return False - - -def main(stdin, stdout): - for module in stdin: - module = module.strip() - # Don't print the boolean directly as it is captilized in Python. - print( - "true" if is_std_modules(module) else "false", - end="\n", - file=stdout, - ) - stdout.flush() - - -if __name__ == "__main__": - exit(main(sys.stdin, sys.stdout)) diff --git a/gazelle/testdata/dependency_resolution_order/__init__.py b/gazelle/testdata/dependency_resolution_order/__init__.py deleted file mode 100644 index f2a1c081ad..0000000000 --- a/gazelle/testdata/dependency_resolution_order/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -import sys - -import bar -import baz -import foo - -_ = sys -_ = bar -_ = baz -_ = foo diff --git a/gazelle/testdata/dependency_resolution_order/bar/__init__.py b/gazelle/testdata/dependency_resolution_order/bar/__init__.py deleted file mode 100644 index 76c3313f0e..0000000000 --- a/gazelle/testdata/dependency_resolution_order/bar/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import os - -_ = os diff --git a/gazelle/testdata/dependency_resolution_order/baz/__init__.py b/gazelle/testdata/dependency_resolution_order/baz/__init__.py deleted file mode 100644 index 76c3313f0e..0000000000 --- a/gazelle/testdata/dependency_resolution_order/baz/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import os - -_ = os diff --git a/gazelle/testdata/dependency_resolution_order/foo/__init__.py b/gazelle/testdata/dependency_resolution_order/foo/__init__.py deleted file mode 100644 index 76c3313f0e..0000000000 --- a/gazelle/testdata/dependency_resolution_order/foo/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import os - -_ = os diff --git a/gazelle/testdata/dependency_resolution_order/gazelle_python.yaml b/gazelle/testdata/dependency_resolution_order/gazelle_python.yaml deleted file mode 100644 index 7e911bf29b..0000000000 --- a/gazelle/testdata/dependency_resolution_order/gazelle_python.yaml +++ /dev/null @@ -1,4 +0,0 @@ -manifest: - modules_mapping: - foo: some_foo - pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/testdata/dependency_resolution_order/somewhere/bar/__init__.py b/gazelle/testdata/dependency_resolution_order/somewhere/bar/__init__.py deleted file mode 100644 index 76c3313f0e..0000000000 --- a/gazelle/testdata/dependency_resolution_order/somewhere/bar/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import os - -_ = os diff --git a/gazelle/testdata/disable_import_statements_validation/__init__.py b/gazelle/testdata/disable_import_statements_validation/__init__.py deleted file mode 100644 index 88eba74539..0000000000 --- a/gazelle/testdata/disable_import_statements_validation/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import abcdefg - -_ = abcdefg diff --git a/gazelle/testdata/disable_import_statements_validation/test.yaml b/gazelle/testdata/disable_import_statements_validation/test.yaml deleted file mode 100644 index 36dd656b39..0000000000 --- a/gazelle/testdata/disable_import_statements_validation/test.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expect: - exit_code: 0 diff --git a/gazelle/testdata/dont_rename_target/test.yaml b/gazelle/testdata/dont_rename_target/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/dont_rename_target/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/file_name_matches_import_statement/__init__.py b/gazelle/testdata/file_name_matches_import_statement/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/file_name_matches_import_statement/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/file_name_matches_import_statement/gazelle_python.yaml b/gazelle/testdata/file_name_matches_import_statement/gazelle_python.yaml deleted file mode 100644 index 63e6966941..0000000000 --- a/gazelle/testdata/file_name_matches_import_statement/gazelle_python.yaml +++ /dev/null @@ -1,4 +0,0 @@ -manifest: - modules_mapping: - rest_framework: djangorestframework - pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/testdata/file_name_matches_import_statement/rest_framework.py b/gazelle/testdata/file_name_matches_import_statement/rest_framework.py deleted file mode 100644 index 9bede69c55..0000000000 --- a/gazelle/testdata/file_name_matches_import_statement/rest_framework.py +++ /dev/null @@ -1,3 +0,0 @@ -import rest_framework - -_ = rest_framework diff --git a/gazelle/testdata/file_name_matches_import_statement/test.yaml b/gazelle/testdata/file_name_matches_import_statement/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/file_name_matches_import_statement/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/first_party_dependencies/one/__main__.py b/gazelle/testdata/first_party_dependencies/one/__main__.py deleted file mode 100644 index 2d241cc41e..0000000000 --- a/gazelle/testdata/first_party_dependencies/one/__main__.py +++ /dev/null @@ -1,12 +0,0 @@ -import os - -from bar import bar -from bar.baz import baz -from foo import foo - -if __name__ == "__main__": - INIT_FILENAME = "__init__.py" - dirname = os.path.dirname(os.path.abspath(__file__)) - assert bar() == os.path.join(dirname, "bar", INIT_FILENAME) - assert baz() == os.path.join(dirname, "bar", "baz", INIT_FILENAME) - assert foo() == os.path.join(dirname, "foo", INIT_FILENAME) diff --git a/gazelle/testdata/first_party_dependencies/one/bar/__init__.py b/gazelle/testdata/first_party_dependencies/one/bar/__init__.py deleted file mode 100644 index e311ff122a..0000000000 --- a/gazelle/testdata/first_party_dependencies/one/bar/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -def bar(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/first_party_dependencies/one/bar/baz/__init__.py b/gazelle/testdata/first_party_dependencies/one/bar/baz/__init__.py deleted file mode 100644 index e74f519643..0000000000 --- a/gazelle/testdata/first_party_dependencies/one/bar/baz/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -def baz(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/first_party_dependencies/one/foo/__init__.py b/gazelle/testdata/first_party_dependencies/one/foo/__init__.py deleted file mode 100644 index 8aeca3de74..0000000000 --- a/gazelle/testdata/first_party_dependencies/one/foo/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -def foo(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/first_party_dependencies/test.yaml b/gazelle/testdata/first_party_dependencies/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/first_party_dependencies/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/first_party_dependencies/three/__init__.py b/gazelle/testdata/first_party_dependencies/three/__init__.py deleted file mode 100644 index 41bec88fd3..0000000000 --- a/gazelle/testdata/first_party_dependencies/three/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -import os - -from bar import bar -from bar.baz import baz -from foo import foo - -_ = os -_ = bar -_ = baz -_ = foo diff --git a/gazelle/testdata/first_party_dependencies/two/__init__.py b/gazelle/testdata/first_party_dependencies/two/__init__.py deleted file mode 100644 index a0bb5c8715..0000000000 --- a/gazelle/testdata/first_party_dependencies/two/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -import os - -from foo import foo - -_ = os -_ = foo diff --git a/gazelle/testdata/first_party_file_and_directory_modules/__main__.py b/gazelle/testdata/first_party_file_and_directory_modules/__main__.py deleted file mode 100644 index acf5f10a71..0000000000 --- a/gazelle/testdata/first_party_file_and_directory_modules/__main__.py +++ /dev/null @@ -1,11 +0,0 @@ -import foo -from baz import baz as another_baz -from foo.bar import baz -from one.two import two -from package1.subpackage1.module1 import find_me - -assert not hasattr(foo, "foo") -assert baz() == "baz from foo/bar.py" -assert another_baz() == "baz from baz.py" -assert two() == "two" -assert find_me() == "found" diff --git a/gazelle/testdata/first_party_file_and_directory_modules/baz.py b/gazelle/testdata/first_party_file_and_directory_modules/baz.py deleted file mode 100644 index b161d6ab5e..0000000000 --- a/gazelle/testdata/first_party_file_and_directory_modules/baz.py +++ /dev/null @@ -1,2 +0,0 @@ -def baz(): - return "baz from baz.py" diff --git a/gazelle/testdata/first_party_file_and_directory_modules/foo.py b/gazelle/testdata/first_party_file_and_directory_modules/foo.py deleted file mode 100644 index af3cbda705..0000000000 --- a/gazelle/testdata/first_party_file_and_directory_modules/foo.py +++ /dev/null @@ -1,2 +0,0 @@ -def foo(): - print("foo") diff --git a/gazelle/testdata/first_party_file_and_directory_modules/foo/__init__.py b/gazelle/testdata/first_party_file_and_directory_modules/foo/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/first_party_file_and_directory_modules/foo/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/first_party_file_and_directory_modules/foo/bar.py b/gazelle/testdata/first_party_file_and_directory_modules/foo/bar.py deleted file mode 100644 index d6524cca2a..0000000000 --- a/gazelle/testdata/first_party_file_and_directory_modules/foo/bar.py +++ /dev/null @@ -1,7 +0,0 @@ -import one.two as two - -_ = two - - -def baz(): - return "baz from foo/bar.py" diff --git a/gazelle/testdata/first_party_file_and_directory_modules/one/__init__.py b/gazelle/testdata/first_party_file_and_directory_modules/one/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/first_party_file_and_directory_modules/one/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/first_party_file_and_directory_modules/one/two.py b/gazelle/testdata/first_party_file_and_directory_modules/one/two.py deleted file mode 100644 index 0020c44f2f..0000000000 --- a/gazelle/testdata/first_party_file_and_directory_modules/one/two.py +++ /dev/null @@ -1,2 +0,0 @@ -def two(): - return "two" diff --git a/gazelle/testdata/first_party_file_and_directory_modules/test.yaml b/gazelle/testdata/first_party_file_and_directory_modules/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/first_party_file_and_directory_modules/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/__init__.py b/gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/module1.py b/gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/module1.py deleted file mode 100644 index 0ff1c4256c..0000000000 --- a/gazelle/testdata/first_party_file_and_directory_modules/undiscoverable/package1/subpackage1/module1.py +++ /dev/null @@ -1,2 +0,0 @@ -def find_me(): - return "found" diff --git a/gazelle/testdata/generated_test_entrypoint/__init__.py b/gazelle/testdata/generated_test_entrypoint/__init__.py deleted file mode 100644 index 6a49193fe4..0000000000 --- a/gazelle/testdata/generated_test_entrypoint/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from foo import foo - -_ = foo diff --git a/gazelle/testdata/generated_test_entrypoint/foo.py b/gazelle/testdata/generated_test_entrypoint/foo.py deleted file mode 100644 index cf68624419..0000000000 --- a/gazelle/testdata/generated_test_entrypoint/foo.py +++ /dev/null @@ -1,2 +0,0 @@ -def foo(): - return "foo" diff --git a/gazelle/testdata/generated_test_entrypoint/test.yaml b/gazelle/testdata/generated_test_entrypoint/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/generated_test_entrypoint/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/ignored_invalid_imported_module/__init__.py b/gazelle/testdata/ignored_invalid_imported_module/__init__.py deleted file mode 100644 index 4301453aec..0000000000 --- a/gazelle/testdata/ignored_invalid_imported_module/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# gazelle:ignore abcdefg1,abcdefg2 -# gazelle:ignore abcdefg3 - -import abcdefg1 -import abcdefg2 -import abcdefg3 -import foo - -_ = abcdefg1 -_ = abcdefg2 -_ = abcdefg3 -_ = foo - -try: - # gazelle:ignore grpc - import grpc - - grpc_available = True -except ImportError: - grpc_available = False - -_ = grpc diff --git a/gazelle/testdata/ignored_invalid_imported_module/gazelle_python.yaml b/gazelle/testdata/ignored_invalid_imported_module/gazelle_python.yaml deleted file mode 100644 index 54b3148810..0000000000 --- a/gazelle/testdata/ignored_invalid_imported_module/gazelle_python.yaml +++ /dev/null @@ -1,4 +0,0 @@ -manifest: - modules_mapping: - foo: foo - pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/testdata/ignored_invalid_imported_module/test.yaml b/gazelle/testdata/ignored_invalid_imported_module/test.yaml deleted file mode 100644 index 36dd656b39..0000000000 --- a/gazelle/testdata/ignored_invalid_imported_module/test.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expect: - exit_code: 0 diff --git a/gazelle/testdata/invalid_imported_module/__init__.py b/gazelle/testdata/invalid_imported_module/__init__.py deleted file mode 100644 index c100931cc4..0000000000 --- a/gazelle/testdata/invalid_imported_module/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -try: - import grpc - - grpc_available = True -except ImportError: - grpc_available = False - -_ = grpc diff --git a/gazelle/testdata/invalid_imported_module/test.yaml b/gazelle/testdata/invalid_imported_module/test.yaml deleted file mode 100644 index f12c36b505..0000000000 --- a/gazelle/testdata/invalid_imported_module/test.yaml +++ /dev/null @@ -1,8 +0,0 @@ ---- -expect: - exit_code: 1 - stderr: | - gazelle: ERROR: failed to validate dependencies for target "//:invalid_imported_module": "grpc" at line 2 from "__init__.py" is an invalid dependency: possible solutions: - 1. Add it as a dependency in the requirements.txt file. - 2. Instruct Gazelle to resolve to a known dependency using the gazelle:resolve directive. - 3. Ignore it with a comment '# gazelle:ignore grpc' in the Python file. diff --git a/gazelle/testdata/monorepo/coarse_grained/__init__.py b/gazelle/testdata/monorepo/coarse_grained/__init__.py deleted file mode 100644 index 2b5b044257..0000000000 --- a/gazelle/testdata/monorepo/coarse_grained/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -import os - -import boto3 -from bar import bar -from bar.baz import baz -from foo import foo - -_ = os -_ = boto3 -_ = bar -_ = baz -_ = foo diff --git a/gazelle/testdata/monorepo/coarse_grained/bar/__init__.py b/gazelle/testdata/monorepo/coarse_grained/bar/__init__.py deleted file mode 100644 index f6ec21462a..0000000000 --- a/gazelle/testdata/monorepo/coarse_grained/bar/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -import os - -import boto3 - -_ = boto3 - - -def bar(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/monorepo/coarse_grained/bar/baz/__init__.py b/gazelle/testdata/monorepo/coarse_grained/bar/baz/__init__.py deleted file mode 100644 index e74f519643..0000000000 --- a/gazelle/testdata/monorepo/coarse_grained/bar/baz/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -def baz(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/monorepo/coarse_grained/bar/baz/first_excluded.py b/gazelle/testdata/monorepo/coarse_grained/bar/baz/first_excluded.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/monorepo/coarse_grained/bar/baz/first_excluded.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/monorepo/coarse_grained/bar/baz/hue.py b/gazelle/testdata/monorepo/coarse_grained/bar/baz/hue.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/monorepo/coarse_grained/bar/baz/hue.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/monorepo/coarse_grained/bar/baz/second_excluded.py b/gazelle/testdata/monorepo/coarse_grained/bar/baz/second_excluded.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/monorepo/coarse_grained/bar/baz/second_excluded.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/monorepo/coarse_grained/foo/__init__.py b/gazelle/testdata/monorepo/coarse_grained/foo/__init__.py deleted file mode 100644 index 8aeca3de74..0000000000 --- a/gazelle/testdata/monorepo/coarse_grained/foo/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -def foo(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/monorepo/gazelle_python.yaml b/gazelle/testdata/monorepo/gazelle_python.yaml deleted file mode 100644 index 5f7922f40f..0000000000 --- a/gazelle/testdata/monorepo/gazelle_python.yaml +++ /dev/null @@ -1,5 +0,0 @@ -manifest: - modules_mapping: - boto3: rootboto3 - boto4: rootboto4 - pip_deps_repository_name: root_pip_deps diff --git a/gazelle/testdata/monorepo/one/__main__.py b/gazelle/testdata/monorepo/one/__main__.py deleted file mode 100644 index f08f5e8009..0000000000 --- a/gazelle/testdata/monorepo/one/__main__.py +++ /dev/null @@ -1,15 +0,0 @@ -import os - -import boto3 -from bar import bar -from bar.baz import baz -from foo import foo - -_ = boto3 - -if __name__ == "__main__": - INIT_FILENAME = "__init__.py" - dirname = os.path.dirname(os.path.abspath(__file__)) - assert bar() == os.path.join(dirname, "bar", INIT_FILENAME) - assert baz() == os.path.join(dirname, "bar", "baz", INIT_FILENAME) - assert foo() == os.path.join(dirname, "foo", INIT_FILENAME) diff --git a/gazelle/testdata/monorepo/one/bar/__init__.py b/gazelle/testdata/monorepo/one/bar/__init__.py deleted file mode 100644 index f6ec21462a..0000000000 --- a/gazelle/testdata/monorepo/one/bar/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -import os - -import boto3 - -_ = boto3 - - -def bar(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/monorepo/one/bar/baz/__init__.py b/gazelle/testdata/monorepo/one/bar/baz/__init__.py deleted file mode 100644 index e74f519643..0000000000 --- a/gazelle/testdata/monorepo/one/bar/baz/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -def baz(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/monorepo/one/foo/__init__.py b/gazelle/testdata/monorepo/one/foo/__init__.py deleted file mode 100644 index 8aeca3de74..0000000000 --- a/gazelle/testdata/monorepo/one/foo/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -def foo(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/monorepo/one/gazelle_python.yaml b/gazelle/testdata/monorepo/one/gazelle_python.yaml deleted file mode 100644 index 67c53451b4..0000000000 --- a/gazelle/testdata/monorepo/one/gazelle_python.yaml +++ /dev/null @@ -1,4 +0,0 @@ -manifest: - modules_mapping: - boto3: oneboto3 - pip_deps_repository_name: one_pip_deps diff --git a/gazelle/testdata/monorepo/test.yaml b/gazelle/testdata/monorepo/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/monorepo/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/monorepo/three/__init__.py b/gazelle/testdata/monorepo/three/__init__.py deleted file mode 100644 index 6f12bd8033..0000000000 --- a/gazelle/testdata/monorepo/three/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -import os - -import bar.baz.hue as hue -import boto3 -import boto4 -from bar import bar -from bar.baz import baz -from foo import foo - -_ = os -_ = boto3 -_ = boto4 -_ = bar -_ = baz -_ = foo -_ = hue diff --git a/gazelle/testdata/monorepo/three/gazelle_python.yaml b/gazelle/testdata/monorepo/three/gazelle_python.yaml deleted file mode 100644 index d46a88f444..0000000000 --- a/gazelle/testdata/monorepo/three/gazelle_python.yaml +++ /dev/null @@ -1,6 +0,0 @@ -manifest: - modules_mapping: - boto3: threeboto3 - pip_repository: - name: three_pip_deps - incremental: true diff --git a/gazelle/testdata/monorepo/two/__init__.py b/gazelle/testdata/monorepo/two/__init__.py deleted file mode 100644 index fb3e877fe5..0000000000 --- a/gazelle/testdata/monorepo/two/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -import os - -import boto3 -from foo import foo - -_ = os -_ = boto3 -_ = foo diff --git a/gazelle/testdata/monorepo/two/gazelle_python.yaml b/gazelle/testdata/monorepo/two/gazelle_python.yaml deleted file mode 100644 index 3bc5939e58..0000000000 --- a/gazelle/testdata/monorepo/two/gazelle_python.yaml +++ /dev/null @@ -1,4 +0,0 @@ -manifest: - modules_mapping: - boto3: twoboto3 - pip_deps_repository_name: two_pip_deps diff --git a/gazelle/testdata/monorepo/wont_generate/__main__.py b/gazelle/testdata/monorepo/wont_generate/__main__.py deleted file mode 100644 index 2d241cc41e..0000000000 --- a/gazelle/testdata/monorepo/wont_generate/__main__.py +++ /dev/null @@ -1,12 +0,0 @@ -import os - -from bar import bar -from bar.baz import baz -from foo import foo - -if __name__ == "__main__": - INIT_FILENAME = "__init__.py" - dirname = os.path.dirname(os.path.abspath(__file__)) - assert bar() == os.path.join(dirname, "bar", INIT_FILENAME) - assert baz() == os.path.join(dirname, "bar", "baz", INIT_FILENAME) - assert foo() == os.path.join(dirname, "foo", INIT_FILENAME) diff --git a/gazelle/testdata/monorepo/wont_generate/bar/__init__.py b/gazelle/testdata/monorepo/wont_generate/bar/__init__.py deleted file mode 100644 index e311ff122a..0000000000 --- a/gazelle/testdata/monorepo/wont_generate/bar/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -def bar(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/monorepo/wont_generate/bar/baz/__init__.py b/gazelle/testdata/monorepo/wont_generate/bar/baz/__init__.py deleted file mode 100644 index e74f519643..0000000000 --- a/gazelle/testdata/monorepo/wont_generate/bar/baz/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -def baz(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/monorepo/wont_generate/foo/__init__.py b/gazelle/testdata/monorepo/wont_generate/foo/__init__.py deleted file mode 100644 index 8aeca3de74..0000000000 --- a/gazelle/testdata/monorepo/wont_generate/foo/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os - - -def foo(): - return os.path.abspath(__file__) diff --git a/gazelle/testdata/naming_convention/__init__.py b/gazelle/testdata/naming_convention/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention/__main__.py b/gazelle/testdata/naming_convention/__main__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention/__main__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention/__test__.py b/gazelle/testdata/naming_convention/__test__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention/__test__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention/dont_rename/__init__.py b/gazelle/testdata/naming_convention/dont_rename/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention/dont_rename/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention/dont_rename/__main__.py b/gazelle/testdata/naming_convention/dont_rename/__main__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention/dont_rename/__main__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention/dont_rename/__test__.py b/gazelle/testdata/naming_convention/dont_rename/__test__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention/dont_rename/__test__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention/resolve_conflict/__init__.py b/gazelle/testdata/naming_convention/resolve_conflict/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention/resolve_conflict/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention/resolve_conflict/__main__.py b/gazelle/testdata/naming_convention/resolve_conflict/__main__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention/resolve_conflict/__main__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention/resolve_conflict/__test__.py b/gazelle/testdata/naming_convention/resolve_conflict/__test__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention/resolve_conflict/__test__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention/test.yaml b/gazelle/testdata/naming_convention/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/naming_convention/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/naming_convention_binary_fail/__main__.py b/gazelle/testdata/naming_convention_binary_fail/__main__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention_binary_fail/__main__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention_binary_fail/test.yaml b/gazelle/testdata/naming_convention_binary_fail/test.yaml deleted file mode 100644 index bc30dd0858..0000000000 --- a/gazelle/testdata/naming_convention_binary_fail/test.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -expect: - exit_code: 1 - stderr: > - gazelle: ERROR: failed to generate target "//:naming_convention_binary_fail_bin" of kind "py_binary": - a target of kind "go_binary" with the same name already exists. - Use the '# gazelle:python_binary_naming_convention' directive to change the naming convention. diff --git a/gazelle/testdata/naming_convention_library_fail/__init__.py b/gazelle/testdata/naming_convention_library_fail/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention_library_fail/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention_library_fail/test.yaml b/gazelle/testdata/naming_convention_library_fail/test.yaml deleted file mode 100644 index 3743c324df..0000000000 --- a/gazelle/testdata/naming_convention_library_fail/test.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -expect: - exit_code: 1 - stderr: > - gazelle: ERROR: failed to generate target "//:naming_convention_library_fail" of kind "py_library": - a target of kind "go_library" with the same name already exists. - Use the '# gazelle:python_library_naming_convention' directive to change the naming convention. diff --git a/gazelle/testdata/naming_convention_test_fail/__test__.py b/gazelle/testdata/naming_convention_test_fail/__test__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/naming_convention_test_fail/__test__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/naming_convention_test_fail/test.yaml b/gazelle/testdata/naming_convention_test_fail/test.yaml deleted file mode 100644 index fc4e24e830..0000000000 --- a/gazelle/testdata/naming_convention_test_fail/test.yaml +++ /dev/null @@ -1,7 +0,0 @@ ---- -expect: - exit_code: 1 - stderr: > - gazelle: ERROR: failed to generate target "//:naming_convention_test_fail_test" of kind "py_test": - a target of kind "go_test" with the same name already exists. - Use the '# gazelle:python_test_naming_convention' directive to change the naming convention. diff --git a/gazelle/testdata/python_ignore_dependencies_directive/__init__.py b/gazelle/testdata/python_ignore_dependencies_directive/__init__.py deleted file mode 100644 index 79935a70c4..0000000000 --- a/gazelle/testdata/python_ignore_dependencies_directive/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -import bar -import boto3 -import foo -import foo.bar.baz -from baz import baz as bazfn - -_ = foo -_ = bar -_ = bazfn -_ = baz -_ = boto3 diff --git a/gazelle/testdata/python_ignore_dependencies_directive/gazelle_python.yaml b/gazelle/testdata/python_ignore_dependencies_directive/gazelle_python.yaml deleted file mode 100644 index 7288b798e1..0000000000 --- a/gazelle/testdata/python_ignore_dependencies_directive/gazelle_python.yaml +++ /dev/null @@ -1,4 +0,0 @@ -manifest: - modules_mapping: - boto3: boto3 - pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/testdata/python_ignore_dependencies_directive/test.yaml b/gazelle/testdata/python_ignore_dependencies_directive/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/python_ignore_dependencies_directive/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/python_ignore_files_directive/__init__.py b/gazelle/testdata/python_ignore_files_directive/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/python_ignore_files_directive/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/python_ignore_files_directive/bar/baz.py b/gazelle/testdata/python_ignore_files_directive/bar/baz.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/python_ignore_files_directive/bar/baz.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/python_ignore_files_directive/bar/some_other.py b/gazelle/testdata/python_ignore_files_directive/bar/some_other.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/python_ignore_files_directive/bar/some_other.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/python_ignore_files_directive/foo/baz.py b/gazelle/testdata/python_ignore_files_directive/foo/baz.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/python_ignore_files_directive/foo/baz.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/python_ignore_files_directive/setup.py b/gazelle/testdata/python_ignore_files_directive/setup.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/python_ignore_files_directive/setup.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/python_ignore_files_directive/some_other.py b/gazelle/testdata/python_ignore_files_directive/some_other.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/python_ignore_files_directive/some_other.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/python_ignore_files_directive/test.yaml b/gazelle/testdata/python_ignore_files_directive/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/python_ignore_files_directive/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/python_target_with_test_in_name/BUILD.out b/gazelle/testdata/python_target_with_test_in_name/BUILD.out deleted file mode 100644 index bdde605c09..0000000000 --- a/gazelle/testdata/python_target_with_test_in_name/BUILD.out +++ /dev/null @@ -1,12 +0,0 @@ -load("@rules_python//python:defs.bzl", "py_library") - -py_library( - name = "python_target_with_test_in_name", - srcs = [ - "__init__.py", - "not_a_real_test.py", - "test_not_a_real.py", - ], - visibility = ["//:__subpackages__"], - deps = ["@gazelle_python_test//pypi__boto3"], -) diff --git a/gazelle/testdata/python_target_with_test_in_name/__init__.py b/gazelle/testdata/python_target_with_test_in_name/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/python_target_with_test_in_name/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/python_target_with_test_in_name/gazelle_python.yaml b/gazelle/testdata/python_target_with_test_in_name/gazelle_python.yaml deleted file mode 100644 index 7288b798e1..0000000000 --- a/gazelle/testdata/python_target_with_test_in_name/gazelle_python.yaml +++ /dev/null @@ -1,4 +0,0 @@ -manifest: - modules_mapping: - boto3: boto3 - pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/testdata/python_target_with_test_in_name/not_a_real_test.py b/gazelle/testdata/python_target_with_test_in_name/not_a_real_test.py deleted file mode 100644 index 57c019daab..0000000000 --- a/gazelle/testdata/python_target_with_test_in_name/not_a_real_test.py +++ /dev/null @@ -1,3 +0,0 @@ -import boto3 - -_ = boto3 diff --git a/gazelle/testdata/python_target_with_test_in_name/test.yaml b/gazelle/testdata/python_target_with_test_in_name/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/python_target_with_test_in_name/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/python_target_with_test_in_name/test_not_a_real.py b/gazelle/testdata/python_target_with_test_in_name/test_not_a_real.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/python_target_with_test_in_name/test_not_a_real.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/relative_imports/__main__.py b/gazelle/testdata/relative_imports/__main__.py deleted file mode 100644 index 4fb887a803..0000000000 --- a/gazelle/testdata/relative_imports/__main__.py +++ /dev/null @@ -1,5 +0,0 @@ -from package1.module1 import function1 -from package2.module3 import function3 - -print(function1()) -print(function3()) diff --git a/gazelle/testdata/relative_imports/package1/module1.py b/gazelle/testdata/relative_imports/package1/module1.py deleted file mode 100644 index 69cdde2633..0000000000 --- a/gazelle/testdata/relative_imports/package1/module1.py +++ /dev/null @@ -1,5 +0,0 @@ -from .module2 import function2 - - -def function1(): - return "function1 " + function2() diff --git a/gazelle/testdata/relative_imports/package1/module2.py b/gazelle/testdata/relative_imports/package1/module2.py deleted file mode 100644 index 1e731b4ec1..0000000000 --- a/gazelle/testdata/relative_imports/package1/module2.py +++ /dev/null @@ -1,2 +0,0 @@ -def function2(): - return "function2" diff --git a/gazelle/testdata/relative_imports/package2/__init__.py b/gazelle/testdata/relative_imports/package2/__init__.py deleted file mode 100644 index fd0384ba7e..0000000000 --- a/gazelle/testdata/relative_imports/package2/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -class Class1: - def method1(self): - return "method1" diff --git a/gazelle/testdata/relative_imports/package2/module3.py b/gazelle/testdata/relative_imports/package2/module3.py deleted file mode 100644 index a5102dd8bd..0000000000 --- a/gazelle/testdata/relative_imports/package2/module3.py +++ /dev/null @@ -1,7 +0,0 @@ -from . import Class1 -from .subpackage1.module5 import function5 - - -def function3(): - c1 = Class1() - return "function3 " + c1.method1() + " " + function5() diff --git a/gazelle/testdata/relative_imports/package2/module4.py b/gazelle/testdata/relative_imports/package2/module4.py deleted file mode 100644 index 6e69699985..0000000000 --- a/gazelle/testdata/relative_imports/package2/module4.py +++ /dev/null @@ -1,2 +0,0 @@ -def function4(): - return "function4" diff --git a/gazelle/testdata/relative_imports/package2/subpackage1/module5.py b/gazelle/testdata/relative_imports/package2/subpackage1/module5.py deleted file mode 100644 index ac1f7257df..0000000000 --- a/gazelle/testdata/relative_imports/package2/subpackage1/module5.py +++ /dev/null @@ -1,5 +0,0 @@ -from ..module4 import function4 - - -def function5(): - return "function5 " + function4() diff --git a/gazelle/testdata/relative_imports/test.yaml b/gazelle/testdata/relative_imports/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/relative_imports/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/simple_binary/__main__.py b/gazelle/testdata/simple_binary/__main__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/simple_binary/__main__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/simple_binary/test.yaml b/gazelle/testdata/simple_binary/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/simple_binary/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/simple_binary_with_library/__init__.py b/gazelle/testdata/simple_binary_with_library/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/simple_binary_with_library/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/simple_binary_with_library/__main__.py b/gazelle/testdata/simple_binary_with_library/__main__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/simple_binary_with_library/__main__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/simple_binary_with_library/bar.py b/gazelle/testdata/simple_binary_with_library/bar.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/simple_binary_with_library/bar.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/simple_binary_with_library/foo.py b/gazelle/testdata/simple_binary_with_library/foo.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/simple_binary_with_library/foo.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/simple_binary_with_library/test.yaml b/gazelle/testdata/simple_binary_with_library/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/simple_binary_with_library/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/simple_library/__init__.py b/gazelle/testdata/simple_library/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/simple_library/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/simple_library/test.yaml b/gazelle/testdata/simple_library/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/simple_library/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/simple_library_without_init/foo/foo.py b/gazelle/testdata/simple_library_without_init/foo/foo.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/simple_library_without_init/foo/foo.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/simple_library_without_init/test.yaml b/gazelle/testdata/simple_library_without_init/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/simple_library_without_init/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/simple_test/__init__.py b/gazelle/testdata/simple_test/__init__.py deleted file mode 100644 index 6a49193fe4..0000000000 --- a/gazelle/testdata/simple_test/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -from foo import foo - -_ = foo diff --git a/gazelle/testdata/simple_test/__test__.py b/gazelle/testdata/simple_test/__test__.py deleted file mode 100644 index d6085a41b4..0000000000 --- a/gazelle/testdata/simple_test/__test__.py +++ /dev/null @@ -1,12 +0,0 @@ -import unittest - -from __init__ import foo - - -class FooTest(unittest.TestCase): - def test_foo(self): - self.assertEqual("foo", foo()) - - -if __name__ == "__main__": - unittest.main() diff --git a/gazelle/testdata/simple_test/foo.py b/gazelle/testdata/simple_test/foo.py deleted file mode 100644 index cf68624419..0000000000 --- a/gazelle/testdata/simple_test/foo.py +++ /dev/null @@ -1,2 +0,0 @@ -def foo(): - return "foo" diff --git a/gazelle/testdata/simple_test/test.yaml b/gazelle/testdata/simple_test/test.yaml deleted file mode 100644 index 36dd656b39..0000000000 --- a/gazelle/testdata/simple_test/test.yaml +++ /dev/null @@ -1,3 +0,0 @@ ---- -expect: - exit_code: 0 diff --git a/gazelle/testdata/subdir_sources/__main__.py b/gazelle/testdata/subdir_sources/__main__.py deleted file mode 100644 index 3cc8834990..0000000000 --- a/gazelle/testdata/subdir_sources/__main__.py +++ /dev/null @@ -1,7 +0,0 @@ -import foo.bar.bar as bar -import foo.baz.baz as baz -import one.two.three as three - -_ = bar -_ = baz -_ = three diff --git a/gazelle/testdata/subdir_sources/foo/__init__.py b/gazelle/testdata/subdir_sources/foo/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/bar/bar.py b/gazelle/testdata/subdir_sources/foo/bar/bar.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/bar/bar.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/baz/baz.py b/gazelle/testdata/subdir_sources/foo/baz/baz.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/baz/baz.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/foo.py b/gazelle/testdata/subdir_sources/foo/foo.py deleted file mode 100644 index 6752f22f90..0000000000 --- a/gazelle/testdata/subdir_sources/foo/foo.py +++ /dev/null @@ -1,3 +0,0 @@ -import foo.bar.bar as bar - -_ = bar diff --git a/gazelle/testdata/subdir_sources/foo/has_build/python/my_module.py b/gazelle/testdata/subdir_sources/foo/has_build/python/my_module.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/has_build/python/my_module.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/has_build_bazel/python/my_module.py b/gazelle/testdata/subdir_sources/foo/has_build_bazel/python/my_module.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/has_build_bazel/python/my_module.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/has_init/__init__.py b/gazelle/testdata/subdir_sources/foo/has_init/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/has_init/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/has_init/python/my_module.py b/gazelle/testdata/subdir_sources/foo/has_init/python/my_module.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/has_init/python/my_module.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/has_main/__main__.py b/gazelle/testdata/subdir_sources/foo/has_main/__main__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/has_main/__main__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/has_main/python/my_module.py b/gazelle/testdata/subdir_sources/foo/has_main/python/my_module.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/has_main/python/my_module.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/has_test/__test__.py b/gazelle/testdata/subdir_sources/foo/has_test/__test__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/has_test/__test__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/foo/has_test/python/my_module.py b/gazelle/testdata/subdir_sources/foo/has_test/python/my_module.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/foo/has_test/python/my_module.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/one/__init__.py b/gazelle/testdata/subdir_sources/one/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/one/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/one/two/__init__.py b/gazelle/testdata/subdir_sources/one/two/__init__.py deleted file mode 100644 index f6c7d2a988..0000000000 --- a/gazelle/testdata/subdir_sources/one/two/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import foo.baz.baz as baz - -_ = baz diff --git a/gazelle/testdata/subdir_sources/one/two/three.py b/gazelle/testdata/subdir_sources/one/two/three.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/subdir_sources/one/two/three.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/subdir_sources/test.yaml b/gazelle/testdata/subdir_sources/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/subdir_sources/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/with_nested_import_statements/__init__.py b/gazelle/testdata/with_nested_import_statements/__init__.py deleted file mode 100644 index 6871953f88..0000000000 --- a/gazelle/testdata/with_nested_import_statements/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -import os -import sys - -_ = os -_ = sys - - -def main(): - import boto3 - - _ = boto3 diff --git a/gazelle/testdata/with_nested_import_statements/gazelle_python.yaml b/gazelle/testdata/with_nested_import_statements/gazelle_python.yaml deleted file mode 100644 index 7288b798e1..0000000000 --- a/gazelle/testdata/with_nested_import_statements/gazelle_python.yaml +++ /dev/null @@ -1,4 +0,0 @@ -manifest: - modules_mapping: - boto3: boto3 - pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/testdata/with_nested_import_statements/test.yaml b/gazelle/testdata/with_nested_import_statements/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/with_nested_import_statements/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/with_std_requirements/__init__.py b/gazelle/testdata/with_std_requirements/__init__.py deleted file mode 100644 index 154689a5f4..0000000000 --- a/gazelle/testdata/with_std_requirements/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -import os -import sys - -_ = os -_ = sys diff --git a/gazelle/testdata/with_std_requirements/test.yaml b/gazelle/testdata/with_std_requirements/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/with_std_requirements/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/gazelle/testdata/with_third_party_requirements/README.md b/gazelle/testdata/with_third_party_requirements/README.md deleted file mode 100644 index b47101c8f8..0000000000 --- a/gazelle/testdata/with_third_party_requirements/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# With third-party requirements - -This test case asserts that a `py_library` is generated with dependencies -extracted from its sources and a `py_binary` is generated embeding the -`py_library` and inherits its dependencies, without specifying the `deps` again. diff --git a/gazelle/testdata/with_third_party_requirements/__init__.py b/gazelle/testdata/with_third_party_requirements/__init__.py deleted file mode 100644 index 6b58ff30a8..0000000000 --- a/gazelle/testdata/with_third_party_requirements/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# For test purposes only. diff --git a/gazelle/testdata/with_third_party_requirements/__main__.py b/gazelle/testdata/with_third_party_requirements/__main__.py deleted file mode 100644 index fe551aa423..0000000000 --- a/gazelle/testdata/with_third_party_requirements/__main__.py +++ /dev/null @@ -1,5 +0,0 @@ -import bar -import foo - -_ = bar -_ = foo diff --git a/gazelle/testdata/with_third_party_requirements/bar.py b/gazelle/testdata/with_third_party_requirements/bar.py deleted file mode 100644 index 19ddd97a87..0000000000 --- a/gazelle/testdata/with_third_party_requirements/bar.py +++ /dev/null @@ -1,11 +0,0 @@ -import os - -import bar -import boto3 -import rest_framework - -_ = os - -_ = bar -_ = boto3 -_ = rest_framework diff --git a/gazelle/testdata/with_third_party_requirements/foo.py b/gazelle/testdata/with_third_party_requirements/foo.py deleted file mode 100644 index 29a1f3b612..0000000000 --- a/gazelle/testdata/with_third_party_requirements/foo.py +++ /dev/null @@ -1,11 +0,0 @@ -import sys - -import boto3 -import foo -import rest_framework - -_ = sys - -_ = boto3 -_ = foo -_ = rest_framework diff --git a/gazelle/testdata/with_third_party_requirements/gazelle_python.yaml b/gazelle/testdata/with_third_party_requirements/gazelle_python.yaml deleted file mode 100644 index 76bb8bfa7b..0000000000 --- a/gazelle/testdata/with_third_party_requirements/gazelle_python.yaml +++ /dev/null @@ -1,7 +0,0 @@ -manifest: - modules_mapping: - boto3: boto3 - rest_framework: djangorestframework - foo: baz - bar: baz - pip_deps_repository_name: gazelle_python_test diff --git a/gazelle/testdata/with_third_party_requirements/test.yaml b/gazelle/testdata/with_third_party_requirements/test.yaml deleted file mode 100644 index ed97d539c0..0000000000 --- a/gazelle/testdata/with_third_party_requirements/test.yaml +++ /dev/null @@ -1 +0,0 @@ ---- diff --git a/go.mod b/go.mod deleted file mode 100644 index 7903ca1b32..0000000000 --- a/go.mod +++ /dev/null @@ -1,14 +0,0 @@ -module github.com/bazelbuild/rules_python - -go 1.18 - -require ( - github.com/bazelbuild/bazel-gazelle v0.23.0 - github.com/bazelbuild/buildtools v0.0.0-20200718160251-b1667ff58f71 - github.com/bazelbuild/rules_go v0.0.0-20190719190356-6dae44dc5cab - github.com/bmatcuk/doublestar v1.2.2 - github.com/emirpasic/gods v1.12.0 - github.com/ghodss/yaml v1.0.0 - github.com/google/uuid v1.3.0 - gopkg.in/yaml.v2 v2.2.8 -) diff --git a/go.sum b/go.sum deleted file mode 100644 index 4a8161ff6b..0000000000 --- a/go.sum +++ /dev/null @@ -1,48 +0,0 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/bazelbuild/bazel-gazelle v0.23.0 h1:Ks6YN+WkOv2lYWlvf7ksxUpLvrDbBHPBXXUrBFQ3BZM= -github.com/bazelbuild/bazel-gazelle v0.23.0/go.mod h1:3mHi4TYn0QxwdMKPJfj3FKhZxYgWm46DjWQQPOg20BY= -github.com/bazelbuild/buildtools v0.0.0-20200718160251-b1667ff58f71 h1:Et1IIXrXwhpDvR5wH9REPEZ0sUtzUoJSq19nfmBqzBY= -github.com/bazelbuild/buildtools v0.0.0-20200718160251-b1667ff58f71/go.mod h1:5JP0TXzWDHXv8qvxRC4InIazwdyDseBDbzESUMKk1yU= -github.com/bazelbuild/rules_go v0.0.0-20190719190356-6dae44dc5cab h1:wzbawlkLtl2ze9w/312NHZ84c7kpUCtlkD8HgFY27sw= -github.com/bazelbuild/rules_go v0.0.0-20190719190356-6dae44dc5cab/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= -github.com/bmatcuk/doublestar v1.2.2 h1:oC24CykoSAB8zd7XgruHo33E0cHJf/WhQA/7BeXj+x0= -github.com/bmatcuk/doublestar v1.2.2/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= -github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e h1:aZzprAO9/8oim3qStq3wc1Xuxx4QmAGriC4VU4ojemQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/internal_deps.bzl b/internal_deps.bzl index 7d6e3328c4..e4d2f69d41 100644 --- a/internal_deps.bzl +++ b/internal_deps.bzl @@ -1,3 +1,17 @@ +# 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. + """Dependencies that are needed for rules_python tests and tools.""" load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file") @@ -6,6 +20,9 @@ load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") def rules_python_internal_deps(): """Fetches all required dependencies for rules_python tests and tools.""" + # This version is also used in python/tests/toolchains/workspace_template/WORKSPACE.tmpl + # and tests/ignore_root_user_error/WORKSPACE. + # If you update this dependency, please update the tests as well. maybe( http_archive, name = "bazel_skylib", @@ -20,40 +37,46 @@ def rules_python_internal_deps(): http_archive, name = "rules_pkg", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.2.4/rules_pkg-0.2.4.tar.gz", - "https://github.com/bazelbuild/rules_pkg/releases/download/0.2.4/rules_pkg-0.2.4.tar.gz", + "https://mirror.bazel.build/github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz", + "https://github.com/bazelbuild/rules_pkg/releases/download/0.7.0/rules_pkg-0.7.0.tar.gz", ], - sha256 = "4ba8f4ab0ff85f2484287ab06c0d871dcb31cc54d439457d28fd4ae14b18450a", + sha256 = "8a298e832762eda1830597d64fe7db58178aa84cd5926d76d5b744d6558941c2", + ) + maybe( + http_archive, + name = "rules_testing", + sha256 = "0c2abee201f566a088c720e12bc1d968bc56e6a51b692d9c81b1fe861bdf2be2", + strip_prefix = "rules_testing-0.0.5", + url = "https://github.com/bazelbuild/rules_testing/releases/download/v0.0.5/rules_testing-v0.0.5.tar.gz", ) maybe( http_archive, name = "io_bazel_stardoc", - url = "https://github.com/bazelbuild/stardoc/archive/0.4.0.tar.gz", - sha256 = "6d07d18c15abb0f6d393adbd6075cd661a2219faab56a9517741f0fc755f6f3c", - strip_prefix = "stardoc-0.4.0", + url = "https://github.com/bazelbuild/stardoc/archive/6f274e903009158504a9d9130d7f7d5f3e9421ed.tar.gz", + sha256 = "b5d6891f869d5b5a224316ec4dd9e9d481885a9b1a1c81eb846e20180156f2fa", + strip_prefix = "stardoc-6f274e903009158504a9d9130d7f7d5f3e9421ed", ) + # The below two deps are required for the integration test with bazel + # gazelle. Maybe the test should be moved to the `gazelle` workspace? maybe( http_archive, name = "io_bazel_rules_go", - sha256 = "f2dcd210c7095febe54b804bb1cd3a58fe8435a909db2ec04e31542631cf715c", + sha256 = "099a9fb96a376ccbbb7d291ed4ecbdfd42f6bc822ab77ae6f1b5cb9e914e94fa", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.31.0/rules_go-v0.31.0.zip", - "https://github.com/bazelbuild/rules_go/releases/download/v0.31.0/rules_go-v0.31.0.zip", + "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.35.0/rules_go-v0.35.0.zip", + "https://github.com/bazelbuild/rules_go/releases/download/v0.35.0/rules_go-v0.35.0.zip", ], ) maybe( http_archive, name = "bazel_gazelle", - patch_args = ["-p1"], - patches = ["@rules_python//gazelle:bazel_gazelle.pr1095.patch"], - sha256 = "0bb8056ab9ed4cbcab5b74348d8530c0e0b939987b0cfe36c1ab53d35a99e4de", - strip_prefix = "bazel-gazelle-2834ea44b3ec6371c924baaf28704730ec9d4559", + sha256 = "efbbba6ac1a4fd342d5122cbdfdb82aeb2cf2862e35022c752eaddffada7c3f3", urls = [ - # No release since March, and we need subsequent fixes - "https://github.com/bazelbuild/bazel-gazelle/archive/2834ea44b3ec6371c924baaf28704730ec9d4559.zip", + "https://mirror.bazel.build/github.com/bazelbuild/bazel-gazelle/releases/download/v0.27.0/bazel-gazelle-v0.27.0.tar.gz", + "https://github.com/bazelbuild/bazel-gazelle/releases/download/v0.27.0/bazel-gazelle-v0.27.0.tar.gz", ], ) @@ -127,3 +150,24 @@ def rules_python_internal_deps(): strip_prefix = "bazel-integration-testing-165440b2dbda885f8d1ccb8d0f417e6cf8c54f17", sha256 = "2401b1369ef44cc42f91dc94443ef491208dbd06da1e1e10b702d8c189f098e3", ) + + maybe( + http_archive, + name = "rules_proto", + sha256 = "dc3fb206a2cb3441b485eb1e423165b231235a1ea9b031b4433cf7bc1fa460dd", + strip_prefix = "rules_proto-5.3.0-21.7", + urls = [ + "https://github.com/bazelbuild/rules_proto/archive/refs/tags/5.3.0-21.7.tar.gz", + ], + ) + + maybe( + http_archive, + name = "com_google_protobuf", + sha256 = "75be42bd736f4df6d702a0e4e4d30de9ee40eac024c4b845d17ae4cc831fe4ae", + strip_prefix = "protobuf-21.7", + urls = [ + "https://mirror.bazel.build/github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz", + "https://github.com/protocolbuffers/protobuf/archive/v21.7.tar.gz", + ], + ) diff --git a/internal_setup.bzl b/internal_setup.bzl index 5965665b05..c3a7ad452d 100644 --- a/internal_setup.bzl +++ b/internal_setup.bzl @@ -14,12 +14,11 @@ """Setup for rules_python tests and tools.""" -load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies") load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") load("@build_bazel_integration_testing//tools:repositories.bzl", "bazel_binaries") -load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") +load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains") load("//:version.bzl", "SUPPORTED_BAZEL_VERSIONS") -load("//gazelle:deps.bzl", _go_repositories = "gazelle_deps") load("//python/pip_install:repositories.bzl", "pip_install_dependencies") def rules_python_internal_setup(): @@ -33,11 +32,7 @@ def rules_python_internal_setup(): bazel_skylib_workspace() - # gazelle:repository_macro gazelle/deps.bzl%gazelle_deps - _go_repositories() + rules_proto_dependencies() + rules_proto_toolchains() - go_rules_dependencies() - - go_register_toolchains(version = "1.18") - - gazelle_dependencies() + protobuf_deps() diff --git a/python/BUILD b/python/BUILD.bazel similarity index 66% rename from python/BUILD rename to python/BUILD.bazel index ce19653547..d75889d188 100644 --- a/python/BUILD +++ b/python/BUILD.bazel @@ -11,7 +11,6 @@ # 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. - """This package contains two sets of rules: 1) the "core" Python rules, which were historically bundled with Bazel and @@ -24,24 +23,119 @@ In an ideal renaming, we'd move the packaging rules to a different package so that @rules_python//python is only concerned with the core rules. """ -load(":defs.bzl", "current_py_toolchain") +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load(":current_py_toolchain.bzl", "current_py_toolchain") package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) filegroup( name = "distribution", srcs = glob(["**"]) + [ + "//python/config_settings:distribution", "//python/constraints:distribution", - "//python/runfiles:distribution", "//python/private:distribution", + "//python/runfiles:distribution", ], visibility = ["//:__pkg__"], ) +# ========= bzl_library targets end ========= + +bzl_library( + name = "current_py_toolchain_bzl", + srcs = ["current_py_toolchain.bzl"], +) + +bzl_library( + name = "defs_bzl", + srcs = [ + "defs.bzl", + ], + visibility = ["//visibility:public"], + deps = [ + ":current_py_toolchain_bzl", + ":py_binary_bzl", + ":py_import_bzl", + ":py_info_bzl", + ":py_library_bzl", + ":py_runtime_bzl", + ":py_runtime_info_bzl", + ":py_runtime_pair_bzl", + ":py_test_bzl", + "//python/private:bazel_tools_bzl", + ], +) + +bzl_library( + name = "proto_bzl", + srcs = [ + "proto.bzl", + ], + visibility = ["//visibility:public"], + deps = [ + "//python/private/proto:py_proto_library_bzl", + ], +) + +bzl_library( + name = "py_binary_bzl", + srcs = ["py_binary.bzl"], + deps = ["//python/private:util_bzl"], +) + +bzl_library( + name = "py_cc_link_params_info_bzl", + srcs = ["py_cc_link_params_info.bzl"], +) + +bzl_library( + name = "py_import_bzl", + srcs = ["py_import.bzl"], + deps = [":py_info_bzl"], +) + +bzl_library( + name = "py_info_bzl", + srcs = ["py_info.bzl"], + deps = ["//python/private:reexports_bzl"], +) + +bzl_library( + name = "py_library_bzl", + srcs = ["py_library.bzl"], + deps = ["//python/private:util_bzl"], +) + +bzl_library( + name = "py_runtime_bzl", + srcs = ["py_runtime.bzl"], + deps = ["//python/private:util_bzl"], +) + +bzl_library( + name = "py_runtime_pair_bzl", + srcs = ["py_runtime_pair.bzl"], + deps = ["//python/private:bazel_tools_bzl"], +) + +bzl_library( + name = "py_runtime_info_bzl", + srcs = ["py_runtime_info.bzl"], + deps = ["//python/private:reexports_bzl"], +) + +bzl_library( + name = "py_test_bzl", + srcs = ["py_test.bzl"], + deps = ["//python/private:util_bzl"], +) + +# NOTE: Remember to add bzl_library targets to //tests:bzl_libraries +# ========= bzl_library targets end ========= + # Filegroup of bzl files that can be used by downstream rules for documentation generation -# Using a filegroup rather than bzl_library to not give a transitive dependency on Skylib filegroup( name = "bzl", srcs = [ diff --git a/python/config_settings/BUILD.bazel b/python/config_settings/BUILD.bazel new file mode 100644 index 0000000000..272ba78f1f --- /dev/null +++ b/python/config_settings/BUILD.bazel @@ -0,0 +1,12 @@ +load("//python:versions.bzl", "TOOL_VERSIONS") +load(":config_settings.bzl", "construct_config_settings") + +filegroup( + name = "distribution", + srcs = glob(["*.bzl"]) + [ + "BUILD.bazel", + ], + visibility = ["//python:__pkg__"], +) + +construct_config_settings(python_versions = TOOL_VERSIONS.keys()) diff --git a/python/config_settings/config_settings.bzl b/python/config_settings/config_settings.bzl new file mode 100644 index 0000000000..21e477e644 --- /dev/null +++ b/python/config_settings/config_settings.bzl @@ -0,0 +1,40 @@ +# 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. + +"""This module is used to construct the config settings in the BUILD file in this same package. +""" + +load("@bazel_skylib//rules:common_settings.bzl", "string_flag") + +# buildifier: disable=unnamed-macro +def construct_config_settings(python_versions): + """Constructs a set of configs for all Python versions. + + Args: + python_versions: The Python versions supported by rules_python. + """ + string_flag( + name = "python_version", + build_setting_default = python_versions[0], + values = python_versions, + visibility = ["//visibility:public"], + ) + + for python_version in python_versions: + python_version_constraint_setting = "is_python_" + python_version + native.config_setting( + name = python_version_constraint_setting, + flag_values = {":python_version": python_version}, + visibility = ["//visibility:public"], + ) diff --git a/python/config_settings/transition.bzl b/python/config_settings/transition.bzl new file mode 100644 index 0000000000..0a3d51c480 --- /dev/null +++ b/python/config_settings/transition.bzl @@ -0,0 +1,223 @@ +# 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. + +"""The transition module contains the rule definitions to wrap py_binary and py_test and transition +them to the desired target platform. +""" + +load("@bazel_skylib//lib:dicts.bzl", "dicts") +load("//python:defs.bzl", _py_binary = "py_binary", _py_test = "py_test") + +def _transition_python_version_impl(_, attr): + return {"//python/config_settings:python_version": str(attr.python_version)} + +_transition_python_version = transition( + implementation = _transition_python_version_impl, + inputs = [], + outputs = ["//python/config_settings:python_version"], +) + +def _transition_py_impl(ctx): + target = ctx.attr.target + windows_constraint = ctx.attr._windows_constraint[platform_common.ConstraintValueInfo] + target_is_windows = ctx.target_platform_has_constraint(windows_constraint) + executable = ctx.actions.declare_file(ctx.attr.name + (".exe" if target_is_windows else "")) + ctx.actions.symlink( + is_executable = True, + output = executable, + target_file = target[DefaultInfo].files_to_run.executable, + ) + zipfile_symlink = None + if target_is_windows: + # Under Windows, the expected ".zip" does not exist, so we have to + # create the symlink ourselves to achieve the same behaviour as in macOS + # and Linux. + zipfile = None + expected_target_path = target[DefaultInfo].files_to_run.executable.short_path[:-4] + ".zip" + for file in target[DefaultInfo].default_runfiles.files.to_list(): + if file.short_path == expected_target_path: + zipfile = file + zipfile_symlink = ctx.actions.declare_file(ctx.attr.name + ".zip") + ctx.actions.symlink( + is_executable = True, + output = zipfile_symlink, + target_file = zipfile, + ) + env = {} + for k, v in ctx.attr.env.items(): + env[k] = ctx.expand_location(v) + + providers = [ + DefaultInfo( + executable = executable, + files = depset([zipfile_symlink] if zipfile_symlink else [], transitive = [target[DefaultInfo].files]), + runfiles = ctx.runfiles([zipfile_symlink] if zipfile_symlink else []).merge(target[DefaultInfo].default_runfiles), + ), + target[PyInfo], + target[PyRuntimeInfo], + # Ensure that the binary we're wrapping is included in code coverage. + coverage_common.instrumented_files_info( + ctx, + dependency_attributes = ["target"], + ), + target[OutputGroupInfo], + # TODO(f0rmiga): testing.TestEnvironment is deprecated in favour of RunEnvironmentInfo but + # RunEnvironmentInfo is not exposed in Bazel < 5.3. + # https://github.com/bazelbuild/rules_python/issues/901 + # https://github.com/bazelbuild/bazel/commit/dbdfa07e92f99497be9c14265611ad2920161483 + testing.TestEnvironment(env), + ] + return providers + +_COMMON_ATTRS = { + "deps": attr.label_list( + mandatory = False, + ), + "env": attr.string_dict( + mandatory = False, + ), + "python_version": attr.string( + mandatory = True, + ), + "srcs": attr.label_list( + allow_files = True, + mandatory = False, + ), + "target": attr.label( + executable = True, + cfg = "target", + mandatory = True, + providers = [PyInfo], + ), + # "tools" is a hack here. It should be "data" but "data" is not included by default in the + # location expansion in the same way it is in the native Python rules. The difference on how + # the Bazel deals with those special attributes differ on the LocationExpander, e.g.: + # https://github.com/bazelbuild/bazel/blob/ce611646/src/main/java/com/google/devtools/build/lib/analysis/LocationExpander.java#L415-L429 + # + # Since the default LocationExpander used by ctx.expand_location is not the same as the native + # rules (it doesn't set "allowDataAttributeEntriesInLabel"), we use "tools" temporarily while a + # proper fix in Bazel happens. + # + # A fix for this was proposed in https://github.com/bazelbuild/bazel/pull/16381. + "tools": attr.label_list( + allow_files = True, + mandatory = False, + ), + # Required to Opt-in to the transitions feature. + "_allowlist_function_transition": attr.label( + default = "@bazel_tools//tools/allowlists/function_transition_allowlist", + ), + "_windows_constraint": attr.label( + default = "@platforms//os:windows", + ), +} + +_transition_py_binary = rule( + _transition_py_impl, + attrs = _COMMON_ATTRS, + cfg = _transition_python_version, + executable = True, +) + +_transition_py_test = rule( + _transition_py_impl, + attrs = _COMMON_ATTRS, + cfg = _transition_python_version, + test = True, +) + +def _py_rule(rule_impl, transition_rule, name, python_version, **kwargs): + args = kwargs.pop("args", None) + data = kwargs.pop("data", None) + env = kwargs.pop("env", None) + srcs = kwargs.pop("srcs", None) + deps = kwargs.pop("deps", None) + + # Attributes common to all build rules. + # https://bazel.build/reference/be/common-definitions#common-attributes + compatible_with = kwargs.pop("compatible_with", None) + deprecation = kwargs.pop("deprecation", None) + distribs = kwargs.pop("distribs", None) + exec_compatible_with = kwargs.pop("exec_compatible_with", None) + exec_properties = kwargs.pop("exec_properties", None) + features = kwargs.pop("features", None) + restricted_to = kwargs.pop("restricted_to", None) + tags = kwargs.pop("tags", None) + target_compatible_with = kwargs.pop("target_compatible_with", None) + testonly = kwargs.pop("testonly", None) + toolchains = kwargs.pop("toolchains", None) + visibility = kwargs.pop("visibility", None) + + common_attrs = { + "compatible_with": compatible_with, + "deprecation": deprecation, + "distribs": distribs, + "exec_compatible_with": exec_compatible_with, + "exec_properties": exec_properties, + "features": features, + "restricted_to": restricted_to, + "target_compatible_with": target_compatible_with, + "testonly": testonly, + "toolchains": toolchains, + } + + # Test-specific extra attributes. + if "env_inherit" in kwargs: + common_attrs["env_inherit"] = kwargs.pop("env_inherit") + if "size" in kwargs: + common_attrs["size"] = kwargs.pop("size") + if "timeout" in kwargs: + common_attrs["timeout"] = kwargs.pop("timeout") + if "flaky" in kwargs: + common_attrs["flaky"] = kwargs.pop("flaky") + if "shard_count" in kwargs: + common_attrs["shard_count"] = kwargs.pop("shard_count") + if "local" in kwargs: + common_attrs["local"] = kwargs.pop("local") + + # Binary-specific extra attributes. + if "output_licenses" in kwargs: + common_attrs["output_licenses"] = kwargs.pop("output_licenses") + + rule_impl( + name = "_" + name, + args = args, + data = data, + deps = deps, + env = env, + srcs = srcs, + tags = ["manual"] + (tags if tags else []), + visibility = ["//visibility:private"], + **dicts.add(common_attrs, kwargs) + ) + + return transition_rule( + name = name, + args = args, + deps = deps, + env = env, + python_version = python_version, + srcs = srcs, + tags = tags, + target = ":_" + name, + tools = data, + visibility = visibility, + **common_attrs + ) + +def py_binary(name, python_version, **kwargs): + return _py_rule(_py_binary, _transition_py_binary, name, python_version, **kwargs) + +def py_test(name, python_version, **kwargs): + return _py_rule(_py_test, _transition_py_test, name, python_version, **kwargs) diff --git a/python/constraints/BUILD b/python/constraints/BUILD.bazel similarity index 100% rename from python/constraints/BUILD rename to python/constraints/BUILD.bazel diff --git a/python/current_py_toolchain.bzl b/python/current_py_toolchain.bzl new file mode 100644 index 0000000000..e3345cb646 --- /dev/null +++ b/python/current_py_toolchain.bzl @@ -0,0 +1,58 @@ +# 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. + +"""Public entry point for current_py_toolchain rule.""" + +def _current_py_toolchain_impl(ctx): + toolchain = ctx.toolchains[ctx.attr._toolchain] + + direct = [] + transitive = [] + vars = {} + + if toolchain.py3_runtime and toolchain.py3_runtime.interpreter: + direct.append(toolchain.py3_runtime.interpreter) + transitive.append(toolchain.py3_runtime.files) + vars["PYTHON3"] = toolchain.py3_runtime.interpreter.path + + if toolchain.py2_runtime and toolchain.py2_runtime.interpreter: + direct.append(toolchain.py2_runtime.interpreter) + transitive.append(toolchain.py2_runtime.files) + vars["PYTHON2"] = toolchain.py2_runtime.interpreter.path + + files = depset(direct, transitive = transitive) + return [ + toolchain, + platform_common.TemplateVariableInfo(vars), + DefaultInfo( + runfiles = ctx.runfiles(transitive_files = files), + files = files, + ), + ] + +current_py_toolchain = rule( + doc = """ + This rule exists so that the current python toolchain can be used in the `toolchains` attribute of + other rules, such as genrule. It allows exposing a python toolchain after toolchain resolution has + happened, to a rule which expects a concrete implementation of a toolchain, rather than a + toolchain_type which could be resolved to that toolchain. + """, + implementation = _current_py_toolchain_impl, + attrs = { + "_toolchain": attr.string(default = str(Label("@bazel_tools//tools/python:toolchain_type"))), + }, + toolchains = [ + str(Label("@bazel_tools//tools/python:toolchain_type")), + ], +) diff --git a/python/defs.bzl b/python/defs.bzl index 88f28c5fc0..6ded66a568 100644 --- a/python/defs.bzl +++ b/python/defs.bzl @@ -11,22 +11,18 @@ # 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. - -""" -Core rules for building Python projects. -""" +"""Core rules for building Python projects.""" load("@bazel_tools//tools/python:srcs_version.bzl", _find_requirements = "find_requirements") -load("@bazel_tools//tools/python:toolchain.bzl", _py_runtime_pair = "py_runtime_pair") -load( - "//python/private:reexports.bzl", - "internal_PyInfo", - "internal_PyRuntimeInfo", - _py_binary = "py_binary", - _py_library = "py_library", - _py_runtime = "py_runtime", - _py_test = "py_test", -) +load("//python:py_binary.bzl", _py_binary = "py_binary") +load("//python:py_info.bzl", internal_PyInfo = "PyInfo") +load("//python:py_library.bzl", _py_library = "py_library") +load("//python:py_runtime.bzl", _py_runtime = "py_runtime") +load("//python:py_runtime_info.bzl", internal_PyRuntimeInfo = "PyRuntimeInfo") +load("//python:py_runtime_pair.bzl", _py_runtime_pair = "py_runtime_pair") +load("//python:py_test.bzl", _py_test = "py_test") +load(":current_py_toolchain.bzl", _current_py_toolchain = "current_py_toolchain") +load(":py_import.bzl", _py_import = "py_import") # Exports of native-defined providers. @@ -34,98 +30,9 @@ PyInfo = internal_PyInfo PyRuntimeInfo = internal_PyRuntimeInfo -def _current_py_toolchain_impl(ctx): - toolchain = ctx.toolchains[ctx.attr._toolchain] - - direct = [] - transitive = [] - vars = {} - - if toolchain.py3_runtime and toolchain.py3_runtime.interpreter: - direct.append(toolchain.py3_runtime.interpreter) - transitive.append(toolchain.py3_runtime.files) - vars["PYTHON3"] = toolchain.py3_runtime.interpreter.path - - if toolchain.py2_runtime and toolchain.py2_runtime.interpreter: - direct.append(toolchain.py2_runtime.interpreter) - transitive.append(toolchain.py2_runtime.files) - vars["PYTHON2"] = toolchain.py2_runtime.interpreter.path - - files = depset(direct, transitive = transitive) - return [ - toolchain, - platform_common.TemplateVariableInfo(vars), - DefaultInfo( - runfiles = ctx.runfiles(transitive_files = files), - files = files, - ), - ] - -current_py_toolchain = rule( - doc = """ - This rule exists so that the current python toolchain can be used in the `toolchains` attribute of - other rules, such as genrule. It allows exposing a python toolchain after toolchain resolution has - happened, to a rule which expects a concrete implementation of a toolchain, rather than a - toolchain_type which could be resolved to that toolchain. - """, - implementation = _current_py_toolchain_impl, - attrs = { - "_toolchain": attr.string(default = str(Label("@bazel_tools//tools/python:toolchain_type"))), - }, - toolchains = [ - str(Label("@bazel_tools//tools/python:toolchain_type")), - ], -) - -def _py_import_impl(ctx): - # See https://github.com/bazelbuild/bazel/blob/0.24.0/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java#L104 . - import_paths = [ - "/".join([ctx.workspace_name, x.short_path]) - for x in ctx.files.srcs - ] - - return [ - DefaultInfo( - default_runfiles = ctx.runfiles(ctx.files.srcs, collect_default = True), - ), - PyInfo( - transitive_sources = depset(transitive = [ - dep[PyInfo].transitive_sources - for dep in ctx.attr.deps - ]), - imports = depset(direct = import_paths, transitive = [ - dep[PyInfo].imports - for dep in ctx.attr.deps - ]), - ), - ] - -py_import = rule( - doc = """This rule allows the use of Python packages as dependencies. - - It imports the given `.egg` file(s), which might be checked in source files, - fetched externally as with `http_file`, or produced as outputs of other rules. - - It may be used like a `py_library`, in the `deps` of other Python rules. +current_py_toolchain = _current_py_toolchain - This is similar to [java_import](https://docs.bazel.build/versions/master/be/java.html#java_import). - """, - implementation = _py_import_impl, - attrs = { - "deps": attr.label_list( - doc = "The list of other libraries to be linked in to the " + - "binary target.", - providers = [PyInfo], - ), - "srcs": attr.label_list( - doc = "The list of Python package files provided to Python targets " + - "that depend on this target. Note that currently only the .egg " + - "format is accepted. For .whl files, try the whl_library rule. " + - "We accept contributions to extend py_import to handle .whl.", - allow_files = [".egg"], - ), - }, -) +py_import = _py_import # Re-exports of Starlark-defined symbols in @bazel_tools//tools/python. diff --git a/python/extensions.bzl b/python/extensions.bzl index 9c1c87ab22..3bcbb5023d 100644 --- a/python/extensions.bzl +++ b/python/extensions.bzl @@ -1,10 +1,221 @@ +# 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. + "Module extensions for use with bzlmod" +load("@rules_python//python:repositories.bzl", "python_register_toolchains") +load("@rules_python//python/pip_install:pip_repository.bzl", "locked_requirements_label", "pip_repository_attrs", "pip_repository_bzlmod", "use_isolated", "whl_library") 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 toolchain_attr in mod.tags.toolchain: + python_register_toolchains( + 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 = 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( + implementation = _python_impl, + tag_classes = { + "toolchain": tag_class( + attrs = { + "configure_coverage_tool": attr.bool( + mandatory = False, + doc = "Whether or not to configure the default coverage tool for the toolchains.", + ), + "ignore_root_user_error": attr.bool( + default = False, + doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.", + mandatory = False, + ), + "name": attr.string(mandatory = True), + "python_version": attr.string(mandatory = True), + }, + ), + }, +) -def _pip_install_impl(_): +# buildifier: disable=unused-variable +def _internal_deps_impl(module_ctx): pip_install_dependencies() + install_coverage_deps() + +internal_deps = module_extension( + implementation = _internal_deps_impl, + tag_classes = { + "install": tag_class(attrs = dict()), + }, +) + +def _pip_impl(module_ctx): + for mod in module_ctx.modules: + for attr in mod.tags.parse: + requrements_lock = locked_requirements_label(module_ctx, attr) + + # Parse the requirements file directly in starlark to get the information + # needed for the whl_libary declarations below. This is needed to contain + # the pip_repository logic to a single module extension. + requirements_lock_content = module_ctx.read(requrements_lock) + parse_result = parse_requirements(requirements_lock_content) + requirements = parse_result.requirements + extra_pip_args = attr.extra_pip_args + parse_result.options + + # Create the repository where users load the `requirement` macro. Under bzlmod + # this does not create the install_deps() macro. + pip_repository_bzlmod( + name = attr.name, + requirements_lock = attr.requirements_lock, + incompatible_generate_aliases = attr.incompatible_generate_aliases, + ) + + for name, requirement_line in requirements: + whl_library( + name = "%s_%s" % (attr.name, _sanitize_name(name)), + requirement = requirement_line, + repo = attr.name, + repo_prefix = attr.name + "_", + annotation = attr.annotations.get(name), + python_interpreter = attr.python_interpreter, + python_interpreter_target = attr.python_interpreter_target, + quiet = attr.quiet, + timeout = attr.timeout, + isolated = use_isolated(module_ctx, attr), + extra_pip_args = extra_pip_args, + download_only = attr.download_only, + pip_data_exclude = attr.pip_data_exclude, + enable_implicit_namespace_pkgs = attr.enable_implicit_namespace_pkgs, + environment = attr.environment, + ) + +# Keep in sync with python/pip_install/tools/bazel.py +def _sanitize_name(name): + return name.replace("-", "_").replace(".", "_").lower() + +def _pip_parse_ext_attrs(): + attrs = dict({ + "name": attr.string(mandatory = True), + }, **pip_repository_attrs) + + # Like the pip_repository rule, we end up setting this manually so + # don't allow users to override it. + attrs.pop("repo_prefix") + + return attrs + +pip = module_extension( + implementation = _pip_impl, + tag_classes = { + "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") -pip_install = module_extension( - implementation = _pip_install_impl, +# 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/packaging.bzl b/python/packaging.bzl index 9ad2daf647..45d5c963b9 100644 --- a/python/packaging.bzl +++ b/python/packaging.bzl @@ -12,442 +12,167 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Rules for building wheels.""" +"""Public API for for building wheels.""" -load("//python/private:stamp.bzl", "is_stamping_enabled") +load("//python/private:py_package.bzl", "py_package_lib") +load("//python/private:py_wheel.bzl", _PyWheelInfo = "PyWheelInfo", _py_wheel = "py_wheel") +load("//python/private:util.bzl", "copy_propagating_kwargs") -PyWheelInfo = provider( - doc = "Information about a wheel produced by `py_wheel`", - fields = { - "name_file": ( - "File: A file containing the canonical name of the wheel (after " + - "stamping, if enabled)." - ), - "wheel": "File: The wheel file itself.", - }, -) - -def _path_inside_wheel(input_file): - # input_file.short_path is sometimes relative ("../${repository_root}/foobar") - # which is not a valid path within a zip file. Fix that. - short_path = input_file.short_path - if short_path.startswith("..") and len(short_path) >= 3: - # Path separator. '/' on linux. - separator = short_path[2] - - # Consume '../' part. - short_path = short_path[3:] - - # Find position of next '/' and consume everything up to that character. - pos = short_path.find(separator) - short_path = short_path[pos + 1:] - return short_path - -def _input_file_to_arg(input_file): - """Converts a File object to string for --input_file argument to wheelmaker""" - return "%s;%s" % (_path_inside_wheel(input_file), input_file.path) - -def _py_package_impl(ctx): - inputs = depset( - transitive = [dep[DefaultInfo].data_runfiles.files for dep in ctx.attr.deps] + - [dep[DefaultInfo].default_runfiles.files for dep in ctx.attr.deps], - ) - - # TODO: '/' is wrong on windows, but the path separator is not available in starlark. - # Fix this once ctx.configuration has directory separator information. - packages = [p.replace(".", "/") for p in ctx.attr.packages] - if not packages: - filtered_inputs = inputs - else: - filtered_files = [] - - # TODO: flattening depset to list gives poor performance, - for input_file in inputs.to_list(): - wheel_path = _path_inside_wheel(input_file) - for package in packages: - if wheel_path.startswith(package): - filtered_files.append(input_file) - filtered_inputs = depset(direct = filtered_files) - - return [DefaultInfo( - files = filtered_inputs, - )] +# Re-export as public API +PyWheelInfo = _PyWheelInfo py_package = rule( - implementation = _py_package_impl, + implementation = py_package_lib.implementation, doc = """\ A rule to select all files in transitive dependencies of deps which belong to given set of Python packages. -This rule is intended to be used as data dependency to py_wheel rule +This rule is intended to be used as data dependency to py_wheel rule. """, - attrs = { - "deps": attr.label_list( - doc = "", - ), - "packages": attr.string_list( - mandatory = False, - allow_empty = True, - doc = """\ -List of Python packages to include in the distribution. -Sub-packages are automatically included. -""", - ), - }, + attrs = py_package_lib.attrs, ) -def _escape_filename_segment(segment): - """Escape a segment of the wheel filename. - - See https://www.python.org/dev/peps/pep-0427/#escaping-and-unicode - """ - - # TODO: this is wrong, isalnum replaces non-ascii letters, while we should - # not replace them. - # TODO: replace this with a regexp once starlark supports them. - escaped = "" - for character in segment.elems(): - # isalnum doesn't handle unicode characters properly. - if character.isalnum() or character == ".": - escaped += character - elif not escaped.endswith("_"): - escaped += "_" - return escaped - -def _replace_make_variables(flag, ctx): - """Replace $(VERSION) etc make variables in flag""" - if "$" in flag: - for varname, varsub in ctx.var.items(): - flag = flag.replace("$(%s)" % varname, varsub) - return flag - -def _py_wheel_impl(ctx): - version = _replace_make_variables(ctx.attr.version, ctx) - outfile = ctx.actions.declare_file("-".join([ - _escape_filename_segment(ctx.attr.distribution), - _escape_filename_segment(version), - _escape_filename_segment(ctx.attr.python_tag), - _escape_filename_segment(ctx.attr.abi), - _escape_filename_segment(ctx.attr.platform), - ]) + ".whl") - - name_file = ctx.actions.declare_file(ctx.label.name + ".name") - - inputs_to_package = depset( - direct = ctx.files.deps, - ) - - # Inputs to this rule which are not to be packaged. - # Currently this is only the description file (if used). - other_inputs = [] - - # Wrap the inputs into a file to reduce command line length. - packageinputfile = ctx.actions.declare_file(ctx.attr.name + "_target_wrapped_inputs.txt") - content = "" - for input_file in inputs_to_package.to_list(): - content += _input_file_to_arg(input_file) + "\n" - ctx.actions.write(output = packageinputfile, content = content) - other_inputs.append(packageinputfile) - - args = ctx.actions.args() - args.add("--name", ctx.attr.distribution) - args.add("--version", version) - args.add("--python_tag", ctx.attr.python_tag) - args.add("--python_requires", ctx.attr.python_requires) - args.add("--abi", ctx.attr.abi) - args.add("--platform", ctx.attr.platform) - args.add("--out", outfile) - args.add("--name_file", name_file) - args.add_all(ctx.attr.strip_path_prefixes, format_each = "--strip_path_prefix=%s") - - # Pass workspace status files if stamping is enabled - if is_stamping_enabled(ctx.attr): - args.add("--volatile_status_file", ctx.version_file) - args.add("--stable_status_file", ctx.info_file) - other_inputs.extend([ctx.version_file, ctx.info_file]) - - args.add("--input_file_list", packageinputfile) - - extra_headers = [] - if ctx.attr.author: - extra_headers.append("Author: %s" % ctx.attr.author) - if ctx.attr.author_email: - extra_headers.append("Author-email: %s" % ctx.attr.author_email) - if ctx.attr.homepage: - extra_headers.append("Home-page: %s" % ctx.attr.homepage) - if ctx.attr.license: - extra_headers.append("License: %s" % ctx.attr.license) - - for h in extra_headers: - args.add("--header", h) - - for c in ctx.attr.classifiers: - args.add("--classifier", c) - - for r in ctx.attr.requires: - args.add("--requires", r) - - for option, requirements in ctx.attr.extra_requires.items(): - for r in requirements: - args.add("--extra_requires", r + ";" + option) - - # Merge console_scripts into entry_points. - entrypoints = dict(ctx.attr.entry_points) # Copy so we can mutate it - if ctx.attr.console_scripts: - # Copy a console_scripts group that may already exist, so we can mutate it. - console_scripts = list(entrypoints.get("console_scripts", [])) - entrypoints["console_scripts"] = console_scripts - for name, ref in ctx.attr.console_scripts.items(): - console_scripts.append("{name} = {ref}".format(name = name, ref = ref)) - - # If any entry_points are provided, construct the file here and add it to the files to be packaged. - # see: https://packaging.python.org/specifications/entry-points/ - if entrypoints: - lines = [] - for group, entries in sorted(entrypoints.items()): - if lines: - # Blank line between groups - lines.append("") - lines.append("[{group}]".format(group = group)) - lines += sorted(entries) - entry_points_file = ctx.actions.declare_file(ctx.attr.name + "_entry_points.txt") - content = "\n".join(lines) - ctx.actions.write(output = entry_points_file, content = content) - other_inputs.append(entry_points_file) - args.add("--entry_points_file", entry_points_file) - - if ctx.attr.description_file: - description_file = ctx.file.description_file - args.add("--description_file", description_file) - other_inputs.append(description_file) - - ctx.actions.run( - inputs = depset(direct = other_inputs, transitive = [inputs_to_package]), - outputs = [outfile, name_file], - arguments = [args], - executable = ctx.executable._wheelmaker, - progress_message = "Building wheel", +# Based on https://github.com/aspect-build/bazel-lib/tree/main/lib/private/copy_to_directory.bzl +# Avoiding a bazelbuild -> aspect-build dependency :( +def _py_wheel_dist_impl(ctx): + dir = ctx.actions.declare_directory(ctx.attr.out) + name_file = ctx.attr.wheel[PyWheelInfo].name_file + cmds = [ + "mkdir -p \"%s\"" % dir.path, + """cp "{}" "{}/$(cat "{}")" """.format(ctx.files.wheel[0].path, dir.path, name_file.path), + ] + ctx.actions.run_shell( + inputs = ctx.files.wheel + [name_file], + outputs = [dir], + command = "\n".join(cmds), + mnemonic = "CopyToDirectory", + progress_message = "Copying files to directory", + use_default_shell_env = True, ) return [ - DefaultInfo( - files = depset([outfile]), - runfiles = ctx.runfiles(files = [outfile]), - ), - PyWheelInfo( - wheel = outfile, - name_file = name_file, - ), + DefaultInfo(files = depset([dir])), ] -def _concat_dicts(*dicts): - result = {} - for d in dicts: - result.update(d) - return result - -_distribution_attrs = { - "abi": attr.string( - default = "none", - doc = "Python ABI tag. 'none' for pure-Python wheels.", - ), - "distribution": attr.string( - mandatory = True, - doc = """\ -Name of the distribution. - -This should match the project name onm PyPI. It's also the name that is used to -refer to the package in other packages' dependencies. -""", - ), - "platform": attr.string( - default = "any", - doc = """\ -Supported platform. Use 'any' for pure-Python wheel. +py_wheel_dist = rule( + doc = """\ +Prepare a dist/ folder, following Python's packaging standard practice. -If you have included platform-specific data, such as a .pyd or .so -extension module, you will need to specify the platform in standard -pip format. If you support multiple platforms, you can define -platform constraints, then use a select() to specify the appropriate -specifier, eg: +See https://packaging.python.org/en/latest/tutorials/packaging-projects/#generating-distribution-archives +which recommends a dist/ folder containing the wheel file(s), source distributions, etc. -` -platform = select({ - "//platforms:windows_x86_64": "win_amd64", - "//platforms:macos_x86_64": "macosx_10_7_x86_64", - "//platforms:linux_x86_64": "manylinux2014_x86_64", -}) -` +This also has the advantage that stamping information is included in the wheel's filename. """, - ), - "python_tag": attr.string( - default = "py3", - doc = "Supported Python version(s), eg `py3`, `cp35.cp36`, etc", - ), - "stamp": attr.int( - doc = """\ -Whether to encode build information into the wheel. Possible values: - -- `stamp = 1`: Always stamp the build information into the wheel, even in \ -[--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \ -This setting should be avoided, since it potentially kills remote caching for the target and \ -any downstream actions that depend on it. - -- `stamp = 0`: Always replace build information by constant values. This gives good build result caching. + implementation = _py_wheel_dist_impl, + attrs = { + "out": attr.string(doc = "name of the resulting directory", mandatory = True), + "wheel": attr.label(doc = "a [py_wheel rule](/docs/packaging.md#py_wheel_rule)", providers = [PyWheelInfo]), + }, +) -- `stamp = -1`: Embedding of build information is controlled by the \ -[--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag. +def py_wheel(name, twine = None, **kwargs): + """Builds a Python Wheel. -Stamped targets are not rebuilt unless their dependencies change. - """, - default = -1, - values = [1, 0, -1], - ), - "version": attr.string( - mandatory = True, - doc = ( - "Version number of the package. Note that this attribute " + - "supports stamp format strings (eg. `1.2.3-{BUILD_TIMESTAMP}`) " + - "as well as 'make variables' (e.g. `1.2.3-$(VERSION)`)." - ), - ), - "_stamp_flag": attr.label( - doc = "A setting used to determine whether or not the `--stamp` flag is enabled", - default = Label("//python/private:stamp"), - ), -} + Wheels are Python distribution format defined in https://www.python.org/dev/peps/pep-0427/. -_requirement_attrs = { - "extra_requires": attr.string_list_dict( - doc = "List of optional requirements for this package", - ), - "requires": attr.string_list( - doc = "List of requirements for this package", - ), -} + This macro packages a set of targets into a single wheel. + It wraps the [py_wheel rule](#py_wheel_rule). -_entrypoint_attrs = { - "console_scripts": attr.string_dict( - doc = """\ -Deprecated console_script entry points, e.g. `{'main': 'examples.wheel.main:main'}`. + Currently only pure-python wheels are supported. -Deprecated: prefer the `entry_points` attribute, which supports `console_scripts` as well as other entry points. -""", - ), - "entry_points": attr.string_list_dict( - doc = """\ -entry_points, e.g. `{'console_scripts': ['main = examples.wheel.main:main']}`. -""", - ), -} + Examples: -_other_attrs = { - "author": attr.string( - doc = "A string specifying the author of the package.", - default = "", - ), - "author_email": attr.string( - doc = "A string specifying the email address of the package author.", - default = "", - ), - "classifiers": attr.string_list( - doc = "A list of strings describing the categories for the package. For valid classifiers see https://pypi.org/classifiers", - ), - "description_file": attr.label( - doc = "A file containing text describing the package in a single line.", - allow_single_file = True, - ), - "homepage": attr.string( - doc = "A string specifying the URL for the package homepage.", - default = "", - ), - "license": attr.string( - doc = "A string specifying the license of the package.", - default = "", - ), - "python_requires": attr.string( - doc = ( - "A string specifying what other distributions need to be installed " + - "when this one is. See the section on " + - "[Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) " + - "for details and examples of the format of this argument." - ), - default = "", - ), - "strip_path_prefixes": attr.string_list( - default = [], - doc = "path prefixes to strip from files added to the generated package", - ), -} + ```python + # Package some specific py_library targets, without their dependencies + py_wheel( + name = "minimal_with_py_library", + # Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl" + distribution = "example_minimal_library", + python_tag = "py3", + version = "0.0.1", + deps = [ + "//examples/wheel/lib:module_with_data", + "//examples/wheel/lib:simple_module", + ], + ) -py_wheel = rule( - implementation = _py_wheel_impl, - doc = """ -A rule for building Python Wheels. + # Use py_package to collect all transitive dependencies of a target, + # selecting just the files within a specific python package. + py_package( + name = "example_pkg", + # Only include these Python packages. + packages = ["examples.wheel"], + deps = [":main"], + ) -Wheels are Python distribution format defined in https://www.python.org/dev/peps/pep-0427/. + py_wheel( + name = "minimal_with_py_package", + # Package data. We're building "example_minimal_package-0.0.1-py3-none-any.whl" + distribution = "example_minimal_package", + python_tag = "py3", + version = "0.0.1", + deps = [":example_pkg"], + ) + ``` -This rule packages a set of targets into a single wheel. + To publish the wheel to Pypi, the twine package is required. + rules_python doesn't provide twine itself, see https://github.com/bazelbuild/rules_python/issues/1016 + However you can install it with pip_parse, just like we do in the WORKSPACE file in rules_python. -Currently only pure-python wheels are supported. + Once you've installed twine, you can pass its label to the `twine` attribute of this macro, + to get a "[name].publish" target. -Examples: + Example: -```python -# Package some specific py_library targets, without their dependencies -py_wheel( - name = "minimal_with_py_library", - # Package data. We're building "example_minimal_library-0.0.1-py3-none-any.whl" - distribution = "example_minimal_library", - python_tag = "py3", - version = "0.0.1", - deps = [ - "//examples/wheel/lib:module_with_data", - "//examples/wheel/lib:simple_module", - ], -) + ```python + py_wheel( + name = "my_wheel", + twine = "@publish_deps_twine//:pkg", + ... + ) + ``` -# Use py_package to collect all transitive dependencies of a target, -# selecting just the files within a specific python package. -py_package( - name = "example_pkg", - # Only include these Python packages. - packages = ["examples.wheel"], - deps = [":main"], -) + Now you can run a command like the following, which publishes to https://test.pypi.org/ -py_wheel( - name = "minimal_with_py_package", - # Package data. We're building "example_minimal_package-0.0.1-py3-none-any.whl" - distribution = "example_minimal_package", - python_tag = "py3", - version = "0.0.1", - deps = [":example_pkg"], -) -``` -""", - attrs = _concat_dicts( - { - "deps": attr.label_list( - doc = """\ -Targets to be included in the distribution. + ```sh + % TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-*** \\ + bazel run --stamp --embed_label=1.2.4 -- \\ + //path/to:my_wheel.publish --repository testpypi + ``` -The targets to package are usually `py_library` rules or filesets (for packaging data files). + Args: + name: A unique name for this target. + twine: A label of the external location of the py_library target for twine + **kwargs: other named parameters passed to the underlying [py_wheel rule](#py_wheel_rule) + """ + _dist_target = "{}.dist".format(name) + py_wheel_dist( + name = _dist_target, + wheel = name, + out = kwargs.pop("dist_folder", "{}_dist".format(name)), + **copy_propagating_kwargs(kwargs) + ) -Note it's usually better to package `py_library` targets and use -`entry_points` attribute to specify `console_scripts` than to package -`py_binary` rules. `py_binary` targets would wrap a executable script that -tries to locate `.runfiles` directory which is not packaged in the wheel. -""", - ), - "_wheelmaker": attr.label( - executable = True, - cfg = "exec", - default = "//tools:wheelmaker", - ), - }, - _distribution_attrs, - _requirement_attrs, - _entrypoint_attrs, - _other_attrs, - ), -) + _py_wheel(name = name, **kwargs) + + if twine: + if not twine.endswith(":pkg"): + fail("twine label should look like @my_twine_repo//:pkg") + twine_main = twine.replace(":pkg", ":rules_python_wheel_entry_point_twine.py") + + # TODO: use py_binary from //python:defs.bzl after our stardoc setup is less brittle + # buildifier: disable=native-py + native.py_binary( + name = "{}.publish".format(name), + srcs = [twine_main], + args = [ + "upload", + "$(rootpath :{})/*".format(_dist_target), + ], + data = [_dist_target], + imports = ["."], + main = twine_main, + deps = [twine], + visibility = kwargs.get("visibility"), + ) + +py_wheel_rule = _py_wheel diff --git a/python/pip.bzl b/python/pip.bzl index 954317f4b7..3c06301306 100644 --- a/python/pip.bzl +++ b/python/pip.bzl @@ -16,74 +16,25 @@ load("//python/pip_install:pip_repository.bzl", "pip_repository", _package_annotation = "package_annotation") load("//python/pip_install:repositories.bzl", "pip_install_dependencies") load("//python/pip_install:requirements.bzl", _compile_pip_requirements = "compile_pip_requirements") +load(":versions.bzl", "MINOR_MAPPING") compile_pip_requirements = _compile_pip_requirements package_annotation = _package_annotation def pip_install(requirements = None, name = "pip", **kwargs): - """Accepts a `requirements.txt` file and installs the dependencies listed within. - - Those dependencies become available in a generated `requirements.bzl` file. - - This macro wraps the [`pip_repository`](./pip_repository.md) rule that invokes `pip`. - In your WORKSPACE file: + """Accepts a locked/compiled requirements file and installs the dependencies listed within. ```python + load("@rules_python//python:pip.bzl", "pip_install") + pip_install( + name = "pip_deps", requirements = ":requirements.txt", ) - ``` - - You can then reference installed dependencies from a `BUILD` file with: - - ```python - load("@pip//:requirements.bzl", "requirement") - py_library( - name = "bar", - ... - deps = [ - "//my/other:dep", - requirement("requests"), - requirement("numpy"), - ], - ) - ``` - - > Note that this convenience comes with a cost. - > Analysis of any BUILD file which loads the requirements helper in this way will - > cause an eager-fetch of all the pip dependencies, - > even if no python targets are requested to be built. - > In a multi-language repo, this may cause developers to fetch dependencies they don't need, - > so consider using the long form for dependencies if this happens. - - In addition to the `requirement` macro, which is used to access the `py_library` - target generated from a package's wheel, the generated `requirements.bzl` file contains - functionality for exposing [entry points][whl_ep] as `py_binary` targets. - - [whl_ep]: https://packaging.python.org/specifications/entry-points/ - - ```python - load("@pip_deps//:requirements.bzl", "entry_point") - - alias( - name = "pip-compile", - actual = entry_point( - pkg = "pip-tools", - script = "pip-compile", - ), - ) - ``` - Note that for packages whose name and script are the same, only the name of the package - is needed when calling the `entry_point` macro. - - ```python - load("@pip_deps//:requirements.bzl", "entry_point") + load("@pip_deps//:requirements.bzl", "install_deps") - alias( - name = "flake8", - actual = entry_point("flake8"), - ) + install_deps() ``` Args: @@ -92,23 +43,17 @@ def pip_install(requirements = None, name = "pip", **kwargs): **kwargs (dict): Additional arguments to the [`pip_repository`](./pip_repository.md) repository rule. """ - # Just in case our dependencies weren't already fetched - pip_install_dependencies() + # buildifier: disable=print + print("pip_install is deprecated. Please switch to pip_parse. pip_install will be removed in a future release.") + pip_parse(requirements = requirements, name = name, **kwargs) - pip_repository( - name = name, - requirements = requirements, - repo_prefix = "pypi__", - **kwargs - ) - -def pip_parse(requirements_lock, name = "pip_parsed_deps", **kwargs): +def pip_parse(requirements = None, requirements_lock = None, name = "pip_parsed_deps", **kwargs): """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, see the "vendoring" section below. - This macro wraps the [`pip_repository`](./pip_repository.md) rule that invokes `pip`, with `incremental` set. + This macro wraps the [`pip_repository`](./pip_repository.md) rule that invokes `pip`. In your WORKSPACE file: ```python @@ -195,18 +140,217 @@ def pip_parse(requirements_lock, name = "pip_parsed_deps", **kwargs): fetched/built only for the targets specified by 'build/run/test'. Note that if your lockfile is platform-dependent, you can use the `requirements_[platform]` attributes. + requirements (Label): Deprecated. See requirements_lock. name (str, optional): The name of the generated repository. The generated repositories - containing each requirement will be of the form _. + containing each requirement will be of the form `_`. **kwargs (dict): Additional arguments to the [`pip_repository`](./pip_repository.md) repository rule. """ - - # Just in case our dependencies weren't already fetched pip_install_dependencies() + # Temporary compatibility shim. + # pip_install was previously document to use requirements while pip_parse was using requirements_lock. + # We would prefer everyone move to using requirements_lock, but we maintain a temporary shim. + reqs_to_use = requirements_lock if requirements_lock else requirements + pip_repository( name = name, - requirements_lock = requirements_lock, - repo_prefix = "{}_".format(name), - incremental = True, + requirements_lock = reqs_to_use, **kwargs ) + +def _multi_pip_parse_impl(rctx): + rules_python = rctx.attr._rules_python_workspace.workspace_name + load_statements = [] + install_deps_calls = [] + process_requirements_calls = [] + for python_version, pypi_repository in rctx.attr.pip_parses.items(): + sanitized_python_version = python_version.replace(".", "_") + load_statement = """\ +load( + "@{pypi_repository}//:requirements.bzl", + _{sanitized_python_version}_install_deps = "install_deps", + _{sanitized_python_version}_all_requirements = "all_requirements", +)""".format( + pypi_repository = pypi_repository, + sanitized_python_version = sanitized_python_version, + ) + load_statements.append(load_statement) + process_requirements_call = """\ +_process_requirements( + pkg_labels = _{sanitized_python_version}_all_requirements, + python_version = "{python_version}", + repo_prefix = "{pypi_repository}_", +)""".format( + pypi_repository = pypi_repository, + python_version = python_version, + sanitized_python_version = sanitized_python_version, + ) + process_requirements_calls.append(process_requirements_call) + install_deps_call = """ _{sanitized_python_version}_install_deps(**whl_library_kwargs)""".format( + sanitized_python_version = sanitized_python_version, + ) + install_deps_calls.append(install_deps_call) + + requirements_bzl = """\ +# Generated by python/pip.bzl + +load("@{rules_python}//python:pip.bzl", "whl_library_alias") +{load_statements} + +_wheel_names = [] +_version_map = dict() +def _process_requirements(pkg_labels, python_version, repo_prefix): + for pkg_label in pkg_labels: + workspace_name = Label(pkg_label).workspace_name + wheel_name = workspace_name[len(repo_prefix):] + _wheel_names.append(wheel_name) + if not wheel_name in _version_map: + _version_map[wheel_name] = dict() + _version_map[wheel_name][python_version] = repo_prefix + +{process_requirements_calls} + +def _clean_name(name): + return name.replace("-", "_").replace(".", "_").lower() + +def requirement(name): + return "@{name}_" + _clean_name(name) + "//:pkg" + +def whl_requirement(name): + return "@{name}_" + _clean_name(name) + "//:whl" + +def data_requirement(name): + return "@{name}_" + _clean_name(name) + "//:data" + +def dist_info_requirement(name): + return "@{name}_" + _clean_name(name) + "//:dist_info" + +def entry_point(pkg, script = None): + fail("Not implemented yet") + +def install_deps(**whl_library_kwargs): +{install_deps_calls} + for wheel_name in _wheel_names: + whl_library_alias( + name = "{name}_" + wheel_name, + wheel_name = wheel_name, + default_version = "{default_version}", + version_map = _version_map[wheel_name], + ) +""".format( + name = rctx.attr.name, + install_deps_calls = "\n".join(install_deps_calls), + load_statements = "\n".join(load_statements), + process_requirements_calls = "\n".join(process_requirements_calls), + rules_python = rules_python, + default_version = rctx.attr.default_version, + ) + rctx.file("requirements.bzl", requirements_bzl) + rctx.file("BUILD.bazel", "exports_files(['requirements.bzl'])") + +_multi_pip_parse = repository_rule( + _multi_pip_parse_impl, + attrs = { + "default_version": attr.string(), + "pip_parses": attr.string_dict(), + "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")), + }, +) + +def _whl_library_alias_impl(rctx): + rules_python = rctx.attr._rules_python_workspace.workspace_name + default_repo_prefix = rctx.attr.version_map[rctx.attr.default_version] + version_map = rctx.attr.version_map.items() + build_content = ["# Generated by python/pip.bzl"] + for alias_name in ["pkg", "whl", "data", "dist_info"]: + build_content.append(_whl_library_render_alias_target( + alias_name = alias_name, + default_repo_prefix = default_repo_prefix, + rules_python = rules_python, + version_map = version_map, + wheel_name = rctx.attr.wheel_name, + )) + rctx.file("BUILD.bazel", "\n".join(build_content)) + +def _whl_library_render_alias_target( + alias_name, + default_repo_prefix, + rules_python, + version_map, + wheel_name): + alias = ["""\ +alias( + name = "{alias_name}", + actual = select({{""".format(alias_name = alias_name)] + for [python_version, repo_prefix] in version_map: + alias.append("""\ + "@{rules_python}//python/config_settings:is_python_{full_python_version}": "{actual}",""".format( + full_python_version = MINOR_MAPPING[python_version] if python_version in MINOR_MAPPING else python_version, + actual = "@{repo_prefix}{wheel_name}//:{alias_name}".format( + repo_prefix = repo_prefix, + wheel_name = wheel_name, + alias_name = alias_name, + ), + rules_python = rules_python, + )) + alias.append("""\ + "//conditions:default": "{default_actual}", + }}), + visibility = ["//visibility:public"], +)""".format( + default_actual = "@{repo_prefix}{wheel_name}//:{alias_name}".format( + repo_prefix = default_repo_prefix, + wheel_name = wheel_name, + alias_name = alias_name, + ), + )) + return "\n".join(alias) + +whl_library_alias = repository_rule( + _whl_library_alias_impl, + attrs = { + "default_version": attr.string(mandatory = True), + "version_map": attr.string_dict(mandatory = True), + "wheel_name": attr.string(mandatory = True), + "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")), + }, +) + +def multi_pip_parse(name, default_version, python_versions, python_interpreter_target, requirements_lock, **kwargs): + """NOT INTENDED FOR DIRECT USE! + + This is intended to be used by the multi_pip_parse implementation in the template of the + multi_toolchain_aliases repository rule. + + Args: + name: the name of the multi_pip_parse repository. + default_version: the default Python version. + python_versions: all Python toolchain versions currently registered. + python_interpreter_target: a dictionary which keys are Python versions and values are resolved host interpreters. + requirements_lock: a dictionary which keys are Python versions and values are locked requirements files. + **kwargs: extra arguments passed to all wrapped pip_parse. + + Returns: + The internal implementation of multi_pip_parse repository rule. + """ + pip_parses = {} + for python_version in python_versions: + if not python_version in python_interpreter_target: + fail("Missing python_interpreter_target for Python version %s in '%s'" % (python_version, name)) + if not python_version in requirements_lock: + fail("Missing requirements_lock for Python version %s in '%s'" % (python_version, name)) + + pip_parse_name = name + "_" + python_version.replace(".", "_") + pip_parse( + name = pip_parse_name, + python_interpreter_target = python_interpreter_target[python_version], + requirements_lock = requirements_lock[python_version], + **kwargs + ) + pip_parses[python_version] = pip_parse_name + + return _multi_pip_parse( + name = name, + default_version = default_version, + pip_parses = pip_parses, + ) diff --git a/python/pip_install/BUILD b/python/pip_install/BUILD.bazel similarity index 56% rename from python/pip_install/BUILD rename to python/pip_install/BUILD.bazel index 9ff51375da..e8e8633137 100644 --- a/python/pip_install/BUILD +++ b/python/pip_install/BUILD.bazel @@ -1,12 +1,11 @@ -exports_files(["pip_compile.py"]) - filegroup( name = "distribution", srcs = glob(["*.bzl"]) + [ - "BUILD", - "pip_compile.py", - "//python/pip_install/extract_wheels:distribution", + "BUILD.bazel", "//python/pip_install/private:distribution", + "//python/pip_install/tools/dependency_resolver:distribution", + "//python/pip_install/tools/lib:distribution", + "//python/pip_install/tools/wheel_installer:distribution", ], visibility = ["//:__pkg__"], ) @@ -22,7 +21,9 @@ filegroup( filegroup( name = "py_srcs", srcs = [ - "//python/pip_install/extract_wheels:py_srcs", + "//python/pip_install/tools/dependency_resolver:py_srcs", + "//python/pip_install/tools/lib:py_srcs", + "//python/pip_install/tools/wheel_installer:py_srcs", ], visibility = ["//python/pip_install/private:__pkg__"], ) diff --git a/python/pip_install/extract_wheels/BUILD b/python/pip_install/extract_wheels/BUILD deleted file mode 100644 index 158d34ba27..0000000000 --- a/python/pip_install/extract_wheels/BUILD +++ /dev/null @@ -1,187 +0,0 @@ -load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test") -load("//python/pip_install:repositories.bzl", "requirement") -load(":annotations_test_helpers.bzl", "package_annotation", "package_annotations_file") - -py_library( - name = "lib", - srcs = [ - "annotation.py", - "arguments.py", - "bazel.py", - "extract_single_wheel.py", - "extract_wheels.py", - "namespace_pkgs.py", - "parse_requirements_to_bzl.py", - "requirements.py", - "wheel.py", - ], - deps = [ - requirement("installer"), - requirement("setuptools"), - ], -) - -py_binary( - name = "extract_wheels", - srcs = [ - "extract_wheels.py", - ], - deps = [":lib"], -) - -py_binary( - name = "extract_single_wheel", - srcs = [ - "extract_single_wheel.py", - ], - deps = [":lib"], -) - -py_binary( - name = "parse_requirements_to_bzl", - srcs = [ - "parse_requirements_to_bzl.py", - ], - deps = [":lib"], -) - -package_annotations_file( - name = "mock_annotations", - annotations = { - "pkg_a": package_annotation(), - "pkg_b": package_annotation( - data_exclude_glob = [ - "*.foo", - "*.bar", - ], - ), - "pkg_c": package_annotation( - # The `join` and `strip` here accounts for potential differences - # in new lines between unix and windows hosts. - additive_build_content = "\n".join([line.strip() for line in """\ -cc_library( - name = "my_target", - hdrs = glob(["**/*.h"]), - srcs = glob(["**/*.cc"]), -) -""".splitlines()]), - data = [":my_target"], - ), - "pkg_d": package_annotation( - srcs_exclude_glob = ["pkg_d/tests/**"], - ), - }, - tags = ["manual"], -) - -py_test( - name = "annotations_test", - size = "small", - srcs = ["annotations_test.py"], - data = [":mock_annotations"], - env = {"MOCK_ANNOTATIONS": "$(rootpath :mock_annotations)"}, - tags = ["unit"], - deps = [ - ":lib", - "//python/runfiles", - ], -) - -py_test( - name = "bazel_test", - size = "small", - srcs = [ - "bazel_test.py", - ], - tags = ["unit"], - deps = [ - ":lib", - ], -) - -py_test( - name = "namespace_pkgs_test", - size = "small", - srcs = [ - "namespace_pkgs_test.py", - ], - tags = ["unit"], - deps = [ - ":lib", - ], -) - -py_test( - name = "requirements_test", - size = "small", - srcs = [ - "requirements_test.py", - ], - tags = ["unit"], - deps = [ - ":lib", - ], -) - -py_test( - name = "arguments_test", - size = "small", - srcs = [ - "arguments_test.py", - ], - tags = ["unit"], - deps = [ - ":lib", - ], -) - -py_test( - name = "whl_filegroup_test", - size = "small", - srcs = ["whl_filegroup_test.py"], - data = ["//examples/wheel:minimal_with_py_package"], - main = "whl_filegroup_test.py", - tags = ["unit"], - deps = [":lib"], -) - -py_test( - name = "parse_requirements_to_bzl_test", - size = "small", - srcs = [ - "parse_requirements_to_bzl_test.py", - ], - tags = ["unit"], - deps = [ - ":lib", - ], -) - -py_test( - name = "requirements_bzl_test", - size = "small", - srcs = [ - "requirements_bzl_test.py", - ], - deps = [ - ":lib", - ], -) - -filegroup( - name = "distribution", - srcs = glob( - ["*"], - exclude = ["*_test.py"], - ), - visibility = ["//python/pip_install:__subpackages__"], -) - -filegroup( - name = "py_srcs", - srcs = glob( - include = ["**/*.py"], - exclude = ["**/*_test.py"], - ), - visibility = ["//python/pip_install:__subpackages__"], -) diff --git a/python/pip_install/extract_wheels/bazel_test.py b/python/pip_install/extract_wheels/bazel_test.py deleted file mode 100644 index 7ecf422227..0000000000 --- a/python/pip_install/extract_wheels/bazel_test.py +++ /dev/null @@ -1,26 +0,0 @@ -import unittest - -from python.pip_install.extract_wheels.bazel import generate_entry_point_contents - - -class BazelTestCase(unittest.TestCase): - def test_generate_entry_point_contents(self): - got = generate_entry_point_contents("sphinx.cmd.build:main") - want = """#!/usr/bin/env python3 -import sys -from sphinx.cmd.build import main -if __name__ == "__main__": - sys.exit(main()) -""" - self.assertEqual(got, want) - - def test_generate_entry_point_contents_with_shebang(self): - got = generate_entry_point_contents( - "sphinx.cmd.build:main", shebang="#!/usr/bin/python" - ) - want = """#!/usr/bin/python -import sys -from sphinx.cmd.build import main -sys.exit(main()) -""" - self.assertEqual(got, want) diff --git a/python/pip_install/extract_wheels/extract_single_wheel.py b/python/pip_install/extract_wheels/extract_single_wheel.py deleted file mode 100644 index c69fbd5958..0000000000 --- a/python/pip_install/extract_wheels/extract_single_wheel.py +++ /dev/null @@ -1,81 +0,0 @@ -import argparse -import errno -import glob -import os -import subprocess -import sys -from tempfile import NamedTemporaryFile - -from python.pip_install.extract_wheels.extract_wheels import configure_reproducible_wheels -from python.pip_install.extract_wheels import arguments, bazel, requirements -from python.pip_install.extract_wheels.annotation import annotation_from_str_path - - -def main() -> None: - parser = argparse.ArgumentParser( - description="Build and/or fetch a single wheel based on the requirement passed in" - ) - parser.add_argument( - "--requirement", - action="store", - required=True, - help="A single PEP508 requirement specifier string.", - ) - parser.add_argument( - "--annotation", - type=annotation_from_str_path, - help="A json encoded file containing annotations for rendered packages.", - ) - arguments.parse_common_args(parser) - args = parser.parse_args() - deserialized_args = dict(vars(args)) - arguments.deserialize_structured_args(deserialized_args) - - configure_reproducible_wheels() - - pip_args = ( - [sys.executable, "-m", "pip"] - + (["--isolated"] if args.isolated else []) - + ["wheel", "--no-deps"] - + deserialized_args["extra_pip_args"] - ) - - requirement_file = NamedTemporaryFile(mode="wb", delete=False) - try: - requirement_file.write(args.requirement.encode("utf-8")) - requirement_file.flush() - # Close the file so pip is allowed to read it when running on Windows. - # For more information, see: https://bugs.python.org/issue14243 - requirement_file.close() - # Requirement specific args like --hash can only be passed in a requirements file, - # so write our single requirement into a temp file in case it has any of those flags. - pip_args.extend(["-r", requirement_file.name]) - - env = os.environ.copy() - env.update(deserialized_args["environment"]) - # Assumes any errors are logged by pip so do nothing. This command will fail if pip fails - subprocess.run(pip_args, check=True, env=env) - finally: - try: - os.unlink(requirement_file.name) - except OSError as e: - if e.errno != errno.ENOENT: - raise - - name, extras_for_pkg = requirements._parse_requirement_for_extra(args.requirement) - extras = {name: extras_for_pkg} if extras_for_pkg and name else dict() - - whl = next(iter(glob.glob("*.whl"))) - bazel.extract_wheel( - wheel_file=whl, - extras=extras, - pip_data_exclude=deserialized_args["pip_data_exclude"], - enable_implicit_namespace_pkgs=args.enable_implicit_namespace_pkgs, - incremental=True, - repo_prefix=args.repo_prefix, - annotation=args.annotation, - ) - - -if __name__ == "__main__": - main() diff --git a/python/pip_install/extract_wheels/extract_wheels.py b/python/pip_install/extract_wheels/extract_wheels.py deleted file mode 100644 index 7e583eb442..0000000000 --- a/python/pip_install/extract_wheels/extract_wheels.py +++ /dev/null @@ -1,132 +0,0 @@ -"""extract_wheels - -extract_wheels resolves and fetches artifacts transitively from the Python Package Index (PyPI) based on a -requirements.txt. It generates the required BUILD files to consume these packages as Python libraries. - -Under the hood, it depends on the `pip wheel` command to do resolution, download, and compilation into wheels. -""" -import argparse -import glob -import os -import pathlib -import subprocess -import sys - -from python.pip_install.extract_wheels import ( - annotation, - arguments, - bazel, - requirements, - wheel, -) - - -def configure_reproducible_wheels() -> None: - """Modifies the environment to make wheel building reproducible. - - Wheels created from sdists are not reproducible by default. We can however workaround this by - patching in some configuration with environment variables. - """ - - # wheel, by default, enables debug symbols in GCC. This incidentally captures the build path in the .so file - # We can override this behavior by disabling debug symbols entirely. - # https://github.com/pypa/pip/issues/6505 - if "CFLAGS" in os.environ: - os.environ["CFLAGS"] += " -g0" - else: - os.environ["CFLAGS"] = "-g0" - - # set SOURCE_DATE_EPOCH to 1980 so that we can use python wheels - # https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/python.section.md#python-setuppy-bdist_wheel-cannot-create-whl - if "SOURCE_DATE_EPOCH" not in os.environ: - os.environ["SOURCE_DATE_EPOCH"] = "315532800" - - # Python wheel metadata files can be unstable. - # See https://bitbucket.org/pypa/wheel/pull-requests/74/make-the-output-of-metadata-files/diff - if "PYTHONHASHSEED" not in os.environ: - os.environ["PYTHONHASHSEED"] = "0" - - -def main() -> None: - """Main program. - - Exits zero on successful program termination, non-zero otherwise. - """ - - configure_reproducible_wheels() - - parser = argparse.ArgumentParser( - description="Resolve and fetch artifacts transitively from PyPI" - ) - parser.add_argument( - "--requirements", - action="store", - required=True, - help="Path to requirements.txt from where to install dependencies", - ) - parser.add_argument( - "--annotations", - type=annotation.annotations_map_from_str_path, - help="A json encoded file containing annotations for rendered packages.", - ) - arguments.parse_common_args(parser) - args = parser.parse_args() - deserialized_args = dict(vars(args)) - arguments.deserialize_structured_args(deserialized_args) - - # Pip is run with the working directory changed to the folder containing the requirements.txt file, to allow for - # relative requirements to be correctly resolved. The --wheel-dir is therefore required to be repointed back to the - # current calling working directory (the repo root in .../external/name), where the wheel files should be written to - pip_args = ( - [sys.executable, "-m", "pip"] - + (["--isolated"] if args.isolated else []) - + ["wheel", "-r", args.requirements] - + ["--wheel-dir", os.getcwd()] - + deserialized_args["extra_pip_args"] - ) - - env = os.environ.copy() - env.update(deserialized_args["environment"]) - - # Assumes any errors are logged by pip so do nothing. This command will fail if pip fails - subprocess.run( - pip_args, - check=True, - env=env, - cwd=str(pathlib.Path(args.requirements).parent.resolve()), - ) - - extras = requirements.parse_extras(args.requirements) - - repo_label = "@%s" % args.repo - - # Locate all wheels - wheels = [whl for whl in glob.glob("*.whl")] - - # Collect all annotations - reqs = {whl: wheel.Wheel(whl).name for whl in wheels} - annotations = args.annotations.collect(reqs.values()) - - targets = [ - '"{}{}"'.format( - repo_label, - bazel.extract_wheel( - wheel_file=whl, - extras=extras, - pip_data_exclude=deserialized_args["pip_data_exclude"], - enable_implicit_namespace_pkgs=args.enable_implicit_namespace_pkgs, - repo_prefix=args.repo_prefix, - annotation=annotations.get(name), - ), - ) - for whl, name in reqs.items() - ] - - with open("requirements.bzl", "w") as requirement_file: - requirement_file.write( - bazel.generate_requirements_file_contents(repo_label, targets) - ) - - -if __name__ == "__main__": - main() diff --git a/python/pip_install/extract_wheels/parse_requirements_to_bzl.py b/python/pip_install/extract_wheels/parse_requirements_to_bzl.py deleted file mode 100644 index e2efa5ae1c..0000000000 --- a/python/pip_install/extract_wheels/parse_requirements_to_bzl.py +++ /dev/null @@ -1,282 +0,0 @@ -import argparse -import json -import shlex -import sys -import textwrap -from pathlib import Path -from typing import Any, Dict, List, TextIO, Tuple - -from pip._internal.network.session import PipSession -from pip._internal.req import constructors -from pip._internal.req.req_file import ( - RequirementsFileParser, - get_file_content, - get_line_parser, - preprocess, -) -from pip._internal.req.req_install import InstallRequirement - -from python.pip_install.extract_wheels import annotation, arguments, bazel - - -def parse_install_requirements( - requirements_lock: str, extra_pip_args: List[str] -) -> List[Tuple[InstallRequirement, str]]: - ps = PipSession() - # This is roughly taken from pip._internal.req.req_file.parse_requirements - # (https://github.com/pypa/pip/blob/21.0.1/src/pip/_internal/req/req_file.py#L127) in order to keep - # the original line (sort-of, its preprocessed) from the requirements_lock file around, to pass to sub repos - # as the requirement. - line_parser = get_line_parser(finder=None) - parser = RequirementsFileParser(ps, line_parser) - install_req_and_lines: List[Tuple[InstallRequirement, str]] = [] - _, content = get_file_content(requirements_lock, ps) - unpinned_reqs = [] - for parsed_line, (_, line) in zip( - parser.parse(requirements_lock, constraint=False), preprocess(content) - ): - if parsed_line.is_requirement: - install_req = constructors.install_req_from_line(parsed_line.requirement) - if ( - # PEP-440 direct references are considered pinned - # See: https://peps.python.org/pep-0440/#direct-references and https://peps.python.org/pep-0508/ - not install_req.link and - not install_req.is_pinned - ): - unpinned_reqs.append(str(install_req)) - install_req_and_lines.append( - (install_req, line) - ) - - else: - extra_pip_args.extend(shlex.split(line)) - - if len(unpinned_reqs) > 0: - unpinned_reqs_str = "\n".join(unpinned_reqs) - raise RuntimeError(f"""\ -The `requirements_lock` file must be fully pinned. See `compile_pip_requirements`. -Alternatively, use `pip-tools` or a similar mechanism to produce a pinned lockfile. - -The following requirements were not pinned: -{unpinned_reqs_str}""") - - return install_req_and_lines - - -def repo_names_and_requirements( - install_reqs: List[Tuple[InstallRequirement, str]], repo_prefix: str -) -> List[Tuple[str, str]]: - return [ - ( - bazel.sanitise_name(ir.name, prefix=repo_prefix), - line, - ) - for ir, line in install_reqs - ] - - -def parse_whl_library_args(args: argparse.Namespace) -> Dict[str, Any]: - whl_library_args = dict(vars(args)) - whl_library_args = arguments.deserialize_structured_args(whl_library_args) - whl_library_args.setdefault("python_interpreter", sys.executable) - - # These arguments are not used by `whl_library` - for arg in ("requirements_lock", "requirements_lock_label", "annotations"): - if arg in whl_library_args: - whl_library_args.pop(arg) - - return whl_library_args - - -def generate_parsed_requirements_contents( - requirements_lock: Path, - repo_prefix: str, - whl_library_args: Dict[str, Any], - annotations: Dict[str, str] = dict(), -) -> str: - """ - Parse each requirement from the requirements_lock file, and prepare arguments for each - repository rule, which will represent the individual requirements. - - Generates a requirements.bzl file containing a macro (install_deps()) which instantiates - a repository rule for each requirement in the lock file. - """ - install_req_and_lines = parse_install_requirements( - requirements_lock, whl_library_args["extra_pip_args"] - ) - repo_names_and_reqs = repo_names_and_requirements( - install_req_and_lines, repo_prefix - ) - all_requirements = ", ".join( - [ - bazel.sanitised_repo_library_label(ir.name, repo_prefix=repo_prefix) - for ir, _ in install_req_and_lines - ] - ) - all_whl_requirements = ", ".join( - [ - bazel.sanitised_repo_file_label(ir.name, repo_prefix=repo_prefix) - for ir, _ in install_req_and_lines - ] - ) - return textwrap.dedent( - """\ - - load("@rules_python//python/pip_install:pip_repository.bzl", "whl_library") - - all_requirements = [{all_requirements}] - - all_whl_requirements = [{all_whl_requirements}] - - _packages = {repo_names_and_reqs} - _config = {args} - _annotations = {annotations} - - def _clean_name(name): - return name.replace("-", "_").replace(".", "_").lower() - - def requirement(name): - return "@{repo_prefix}" + _clean_name(name) + "//:{py_library_label}" - - def whl_requirement(name): - return "@{repo_prefix}" + _clean_name(name) + "//:{wheel_file_label}" - - def data_requirement(name): - return "@{repo_prefix}" + _clean_name(name) + "//:{data_label}" - - def dist_info_requirement(name): - return "@{repo_prefix}" + _clean_name(name) + "//:{dist_info_label}" - - def entry_point(pkg, script = None): - if not script: - script = pkg - return "@{repo_prefix}" + _clean_name(pkg) + "//:{entry_point_prefix}_" + script - - def _get_annotation(requirement): - # This expects to parse `setuptools==58.2.0 --hash=sha256:2551203ae6955b9876741a26ab3e767bb3242dafe86a32a749ea0d78b6792f11` - # down wo `setuptools`. - name = requirement.split(" ")[0].split("=")[0] - return _annotations.get(name) - - def install_deps(): - for name, requirement in _packages: - whl_library( - name = name, - requirement = requirement, - annotation = _get_annotation(requirement), - **_config - ) - """.format( - all_requirements=all_requirements, - all_whl_requirements=all_whl_requirements, - annotations=json.dumps(annotations), - args=dict(sorted(whl_library_args.items())), - data_label=bazel.DATA_LABEL, - dist_info_label=bazel.DIST_INFO_LABEL, - entry_point_prefix=bazel.WHEEL_ENTRY_POINT_PREFIX, - py_library_label=bazel.PY_LIBRARY_LABEL, - repo_names_and_reqs=repo_names_and_reqs, - repo_prefix=repo_prefix, - wheel_file_label=bazel.WHEEL_FILE_LABEL, - ) - ) - - -def coerce_to_bool(option): - return str(option).lower() == "true" - - -def main(output: TextIO) -> None: - """Args: - - output: where to write the resulting starlark, such as sys.stdout or an open file - """ - parser = argparse.ArgumentParser( - description="Create rules to incrementally fetch needed \ -dependencies from a fully resolved requirements lock file." - ) - parser.add_argument( - "--requirements_lock", - action="store", - required=True, - help="Path to fully resolved requirements.txt to use as the source of repos.", - ) - parser.add_argument( - "--requirements_lock_label", - help="Label used to declare the requirements.lock, included in comments in the file.", - ) - parser.add_argument( - "--python_interpreter", - help="The python interpreter that will be used to download and unpack the wheels.", - ) - parser.add_argument( - "--python_interpreter_target", - help="Bazel target of a python interpreter.\ -It will be used in repository rules so it must be an already built interpreter.\ -If set, it will take precedence over python_interpreter.", - ) - parser.add_argument( - "--quiet", - type=coerce_to_bool, - default=True, - required=True, - help="Whether to print stdout / stderr from child repos.", - ) - parser.add_argument( - "--timeout", - type=int, - action="store", - required=True, - help="timeout to use for pip operation.", - ) - parser.add_argument( - "--annotations", - type=annotation.annotations_map_from_str_path, - help="A json encoded file containing annotations for rendered packages.", - ) - arguments.parse_common_args(parser) - args = parser.parse_args() - - whl_library_args = parse_whl_library_args(args) - - # Check for any annotations which match packages in the locked requirements file - install_requirements = parse_install_requirements( - args.requirements_lock, whl_library_args["extra_pip_args"] - ) - req_names = sorted([req.name for req, _ in install_requirements]) - annotations = args.annotations.collect(req_names) if args.annotations else {} - - # Write all rendered annotation files and generate a list of the labels to write to the requirements file - annotated_requirements = dict() - for name, content in annotations.items(): - annotation_path = Path(name + ".annotation.json") - annotation_path.write_text(json.dumps(content, indent=4)) - annotated_requirements.update( - { - name: "@{}//:{}.annotation.json".format( - args.repo_prefix.rstrip("_"), name - ) - } - ) - - output.write(textwrap.dedent( - """\ - \"\"\"Starlark representation of locked requirements. - - @generated by rules_python pip_parse repository rule - from {} - \"\"\" - """.format(args.requirements_lock_label))) - output.write( - generate_parsed_requirements_contents( - requirements_lock=args.requirements_lock, - repo_prefix=args.repo_prefix, - whl_library_args=whl_library_args, - annotations=annotated_requirements, - ) - ) - - -if __name__ == "__main__": - with open("requirements.bzl", "w") as requirement_file: - main(requirement_file) diff --git a/python/pip_install/extract_wheels/parse_requirements_to_bzl_test.py b/python/pip_install/extract_wheels/parse_requirements_to_bzl_test.py deleted file mode 100644 index a9a4c95afe..0000000000 --- a/python/pip_install/extract_wheels/parse_requirements_to_bzl_test.py +++ /dev/null @@ -1,151 +0,0 @@ -import argparse -import json -import tempfile -import unittest -from pathlib import Path -from textwrap import dedent - -from pip._internal.req.req_install import InstallRequirement - -from python.pip_install.extract_wheels.parse_requirements_to_bzl import ( - generate_parsed_requirements_contents, - parse_install_requirements, - parse_whl_library_args, -) - - -class TestParseRequirementsToBzl(unittest.TestCase): - maxDiff = None - - def test_generated_requirements_bzl(self) -> None: - with tempfile.TemporaryDirectory() as temp_dir: - requirements_lock = Path(temp_dir) / "requirements.txt" - comments_and_flags = "#comment\n--require-hashes True\n" - requirement_string = "foo==0.0.0 --hash=sha256:hashofFoowhl" - requirements_lock.write_bytes( - bytes(comments_and_flags + requirement_string, encoding="utf-8") - ) - args = argparse.Namespace() - args.requirements_lock = str(requirements_lock.resolve()) - args.repo_prefix = "pip_parsed_deps_pypi__" - extra_pip_args = ["--index-url=pypi.org/simple"] - pip_data_exclude = ["**.foo"] - args.extra_pip_args = json.dumps({"arg": extra_pip_args}) - args.pip_data_exclude = json.dumps({"arg": pip_data_exclude}) - args.python_interpreter = "/custom/python3" - args.python_interpreter_target = "@custom_python//:exec" - args.environment = json.dumps({"arg": {}}) - whl_library_args = parse_whl_library_args(args) - contents = generate_parsed_requirements_contents( - requirements_lock=args.requirements_lock, - repo_prefix=args.repo_prefix, - whl_library_args=whl_library_args, - ) - library_target = "@pip_parsed_deps_pypi__foo//:pkg" - whl_target = "@pip_parsed_deps_pypi__foo//:whl" - all_requirements = 'all_requirements = ["{library_target}"]'.format( - library_target=library_target - ) - all_whl_requirements = 'all_whl_requirements = ["{whl_target}"]'.format( - whl_target=whl_target - ) - self.assertIn(all_requirements, contents, contents) - self.assertIn(all_whl_requirements, contents, contents) - self.assertIn(requirement_string, contents, contents) - all_flags = extra_pip_args + ["--require-hashes", "True"] - self.assertIn( - "'extra_pip_args': {}".format(repr(all_flags)), contents, contents - ) - self.assertIn( - "'pip_data_exclude': {}".format(repr(pip_data_exclude)), - contents, - contents, - ) - self.assertIn("'python_interpreter': '/custom/python3'", contents, contents) - self.assertIn( - "'python_interpreter_target': '@custom_python//:exec'", - contents, - contents, - ) - # Assert it gets set to an empty dict by default. - self.assertIn("'environment': {}", contents, contents) - - def test_parse_install_requirements_with_args(self): - # Test requirements files with varying arguments - for requirement_args in ("", "--index-url https://index.python.com"): - with tempfile.TemporaryDirectory() as temp_dir: - requirements_lock = Path(temp_dir) / "requirements.txt" - requirements_lock.write_text( - dedent( - """\ - {} - - wheel==0.37.1 \\ - --hash=sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a \\ - --hash=sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4 - # via -r requirements.in - setuptools==58.2.0 \\ - --hash=sha256:2551203ae6955b9876741a26ab3e767bb3242dafe86a32a749ea0d78b6792f11 \ - --hash=sha256:2c55bdb85d5bb460bd2e3b12052b677879cffcf46c0c688f2e5bf51d36001145 - # via -r requirements.in - """.format( - requirement_args - ) - ) - ) - - install_req_and_lines = parse_install_requirements( - str(requirements_lock), ["-v"] - ) - - # There should only be two entries for the two requirements - self.assertEqual(len(install_req_and_lines), 2) - - # The first index in each tuple is expected to be an `InstallRequirement` object - self.assertIsInstance(install_req_and_lines[0][0], InstallRequirement) - self.assertIsInstance(install_req_and_lines[1][0], InstallRequirement) - - # Ensure the requirements text is correctly parsed with the trailing arguments - self.assertTupleEqual( - install_req_and_lines[0][1:], - ( - "wheel==0.37.1 --hash=sha256:4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a --hash=sha256:e9a504e793efbca1b8e0e9cb979a249cf4a0a7b5b8c9e8b65a5e39d49529c1c4", - ), - ) - self.assertTupleEqual( - install_req_and_lines[1][1:], - ( - "setuptools==58.2.0 --hash=sha256:2551203ae6955b9876741a26ab3e767bb3242dafe86a32a749ea0d78b6792f11 --hash=sha256:2c55bdb85d5bb460bd2e3b12052b677879cffcf46c0c688f2e5bf51d36001145", - ), - ) - - def test_parse_install_requirements_pinned_direct_reference(self): - # Test PEP-440 direct references - with tempfile.TemporaryDirectory() as temp_dir: - requirements_lock = Path(temp_dir) / "requirements.txt" - requirements_lock.write_text( - dedent( - """\ - onnx @ https://files.pythonhosted.org/packages/24/93/f5b001dc0f5de84ce049a34ff382032cd9478e1080aa6ac48470fa810577/onnx-1.11.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl \ - --hash=sha256:67c6d2654c1c203e5c839a47900b51f588fd0de71bbd497fb193d30a0b3ec1e9 - """ - ) - ) - - install_req_and_lines = parse_install_requirements( - str(requirements_lock), ["-v"] - ) - - self.assertEqual(len(install_req_and_lines), 1) - self.assertEqual(install_req_and_lines[0][0].name, "onnx") - - self.assertTupleEqual( - install_req_and_lines[0][1:], - ( - "onnx @ https://files.pythonhosted.org/packages/24/93/f5b001dc0f5de84ce049a34ff382032cd9478e1080aa6ac48470fa810577/onnx-1.11.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl --hash=sha256:67c6d2654c1c203e5c839a47900b51f588fd0de71bbd497fb193d30a0b3ec1e9", - ), - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/python/pip_install/extract_wheels/requirements.py b/python/pip_install/extract_wheels/requirements.py deleted file mode 100644 index caf20d0f79..0000000000 --- a/python/pip_install/extract_wheels/requirements.py +++ /dev/null @@ -1,47 +0,0 @@ -import re -from typing import Dict, Optional, Set, Tuple - -from pip._vendor.packaging.utils import canonicalize_name - - -def parse_extras(requirements_path: str) -> Dict[str, Set[str]]: - """Parse over the requirements.txt file to find extras requested. - - Args: - requirements_path: The filepath for the requirements.txt file to parse. - - Returns: - A dictionary mapping the requirement name to a set of extras requested. - """ - - extras_requested = {} - with open(requirements_path, "r") as requirements: - # Merge all backslash line continuations so we parse each requirement as a single line. - for line in requirements.read().replace("\\\n", "").split("\n"): - requirement, extras = _parse_requirement_for_extra(line) - if requirement and extras: - extras_requested[requirement] = extras - - return extras_requested - - -def _parse_requirement_for_extra( - requirement: str, -) -> Tuple[Optional[str], Optional[Set[str]]]: - """Given a requirement string, returns the requirement name and set of extras, if extras specified. - Else, returns (None, None) - """ - - # https://www.python.org/dev/peps/pep-0508/#grammar - extras_pattern = re.compile( - r"^\s*([0-9A-Za-z][0-9A-Za-z_.\-]*)\s*\[\s*([0-9A-Za-z][0-9A-Za-z_.\-]*(?:\s*,\s*[0-9A-Za-z][0-9A-Za-z_.\-]*)*)\s*\]" - ) - - matches = extras_pattern.match(requirement) - if matches: - return ( - canonicalize_name(matches.group(1)), - {extra.strip() for extra in matches.group(2).split(",")}, - ) - - return None, None diff --git a/python/pip_install/extract_wheels/requirements_bzl_test.py b/python/pip_install/extract_wheels/requirements_bzl_test.py deleted file mode 100644 index ae28e1fc38..0000000000 --- a/python/pip_install/extract_wheels/requirements_bzl_test.py +++ /dev/null @@ -1,19 +0,0 @@ -import unittest - -from python.pip_install.extract_wheels import bazel - - -class TestGenerateRequirementsFileContents(unittest.TestCase): - def test_all_wheel_requirements(self) -> None: - contents = bazel.generate_requirements_file_contents( - repo_name="test", - targets=['"@test//pypi__pkg1"', '"@test//pypi__pkg2"'], - ) - expected = ( - 'all_whl_requirements = ["@test//pypi__pkg1:whl","@test//pypi__pkg2:whl"]' - ) - self.assertIn(expected, contents) - - -if __name__ == "__main__": - unittest.main() diff --git a/python/pip_install/extract_wheels/requirements_test.py b/python/pip_install/extract_wheels/requirements_test.py deleted file mode 100644 index 297cd91c38..0000000000 --- a/python/pip_install/extract_wheels/requirements_test.py +++ /dev/null @@ -1,40 +0,0 @@ -import unittest - -from python.pip_install.extract_wheels import requirements - - -class TestRequirementExtrasParsing(unittest.TestCase): - def test_parses_requirement_for_extra(self) -> None: - cases = [ - ("name[foo]", ("name", frozenset(["foo"]))), - ("name[ Foo123 ]", ("name", frozenset(["Foo123"]))), - (" name1[ foo ] ", ("name1", frozenset(["foo"]))), - ("Name[foo]", ("name", frozenset(["foo"]))), - ("name_foo[bar]", ("name-foo", frozenset(["bar"]))), - ( - "name [fred,bar] @ http://foo.com ; python_version=='2.7'", - ("name", frozenset(["fred", "bar"])), - ), - ( - "name[quux, strange];python_version<'2.7' and platform_version=='2'", - ("name", frozenset(["quux", "strange"])), - ), - ( - "name; (os_name=='a' or os_name=='b') and os_name=='c'", - (None, None), - ), - ( - "name@http://foo.com", - (None, None), - ), - ] - - for case, expected in cases: - with self.subTest(): - self.assertTupleEqual( - requirements._parse_requirement_for_extra(case), expected - ) - - -if __name__ == "__main__": - unittest.main() diff --git a/python/pip_install/extract_wheels/whl_filegroup_test.py b/python/pip_install/extract_wheels/whl_filegroup_test.py deleted file mode 100644 index 2a7ade3b27..0000000000 --- a/python/pip_install/extract_wheels/whl_filegroup_test.py +++ /dev/null @@ -1,53 +0,0 @@ -import os -import shutil -import tempfile -import unittest -from pathlib import Path - -from python.pip_install.extract_wheels import bazel - - -class TestWhlFilegroup(unittest.TestCase): - def setUp(self) -> None: - self.wheel_name = "example_minimal_package-0.0.1-py3-none-any.whl" - self.wheel_dir = tempfile.mkdtemp() - self.wheel_path = os.path.join(self.wheel_dir, self.wheel_name) - shutil.copy(os.path.join("examples", "wheel", self.wheel_name), self.wheel_dir) - - def tearDown(self): - shutil.rmtree(self.wheel_dir) - - def _run( - self, - repo_prefix: str, - incremental: bool = False, - ) -> None: - generated_bazel_dir = bazel.extract_wheel( - self.wheel_path, - extras={}, - pip_data_exclude=[], - enable_implicit_namespace_pkgs=False, - incremental=incremental, - repo_prefix=repo_prefix, - incremental_dir=Path(self.wheel_dir), - ) - # Take off the leading // from the returned label. - # Assert that the raw wheel ends up in the package. - generated_bazel_dir = ( - generated_bazel_dir[2:] if not incremental else self.wheel_dir - ) - - self.assertIn(self.wheel_name, os.listdir(generated_bazel_dir)) - with open("{}/BUILD.bazel".format(generated_bazel_dir)) as build_file: - build_file_content = build_file.read() - self.assertIn("filegroup", build_file_content) - - def test_nonincremental(self) -> None: - self._run(repo_prefix="prefix_") - - def test_incremental(self) -> None: - self._run(incremental=True, repo_prefix="prefix_") - - -if __name__ == "__main__": - unittest.main() diff --git a/python/pip_install/pip_compile.py b/python/pip_install/pip_compile.py deleted file mode 100644 index aeb36de049..0000000000 --- a/python/pip_install/pip_compile.py +++ /dev/null @@ -1,151 +0,0 @@ -"Set defaults for the pip-compile command to run it under Bazel" - -import os -import sys -from shutil import copyfile - -from piptools.scripts.compile import cli - - -def _select_golden_requirements_file( - requirements_txt, requirements_linux, requirements_darwin, requirements_windows -): - """Switch the golden requirements file, used to validate if updates are needed, - to a specified platform specific one. Fallback on the platform independent one. - """ - - plat = sys.platform - if plat == "linux" and requirements_linux is not None: - return requirements_linux - elif plat == "darwin" and requirements_darwin is not None: - return requirements_darwin - elif plat == "win32" and requirements_windows is not None: - return requirements_windows - else: - return requirements_txt - - -if __name__ == "__main__": - if len(sys.argv) < 4: - print( - "Expected at least two arguments: requirements_in requirements_out", - file=sys.stderr, - ) - sys.exit(1) - - parse_str_none = lambda s: None if s == "None" else s - - requirements_in = os.path.relpath(sys.argv.pop(1)) - requirements_txt = os.path.relpath(sys.argv.pop(1)) - requirements_linux = parse_str_none(sys.argv.pop(1)) - requirements_darwin = parse_str_none(sys.argv.pop(1)) - requirements_windows = parse_str_none(sys.argv.pop(1)) - parts = requirements_in.split(os.path.sep, 2) - if parts[0] == "external": - requirements_in = parts[2] - requirements_txt = requirements_txt if "BUILD_WORKSPACE_DIRECTORY" in os.environ else os.path.join("..", "..", requirements_txt) - os.chdir(os.path.join(parts[0], parts[1])) - update_target_label = sys.argv.pop(1) - - # Before loading click, set the locale for its parser. - # If it leaks through to the system setting, it may fail: - # RuntimeError: Click will abort further execution because Python 3 was configured to use ASCII - # as encoding for the environment. Consult https://click.palletsprojects.com/python3/ for - # mitigation steps. - os.environ["LC_ALL"] = "C.UTF-8" - os.environ["LANG"] = "C.UTF-8" - - UPDATE = True - # Detect if we are running under `bazel test` - if "TEST_TMPDIR" in os.environ: - UPDATE = False - # pip-compile wants the cache files to be writeable, but if we point - # to the real user cache, Bazel sandboxing makes the file read-only - # and we fail. - # In theory this makes the test more hermetic as well. - sys.argv.append("--cache-dir") - sys.argv.append(os.environ["TEST_TMPDIR"]) - # Make a copy for pip-compile to read and mutate - requirements_out = os.path.join( - os.environ["TEST_TMPDIR"], os.path.basename(requirements_txt) + ".out" - ) - copyfile(requirements_txt, requirements_out) - - elif "BUILD_WORKSPACE_DIRECTORY" in os.environ: - # This value, populated when running under `bazel run`, is a path to the - # "root of the workspace where the build was run." - # This matches up with the values passed in via the macro using the 'rootpath' Make variable, - # which for source files provides a path "relative to your workspace root." - # - # Changing to the WORKSPACE root avoids 'file not found' errors when the `.update` target is run - # from different directories within the WORKSPACE. - requirements_txt = os.path.join(os.environ["BUILD_WORKSPACE_DIRECTORY"], requirements_txt) - else: - err_msg = ( - "Expected to find BUILD_WORKSPACE_DIRECTORY (running under `bazel run`) or " - "TEST_TMPDIR (running under `bazel test`) in environment." - ) - print( - err_msg, - file=sys.stderr, - ) - sys.exit(1) - - update_command = os.getenv("CUSTOM_COMPILE_COMMAND") or "bazel run %s" % ( - update_target_label, - ) - - os.environ["CUSTOM_COMPILE_COMMAND"] = update_command - os.environ["PIP_CONFIG_FILE"] = os.getenv("PIP_CONFIG_FILE") or os.devnull - - sys.argv.append("--generate-hashes") - sys.argv.append("--output-file") - sys.argv.append(requirements_txt if UPDATE else requirements_out) - sys.argv.append(requirements_in) - - if UPDATE: - print("Updating " + requirements_txt) - cli() - else: - # cli will exit(0) on success - try: - print("Checking " + requirements_txt) - cli() - print("cli() should exit", file=sys.stderr) - sys.exit(1) - except SystemExit as e: - if e.code == 2: - print( - "pip-compile exited with code 2. This means that pip-compile found " - "incompatible requirements or could not find a version that matches " - f"the install requirement in {requirements_in}.", - file=sys.stderr, - ) - sys.exit(1) - elif e.code == 0: - golden_filename = _select_golden_requirements_file( - requirements_txt, - requirements_darwin, - requirements_linux, - requirements_windows, - ) - golden = open(golden_filename).readlines() - out = open(requirements_out).readlines() - if golden != out: - import difflib - - print("".join(difflib.unified_diff(golden, out)), file=sys.stderr) - print( - "Lock file out of date. Run '" - + update_command - + "' to update.", - file=sys.stderr, - ) - sys.exit(1) - sys.exit(0) - else: - print( - f"pip-compile unexpectedly exited with code {e.code}.", - file=sys.stderr, - ) - sys.exit(1) diff --git a/python/pip_install/pip_repository.bzl b/python/pip_install/pip_repository.bzl index 706edef5de..5462f1b14d 100644 --- a/python/pip_install/pip_repository.bzl +++ b/python/pip_install/pip_repository.bzl @@ -1,7 +1,22 @@ +# 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. + "" -load("//python:repositories.bzl", "STANDALONE_INTERPRETER_FILENAME") +load("//python:repositories.bzl", "get_interpreter_dirname", "is_standalone_interpreter") load("//python/pip_install:repositories.bzl", "all_requirements") +load("//python/pip_install:requirements_parser.bzl", parse_requirements = "parse") load("//python/pip_install/private:srcs.bzl", "PIP_INSTALL_PY_SRCS") CPPFLAGS = "CPPFLAGS" @@ -20,7 +35,7 @@ def _construct_pypath(rctx): """ # Get the root directory of these rules - rules_root = rctx.path(Label("//:BUILD")).dirname + rules_root = rctx.path(Label("//:BUILD.bazel")).dirname thirdparty_roots = [ # Includes all the external dependencies from repositories.bzl rctx.path(Label("@" + repo + "//:BUILD.bazel")).dirname @@ -59,67 +74,107 @@ def _resolve_python_interpreter(rctx): if rctx.attr.python_interpreter_target != None: target = rctx.attr.python_interpreter_target python_interpreter = rctx.path(target) - else: - if "/" not in python_interpreter: - python_interpreter = rctx.which(python_interpreter) - if not python_interpreter: + elif "/" not in python_interpreter: + found_python_interpreter = rctx.which(python_interpreter) + if not found_python_interpreter: fail("python interpreter `{}` not found in PATH".format(python_interpreter)) + python_interpreter = found_python_interpreter return python_interpreter -def _maybe_set_xcode_location_cflags(rctx, environment): - """Patch environment with CPPFLAGS of xcode sdk location. +def _get_xcode_location_cflags(rctx): + """Query the xcode sdk location to update cflags Figure out if this interpreter target comes from rules_python, and patch the xcode sdk location if so. Pip won't be able to compile c extensions from sdists with the pre built python distributions from indygreg otherwise. See https://github.com/indygreg/python-build-standalone/issues/103 """ - if ( - rctx.os.name.lower().startswith("mac os") and - rctx.attr.python_interpreter_target != None and - # This is a rules_python provided toolchain. - rctx.execute([ - "ls", - "{}/{}".format( - rctx.path(Label("@{}//:WORKSPACE".format(rctx.attr.python_interpreter_target.workspace_name))).dirname, - STANDALONE_INTERPRETER_FILENAME, - ), - ]).return_code == 0 and - not environment.get(CPPFLAGS) - ): - xcode_sdk_location = rctx.execute(["xcode-select", "--print-path"]) - if xcode_sdk_location.return_code == 0: - xcode_root = xcode_sdk_location.stdout.strip() - if COMMAND_LINE_TOOLS_PATH_SLUG not in xcode_root.lower(): - # This is a full xcode installation somewhere like /Applications/Xcode13.0.app/Contents/Developer - # so we need to change the path to to the macos specific tools which are in a different relative - # path than xcode installed command line tools. - xcode_root = "{}/Platforms/MacOSX.platform/Developer".format(xcode_root) - environment[CPPFLAGS] = "-isysroot {}/SDKs/MacOSX.sdk".format(xcode_root) -def _parse_optional_attrs(rctx, args): - """Helper function to parse common attributes of pip_repository and whl_library repository rules. + # Only run on MacOS hosts + if not rctx.os.name.lower().startswith("mac os"): + return [] + + # Locate xcode-select + xcode_select = rctx.which("xcode-select") + + xcode_sdk_location = rctx.execute([xcode_select, "--print-path"]) + if xcode_sdk_location.return_code != 0: + return [] + + xcode_root = xcode_sdk_location.stdout.strip() + if COMMAND_LINE_TOOLS_PATH_SLUG not in xcode_root.lower(): + # This is a full xcode installation somewhere like /Applications/Xcode13.0.app/Contents/Developer + # so we need to change the path to to the macos specific tools which are in a different relative + # path than xcode installed command line tools. + xcode_root = "{}/Platforms/MacOSX.platform/Developer".format(xcode_root) + return [ + "-isysroot {}/SDKs/MacOSX.sdk".format(xcode_root), + ] - This function also serializes the structured arguments as JSON - so they can be passed on the command line to subprocesses. +def _get_toolchain_unix_cflags(rctx): + """Gather cflags from a standalone toolchain for unix systems. - Args: - rctx: Handle to the rule repository context. - args: A list of parsed args for the rule. - Returns: Augmented args list. + Pip won't be able to compile c extensions from sdists with the pre built python distributions from indygreg + otherwise. See https://github.com/indygreg/python-build-standalone/issues/103 """ - # Determine whether or not to pass the pip `--isloated` flag to the pip invocation - use_isolated = rctx.attr.isolated + # Only run on Unix systems + if not rctx.os.name.lower().startswith(("mac os", "linux")): + return [] + + # Only update the location when using a standalone toolchain. + if not is_standalone_interpreter(rctx, rctx.attr.python_interpreter_target): + return [] + + er = rctx.execute([ + rctx.path(rctx.attr.python_interpreter_target).realpath, + "-c", + "import sys; print(f'{sys.version_info[0]}.{sys.version_info[1]}', end='')", + ]) + if er.return_code != 0: + fail("could not get python version from interpreter (status {}): {}".format(er.return_code, er.stderr)) + _python_version = er.stdout + include_path = "{}/include/python{}".format( + get_interpreter_dirname(rctx, rctx.attr.python_interpreter_target), + _python_version, + ) + + return ["-isystem {}".format(include_path)] + +def use_isolated(ctx, attr): + """Determine whether or not to pass the pip `--isolated` flag to the pip invocation. + + Args: + ctx: repository or module context + attr: attributes for the repo rule or tag extension + + Returns: + True if --isolated should be passed + """ + use_isolated = attr.isolated # The environment variable will take precedence over the attribute - isolated_env = rctx.os.environ.get("RULES_PYTHON_PIP_ISOLATED", None) + isolated_env = ctx.os.environ.get("RULES_PYTHON_PIP_ISOLATED", None) if isolated_env != None: if isolated_env.lower() in ("0", "false"): use_isolated = False else: use_isolated = True - if use_isolated: + return use_isolated + +def _parse_optional_attrs(rctx, args): + """Helper function to parse common attributes of pip_repository and whl_library repository rules. + + This function also serializes the structured arguments as JSON + so they can be passed on the command line to subprocesses. + + Args: + rctx: Handle to the rule repository context. + args: A list of parsed args for the rule. + Returns: Augmented args list. + """ + + if use_isolated(rctx, rctx.attr): args.append("--isolated") # Check for None so we use empty default types from our attrs. @@ -127,13 +182,16 @@ def _parse_optional_attrs(rctx, args): if rctx.attr.extra_pip_args != None: args += [ "--extra_pip_args", - struct(arg = rctx.attr.extra_pip_args).to_json(), + json.encode(struct(arg = rctx.attr.extra_pip_args)), ] + if rctx.attr.download_only: + args.append("--download_only") + if rctx.attr.pip_data_exclude != None: args += [ "--pip_data_exclude", - struct(arg = rctx.attr.pip_data_exclude).to_json(), + json.encode(struct(arg = rctx.attr.pip_data_exclude)), ] if rctx.attr.enable_implicit_namespace_pkgs: @@ -142,7 +200,7 @@ def _parse_optional_attrs(rctx, args): if rctx.attr.environment != None: args += [ "--environment", - struct(arg = rctx.attr.environment).to_json(), + json.encode(struct(arg = rctx.attr.environment)), ] return args @@ -152,10 +210,20 @@ def _create_repository_execution_environment(rctx): Args: rctx: The repository context. - Returns: Dictionary of envrionment variable suitable to pass to rctx.execute. + Returns: + Dictionary of environment variable suitable to pass to rctx.execute. """ - env = {"PYTHONPATH": _construct_pypath(rctx)} - _maybe_set_xcode_location_cflags(rctx, env) + + # Gather any available CPPFLAGS values + cppflags = [] + cppflags.extend(_get_xcode_location_cflags(rctx)) + cppflags.extend(_get_toolchain_unix_cflags(rctx)) + + env = { + "PYTHONPATH": _construct_pypath(rctx), + CPPFLAGS: " ".join(cppflags), + } + return env _BUILD_FILE_CONTENTS = """\ @@ -165,83 +233,268 @@ package(default_visibility = ["//visibility:public"]) exports_files(["requirements.bzl"]) """ -def _locked_requirements(rctx): - os = rctx.os.name.lower() - requirements_txt = rctx.attr.requirements_lock - if os.startswith("mac os") and rctx.attr.requirements_darwin != None: - requirements_txt = rctx.attr.requirements_darwin - elif os.startswith("linux") and rctx.attr.requirements_linux != None: - requirements_txt = rctx.attr.requirements_linux - elif "win" in os and rctx.attr.requirements_windows != None: - requirements_txt = rctx.attr.requirements_windows +def locked_requirements_label(ctx, attr): + """Get the preferred label for a locked requirements file based on platform. + + Args: + ctx: repository or module context + attr: attributes for the repo rule or tag extension + + Returns: + Label + """ + os = ctx.os.name.lower() + requirements_txt = attr.requirements_lock + if os.startswith("mac os") and attr.requirements_darwin != None: + requirements_txt = attr.requirements_darwin + elif os.startswith("linux") and attr.requirements_linux != None: + requirements_txt = attr.requirements_linux + elif "win" in os and attr.requirements_windows != None: + requirements_txt = attr.requirements_windows if not requirements_txt: fail("""\ -Incremental mode requires a requirements_lock attribute be specified, -or a platform-specific lockfile using one of the requirements_* attributes. +A requirements_lock attribute must be specified, or a platform-specific lockfile using one of the requirements_* attributes. """) return requirements_txt -def _pip_repository_impl(rctx): - python_interpreter = _resolve_python_interpreter(rctx) +# Keep in sync with `_clean_pkg_name` in generated bzlmod requirements.bzl +def _clean_pkg_name(name): + return name.replace("-", "_").replace(".", "_").lower() - # Write the annotations file to pass to the wheel maker - annotations = {package: json.decode(data) for (package, data) in rctx.attr.annotations.items()} - annotations_file = rctx.path("annotations.json") - rctx.file(annotations_file, json.encode_indent(annotations, indent = " " * 4)) - - if rctx.attr.incremental: - requirements_txt = _locked_requirements(rctx) - args = [ - python_interpreter, - "-m", - "python.pip_install.extract_wheels.parse_requirements_to_bzl", - "--requirements_lock", - rctx.path(requirements_txt), - "--requirements_lock_label", - str(requirements_txt), - # pass quiet and timeout args through to child repos. - "--quiet", - str(rctx.attr.quiet), - "--timeout", - str(rctx.attr.timeout), - "--annotations", - annotations_file, - ] +def _pkg_aliases(rctx, repo_name, bzl_packages): + """Create alias declarations for each python dependency. + + The aliases should be appended to the pip_repository BUILD.bazel file. These aliases + allow users to use requirement() without needed a corresponding `use_repo()` for each dep + when using bzlmod. + + Args: + rctx: the repository context. + repo_name: the repository name of the parent that is visible to the users. + bzl_packages: the list of packages to setup. + """ + for name in bzl_packages: + build_content = """package(default_visibility = ["//visibility:public"]) + +alias( + name = "{name}", + actual = "@{repo_name}_{dep}//:pkg", +) + +alias( + name = "pkg", + actual = "@{repo_name}_{dep}//:pkg", +) + +alias( + name = "whl", + actual = "@{repo_name}_{dep}//:whl", +) + +alias( + name = "data", + actual = "@{repo_name}_{dep}//:data", +) + +alias( + name = "dist_info", + actual = "@{repo_name}_{dep}//:dist_info", +) +""".format( + name = name, + repo_name = repo_name, + dep = name, + ) + rctx.file("{}/BUILD.bazel".format(name), build_content) + +def _bzlmod_pkg_aliases(repo_name, bzl_packages): + """Create alias declarations for each python dependency. + + The aliases should be appended to the pip_repository BUILD.bazel file. These aliases + allow users to use requirement() without needed a corresponding `use_repo()` for each dep + when using bzlmod. + + Args: + repo_name: the repository name of the parent that is visible to the users. + bzl_packages: the list of packages to setup. + """ + build_content = "" + for name in bzl_packages: + build_content += """\ + +alias( + name = "{name}_pkg", + actual = "@{repo_name}_{dep}//:pkg", +) - args += ["--python_interpreter", _get_python_interpreter_attr(rctx)] - if rctx.attr.python_interpreter_target: - args += ["--python_interpreter_target", str(rctx.attr.python_interpreter_target)] - progress_message = "Parsing requirements to starlark" +alias( + name = "{name}_whl", + actual = "@{repo_name}_{dep}//:whl", +) + +alias( + name = "{name}_data", + actual = "@{repo_name}_{dep}//:data", +) + +alias( + name = "{name}_dist_info", + actual = "@{repo_name}_{dep}//:dist_info", +) +""".format( + name = name, + repo_name = repo_name, + dep = name, + ) + + return build_content + +def _pip_repository_bzlmod_impl(rctx): + requirements_txt = locked_requirements_label(rctx, rctx.attr) + content = rctx.read(requirements_txt) + parsed_requirements_txt = parse_requirements(content) + + packages = [(_clean_pkg_name(name), requirement) for name, requirement in parsed_requirements_txt.requirements] + + bzl_packages = sorted([name for name, _ in packages]) + + repo_name = rctx.attr.name.split("~")[-1] + + build_contents = _BUILD_FILE_CONTENTS + + if rctx.attr.incompatible_generate_aliases: + _pkg_aliases(rctx, repo_name, bzl_packages) else: - args = [ - python_interpreter, - "-m", - "python.pip_install.extract_wheels.extract_wheels", - "--requirements", - rctx.path(rctx.attr.requirements), - "--annotations", - annotations_file, - ] - progress_message = "Extracting wheels" + build_contents += _bzlmod_pkg_aliases(repo_name, bzl_packages) + + # NOTE: we are using the canonical name with the double '@' in order to + # always uniquely identify a repository, as the labels are being passed as + # a string and the resolution of the label happens at the call-site of the + # `requirement`, et al. macros. + if rctx.attr.incompatible_generate_aliases: + macro_tmpl = "@@{name}//{{}}:{{}}".format(name = rctx.attr.name) + else: + macro_tmpl = "@@{name}//:{{}}_{{}}".format(name = rctx.attr.name) + + rctx.file("BUILD.bazel", build_contents) + rctx.template("requirements.bzl", rctx.attr._template, substitutions = { + "%%ALL_REQUIREMENTS%%": _format_repr_list([ + "@{}//{}".format(repo_name, p) if rctx.attr.incompatible_generate_aliases else "@{}_{}//:pkg".format(rctx.attr.name, p) + for p in bzl_packages + ]), + "%%ALL_WHL_REQUIREMENTS%%": _format_repr_list([ + "@{}//{}:whl".format(repo_name, p) if rctx.attr.incompatible_generate_aliases else "@{}_{}//:whl".format(rctx.attr.name, p) + for p in bzl_packages + ]), + "%%MACRO_TMPL%%": macro_tmpl, + "%%REQUIREMENTS_LOCK%%": str(requirements_txt), + }) + +pip_repository_bzlmod_attrs = { + "incompatible_generate_aliases": attr.bool( + default = False, + doc = "Allow generating aliases in '@pip//:' -> '@pip_//:pkg'. This replaces the aliases generated by the `bzlmod` tooling.", + ), + "requirements_darwin": attr.label( + allow_single_file = True, + doc = "Override the requirements_lock attribute when the host platform is Mac OS", + ), + "requirements_linux": attr.label( + allow_single_file = True, + doc = "Override the requirements_lock attribute when the host platform is Linux", + ), + "requirements_lock": attr.label( + allow_single_file = True, + doc = """ +A fully resolved 'requirements.txt' pip requirement file containing the transitive set of your dependencies. If this file is passed instead +of 'requirements' no resolve will take place and pip_repository will create individual repositories for each of your dependencies so that +wheels are fetched/built only for the targets specified by 'build/run/test'. +""", + ), + "requirements_windows": attr.label( + allow_single_file = True, + doc = "Override the requirements_lock attribute when the host platform is Windows", + ), + "_template": attr.label( + default = ":pip_repository_requirements_bzlmod.bzl.tmpl", + ), +} - args += ["--repo", rctx.attr.name, "--repo-prefix", rctx.attr.repo_prefix] - args = _parse_optional_attrs(rctx, args) +pip_repository_bzlmod = repository_rule( + attrs = pip_repository_bzlmod_attrs, + doc = """A rule for bzlmod pip_repository creation. Intended for private use only.""", + implementation = _pip_repository_bzlmod_impl, +) - rctx.report_progress(progress_message) +def _pip_repository_impl(rctx): + requirements_txt = locked_requirements_label(rctx, rctx.attr) + content = rctx.read(requirements_txt) + parsed_requirements_txt = parse_requirements(content) - result = rctx.execute( - args, - # Manually construct the PYTHONPATH since we cannot use the toolchain here - environment = _create_repository_execution_environment(rctx), - timeout = rctx.attr.timeout, - quiet = rctx.attr.quiet, - ) + packages = [(_clean_pkg_name(name), requirement) for name, requirement in parsed_requirements_txt.requirements] - if result.return_code: - fail("rules_python failed: %s (%s)" % (result.stdout, result.stderr)) + bzl_packages = sorted([name for name, _ in packages]) - # We need a BUILD file to load the generated requirements.bzl - rctx.file("BUILD.bazel", _BUILD_FILE_CONTENTS + "\n# The requirements.bzl file was generated by running:\n# " + " ".join([str(a) for a in args])) + imports = [ + 'load("@rules_python//python/pip_install:pip_repository.bzl", "whl_library")', + ] + + annotations = {} + for pkg, annotation in rctx.attr.annotations.items(): + filename = "{}.annotation.json".format(_clean_pkg_name(pkg)) + rctx.file(filename, json.encode_indent(json.decode(annotation))) + annotations[pkg] = "@{name}//:{filename}".format(name = rctx.attr.name, filename = filename) + + tokenized_options = [] + for opt in parsed_requirements_txt.options: + for p in opt.split(" "): + tokenized_options.append(p) + + options = tokenized_options + rctx.attr.extra_pip_args + + config = { + "download_only": rctx.attr.download_only, + "enable_implicit_namespace_pkgs": rctx.attr.enable_implicit_namespace_pkgs, + "environment": rctx.attr.environment, + "extra_pip_args": options, + "isolated": use_isolated(rctx, rctx.attr), + "pip_data_exclude": rctx.attr.pip_data_exclude, + "python_interpreter": _get_python_interpreter_attr(rctx), + "quiet": rctx.attr.quiet, + "repo": rctx.attr.name, + "repo_prefix": "{}_".format(rctx.attr.name), + "timeout": rctx.attr.timeout, + } + + if rctx.attr.python_interpreter_target: + config["python_interpreter_target"] = str(rctx.attr.python_interpreter_target) + + if rctx.attr.incompatible_generate_aliases: + _pkg_aliases(rctx, rctx.attr.name, bzl_packages) + + rctx.file("BUILD.bazel", _BUILD_FILE_CONTENTS) + rctx.template("requirements.bzl", rctx.attr._template, substitutions = { + "%%ALL_REQUIREMENTS%%": _format_repr_list([ + "@{}//{}".format(rctx.attr.name, p) if rctx.attr.incompatible_generate_aliases else "@{}_{}//:pkg".format(rctx.attr.name, p) + for p in bzl_packages + ]), + "%%ALL_WHL_REQUIREMENTS%%": _format_repr_list([ + "@{}//{}:whl".format(rctx.attr.name, p) if rctx.attr.incompatible_generate_aliases else "@{}_{}//:whl".format(rctx.attr.name, p) + for p in bzl_packages + ]), + "%%ANNOTATIONS%%": _format_dict(_repr_dict(annotations)), + "%%CONFIG%%": _format_dict(_repr_dict(config)), + "%%EXTRA_PIP_ARGS%%": json.encode(options), + "%%IMPORTS%%": "\n".join(sorted(imports)), + "%%NAME%%": rctx.attr.name, + "%%PACKAGES%%": _format_repr_list( + [ + ("{}_{}".format(rctx.attr.name, p), r) + for p, r in packages + ], + ), + "%%REQUIREMENTS_LOCK%%": str(requirements_txt), + }) return @@ -250,6 +503,13 @@ common_env = [ ] common_attrs = { + "download_only": attr.bool( + doc = """ +Whether to use "pip download" instead of "pip wheel". Disables building wheels from source, but allows use of +--platform, --python-version, --implementation, and --abi in --extra_pip_args to download wheels for a different +platform from the host platform. + """, + ), "enable_implicit_namespace_pkgs": attr.bool( default = False, doc = """ @@ -264,7 +524,7 @@ This option is required to support some packages which cannot handle the convers doc = """ Environment variables to set in the pip subprocess. Can be used to set common variables such as `http_proxy`, `https_proxy` and `no_proxy` -Note that pip is run with "--isolated" on the CLI so PIP__ +Note that pip is run with "--isolated" on the CLI so `PIP__` style env vars are ignored, but env vars that control requests and urllib3 can be passed. """, @@ -276,7 +536,7 @@ can be passed. "isolated": attr.bool( doc = """\ 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 `RULES_PYTHON_PIP_ISOLATED` enviornment varaible can be used +the underlying pip command. Alternatively, the `RULES_PYTHON_PIP_ISOLATED` environment variable can be used to control this flag. """, default = True, @@ -299,7 +559,7 @@ of a binary found on the host's `PATH` environment variable. If no value is set 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( @@ -308,14 +568,7 @@ python_interpreter. ), "repo_prefix": attr.string( doc = """ -Prefix for the generated packages. For non-incremental mode the -packages will be of the form - -@///... - -For incremental mode the packages will be of the form - -@//... +Prefix for the generated packages will be of the form `@//...` """, ), # 600 is documented as default here: https://docs.bazel.build/versions/master/skylark/lib/repository_ctx.html#execute @@ -334,13 +587,9 @@ pip_repository_attrs = { "annotations": attr.string_dict( doc = "Optional annotations to apply to packages", ), - "incremental": attr.bool( + "incompatible_generate_aliases": attr.bool( default = False, - doc = "Create the repository in incremental mode.", - ), - "requirements": attr.label( - allow_single_file = True, - doc = "A 'requirements.txt' pip requirements file.", + doc = "Allow generating aliases '@pip//' -> '@pip_//:pkg'.", ), "requirements_darwin": attr.label( allow_single_file = True, @@ -362,6 +611,9 @@ wheels are fetched/built only for the targets specified by 'build/run/test'. allow_single_file = True, doc = "Override the requirements_lock attribute when the host platform is Windows", ), + "_template": attr.label( + default = ":pip_repository_requirements.bzl.tmpl", + ), } pip_repository_attrs.update(**common_attrs) @@ -417,7 +669,7 @@ def _whl_library_impl(rctx): args = [ python_interpreter, "-m", - "python.pip_install.extract_wheels.extract_single_wheel", + "python.pip_install.tools.wheel_installer.wheel_installer", "--requirement", rctx.attr.requirement, "--repo", @@ -508,3 +760,19 @@ def package_annotation( data_exclude_glob = data_exclude_glob, srcs_exclude_glob = srcs_exclude_glob, )) + +# pip_repository implementation + +def _format_list(items): + return "[{}]".format(", ".join(items)) + +def _format_repr_list(strings): + return _format_list( + [repr(s) for s in strings], + ) + +def _repr_dict(items): + return {k: repr(v) for k, v in items.items()} + +def _format_dict(items): + return "{{{}}}".format(", ".join(sorted(['"{}": {}'.format(k, v) for k, v in items.items()]))) diff --git a/python/pip_install/pip_repository_requirements.bzl.tmpl b/python/pip_install/pip_repository_requirements.bzl.tmpl new file mode 100644 index 0000000000..bf6a053622 --- /dev/null +++ b/python/pip_install/pip_repository_requirements.bzl.tmpl @@ -0,0 +1,52 @@ +"""Starlark representation of locked requirements. + +@generated by rules_python pip_parse repository rule +from %%REQUIREMENTS_LOCK%% +""" + +%%IMPORTS%% + +all_requirements = %%ALL_REQUIREMENTS%% + +all_whl_requirements = %%ALL_WHL_REQUIREMENTS%% + +_packages = %%PACKAGES%% +_config = %%CONFIG%% +_annotations = %%ANNOTATIONS%% + +def _clean_name(name): + return name.replace("-", "_").replace(".", "_").lower() + +def requirement(name): + return "@%%NAME%%_" + _clean_name(name) + "//:pkg" + +def whl_requirement(name): + return "@%%NAME%%_" + _clean_name(name) + "//:whl" + +def data_requirement(name): + return "@%%NAME%%_" + _clean_name(name) + "//:data" + +def dist_info_requirement(name): + return "@%%NAME%%_" + _clean_name(name) + "//:dist_info" + +def entry_point(pkg, script = None): + if not script: + script = pkg + return "@%%NAME%%_" + _clean_name(pkg) + "//:rules_python_wheel_entry_point_" + script + +def _get_annotation(requirement): + # This expects to parse `setuptools==58.2.0 --hash=sha256:2551203ae6955b9876741a26ab3e767bb3242dafe86a32a749ea0d78b6792f11` + # down to `setuptools`. + name = requirement.split(" ")[0].split("=")[0].split("[")[0] + return _annotations.get(name) + +def install_deps(**whl_library_kwargs): + whl_config = dict(_config) + whl_config.update(whl_library_kwargs) + for name, requirement in _packages: + whl_library( + name = name, + requirement = requirement, + annotation = _get_annotation(requirement), + **whl_config + ) diff --git a/python/pip_install/pip_repository_requirements_bzlmod.bzl.tmpl b/python/pip_install/pip_repository_requirements_bzlmod.bzl.tmpl new file mode 100644 index 0000000000..1b2e2178bb --- /dev/null +++ b/python/pip_install/pip_repository_requirements_bzlmod.bzl.tmpl @@ -0,0 +1,24 @@ +"""Starlark representation of locked requirements. + +@generated by rules_python pip_parse repository rule +from %%REQUIREMENTS_LOCK%%. +""" + +all_requirements = %%ALL_REQUIREMENTS%% + +all_whl_requirements = %%ALL_WHL_REQUIREMENTS%% + +def _clean_name(name): + return name.replace("-", "_").replace(".", "_").lower() + +def requirement(name): + return "%%MACRO_TMPL%%".format(_clean_name(name), "pkg") + +def whl_requirement(name): + return "%%MACRO_TMPL%%".format(_clean_name(name), "whl") + +def data_requirement(name): + return "%%MACRO_TMPL%%".format(_clean_name(name), "data") + +def dist_info_requirement(name): + return "%%MACRO_TMPL%%".format(_clean_name(name), "dist_info") diff --git a/python/pip_install/private/BUILD b/python/pip_install/private/BUILD.bazel similarity index 100% rename from python/pip_install/private/BUILD rename to python/pip_install/private/BUILD.bazel diff --git a/python/pip_install/private/pip_install_utils.bzl b/python/pip_install/private/pip_install_utils.bzl index 038ee0e8c8..488583dcb8 100644 --- a/python/pip_install/private/pip_install_utils.bzl +++ b/python/pip_install/private/pip_install_utils.bzl @@ -1,9 +1,23 @@ +# 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. + """Utilities for `rules_python` pip rules""" _SRCS_TEMPLATE = """\ -\"\"\"A generate file containing all source files used for `@rules_python//python/pip_install:pip_repository.bzl` rules +\"\"\"A generated file containing all source files used for `@rules_python//python/pip_install:pip_repository.bzl` rules -This file is auto-generated from the `@rules_python//python/pip_install/private:srcs_module.install` target. Please +This file is auto-generated from the `@rules_python//python/pip_install/private:srcs_module.update` target. Please `bazel run` this target to apply any updates. Note that doing so will discard any local modifications. "\"\" diff --git a/python/pip_install/private/srcs.bzl b/python/pip_install/private/srcs.bzl index bdd76b17d4..f3064a3aec 100644 --- a/python/pip_install/private/srcs.bzl +++ b/python/pip_install/private/srcs.bzl @@ -1,20 +1,19 @@ -"""A generate file containing all source files used for `@rules_python//python/pip_install:pip_repository.bzl` rules +"""A generated file containing all source files used for `@rules_python//python/pip_install:pip_repository.bzl` rules -This file is auto-generated from the `@rules_python//python/pip_install/private:srcs_module.install` target. Please +This file is auto-generated from the `@rules_python//python/pip_install/private:srcs_module.update` target. Please `bazel run` this target to apply any updates. Note that doing so will discard any local modifications. """ # Each source file is tracked as a target so `pip_repository` rules will know to automatically rebuild if any of the # sources changed. PIP_INSTALL_PY_SRCS = [ - "@rules_python//python/pip_install/extract_wheels:__init__.py", - "@rules_python//python/pip_install/extract_wheels:annotation.py", - "@rules_python//python/pip_install/extract_wheels:arguments.py", - "@rules_python//python/pip_install/extract_wheels:bazel.py", - "@rules_python//python/pip_install/extract_wheels:extract_single_wheel.py", - "@rules_python//python/pip_install/extract_wheels:extract_wheels.py", - "@rules_python//python/pip_install/extract_wheels:namespace_pkgs.py", - "@rules_python//python/pip_install/extract_wheels:parse_requirements_to_bzl.py", - "@rules_python//python/pip_install/extract_wheels:requirements.py", - "@rules_python//python/pip_install/extract_wheels:wheel.py", + "@rules_python//python/pip_install/tools/dependency_resolver:__init__.py", + "@rules_python//python/pip_install/tools/dependency_resolver:dependency_resolver.py", + "@rules_python//python/pip_install/tools/lib:__init__.py", + "@rules_python//python/pip_install/tools/lib:annotation.py", + "@rules_python//python/pip_install/tools/lib:arguments.py", + "@rules_python//python/pip_install/tools/lib:bazel.py", + "@rules_python//python/pip_install/tools/wheel_installer:namespace_pkgs.py", + "@rules_python//python/pip_install/tools/wheel_installer:wheel.py", + "@rules_python//python/pip_install/tools/wheel_installer:wheel_installer.py", ] diff --git a/python/pip_install/private/test/BUILD b/python/pip_install/private/test/BUILD.bazel similarity index 82% rename from python/pip_install/private/test/BUILD rename to python/pip_install/private/test/BUILD.bazel index 60d25de7df..d4978f3979 100644 --- a/python/pip_install/private/test/BUILD +++ b/python/pip_install/private/test/BUILD.bazel @@ -1,4 +1,5 @@ load("@bazel_skylib//rules:diff_test.bzl", "diff_test") +load(":requirements_parser_tests.bzl", parse_requirements_tests = "parse_tests") diff_test( name = "srcs_diff_test", @@ -15,3 +16,5 @@ diff_test( "//conditions:default": [], }), ) + +parse_requirements_tests(name = "test_parse_requirements") diff --git a/python/pip_install/private/test/requirements_parser_tests.bzl b/python/pip_install/private/test/requirements_parser_tests.bzl new file mode 100644 index 0000000000..c13ec204fb --- /dev/null +++ b/python/pip_install/private/test/requirements_parser_tests.bzl @@ -0,0 +1,216 @@ +# 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. + +"Unit tests for yaml.bzl" + +load("@bazel_skylib//lib:unittest.bzl", "asserts", "unittest") +load("//python/pip_install:requirements_parser.bzl", "parse") + +def _parse_basic_test_impl(ctx): + env = unittest.begin(ctx) + + # Base cases + asserts.equals(env, [], parse("").requirements) + asserts.equals(env, [], parse("\n").requirements) + + # Various requirement specifiers (https://pip.pypa.io/en/stable/reference/requirement-specifiers/#requirement-specifiers) + asserts.equals(env, [("SomeProject", "SomeProject")], parse("SomeProject\n").requirements) + asserts.equals(env, [("SomeProject", "SomeProject == 1.3")], parse("SomeProject == 1.3\n").requirements) + asserts.equals(env, [("SomeProject", "SomeProject >= 1.2, < 2.0")], parse("SomeProject >= 1.2, < 2.0\n").requirements) + asserts.equals(env, [("SomeProject", "SomeProject[foo, bar]")], parse("SomeProject[foo, bar]\n").requirements) + asserts.equals(env, [("SomeProject", "SomeProject ~= 1.4.2")], parse("SomeProject ~= 1.4.2\n").requirements) + asserts.equals(env, [("SomeProject", "SomeProject == 5.4 ; python_version < '3.8'")], parse("SomeProject == 5.4 ; python_version < '3.8'\n").requirements) + asserts.equals(env, [("SomeProject", "SomeProject ; sys_platform == 'win32'")], parse("SomeProject ; sys_platform == 'win32'\n").requirements) + asserts.equals(env, [("requests", "requests [security] >= 2.8.1, == 2.8.* ; python_version < 2.7")], parse("requests [security] >= 2.8.1, == 2.8.* ; python_version < 2.7\n").requirements) + + # Multiple requirements + asserts.equals(env, [("FooProject", "FooProject==1.0.0"), ("BarProject", "BarProject==2.0.0")], parse("""\ +FooProject==1.0.0 +BarProject==2.0.0 +""").requirements) + + asserts.equals(env, [("FooProject", "FooProject==1.0.0"), ("BarProject", "BarProject==2.0.0")], parse("""\ +FooProject==1.0.0 + +BarProject==2.0.0 +""").requirements) + + # Comments + asserts.equals(env, [("SomeProject", "SomeProject")], parse("""\ +# This is a comment +SomeProject +""").requirements) + asserts.equals(env, [("SomeProject", "SomeProject")], parse("""\ +SomeProject +# This is a comment +""").requirements) + asserts.equals(env, [("SomeProject", "SomeProject == 1.3")], parse("""\ +SomeProject == 1.3 # This is a comment +""").requirements) + asserts.equals(env, [("FooProject", "FooProject==1.0.0"), ("BarProject", "BarProject==2.0.0")], parse("""\ +FooProject==1.0.0 +# Comment +BarProject==2.0.0 #Comment +""").requirements) + + # Multiline + asserts.equals(env, [("certifi", "certifi==2021.10.8 --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569")], parse("""\ +certifi==2021.10.8 \ + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 + # via requests +""").requirements) + + # Options + asserts.equals(env, ["--pre"], parse("--pre\n").options) + asserts.equals(env, ["--find-links", "/my/local/archives"], parse("--find-links /my/local/archives\n").options) + asserts.equals(env, ["--pre", "--find-links", "/my/local/archives"], parse("""\ +--pre +--find-links /my/local/archives +""").options) + asserts.equals(env, ["--pre", "--find-links", "/my/local/archives"], parse("""\ +--pre # Comment +--find-links /my/local/archives +""").options) + asserts.equals(env, struct(requirements = [("FooProject", "FooProject==1.0.0")], options = ["--pre", "--find-links", "/my/local/archives"]), parse("""\ +--pre # Comment +FooProject==1.0.0 +--find-links /my/local/archives +""")) + + return unittest.end(env) + +def _parse_requirements_lockfile_test_impl(ctx): + env = unittest.begin(ctx) + + asserts.equals(env, [ + ("certifi", "certifi==2021.10.8 --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"), + ("chardet", "chardet==4.0.0 --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"), + ("idna", "idna==2.10 --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"), + ("pathspec", "pathspec==0.9.0 --hash=sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a --hash=sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"), + ("python-dateutil", "python-dateutil==2.8.2 --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"), + ("python-magic", "python-magic==0.4.24 --hash=sha256:4fec8ee805fea30c07afccd1592c0f17977089895bdfaae5fec870a84e997626 --hash=sha256:de800df9fb50f8ec5974761054a708af6e4246b03b4bdaee993f948947b0ebcf"), + ("pyyaml", "pyyaml==6.0 --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:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"), + ("requests", "requests==2.25.1 --hash=sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804 --hash=sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"), + ("s3cmd", "s3cmd==2.1.0 --hash=sha256:49cd23d516b17974b22b611a95ce4d93fe326feaa07320bd1d234fed68cbccfa --hash=sha256:966b0a494a916fc3b4324de38f089c86c70ee90e8e1cae6d59102103a4c0cc03"), + ("six", "six==1.16.0 --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"), + ("urllib3", "urllib3==1.26.7 --hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece --hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"), + ("yamllint", "yamllint==1.26.3 --hash=sha256:3934dcde484374596d6b52d8db412929a169f6d9e52e20f9ade5bf3523d9b96e"), + ("setuptools", "setuptools==59.6.0 --hash=sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373 --hash=sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e"), + ], parse("""\ +# +# This file is autogenerated by pip-compile with python 3.9 +# To update, run: +# +# bazel run //:requirements.update +# +certifi==2021.10.8 \ + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 + # via requests +chardet==4.0.0 \ + --hash=sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa \ + --hash=sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5 + # via requests +idna==2.10 \ + --hash=sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6 \ + --hash=sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0 + # via requests +pathspec==0.9.0 \ + --hash=sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a \ + --hash=sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1 + # via yamllint +python-dateutil==2.8.2 \ + --hash=sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86 \ + --hash=sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9 + # via s3cmd +python-magic==0.4.24 \ + --hash=sha256:4fec8ee805fea30c07afccd1592c0f17977089895bdfaae5fec870a84e997626 \ + --hash=sha256:de800df9fb50f8ec5974761054a708af6e4246b03b4bdaee993f948947b0ebcf + # via s3cmd +pyyaml==6.0 \ + --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:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \ + --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \ + --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \ + --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \ + --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \ + --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \ + --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \ + --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \ + --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \ + --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \ + --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \ + --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \ + --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \ + --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \ + --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \ + --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \ + --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \ + --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \ + --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \ + --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ + --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 +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 + # via python-dateutil +urllib3==1.26.7 \ + --hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece \ + --hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844 + # via requests +yamllint==1.26.3 \ + --hash=sha256:3934dcde484374596d6b52d8db412929a169f6d9e52e20f9ade5bf3523d9b96e + # via -r requirements.in + +# The following packages are considered to be unsafe in a requirements file: +setuptools==59.6.0 \ + --hash=sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373 \ + --hash=sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e + # via yamllint +""").requirements) + + return unittest.end(env) + +parse_basic_test = unittest.make( + _parse_basic_test_impl, + attrs = {}, +) +parse_requirements_lockfile_test = unittest.make( + _parse_requirements_lockfile_test_impl, + attrs = {}, +) + +def parse_tests(name): + unittest.suite(name, parse_basic_test, parse_requirements_lockfile_test) diff --git a/python/pip_install/repositories.bzl b/python/pip_install/repositories.bzl index 91390f5de1..2dd4a3724b 100644 --- a/python/pip_install/repositories.bzl +++ b/python/pip_install/repositories.bzl @@ -1,12 +1,30 @@ +# 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. + "" +load("@bazel_skylib//lib:versions.bzl", "versions") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") - -# Avoid a load from @bazel_skylib repository as users don't necessarily have it installed -load("//third_party/github.com/bazelbuild/bazel-skylib/lib:versions.bzl", "versions") +load("//:version.bzl", "MINIMUM_BAZEL_VERSION") _RULE_DEPS = [ + ( + "pypi__build", + "https://files.pythonhosted.org/packages/03/97/f58c723ff036a8d8b4d3115377c0a37ed05c1f68dd9a0d66dab5e82c5c1c/build-0.9.0-py3-none-any.whl", + "38a7a2b7a0bdc61a42a0a67509d88c71ecfc37b393baba770fae34e20929ff69", + ), ( "pypi__click", "https://files.pythonhosted.org/packages/76/0a/b6c5f311e32aeb3b406e03c079ade51e905ea630fc19d1262a46249c1c86/click-8.0.1-py3-none-any.whl", @@ -14,28 +32,33 @@ _RULE_DEPS = [ ), ( "pypi__colorama", - "https://files.pythonhosted.org/packages/44/98/5b86278fbbf250d239ae0ecb724f8572af1c91f4a11edf4d36a206189440/colorama-0.4.4-py2.py3-none-any.whl", - "9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2", + "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", + "4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", ), ( "pypi__installer", - "https://files.pythonhosted.org/packages/1b/21/3e6ebd12d8dccc55bcb7338db462c75ac86dbd0ac7439ac114616b21667b/installer-0.5.1-py3-none-any.whl", - "1d6c8d916ed82771945b9c813699e6f57424ded970c9d8bf16bbc23e1e826ed3", + "https://files.pythonhosted.org/packages/e5/ca/1172b6638d52f2d6caa2dd262ec4c811ba59eee96d54a7701930726bce18/installer-0.7.0-py3-none-any.whl", + "05d1933f0a5ba7d8d6296bb6d5018e7c94fa473ceb10cf198a92ccea19c27b53", + ), + ( + "pypi__packaging", + "https://files.pythonhosted.org/packages/8f/7b/42582927d281d7cb035609cd3a543ffac89b74f3f4ee8e1c50914bcb57eb/packaging-22.0-py3-none-any.whl", + "957e2148ba0e1a3b282772e791ef1d8083648bc131c8ab0c1feba110ce1146c3", ), ( "pypi__pep517", - "https://files.pythonhosted.org/packages/f4/67/846c08e18fefb265a66e6fd5a34269d649b779718d9bf59622085dabd370/pep517-0.12.0-py2.py3-none-any.whl", - "dd884c326898e2c6e11f9e0b64940606a93eb10ea022a2e067959f3a110cf161", + "https://files.pythonhosted.org/packages/ee/2f/ef63e64e9429111e73d3d6cbee80591672d16f2725e648ebc52096f3d323/pep517-0.13.0-py3-none-any.whl", + "4ba4446d80aed5b5eac6509ade100bff3e7943a8489de249654a5ae9b33ee35b", ), ( "pypi__pip", - "https://files.pythonhosted.org/packages/96/2f/caec18213f6a67852f6997fb0673ae08d2e93d1b81573edb93ba4ef06970/pip-22.1.2-py3-none-any.whl", - "a3edacb89022ef5258bf61852728bf866632a394da837ca49eb4303635835f17", + "https://files.pythonhosted.org/packages/09/bd/2410905c76ee14c62baf69e3f4aa780226c1bbfc9485731ad018e35b0cb5/pip-22.3.1-py3-none-any.whl", + "908c78e6bc29b676ede1c4d57981d490cb892eb45cd8c214ab6298125119e077", ), ( "pypi__pip_tools", - "https://files.pythonhosted.org/packages/fe/5c/8995799b0ccf832906b4968b4eb2045beb9b3de79e96e6b1a6e4fc4e6974/pip_tools-6.6.2-py3-none-any.whl", - "6b486548e5a139e30e4c4a225b3b7c2d46942a9f6d1a91143c21b1de4d02fd9b", + "https://files.pythonhosted.org/packages/5e/e8/f6d7d1847c7351048da870417724ace5c4506e816b38db02f4d7c675c189/pip_tools-6.12.1-py3-none-any.whl", + "f0c0c0ec57b58250afce458e2e6058b1f30a4263db895b7d72fd6311bf1dc6f7", ), ( "pypi__setuptools", @@ -49,8 +72,23 @@ _RULE_DEPS = [ ), ( "pypi__wheel", - "https://files.pythonhosted.org/packages/27/d6/003e593296a85fd6ed616ed962795b2f87709c3eee2bca4f6d0fe55c6d00/wheel-0.37.1-py2.py3-none-any.whl", - "4bdcd7d840138086126cd09254dc6195fb4fc6f01c050a1d7236f2630db1d22a", + "https://files.pythonhosted.org/packages/bd/7c/d38a0b30ce22fc26ed7dbc087c6d00851fb3395e9d0dac40bec1f905030c/wheel-0.38.4-py3-none-any.whl", + "b60533f3f5d530e971d6737ca6d58681ee434818fab630c83a734bb10c083ce8", + ), + ( + "pypi__importlib_metadata", + "https://files.pythonhosted.org/packages/d7/31/74dcb59a601b95fce3b0334e8fc9db758f78e43075f22aeb3677dfb19f4c/importlib_metadata-1.4.0-py2.py3-none-any.whl", + "bdd9b7c397c273bcc9a11d6629a38487cd07154fa255a467bf704cd2c258e359", + ), + ( + "pypi__zipp", + "https://files.pythonhosted.org/packages/f4/50/cc72c5bcd48f6e98219fc4a88a5227e9e28b81637a99c49feba1d51f4d50/zipp-1.0.0-py2.py3-none-any.whl", + "8dda78f06bd1674bd8720df8a50bb47b6e1233c503a4eed8e7810686bde37656", + ), + ( + "pypi__more_itertools", + "https://files.pythonhosted.org/packages/bd/3f/c4b3dbd315e248f84c388bd4a72b131a29f123ecacc37ffb2b3834546e42/more_itertools-8.13.0-py3-none-any.whl", + "c5122bffc5f104d37c1626b8615b511f3427aa5389b94d61e5ef8236bfbc3ddb", ), ] @@ -62,7 +100,17 @@ load("@rules_python//python:defs.bzl", "py_library") py_library( name = "lib", srcs = glob(["**/*.py"]), - data = glob(["**/*"], exclude=["**/*.py", "**/* *", "BUILD", "WORKSPACE"]), + data = glob(["**/*"], exclude=[ + # These entries include those put into user-installed dependencies by + # data_exclude in /python/pip_install/tools/bazel.py + # to avoid non-determinism following pip install's behavior. + "**/*.py", + "**/*.pyc", + "**/* *", + "**/*.dist-info/RECORD", + "BUILD", + "WORKSPACE", + ]), # This makes this directory a top-level in the python import # search path for anything that depends on this. imports = ["."], @@ -73,7 +121,7 @@ py_library( all_requirements = [name for (name, _, _) in _RULE_DEPS] def requirement(pkg): - return "@pypi__" + pkg + "//:lib" + return Label("@pypi__" + pkg + "//:lib") def pip_install_dependencies(): """ @@ -86,7 +134,7 @@ def pip_install_dependencies(): # Give the user an obvious error to upgrade rather than some obscure missing symbol later. # It's not guaranteed that users call this function, but it's used by all the pip fetch # repository rules so it's likely that most users get the right error. - versions.check("4.0.0") + versions.check(MINIMUM_BAZEL_VERSION) for (name, url, sha256) in _RULE_DEPS: maybe( diff --git a/python/pip_install/requirements.bzl b/python/pip_install/requirements.bzl index 0c661339b1..7594471897 100644 --- a/python/pip_install/requirements.bzl +++ b/python/pip_install/requirements.bzl @@ -1,17 +1,34 @@ +# 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. + """Rules to verify and update pip-compile locked requirements.txt""" -load("//python:defs.bzl", "py_binary", "py_test") +load("//python:defs.bzl", _py_binary = "py_binary", _py_test = "py_test") load("//python/pip_install:repositories.bzl", "requirement") def compile_pip_requirements( name, extra_args = [], - visibility = ["//visibility:private"], + extra_deps = [], + py_binary = _py_binary, + py_test = _py_test, requirements_in = None, requirements_txt = None, - requirements_linux = None, requirements_darwin = None, + requirements_linux = None, requirements_windows = None, + visibility = ["//visibility:private"], tags = None, **kwargs): """Generates targets for managing pip dependencies with pip-compile. @@ -22,20 +39,23 @@ def compile_pip_requirements( It also generates two targets for running pip-compile: - - validate with `bazel test _test` - - update with `bazel run .update` + - validate with `bazel test [name]_test` + - update with `bazel run [name].update` Args: - name: base name for generated targets, typically "requirements" - extra_args: passed to pip-compile - visibility: passed to both the _test and .update rules - requirements_in: file expressing desired dependencies - requirements_txt: result of "compiling" the requirements.in file + name: base name for generated targets, typically "requirements". + extra_args: passed to pip-compile. + extra_deps: extra dependencies passed to pip-compile. + py_binary: the py_binary rule to be used. + py_test: the py_test rule to be used. + requirements_in: file expressing desired dependencies. + requirements_txt: result of "compiling" the requirements.in file. requirements_linux: File of linux specific resolve output to check validate if requirement.in has changes. requirements_darwin: File of darwin specific resolve output to check validate if requirement.in has changes. requirements_windows: File of windows specific resolve output to check validate if requirement.in has changes. - tags: tagging attribute common to all build rules, passed to both the _test and .update rules - **kwargs: other bazel attributes passed to the "_test" rule + tags: tagging attribute common to all build rules, passed to both the _test and .update rules. + visibility: passed to both the _test and .update rules. + **kwargs: other bazel attributes passed to the "_test" rule. """ requirements_in = name + ".in" if requirements_in == None else requirements_in requirements_txt = name + ".txt" if requirements_txt == None else requirements_txt @@ -53,9 +73,9 @@ def compile_pip_requirements( # Use the Label constructor so this is expanded in the context of the file # where it appears, which is to say, in @rules_python - pip_compile = Label("//python/pip_install:pip_compile.py") + pip_compile = Label("//python/pip_install/tools/dependency_resolver:dependency_resolver.py") - loc = "$(rootpath {})" + loc = "$(rlocationpath {})" args = [ loc.format(requirements_in), @@ -68,6 +88,7 @@ def compile_pip_requirements( ] + extra_args deps = [ + requirement("build"), requirement("click"), requirement("colorama"), requirement("pep517"), @@ -75,8 +96,16 @@ def compile_pip_requirements( requirement("pip_tools"), requirement("setuptools"), requirement("tomli"), - ] - + requirement("importlib_metadata"), + requirement("zipp"), + requirement("more_itertools"), + Label("//python/runfiles:runfiles"), + ] + extra_deps + + tags = tags or [] + tags.append("requires-network") + tags.append("no-remote-exec") + tags.append("no-sandbox") attrs = { "args": args, "data": data, diff --git a/python/pip_install/requirements_parser.bzl b/python/pip_install/requirements_parser.bzl new file mode 100644 index 0000000000..ac90b95328 --- /dev/null +++ b/python/pip_install/requirements_parser.bzl @@ -0,0 +1,133 @@ +# 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. + +"Pip requirements parser for Starlark" + +_STATE = struct( + # Consume extraneous whitespace + ConsumeSpace = 0, + # Consume a comment + ConsumeComment = 1, + # Parse the name of a pip package + ParseDependency = 2, + # Parse a full requirement line + ParseRequirement = 3, + # Parse a pip option + ParseOption = 4, +) + +EOF = {} + +def parse(content): + """A simplistic (and incomplete) pip requirements lockfile parser. + + Parses package names and their full requirement lines, as well pip + options. + + Args: + content: lockfile content as a string + + Returns: + Struct with fields `requirements` and `options`. + + requirements: List of requirements, where each requirement is a 2-element + tuple containing the package name and the requirement line. + E.g., [(certifi', 'certifi==2021.10.8 --hash=sha256:7888...'), ...] + + options: List of pip option lines + """ + content = content.replace("\r", "") + + result = struct( + requirements = [], + options = [], + ) + state = _STATE.ConsumeSpace + buffer = "" + + inputs = content.elems()[:] + inputs.append(EOF) + + for input in inputs: + if state == _STATE.ConsumeSpace: + (state, buffer) = _handleConsumeSpace(input) + elif state == _STATE.ConsumeComment: + (state, buffer) = _handleConsumeComment(input, buffer, result) + elif state == _STATE.ParseDependency: + (state, buffer) = _handleParseDependency(input, buffer, result) + elif state == _STATE.ParseOption: + (state, buffer) = _handleParseOption(input, buffer, result) + elif state == _STATE.ParseRequirement: + (state, buffer) = _handleParseRequirement(input, buffer, result) + else: + fail("Unknown state %d" % state) + + return result + +def _handleConsumeSpace(input): + if input == EOF: + return (_STATE.ConsumeSpace, "") + if input.isspace(): + return (_STATE.ConsumeSpace, "") + elif input == "#": + return (_STATE.ConsumeComment, "") + elif input == "-": + return (_STATE.ParseOption, input) + + return (_STATE.ParseDependency, input) + +def _handleConsumeComment(input, buffer, result): + if input == "\n": + if len(result.requirements) > 0 and len(result.requirements[-1]) == 1: + result.requirements[-1] = (result.requirements[-1][0], buffer.rstrip(" \n")) + return (_STATE.ConsumeSpace, "") + elif len(buffer) > 0: + result.options.append(buffer.rstrip(" \n")) + return (_STATE.ConsumeSpace, "") + return (_STATE.ConsumeSpace, "") + return (_STATE.ConsumeComment, buffer) + +def _handleParseDependency(input, buffer, result): + if input == EOF: + fail("Enountered unexpected end of file while parsing requirement") + elif input.isspace() or input in [">", "<", "~", "=", ";", "["]: + result.requirements.append((buffer,)) + return (_STATE.ParseRequirement, buffer + input) + + return (_STATE.ParseDependency, buffer + input) + +def _handleParseOption(input, buffer, result): + if input == "\n" and buffer.endswith("\\"): + return (_STATE.ParseOption, buffer[0:-1]) + elif input == " ": + result.options.append(buffer.rstrip("\n")) + return (_STATE.ParseOption, "") + elif input == "\n" or input == EOF: + result.options.append(buffer.rstrip("\n")) + return (_STATE.ConsumeSpace, "") + elif input == "#": + return (_STATE.ConsumeComment, buffer) + + return (_STATE.ParseOption, buffer + input) + +def _handleParseRequirement(input, buffer, result): + if input == "\n" and buffer.endswith("\\"): + return (_STATE.ParseRequirement, buffer[0:-1]) + elif input == "\n" or input == EOF: + result.requirements[-1] = (result.requirements[-1][0], buffer.rstrip(" \n")) + return (_STATE.ConsumeSpace, "") + elif input == "#": + return (_STATE.ConsumeComment, buffer) + + return (_STATE.ParseRequirement, buffer + input) diff --git a/python/pip_install/tools/dependency_resolver/BUILD.bazel b/python/pip_install/tools/dependency_resolver/BUILD.bazel new file mode 100644 index 0000000000..c2cfb39509 --- /dev/null +++ b/python/pip_install/tools/dependency_resolver/BUILD.bazel @@ -0,0 +1,19 @@ +exports_files(["dependency_resolver.py"]) + +filegroup( + name = "distribution", + srcs = glob( + ["*"], + exclude = ["*_test.py"], + ), + visibility = ["//python/pip_install:__subpackages__"], +) + +filegroup( + name = "py_srcs", + srcs = glob( + include = ["**/*.py"], + exclude = ["**/*_test.py"], + ), + visibility = ["//python/pip_install:__subpackages__"], +) diff --git a/python/pip_install/tools/dependency_resolver/__init__.py b/python/pip_install/tools/dependency_resolver/__init__.py new file mode 100644 index 0000000000..bbdfb4c588 --- /dev/null +++ b/python/pip_install/tools/dependency_resolver/__init__.py @@ -0,0 +1,14 @@ +# 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. + diff --git a/python/pip_install/tools/dependency_resolver/dependency_resolver.py b/python/pip_install/tools/dependency_resolver/dependency_resolver.py new file mode 100644 index 0000000000..89e355806c --- /dev/null +++ b/python/pip_install/tools/dependency_resolver/dependency_resolver.py @@ -0,0 +1,223 @@ +# 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. + +"Set defaults for the pip-compile command to run it under Bazel" + +import atexit +import os +import shutil +import sys +from pathlib import Path + +import piptools.writer as piptools_writer +from piptools.scripts.compile import cli + +from python.runfiles import runfiles + +# Replace the os.replace function with shutil.copy to work around os.replace not being able to +# replace or move files across filesystems. +os.replace = shutil.copy + +# Next, we override the annotation_style_split and annotation_style_line functions to replace the +# backslashes in the paths with forward slashes. This is so that we can have the same requirements +# file on Windows and Unix-like. +original_annotation_style_split = piptools_writer.annotation_style_split +original_annotation_style_line = piptools_writer.annotation_style_line + + +def annotation_style_split(required_by) -> str: + required_by = set([v.replace("\\", "/") for v in required_by]) + return original_annotation_style_split(required_by) + + +def annotation_style_line(required_by) -> str: + required_by = set([v.replace("\\", "/") for v in required_by]) + return original_annotation_style_line(required_by) + + +piptools_writer.annotation_style_split = annotation_style_split +piptools_writer.annotation_style_line = annotation_style_line + + +def _select_golden_requirements_file( + requirements_txt, requirements_linux, requirements_darwin, requirements_windows +): + """Switch the golden requirements file, used to validate if updates are needed, + to a specified platform specific one. Fallback on the platform independent one. + """ + + plat = sys.platform + if plat == "linux" and requirements_linux is not None: + return requirements_linux + elif plat == "darwin" and requirements_darwin is not None: + return requirements_darwin + elif plat == "win32" and requirements_windows is not None: + return requirements_windows + else: + return requirements_txt + + +def _locate(bazel_runfiles, file): + """Look up the file via Rlocation""" + + if not file: + return file + + return bazel_runfiles.Rlocation(file) + + +if __name__ == "__main__": + if len(sys.argv) < 4: + print( + "Expected at least two arguments: requirements_in requirements_out", + file=sys.stderr, + ) + sys.exit(1) + + parse_str_none = lambda s: None if s == "None" else s + bazel_runfiles = runfiles.Create() + + requirements_in = sys.argv.pop(1) + requirements_txt = sys.argv.pop(1) + requirements_linux = parse_str_none(sys.argv.pop(1)) + requirements_darwin = parse_str_none(sys.argv.pop(1)) + requirements_windows = parse_str_none(sys.argv.pop(1)) + update_target_label = sys.argv.pop(1) + + resolved_requirements_in = _locate(bazel_runfiles, requirements_in) + resolved_requirements_txt = _locate(bazel_runfiles, requirements_txt) + + # Files in the runfiles directory has the following naming schema: + # Main repo: __main__/ + # External repo: / + # We want to strip both __main__ and from the absolute prefix + # to keep the requirements lock file agnostic. + repository_prefix = requirements_txt[: requirements_txt.index("/") + 1] + absolute_path_prefix = resolved_requirements_txt[ + : -(len(requirements_txt) - len(repository_prefix)) + ] + + # As requirements_in might contain references to generated files we want to + # use the runfiles file first. Thus, we need to compute the relative path + # from the execution root. + # Note: Windows cannot reference generated files without runfiles support enabled. + requirements_in_relative = requirements_in[len(repository_prefix) :] + requirements_txt_relative = requirements_txt[len(repository_prefix) :] + + # Before loading click, set the locale for its parser. + # If it leaks through to the system setting, it may fail: + # RuntimeError: Click will abort further execution because Python 3 was configured to use ASCII + # as encoding for the environment. Consult https://click.palletsprojects.com/python3/ for + # mitigation steps. + os.environ["LC_ALL"] = "C.UTF-8" + os.environ["LANG"] = "C.UTF-8" + + UPDATE = True + # Detect if we are running under `bazel test`. + if "TEST_TMPDIR" in os.environ: + UPDATE = False + # pip-compile wants the cache files to be writeable, but if we point + # to the real user cache, Bazel sandboxing makes the file read-only + # and we fail. + # In theory this makes the test more hermetic as well. + sys.argv.append("--cache-dir") + sys.argv.append(os.environ["TEST_TMPDIR"]) + # Make a copy for pip-compile to read and mutate. + requirements_out = os.path.join( + os.environ["TEST_TMPDIR"], os.path.basename(requirements_txt) + ".out" + ) + # Those two files won't necessarily be on the same filesystem, so we can't use os.replace + # or shutil.copyfile, as they will fail with OSError: [Errno 18] Invalid cross-device link. + shutil.copy(resolved_requirements_txt, requirements_out) + + update_command = os.getenv("CUSTOM_COMPILE_COMMAND") or "bazel run %s" % ( + update_target_label, + ) + + os.environ["CUSTOM_COMPILE_COMMAND"] = update_command + os.environ["PIP_CONFIG_FILE"] = os.getenv("PIP_CONFIG_FILE") or os.devnull + + sys.argv.append("--generate-hashes") + sys.argv.append("--output-file") + sys.argv.append(requirements_txt_relative if UPDATE else requirements_out) + sys.argv.append( + requirements_in_relative + if Path(requirements_in_relative).exists() + else resolved_requirements_in + ) + print(sys.argv) + + if UPDATE: + print("Updating " + requirements_txt_relative) + if "BUILD_WORKSPACE_DIRECTORY" in os.environ: + workspace = os.environ["BUILD_WORKSPACE_DIRECTORY"] + requirements_txt_tree = os.path.join(workspace, requirements_txt_relative) + # In most cases, requirements_txt will be a symlink to the real file in the source tree. + # If symlinks are not enabled (e.g. on Windows), then requirements_txt will be a copy, + # and we should copy the updated requirements back to the source tree. + if not os.path.samefile(resolved_requirements_txt, requirements_txt_tree): + atexit.register( + lambda: shutil.copy( + resolved_requirements_txt, requirements_txt_tree + ) + ) + cli() + requirements_txt_relative_path = Path(requirements_txt_relative) + content = requirements_txt_relative_path.read_text() + content = content.replace(absolute_path_prefix, "") + requirements_txt_relative_path.write_text(content) + else: + # cli will exit(0) on success + try: + print("Checking " + requirements_txt) + cli() + print("cli() should exit", file=sys.stderr) + sys.exit(1) + except SystemExit as e: + if e.code == 2: + print( + "pip-compile exited with code 2. This means that pip-compile found " + "incompatible requirements or could not find a version that matches " + f"the install requirement in {requirements_in_relative}.", + file=sys.stderr, + ) + sys.exit(1) + elif e.code == 0: + golden_filename = _select_golden_requirements_file( + requirements_txt, + requirements_linux, + requirements_darwin, + requirements_windows, + ) + golden = open(_locate(bazel_runfiles, golden_filename)).readlines() + out = open(requirements_out).readlines() + out = [line.replace(absolute_path_prefix, "") for line in out] + if golden != out: + import difflib + + print("".join(difflib.unified_diff(golden, out)), file=sys.stderr) + print( + "Lock file out of date. Run '" + + update_command + + "' to update.", + file=sys.stderr, + ) + sys.exit(1) + sys.exit(0) + else: + print( + f"pip-compile unexpectedly exited with code {e.code}.", + file=sys.stderr, + ) + sys.exit(1) diff --git a/python/pip_install/tools/lib/BUILD.bazel b/python/pip_install/tools/lib/BUILD.bazel new file mode 100644 index 0000000000..37a8b09be2 --- /dev/null +++ b/python/pip_install/tools/lib/BUILD.bazel @@ -0,0 +1,82 @@ +load("//python:defs.bzl", "py_library", "py_test") +load(":annotations_test_helpers.bzl", "package_annotation", "package_annotations_file") + +py_library( + name = "lib", + srcs = [ + "annotation.py", + "arguments.py", + "bazel.py", + ], + visibility = ["//python/pip_install:__subpackages__"], +) + +package_annotations_file( + name = "mock_annotations", + annotations = { + "pkg_a": package_annotation(), + "pkg_b": package_annotation( + data_exclude_glob = [ + "*.foo", + "*.bar", + ], + ), + "pkg_c": package_annotation( + # The `join` and `strip` here accounts for potential differences + # in new lines between unix and windows hosts. + additive_build_content = "\n".join([line.strip() for line in """\ +cc_library( + name = "my_target", + hdrs = glob(["**/*.h"]), + srcs = glob(["**/*.cc"]), +) +""".splitlines()]), + data = [":my_target"], + ), + "pkg_d": package_annotation( + srcs_exclude_glob = ["pkg_d/tests/**"], + ), + }, + tags = ["manual"], +) + +py_test( + name = "annotations_test", + size = "small", + srcs = ["annotations_test.py"], + data = [":mock_annotations"], + env = {"MOCK_ANNOTATIONS": "$(rootpath :mock_annotations)"}, + deps = [ + ":lib", + "//python/runfiles", + ], +) + +py_test( + name = "arguments_test", + size = "small", + srcs = [ + "arguments_test.py", + ], + deps = [ + ":lib", + ], +) + +filegroup( + name = "distribution", + srcs = glob( + ["*"], + exclude = ["*_test.py"], + ), + visibility = ["//python/pip_install:__subpackages__"], +) + +filegroup( + name = "py_srcs", + srcs = glob( + include = ["**/*.py"], + exclude = ["**/*_test.py"], + ), + visibility = ["//python/pip_install:__subpackages__"], +) diff --git a/python/pip_install/tools/lib/__init__.py b/python/pip_install/tools/lib/__init__.py new file mode 100644 index 0000000000..bbdfb4c588 --- /dev/null +++ b/python/pip_install/tools/lib/__init__.py @@ -0,0 +1,14 @@ +# 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. + diff --git a/python/pip_install/extract_wheels/annotation.py b/python/pip_install/tools/lib/annotation.py similarity index 83% rename from python/pip_install/extract_wheels/annotation.py rename to python/pip_install/tools/lib/annotation.py index 48aaa8026a..c98008005e 100644 --- a/python/pip_install/extract_wheels/annotation.py +++ b/python/pip_install/tools/lib/annotation.py @@ -1,3 +1,17 @@ +# 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 json import logging from collections import OrderedDict diff --git a/python/pip_install/extract_wheels/annotations_test.py b/python/pip_install/tools/lib/annotations_test.py similarity index 82% rename from python/pip_install/extract_wheels/annotations_test.py rename to python/pip_install/tools/lib/annotations_test.py index 0c41bf70a4..f7c360fbc9 100644 --- a/python/pip_install/extract_wheels/annotations_test.py +++ b/python/pip_install/tools/lib/annotations_test.py @@ -1,11 +1,25 @@ #!/usr/bin/env python3 +# 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 textwrap import unittest from pathlib import Path -from python.pip_install.extract_wheels.annotation import Annotation, AnnotationsMap +from python.pip_install.tools.lib.annotation import Annotation, AnnotationsMap from python.runfiles import runfiles diff --git a/python/pip_install/extract_wheels/annotations_test_helpers.bzl b/python/pip_install/tools/lib/annotations_test_helpers.bzl similarity index 63% rename from python/pip_install/extract_wheels/annotations_test_helpers.bzl rename to python/pip_install/tools/lib/annotations_test_helpers.bzl index dbd1124670..4f56bb7022 100644 --- a/python/pip_install/extract_wheels/annotations_test_helpers.bzl +++ b/python/pip_install/tools/lib/annotations_test_helpers.bzl @@ -1,4 +1,18 @@ -"""Helper macros and rules for testing the `annotations` module of `extract_wheels`""" +# 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. + +"""Helper macros and rules for testing the `annotations` module of `tools`""" load("//python:pip.bzl", _package_annotation = "package_annotation") diff --git a/python/pip_install/extract_wheels/arguments.py b/python/pip_install/tools/lib/arguments.py similarity index 65% rename from python/pip_install/extract_wheels/arguments.py rename to python/pip_install/tools/lib/arguments.py index d7d34523a7..974f03cbdd 100644 --- a/python/pip_install/extract_wheels/arguments.py +++ b/python/pip_install/tools/lib/arguments.py @@ -1,3 +1,17 @@ +# 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 json from argparse import ArgumentParser @@ -39,6 +53,12 @@ def parse_common_args(parser: ArgumentParser) -> ArgumentParser: required=True, help="Prefix to prepend to packages", ) + parser.add_argument( + "--download_only", + action="store_true", + help="Use 'pip download' instead of 'pip wheel'. Disables building wheels from source, but allows use of " + "--platform, --python-version, --implementation, and --abi in --extra_pip_args.", + ) return parser diff --git a/python/pip_install/extract_wheels/arguments_test.py b/python/pip_install/tools/lib/arguments_test.py similarity index 72% rename from python/pip_install/extract_wheels/arguments_test.py rename to python/pip_install/tools/lib/arguments_test.py index 8a3aec7a37..dfa96a890e 100644 --- a/python/pip_install/extract_wheels/arguments_test.py +++ b/python/pip_install/tools/lib/arguments_test.py @@ -1,8 +1,22 @@ +# 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 argparse import json import unittest -from python.pip_install.extract_wheels import arguments +from python.pip_install.tools.lib import arguments class ArgumentsTestCase(unittest.TestCase): diff --git a/python/pip_install/tools/lib/bazel.py b/python/pip_install/tools/lib/bazel.py new file mode 100644 index 0000000000..5ee221f1bf --- /dev/null +++ b/python/pip_install/tools/lib/bazel.py @@ -0,0 +1,55 @@ +# 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. + +WHEEL_FILE_LABEL = "whl" +PY_LIBRARY_LABEL = "pkg" +DATA_LABEL = "data" +DIST_INFO_LABEL = "dist_info" +WHEEL_ENTRY_POINT_PREFIX = "rules_python_wheel_entry_point" + + +def sanitise_name(name: str, prefix: str) -> str: + """Sanitises the name to be compatible with Bazel labels. + + There are certain requirements around Bazel labels that we need to consider. From the Bazel docs: + + Package names must be composed entirely of characters drawn from the set A-Z, a–z, 0–9, '/', '-', '.', and '_', + and cannot start with a slash. + + Due to restrictions on Bazel labels we also cannot allow hyphens. See + https://github.com/bazelbuild/bazel/issues/6841 + + Further, rules-python automatically adds the repository root to the PYTHONPATH, meaning a package that has the same + name as a module is picked up. We workaround this by prefixing with `pypi__`. Alternatively we could require + `--noexperimental_python_import_all_repositories` be set, however this breaks rules_docker. + See: https://github.com/bazelbuild/bazel/issues/2636 + """ + + return prefix + name.replace("-", "_").replace(".", "_").lower() + + +def _whl_name_to_repo_root(whl_name: str, repo_prefix: str) -> str: + return "@{}//".format(sanitise_name(whl_name, prefix=repo_prefix)) + + +def sanitised_repo_library_label(whl_name: str, repo_prefix: str) -> str: + return '"{}:{}"'.format( + _whl_name_to_repo_root(whl_name, repo_prefix), PY_LIBRARY_LABEL + ) + + +def sanitised_repo_file_label(whl_name: str, repo_prefix: str) -> str: + return '"{}:{}"'.format( + _whl_name_to_repo_root(whl_name, repo_prefix), WHEEL_FILE_LABEL + ) diff --git a/python/pip_install/tools/wheel_installer/BUILD.bazel b/python/pip_install/tools/wheel_installer/BUILD.bazel new file mode 100644 index 0000000000..54bbc46546 --- /dev/null +++ b/python/pip_install/tools/wheel_installer/BUILD.bazel @@ -0,0 +1,66 @@ +load("//python:defs.bzl", "py_binary", "py_library", "py_test") +load("//python/pip_install:repositories.bzl", "requirement") + +py_library( + name = "lib", + srcs = [ + "namespace_pkgs.py", + "wheel.py", + "wheel_installer.py", + ], + deps = [ + "//python/pip_install/tools/lib", + requirement("installer"), + requirement("pip"), + requirement("setuptools"), + ], +) + +py_binary( + name = "wheel_installer", + srcs = [ + "wheel_installer.py", + ], + deps = [":lib"], +) + +py_test( + name = "namespace_pkgs_test", + size = "small", + srcs = [ + "namespace_pkgs_test.py", + ], + deps = [ + ":lib", + ], +) + +py_test( + name = "wheel_installer_test", + size = "small", + srcs = [ + "wheel_installer_test.py", + ], + data = ["//examples/wheel:minimal_with_py_package"], + deps = [ + ":lib", + ], +) + +filegroup( + name = "distribution", + srcs = glob( + ["*"], + exclude = ["*_test.py"], + ), + visibility = ["//python/pip_install:__subpackages__"], +) + +filegroup( + name = "py_srcs", + srcs = glob( + include = ["**/*.py"], + exclude = ["**/*_test.py"], + ), + visibility = ["//python/pip_install:__subpackages__"], +) diff --git a/python/pip_install/extract_wheels/namespace_pkgs.py b/python/pip_install/tools/wheel_installer/namespace_pkgs.py similarity index 87% rename from python/pip_install/extract_wheels/namespace_pkgs.py rename to python/pip_install/tools/wheel_installer/namespace_pkgs.py index 5ddd4e1f85..7d23c0e34b 100644 --- a/python/pip_install/extract_wheels/namespace_pkgs.py +++ b/python/pip_install/tools/wheel_installer/namespace_pkgs.py @@ -1,3 +1,17 @@ +# 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. + """Utility functions to discover python package types""" import os import textwrap diff --git a/python/pip_install/extract_wheels/namespace_pkgs_test.py b/python/pip_install/tools/wheel_installer/namespace_pkgs_test.py similarity index 90% rename from python/pip_install/extract_wheels/namespace_pkgs_test.py rename to python/pip_install/tools/wheel_installer/namespace_pkgs_test.py index 8a9d97ca39..4aa0fea978 100644 --- a/python/pip_install/extract_wheels/namespace_pkgs_test.py +++ b/python/pip_install/tools/wheel_installer/namespace_pkgs_test.py @@ -1,3 +1,17 @@ +# 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 pathlib import shutil @@ -5,7 +19,7 @@ import unittest from typing import Optional, Set -from python.pip_install.extract_wheels import namespace_pkgs +from python.pip_install.tools.wheel_installer import namespace_pkgs class TempDir: diff --git a/python/pip_install/extract_wheels/wheel.py b/python/pip_install/tools/wheel_installer/wheel.py similarity index 81% rename from python/pip_install/extract_wheels/wheel.py rename to python/pip_install/tools/wheel_installer/wheel.py index 3f101005d0..84af04ca59 100644 --- a/python/pip_install/extract_wheels/wheel.py +++ b/python/pip_install/tools/wheel_installer/wheel.py @@ -1,3 +1,17 @@ +# 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. + """Utility class to inspect an extracted wheel directory""" import email from typing import Dict, Optional, Set, Tuple @@ -20,7 +34,7 @@ def path(self) -> str: @property def name(self) -> str: # TODO Also available as installer.sources.WheelSource.distribution - name = str(self.metadata['Name']) + name = str(self.metadata["Name"]) return canonicalize_name(name) @property @@ -59,7 +73,7 @@ def entry_points(self) -> Dict[str, Tuple[str, str]]: def dependencies(self, extras_requested: Optional[Set[str]] = None) -> Set[str]: dependency_set = set() - for wheel_req in self.metadata.get_all('Requires-Dist', []): + for wheel_req in self.metadata.get_all("Requires-Dist", []): req = pkg_resources.Requirement(wheel_req) # type: ignore if req.marker is None or any( @@ -84,6 +98,7 @@ def unzip(self, directory: str) -> None: interpreter="/dev/null", script_kind="posix", destdir=directory, + bytecode_optimization_levels=[], ) with installer.sources.WheelFile.open(self.path) as wheel_source: diff --git a/python/pip_install/extract_wheels/bazel.py b/python/pip_install/tools/wheel_installer/wheel_installer.py similarity index 58% rename from python/pip_install/extract_wheels/bazel.py rename to python/pip_install/tools/wheel_installer/wheel_installer.py index d7aa706433..77aa3a406c 100644 --- a/python/pip_install/extract_wheels/bazel.py +++ b/python/pip_install/tools/wheel_installer/wheel_installer.py @@ -1,25 +1,109 @@ -"""Utility functions to manipulate Bazel files""" +# 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 argparse +import errno +import glob import json import os +import re import shutil +import subprocess +import sys import textwrap from pathlib import Path -from typing import Dict, Iterable, List, Optional, Set +from tempfile import NamedTemporaryFile +from typing import Dict, Iterable, List, Optional, Set, Tuple -from python.pip_install.extract_wheels import ( - annotation, - namespace_pkgs, - wheel, -) +from pip._vendor.packaging.utils import canonicalize_name -WHEEL_FILE_LABEL = "whl" -PY_LIBRARY_LABEL = "pkg" -DATA_LABEL = "data" -DIST_INFO_LABEL = "dist_info" -WHEEL_ENTRY_POINT_PREFIX = "rules_python_wheel_entry_point" +from python.pip_install.tools.lib import annotation, arguments, bazel +from python.pip_install.tools.wheel_installer import namespace_pkgs, wheel -def generate_entry_point_contents( +def _configure_reproducible_wheels() -> None: + """Modifies the environment to make wheel building reproducible. + Wheels created from sdists are not reproducible by default. We can however workaround this by + patching in some configuration with environment variables. + """ + + # wheel, by default, enables debug symbols in GCC. This incidentally captures the build path in the .so file + # We can override this behavior by disabling debug symbols entirely. + # https://github.com/pypa/pip/issues/6505 + if "CFLAGS" in os.environ: + os.environ["CFLAGS"] += " -g0" + else: + os.environ["CFLAGS"] = "-g0" + + # set SOURCE_DATE_EPOCH to 1980 so that we can use python wheels + # https://github.com/NixOS/nixpkgs/blob/master/doc/languages-frameworks/python.section.md#python-setuppy-bdist_wheel-cannot-create-whl + if "SOURCE_DATE_EPOCH" not in os.environ: + os.environ["SOURCE_DATE_EPOCH"] = "315532800" + + # Python wheel metadata files can be unstable. + # See https://bitbucket.org/pypa/wheel/pull-requests/74/make-the-output-of-metadata-files/diff + if "PYTHONHASHSEED" not in os.environ: + os.environ["PYTHONHASHSEED"] = "0" + + +def _parse_requirement_for_extra( + requirement: str, +) -> Tuple[Optional[str], Optional[Set[str]]]: + """Given a requirement string, returns the requirement name and set of extras, if extras specified. + Else, returns (None, None) + """ + + # https://www.python.org/dev/peps/pep-0508/#grammar + extras_pattern = re.compile( + r"^\s*([0-9A-Za-z][0-9A-Za-z_.\-]*)\s*\[\s*([0-9A-Za-z][0-9A-Za-z_.\-]*(?:\s*,\s*[0-9A-Za-z][0-9A-Za-z_.\-]*)*)\s*\]" + ) + + matches = extras_pattern.match(requirement) + if matches: + return ( + canonicalize_name(matches.group(1)), + {extra.strip() for extra in matches.group(2).split(",")}, + ) + + return None, None + + +def _setup_namespace_pkg_compatibility(wheel_dir: str) -> None: + """Converts native namespace packages to pkgutil-style packages + + Namespace packages can be created in one of three ways. They are detailed here: + https://packaging.python.org/guides/packaging-namespace-packages/#creating-a-namespace-package + + 'pkgutil-style namespace packages' (2) and 'pkg_resources-style namespace packages' (3) works in Bazel, but + 'native namespace packages' (1) do not. + + We ensure compatibility with Bazel of method 1 by converting them into method 2. + + Args: + wheel_dir: the directory of the wheel to convert + """ + + namespace_pkg_dirs = namespace_pkgs.implicit_namespace_packages( + wheel_dir, + ignored_dirnames=["%s/bin" % wheel_dir], + ) + + for ns_pkg_dir in namespace_pkg_dirs: + namespace_pkgs.add_pkgutil_style_namespace_pkg_init(ns_pkg_dir) + + +def _generate_entry_point_contents( module: str, attribute: str, shebang: str = "#!/usr/bin/env python3" ) -> str: """Generate the contents of an entry point script. @@ -46,7 +130,7 @@ def generate_entry_point_contents( ) -def generate_entry_point_rule(name: str, script: str, pkg: str) -> str: +def _generate_entry_point_rule(name: str, script: str, pkg: str) -> str: """Generate a Bazel `py_binary` rule for an entry point script. Note that the script is used to determine the name of the target. The name of @@ -79,7 +163,7 @@ def generate_entry_point_rule(name: str, script: str, pkg: str) -> str: ) -def generate_copy_commands(src, dest, is_executable=False) -> str: +def _generate_copy_commands(src, dest, is_executable=False) -> str: """Generate a [@bazel_skylib//rules:copy_file.bzl%copy_file][cf] target [cf]: https://github.com/bazelbuild/bazel-skylib/blob/1.1.1/docs/copy_file_doc.md @@ -109,7 +193,7 @@ def generate_copy_commands(src, dest, is_executable=False) -> str: ) -def generate_build_file_contents( +def _generate_build_file_contents( name: str, dependencies: List[str], whl_file_deps: List[str], @@ -156,7 +240,7 @@ def generate_build_file_contents( textwrap.dedent( """\ load("@rules_python//python:defs.bzl", "py_library", "py_binary") - load("@rules_python//third_party/github.com/bazelbuild/bazel-skylib/rules:copy_file.bzl", "copy_file") + load("@bazel_skylib//rules:copy_file.bzl", "copy_file") package(default_visibility = ["//visibility:public"]) @@ -190,12 +274,12 @@ def generate_build_file_contents( name=name, dependencies=",".join(sorted(dependencies)), data_exclude=json.dumps(sorted(data_exclude)), - whl_file_label=WHEEL_FILE_LABEL, + whl_file_label=bazel.WHEEL_FILE_LABEL, whl_file_deps=",".join(sorted(whl_file_deps)), tags=",".join(sorted(['"%s"' % t for t in tags])), - data_label=DATA_LABEL, - dist_info_label=DIST_INFO_LABEL, - entry_point_prefix=WHEEL_ENTRY_POINT_PREFIX, + data_label=bazel.DATA_LABEL, + dist_info_label=bazel.DIST_INFO_LABEL, + entry_point_prefix=bazel.WHEEL_ENTRY_POINT_PREFIX, srcs_exclude=json.dumps(sorted(srcs_exclude)), data=json.dumps(sorted(data)), ) @@ -205,170 +289,31 @@ def generate_build_file_contents( ) -def generate_requirements_file_contents(repo_name: str, targets: Iterable[str]) -> str: - """Generate a requirements.bzl file for a given pip repository - - The file allows converting the PyPI name to a bazel label. Additionally, it adds a function which can glob all the - installed dependencies. - - Args: - repo_name: the name of the pip repository - targets: a list of Bazel labels pointing to all the generated targets - - Returns: - A complete requirements.bzl file as a string - """ - - sorted_targets = sorted(targets) - requirement_labels = ",".join(sorted_targets) - whl_requirement_labels = ",".join( - '"{}:whl"'.format(target.strip('"')) for target in sorted_targets - ) - return textwrap.dedent( - """\ - all_requirements = [{requirement_labels}] - - all_whl_requirements = [{whl_requirement_labels}] - - def requirement(name): - name_key = name.replace("-", "_").replace(".", "_").lower() - return "{repo}//pypi__" + name_key - - def whl_requirement(name): - return requirement(name) + ":{whl_file_label}" - - def data_requirement(name): - return requirement(name) + ":{data_label}" - - def dist_info_requirement(name): - return requirement(name) + ":{dist_info_label}" - - def entry_point(pkg, script = None): - if not script: - script = pkg - return requirement(pkg) + ":{entry_point_prefix}_" + script - - def install_deps(): - fail("install_deps() only works if you are creating an incremental repo. Did you mean to use pip_parse()?") - """.format( - repo=repo_name, - requirement_labels=requirement_labels, - whl_requirement_labels=whl_requirement_labels, - whl_file_label=WHEEL_FILE_LABEL, - data_label=DATA_LABEL, - dist_info_label=DIST_INFO_LABEL, - entry_point_prefix=WHEEL_ENTRY_POINT_PREFIX, - ) - ) - - -def sanitise_name(name: str, prefix: str) -> str: - """Sanitises the name to be compatible with Bazel labels. - - There are certain requirements around Bazel labels that we need to consider. From the Bazel docs: - - Package names must be composed entirely of characters drawn from the set A-Z, a–z, 0–9, '/', '-', '.', and '_', - and cannot start with a slash. - - Due to restrictions on Bazel labels we also cannot allow hyphens. See - https://github.com/bazelbuild/bazel/issues/6841 - - Further, rules-python automatically adds the repository root to the PYTHONPATH, meaning a package that has the same - name as a module is picked up. We workaround this by prefixing with `pypi__`. Alternatively we could require - `--noexperimental_python_import_all_repositories` be set, however this breaks rules_docker. - See: https://github.com/bazelbuild/bazel/issues/2636 - """ - - return prefix + name.replace("-", "_").replace(".", "_").lower() - - -def setup_namespace_pkg_compatibility(wheel_dir: str) -> None: - """Converts native namespace packages to pkgutil-style packages - - Namespace packages can be created in one of three ways. They are detailed here: - https://packaging.python.org/guides/packaging-namespace-packages/#creating-a-namespace-package - - 'pkgutil-style namespace packages' (2) and 'pkg_resources-style namespace packages' (3) works in Bazel, but - 'native namespace packages' (1) do not. - - We ensure compatibility with Bazel of method 1 by converting them into method 2. - - Args: - wheel_dir: the directory of the wheel to convert - """ - - namespace_pkg_dirs = namespace_pkgs.implicit_namespace_packages( - wheel_dir, - ignored_dirnames=["%s/bin" % wheel_dir], - ) - - for ns_pkg_dir in namespace_pkg_dirs: - namespace_pkgs.add_pkgutil_style_namespace_pkg_init(ns_pkg_dir) - - -def sanitised_library_label(whl_name: str, prefix: str) -> str: - return '"//%s"' % sanitise_name(whl_name, prefix) - - -def sanitised_file_label(whl_name: str, prefix: str) -> str: - return '"//%s:%s"' % (sanitise_name(whl_name, prefix), WHEEL_FILE_LABEL) - - -def _whl_name_to_repo_root(whl_name: str, repo_prefix: str) -> str: - return "@{}//".format(sanitise_name(whl_name, prefix=repo_prefix)) - - -def sanitised_repo_library_label(whl_name: str, repo_prefix: str) -> str: - return '"{}:{}"'.format( - _whl_name_to_repo_root(whl_name, repo_prefix), PY_LIBRARY_LABEL - ) - - -def sanitised_repo_file_label(whl_name: str, repo_prefix: str) -> str: - return '"{}:{}"'.format( - _whl_name_to_repo_root(whl_name, repo_prefix), WHEEL_FILE_LABEL - ) - - -def extract_wheel( +def _extract_wheel( wheel_file: str, extras: Dict[str, Set[str]], pip_data_exclude: List[str], enable_implicit_namespace_pkgs: bool, repo_prefix: str, - incremental: bool = False, - incremental_dir: Path = Path("."), + installation_dir: Path = Path("."), annotation: Optional[annotation.Annotation] = None, -) -> Optional[str]: +) -> None: """Extracts wheel into given directory and creates py_library and filegroup targets. Args: wheel_file: the filepath of the .whl + installation_dir: the destination directory for installation of the wheel. extras: a list of extras to add as dependencies for the installed wheel pip_data_exclude: list of file patterns to exclude from the generated data section of the py_library enable_implicit_namespace_pkgs: if true, disables conversion of implicit namespace packages and will unzip as-is - incremental: If true the extract the wheel in a format suitable for an external repository. This - effects the names of libraries and their dependencies, which point to other external repositories. - incremental_dir: An optional override for the working directory of incremental builds. annotation: An optional set of annotations to apply to the BUILD contents of the wheel. - - Returns: - The Bazel label for the extracted wheel, in the form '//path/to/wheel'. """ whl = wheel.Wheel(wheel_file) - if incremental: - directory = incremental_dir - else: - directory = sanitise_name(whl.name, prefix=repo_prefix) - - os.mkdir(directory) - # copy the original wheel - shutil.copy(whl.path, directory) - whl.unzip(directory) + whl.unzip(installation_dir) if not enable_implicit_namespace_pkgs: - setup_namespace_pkg_compatibility(directory) + _setup_namespace_pkg_compatibility(installation_dir) extras_requested = extras[whl.name] if whl.name in extras else set() # Packages may create dependency cycles when specifying optional-dependencies / 'extras'. @@ -376,45 +321,34 @@ def extract_wheel( self_edge_dep = set([whl.name]) whl_deps = sorted(whl.dependencies(extras_requested) - self_edge_dep) - if incremental: - sanitised_dependencies = [ - sanitised_repo_library_label(d, repo_prefix=repo_prefix) for d in whl_deps - ] - sanitised_wheel_file_dependencies = [ - sanitised_repo_file_label(d, repo_prefix=repo_prefix) for d in whl_deps - ] - else: - sanitised_dependencies = [ - sanitised_library_label(d, prefix=repo_prefix) for d in whl_deps - ] - sanitised_wheel_file_dependencies = [ - sanitised_file_label(d, prefix=repo_prefix) for d in whl_deps - ] - - library_name = ( - PY_LIBRARY_LABEL if incremental else sanitise_name(whl.name, repo_prefix) - ) + sanitised_dependencies = [ + bazel.sanitised_repo_library_label(d, repo_prefix=repo_prefix) for d in whl_deps + ] + sanitised_wheel_file_dependencies = [ + bazel.sanitised_repo_file_label(d, repo_prefix=repo_prefix) for d in whl_deps + ] - directory_path = Path(directory) entry_points = [] for name, (module, attribute) in sorted(whl.entry_points().items()): # There is an extreme edge-case with entry_points that end with `.py` # See: https://github.com/bazelbuild/bazel/blob/09c621e4cf5b968f4c6cdf905ab142d5961f9ddc/src/test/java/com/google/devtools/build/lib/rules/python/PyBinaryConfiguredTargetTest.java#L174 - entry_point_without_py = name[:-3] if name.endswith(".py") else name - entry_point_target_name = f"{WHEEL_ENTRY_POINT_PREFIX}_{entry_point_without_py}" + entry_point_without_py = f"{name[:-3]}_py" if name.endswith(".py") else name + entry_point_target_name = ( + f"{bazel.WHEEL_ENTRY_POINT_PREFIX}_{entry_point_without_py}" + ) entry_point_script_name = f"{entry_point_target_name}.py" - (directory_path / entry_point_script_name).write_text( - generate_entry_point_contents(module, attribute) + (installation_dir / entry_point_script_name).write_text( + _generate_entry_point_contents(module, attribute) ) entry_points.append( - generate_entry_point_rule( + _generate_entry_point_rule( entry_point_target_name, entry_point_script_name, - library_name, + bazel.PY_LIBRARY_LABEL, ) ) - with open(os.path.join(directory, "BUILD.bazel"), "w") as build_file: + with open(os.path.join(installation_dir, "BUILD.bazel"), "w") as build_file: additional_content = entry_points data = [] data_exclude = pip_data_exclude @@ -422,11 +356,11 @@ def extract_wheel( if annotation: for src, dest in annotation.copy_files.items(): data.append(dest) - additional_content.append(generate_copy_commands(src, dest)) + additional_content.append(_generate_copy_commands(src, dest)) for src, dest in annotation.copy_executables.items(): data.append(dest) additional_content.append( - generate_copy_commands(src, dest, is_executable=True) + _generate_copy_commands(src, dest, is_executable=True) ) data.extend(annotation.data) data_exclude.extend(annotation.data_exclude_glob) @@ -434,10 +368,8 @@ def extract_wheel( if annotation.additive_build_content: additional_content.append(annotation.additive_build_content) - contents = generate_build_file_contents( - name=PY_LIBRARY_LABEL - if incremental - else sanitise_name(whl.name, repo_prefix), + contents = _generate_build_file_contents( + name=bazel.PY_LIBRARY_LABEL, dependencies=sanitised_dependencies, whl_file_deps=sanitised_wheel_file_dependencies, data_exclude=data_exclude, @@ -448,7 +380,71 @@ def extract_wheel( ) build_file.write(contents) - if not incremental: - os.remove(whl.path) - return f"//{directory}" - return None + +def main() -> None: + parser = argparse.ArgumentParser( + description="Build and/or fetch a single wheel based on the requirement passed in" + ) + parser.add_argument( + "--requirement", + action="store", + required=True, + help="A single PEP508 requirement specifier string.", + ) + parser.add_argument( + "--annotation", + type=annotation.annotation_from_str_path, + help="A json encoded file containing annotations for rendered packages.", + ) + arguments.parse_common_args(parser) + args = parser.parse_args() + deserialized_args = dict(vars(args)) + arguments.deserialize_structured_args(deserialized_args) + + _configure_reproducible_wheels() + + pip_args = ( + [sys.executable, "-m", "pip"] + + (["--isolated"] if args.isolated else []) + + ["download" if args.download_only else "wheel", "--no-deps"] + + deserialized_args["extra_pip_args"] + ) + + requirement_file = NamedTemporaryFile(mode="wb", delete=False) + try: + requirement_file.write(args.requirement.encode("utf-8")) + requirement_file.flush() + # Close the file so pip is allowed to read it when running on Windows. + # For more information, see: https://bugs.python.org/issue14243 + requirement_file.close() + # Requirement specific args like --hash can only be passed in a requirements file, + # so write our single requirement into a temp file in case it has any of those flags. + pip_args.extend(["-r", requirement_file.name]) + + env = os.environ.copy() + env.update(deserialized_args["environment"]) + # Assumes any errors are logged by pip so do nothing. This command will fail if pip fails + subprocess.run(pip_args, check=True, env=env) + finally: + try: + os.unlink(requirement_file.name) + except OSError as e: + if e.errno != errno.ENOENT: + raise + + name, extras_for_pkg = _parse_requirement_for_extra(args.requirement) + extras = {name: extras_for_pkg} if extras_for_pkg and name else dict() + + whl = next(iter(glob.glob("*.whl"))) + _extract_wheel( + wheel_file=whl, + extras=extras, + pip_data_exclude=deserialized_args["pip_data_exclude"], + enable_implicit_namespace_pkgs=args.enable_implicit_namespace_pkgs, + repo_prefix=args.repo_prefix, + annotation=args.annotation, + ) + + +if __name__ == "__main__": + main() diff --git a/python/pip_install/tools/wheel_installer/wheel_installer_test.py b/python/pip_install/tools/wheel_installer/wheel_installer_test.py new file mode 100644 index 0000000000..8758b67a1c --- /dev/null +++ b/python/pip_install/tools/wheel_installer/wheel_installer_test.py @@ -0,0 +1,108 @@ +# 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 shutil +import tempfile +import unittest +from pathlib import Path + +from python.pip_install.tools.wheel_installer import wheel_installer + + +class TestRequirementExtrasParsing(unittest.TestCase): + def test_parses_requirement_for_extra(self) -> None: + cases = [ + ("name[foo]", ("name", frozenset(["foo"]))), + ("name[ Foo123 ]", ("name", frozenset(["Foo123"]))), + (" name1[ foo ] ", ("name1", frozenset(["foo"]))), + ("Name[foo]", ("name", frozenset(["foo"]))), + ("name_foo[bar]", ("name-foo", frozenset(["bar"]))), + ( + "name [fred,bar] @ http://foo.com ; python_version=='2.7'", + ("name", frozenset(["fred", "bar"])), + ), + ( + "name[quux, strange];python_version<'2.7' and platform_version=='2'", + ("name", frozenset(["quux", "strange"])), + ), + ( + "name; (os_name=='a' or os_name=='b') and os_name=='c'", + (None, None), + ), + ( + "name@http://foo.com", + (None, None), + ), + ] + + for case, expected in cases: + with self.subTest(): + self.assertTupleEqual( + wheel_installer._parse_requirement_for_extra(case), expected + ) + + +class BazelTestCase(unittest.TestCase): + def test_generate_entry_point_contents(self): + got = wheel_installer._generate_entry_point_contents("sphinx.cmd.build", "main") + want = """#!/usr/bin/env python3 +import sys +from sphinx.cmd.build import main +if __name__ == "__main__": + sys.exit(main()) +""" + self.assertEqual(got, want) + + def test_generate_entry_point_contents_with_shebang(self): + got = wheel_installer._generate_entry_point_contents( + "sphinx.cmd.build", "main", shebang="#!/usr/bin/python" + ) + want = """#!/usr/bin/python +import sys +from sphinx.cmd.build import main +if __name__ == "__main__": + sys.exit(main()) +""" + self.assertEqual(got, want) + + +class TestWhlFilegroup(unittest.TestCase): + def setUp(self) -> None: + self.wheel_name = "example_minimal_package-0.0.1-py3-none-any.whl" + self.wheel_dir = tempfile.mkdtemp() + self.wheel_path = os.path.join(self.wheel_dir, self.wheel_name) + shutil.copy(os.path.join("examples", "wheel", self.wheel_name), self.wheel_dir) + + def tearDown(self): + shutil.rmtree(self.wheel_dir) + + def test_wheel_exists(self) -> None: + wheel_installer._extract_wheel( + self.wheel_path, + installation_dir=Path(self.wheel_dir), + extras={}, + pip_data_exclude=[], + enable_implicit_namespace_pkgs=False, + repo_prefix="prefix_", + ) + + self.assertIn(self.wheel_name, os.listdir(self.wheel_dir)) + with open("{}/BUILD.bazel".format(self.wheel_dir)) as build_file: + build_file_content = build_file.read() + self.assertIn("filegroup", build_file_content) + + +if __name__ == "__main__": + unittest.main() diff --git a/python/private/BUILD b/python/private/BUILD.bazel similarity index 59% rename from python/private/BUILD rename to python/private/BUILD.bazel index c99b040103..f454f42cf3 100644 --- a/python/private/BUILD +++ b/python/private/BUILD.bazel @@ -12,32 +12,67 @@ # See the License for the specific language governing permissions and # limitations under the License. +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("//python:versions.bzl", "print_toolchains_checksums") load(":stamp.bzl", "stamp_build_setting") -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) filegroup( name = "distribution", - srcs = glob(["**"]), + srcs = glob(["**"]) + ["//python/private/proto:distribution"], visibility = ["//python:__pkg__"], ) # Filegroup of bzl files that can be used by downstream rules for documentation generation -# Using a filegroup rather than bzl_library to not give a transitive dependency on Skylib filegroup( name = "bzl", srcs = glob(["**/*.bzl"]), visibility = ["//python:__pkg__"], ) +bzl_library( + name = "reexports_bzl", + srcs = ["reexports.bzl"], + visibility = [ + "//docs:__pkg__", + "//python:__pkg__", + ], + deps = [":bazel_tools_bzl"], +) + +bzl_library( + name = "util_bzl", + srcs = ["util.bzl"], + visibility = [ + "//docs:__subpackages__", + "//python:__subpackages__", + ], + deps = ["@bazel_skylib//lib:types"], +) + +# @bazel_tools can't define bzl_library itself, so we just put a wrapper around it. +bzl_library( + name = "bazel_tools_bzl", + srcs = [ + "@bazel_tools//tools/python:srcs_version.bzl", + "@bazel_tools//tools/python:toolchain.bzl", + "@bazel_tools//tools/python:utils.bzl", + ], + visibility = ["//python:__pkg__"], +) + # Needed to define bzl_library targets for docgen. (We don't define the # bzl_library target here because it'd give our users a transitive dependency # on Skylib.) exports_files( [ + "coverage.patch", + "py_package.bzl", + "py_wheel.bzl", "reexports.bzl", "stamp.bzl", + "util.bzl", ], visibility = ["//docs:__pkg__"], ) diff --git a/python/private/coverage.patch b/python/private/coverage.patch new file mode 100644 index 0000000000..4cab60cddc --- /dev/null +++ b/python/private/coverage.patch @@ -0,0 +1,15 @@ +# Because of how coverage is run, the current directory is the first in +# sys.path. This is a problem for the tests, because they may import a module of +# the same name as a module in the current directory. +diff --git a/coverage/cmdline.py b/coverage/cmdline.py +index dbf66e0a..780505ac 100644 +--- a/coverage/cmdline.py ++++ b/coverage/cmdline.py +@@ -937,6 +937,7 @@ def main(argv=None): + This is installed as the script entry point. + + """ ++ sys.path.append(sys.path.pop(0)) + if argv is None: + argv = sys.argv[1:] + try: diff --git a/python/private/coverage_deps.bzl b/python/private/coverage_deps.bzl new file mode 100644 index 0000000000..377dfb7494 --- /dev/null +++ b/python/private/coverage_deps.bzl @@ -0,0 +1,183 @@ +# 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. + +"""Dependencies for coverage.py used by the hermetic toolchain. +""" + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +load( + "//python:versions.bzl", + "MINOR_MAPPING", + "PLATFORMS", +) + +# Update with './tools/update_coverage_deps.py ' +#START: managed by update_coverage_deps.py script +_coverage_deps = { + "cp310": { + "aarch64-apple-darwin": ( + "https://files.pythonhosted.org/packages/89/a2/cbf599e50bb4be416e0408c4cf523c354c51d7da39935461a9687e039481/coverage-6.5.0-cp310-cp310-macosx_11_0_arm64.whl", + "784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660", + ), + "aarch64-unknown-linux-gnu": ( + "https://files.pythonhosted.org/packages/15/b0/3639d84ee8a900da0cf6450ab46e22517e4688b6cec0ba8ab6f8166103a2/coverage-6.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", + "b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4", + ), + "x86_64-apple-darwin": ( + "https://files.pythonhosted.org/packages/c4/8d/5ec7d08f4601d2d792563fe31db5e9322c306848fec1e65ec8885927f739/coverage-6.5.0-cp310-cp310-macosx_10_9_x86_64.whl", + "ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53", + ), + "x86_64-unknown-linux-gnu": ( + "https://files.pythonhosted.org/packages/3c/7d/d5211ea782b193ab8064b06dc0cc042cf1a4ca9c93a530071459172c550f/coverage-6.5.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", + "af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0", + ), + }, + "cp311": { + "aarch64-unknown-linux-gnu": ( + "https://files.pythonhosted.org/packages/36/f3/5cbd79cf4cd059c80b59104aca33b8d05af4ad5bf5b1547645ecee716378/coverage-6.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", + "c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75", + ), + "x86_64-apple-darwin": ( + "https://files.pythonhosted.org/packages/50/cf/455930004231fa87efe8be06d13512f34e070ddfee8b8bf5a050cdc47ab3/coverage-6.5.0-cp311-cp311-macosx_10_9_x86_64.whl", + "4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795", + ), + "x86_64-unknown-linux-gnu": ( + "https://files.pythonhosted.org/packages/6a/63/8e82513b7e4a1b8d887b4e85c1c2b6c9b754a581b187c0b084f3330ac479/coverage-6.5.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", + "a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91", + ), + }, + "cp38": { + "aarch64-apple-darwin": ( + "https://files.pythonhosted.org/packages/07/82/79fa21ceca9a9b091eb3c67e27eb648dade27b2c9e1eb23af47232a2a365/coverage-6.5.0-cp38-cp38-macosx_11_0_arm64.whl", + "2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba", + ), + "aarch64-unknown-linux-gnu": ( + "https://files.pythonhosted.org/packages/40/3b/cd68cb278c4966df00158811ec1e357b9a7d132790c240fc65da57e10013/coverage-6.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", + "6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e", + ), + "x86_64-apple-darwin": ( + "https://files.pythonhosted.org/packages/05/63/a789b462075395d34f8152229dccf92b25ca73eac05b3f6cd75fa5017095/coverage-6.5.0-cp38-cp38-macosx_10_9_x86_64.whl", + "d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c", + ), + "x86_64-unknown-linux-gnu": ( + "https://files.pythonhosted.org/packages/bd/a0/e263b115808226fdb2658f1887808c06ac3f1b579ef5dda02309e0d54459/coverage-6.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", + "6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b", + ), + }, + "cp39": { + "aarch64-apple-darwin": ( + "https://files.pythonhosted.org/packages/63/e9/f23e8664ec4032d7802a1cf920853196bcbdce7b56408e3efe1b2da08f3c/coverage-6.5.0-cp39-cp39-macosx_11_0_arm64.whl", + "95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc", + ), + "aarch64-unknown-linux-gnu": ( + "https://files.pythonhosted.org/packages/18/95/27f80dcd8273171b781a19d109aeaed7f13d78ef6d1e2f7134a5826fd1b4/coverage-6.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", + "b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe", + ), + "x86_64-apple-darwin": ( + "https://files.pythonhosted.org/packages/ea/52/c08080405329326a7ff16c0dfdb4feefaa8edd7446413df67386fe1bbfe0/coverage-6.5.0-cp39-cp39-macosx_10_9_x86_64.whl", + "633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745", + ), + "x86_64-unknown-linux-gnu": ( + "https://files.pythonhosted.org/packages/6b/f2/919f0fdc93d3991ca074894402074d847be8ac1e1d78e7e9e1c371b69a6f/coverage-6.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", + "8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5", + ), + }, +} +#END: managed by update_coverage_deps.py script + +_coverage_patch = Label("//python/private:coverage.patch") + +def coverage_dep(name, python_version, platform, visibility, install = True): + """Register a singe coverage dependency based on the python version and platform. + + Args: + name: The name of the registered repository. + python_version: The full python version. + platform: The platform, which can be found in //python:versions.bzl PLATFORMS dict. + visibility: The visibility of the coverage tool. + install: should we install the dependency with a given name or generate the label + of the bzlmod dependency fallback, which is hard-coded in MODULE.bazel? + + Returns: + The label of the coverage tool if the platform is supported, otherwise - None. + """ + if "windows" in platform: + # NOTE @aignas 2023-01-19: currently we do not support windows as the + # upstream coverage wrapper is written in shell. Do not log any warning + # for now as it is not actionable. + return None + + python_short_version = python_version.rpartition(".")[0] + abi = python_short_version.replace("3.", "cp3") + url, sha256 = _coverage_deps.get(abi, {}).get(platform, (None, "")) + + if url == None: + # Some wheels are not present for some builds, so let's silently ignore those. + return None + + if not install: + # FIXME @aignas 2023-01-19: right now we use globally installed coverage + # which has visibility set to public, but is hidden due to repo remapping. + # + # The name of the toolchain is not known when registering the coverage tooling, + # so we use this as a workaround for now. + return Label("@pypi__coverage_{abi}_{platform}//:coverage".format( + abi = abi, + platform = platform, + )) + + maybe( + http_archive, + name = name, + build_file_content = """ +filegroup( + name = "coverage", + srcs = ["coverage/__main__.py"], + data = glob(["coverage/*.py", "coverage/**/*.py", "coverage/*.so"]), + visibility = {visibility}, +) + """.format( + visibility = visibility, + ), + patch_args = ["-p1"], + patches = [_coverage_patch], + sha256 = sha256, + type = "zip", + urls = [url], + ) + + return Label("@@{name}//:coverage".format(name = name)) + +def install_coverage_deps(): + """Register the dependency for the coverage dep. + + This is only used under bzlmod. + """ + + for python_version in MINOR_MAPPING.values(): + for platform in PLATFORMS.keys(): + if "windows" in platform: + continue + + coverage_dep( + name = "pypi__coverage_cp{version_no_dot}_{platform}".format( + version_no_dot = python_version.rpartition(".")[0].replace(".", ""), + platform = platform, + ), + python_version = python_version, + platform = platform, + visibility = ["//visibility:public"], + install = True, + ) diff --git a/python/private/proto/BUILD.bazel b/python/private/proto/BUILD.bazel new file mode 100644 index 0000000000..65c09444f7 --- /dev/null +++ b/python/private/proto/BUILD.bazel @@ -0,0 +1,46 @@ +# Copyright 2022 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. + +load("@bazel_skylib//:bzl_library.bzl", "bzl_library") +load("@rules_proto//proto:defs.bzl", "proto_lang_toolchain") + +package(default_visibility = ["//visibility:private"]) + +licenses(["notice"]) + +filegroup( + name = "distribution", + srcs = glob(["**"]), + visibility = ["//python/private:__pkg__"], +) + +bzl_library( + name = "py_proto_library_bzl", + srcs = ["py_proto_library.bzl"], + visibility = ["//python:__pkg__"], + deps = [ + "//python:defs_bzl", + "@rules_proto//proto:defs", + ], +) + +proto_lang_toolchain( + name = "python_toolchain", + command_line = "--python_out=%s", + progress_message = "Generating Python proto_library %{label}", + runtime = "@com_google_protobuf//:protobuf_python", + # NOTE: This isn't *actually* public. It's an implicit dependency of py_proto_library, + # so must be public so user usages of the rule can reference it. + visibility = ["//visibility:public"], +) diff --git a/python/private/proto/py_proto_library.bzl b/python/private/proto/py_proto_library.bzl new file mode 100644 index 0000000000..988558500d --- /dev/null +++ b/python/private/proto/py_proto_library.bzl @@ -0,0 +1,198 @@ +# Copyright 2022 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. + +"""The implementation of the `py_proto_library` rule and its aspect.""" + +load("@rules_proto//proto:defs.bzl", "ProtoInfo", "proto_common") +load("//python:defs.bzl", "PyInfo") + +ProtoLangToolchainInfo = proto_common.ProtoLangToolchainInfo + +_PyProtoInfo = provider( + doc = "Encapsulates information needed by the Python proto rules.", + fields = { + "imports": """ + (depset[str]) The field forwarding PyInfo.imports coming from + the proto language runtime dependency.""", + "runfiles_from_proto_deps": """ + (depset[File]) Files from the transitive closure implicit proto + dependencies""", + "transitive_sources": """(depset[File]) The Python sources.""", + }, +) + +def _filter_provider(provider, *attrs): + return [dep[provider] for attr in attrs for dep in attr if provider in dep] + +def _py_proto_aspect_impl(target, ctx): + """Generates and compiles Python code for a proto_library. + + The function runs protobuf compiler on the `proto_library` target generating + a .py file for each .proto file. + + Args: + target: (Target) A target providing `ProtoInfo`. Usually this means a + `proto_library` target, but not always; you must expect to visit + non-`proto_library` targets, too. + ctx: (RuleContext) The rule context. + + Returns: + ([_PyProtoInfo]) Providers collecting transitive information about + generated files. + """ + + _proto_library = ctx.rule.attr + + # Check Proto file names + for proto in target[ProtoInfo].direct_sources: + if proto.is_source and "-" in proto.dirname: + fail("Cannot generate Python code for a .proto whose path contains '-' ({}).".format( + proto.path, + )) + + proto_lang_toolchain_info = ctx.attr._aspect_proto_toolchain[ProtoLangToolchainInfo] + api_deps = [proto_lang_toolchain_info.runtime] + + generated_sources = [] + proto_info = target[ProtoInfo] + if proto_info.direct_sources: + # Generate py files + generated_sources = proto_common.declare_generated_files( + actions = ctx.actions, + proto_info = proto_info, + extension = "_pb2.py", + name_mapper = lambda name: name.replace("-", "_").replace(".", "/"), + ) + + proto_common.compile( + actions = ctx.actions, + proto_info = proto_info, + proto_lang_toolchain_info = proto_lang_toolchain_info, + generated_files = generated_sources, + plugin_output = ctx.bin_dir.path, + ) + + # Generated sources == Python sources + python_sources = generated_sources + + deps = _filter_provider(_PyProtoInfo, getattr(_proto_library, "deps", [])) + runfiles_from_proto_deps = depset( + transitive = [dep[DefaultInfo].default_runfiles.files for dep in api_deps] + + [dep.runfiles_from_proto_deps for dep in deps], + ) + transitive_sources = depset( + direct = python_sources, + transitive = [dep.transitive_sources for dep in deps], + ) + + return [ + _PyProtoInfo( + imports = depset( + transitive = [dep[PyInfo].imports for dep in api_deps], + ), + runfiles_from_proto_deps = runfiles_from_proto_deps, + transitive_sources = transitive_sources, + ), + ] + +_py_proto_aspect = aspect( + implementation = _py_proto_aspect_impl, + attrs = { + "_aspect_proto_toolchain": attr.label( + default = ":python_toolchain", + ), + }, + attr_aspects = ["deps"], + required_providers = [ProtoInfo], + provides = [_PyProtoInfo], +) + +def _py_proto_library_rule(ctx): + """Merges results of `py_proto_aspect` in `deps`. + + Args: + ctx: (RuleContext) The rule context. + Returns: + ([PyInfo, DefaultInfo, OutputGroupInfo]) + """ + if not ctx.attr.deps: + fail("'deps' attribute mustn't be empty.") + + pyproto_infos = _filter_provider(_PyProtoInfo, ctx.attr.deps) + default_outputs = depset( + transitive = [info.transitive_sources for info in pyproto_infos], + ) + + return [ + DefaultInfo( + files = default_outputs, + default_runfiles = ctx.runfiles(transitive_files = depset( + transitive = + [default_outputs] + + [info.runfiles_from_proto_deps for info in pyproto_infos], + )), + ), + OutputGroupInfo( + default = depset(), + ), + PyInfo( + transitive_sources = default_outputs, + imports = depset(transitive = [info.imports for info in pyproto_infos]), + # Proto always produces 2- and 3- compatible source files + has_py2_only_sources = False, + has_py3_only_sources = False, + ), + ] + +py_proto_library = rule( + implementation = _py_proto_library_rule, + doc = """ + Use `py_proto_library` to generate Python libraries from `.proto` files. + + The convention is to name the `py_proto_library` rule `foo_py_pb2`, + when it is wrapping `proto_library` rule `foo_proto`. + + `deps` must point to a `proto_library` rule. + + Example: + +```starlark +py_library( + name = "lib", + deps = [":foo_py_pb2"], +) + +py_proto_library( + name = "foo_py_pb2", + deps = [":foo_proto"], +) + +proto_library( + name = "foo_proto", + srcs = ["foo.proto"], +) +```""", + attrs = { + "deps": attr.label_list( + doc = """ + The list of `proto_library` rules to generate Python libraries for. + + Usually this is just the one target: the proto library of interest. + It can be any target providing `ProtoInfo`.""", + providers = [ProtoInfo], + aspects = [_py_proto_aspect], + ), + }, + provides = [PyInfo], +) diff --git a/python/private/py_package.bzl b/python/private/py_package.bzl new file mode 100644 index 0000000000..08f4b0b318 --- /dev/null +++ b/python/private/py_package.bzl @@ -0,0 +1,75 @@ +# 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. + +"Implementation of py_package rule" + +def _path_inside_wheel(input_file): + # input_file.short_path is sometimes relative ("../${repository_root}/foobar") + # which is not a valid path within a zip file. Fix that. + short_path = input_file.short_path + if short_path.startswith("..") and len(short_path) >= 3: + # Path separator. '/' on linux. + separator = short_path[2] + + # Consume '../' part. + short_path = short_path[3:] + + # Find position of next '/' and consume everything up to that character. + pos = short_path.find(separator) + short_path = short_path[pos + 1:] + return short_path + +def _py_package_impl(ctx): + inputs = depset( + transitive = [dep[DefaultInfo].data_runfiles.files for dep in ctx.attr.deps] + + [dep[DefaultInfo].default_runfiles.files for dep in ctx.attr.deps], + ) + + # TODO: '/' is wrong on windows, but the path separator is not available in starlark. + # Fix this once ctx.configuration has directory separator information. + packages = [p.replace(".", "/") for p in ctx.attr.packages] + if not packages: + filtered_inputs = inputs + else: + filtered_files = [] + + # TODO: flattening depset to list gives poor performance, + for input_file in inputs.to_list(): + wheel_path = _path_inside_wheel(input_file) + for package in packages: + if wheel_path.startswith(package): + filtered_files.append(input_file) + filtered_inputs = depset(direct = filtered_files) + + return [DefaultInfo( + files = filtered_inputs, + )] + +py_package_lib = struct( + implementation = _py_package_impl, + attrs = { + "deps": attr.label_list( + doc = "", + ), + "packages": attr.string_list( + mandatory = False, + allow_empty = True, + doc = """\ +List of Python packages to include in the distribution. +Sub-packages are automatically included. +""", + ), + }, + path_inside_wheel = _path_inside_wheel, +) diff --git a/python/private/py_wheel.bzl b/python/private/py_wheel.bzl new file mode 100644 index 0000000000..b6f2bfae56 --- /dev/null +++ b/python/private/py_wheel.bzl @@ -0,0 +1,400 @@ +# 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. + +"Implementation of py_wheel rule" + +load("//python/private:stamp.bzl", "is_stamping_enabled") +load(":py_package.bzl", "py_package_lib") + +PyWheelInfo = provider( + doc = "Information about a wheel produced by `py_wheel`", + fields = { + "name_file": ( + "File: A file containing the canonical name of the wheel (after " + + "stamping, if enabled)." + ), + "wheel": "File: The wheel file itself.", + }, +) + +_distribution_attrs = { + "abi": attr.string( + default = "none", + doc = "Python ABI tag. 'none' for pure-Python wheels.", + ), + "distribution": attr.string( + mandatory = True, + doc = """\ +Name of the distribution. + +This should match the project name onm PyPI. It's also the name that is used to +refer to the package in other packages' dependencies. +""", + ), + "platform": attr.string( + default = "any", + doc = """\ +Supported platform. Use 'any' for pure-Python wheel. + +If you have included platform-specific data, such as a .pyd or .so +extension module, you will need to specify the platform in standard +pip format. If you support multiple platforms, you can define +platform constraints, then use a select() to specify the appropriate +specifier, eg: + +` +platform = select({ + "//platforms:windows_x86_64": "win_amd64", + "//platforms:macos_x86_64": "macosx_10_7_x86_64", + "//platforms:linux_x86_64": "manylinux2014_x86_64", +}) +` +""", + ), + "python_tag": attr.string( + default = "py3", + doc = "Supported Python version(s), eg `py3`, `cp35.cp36`, etc", + ), + "stamp": attr.int( + doc = """\ +Whether to encode build information into the wheel. Possible values: + +- `stamp = 1`: Always stamp the build information into the wheel, even in \ +[--nostamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) builds. \ +This setting should be avoided, since it potentially kills remote caching for the target and \ +any downstream actions that depend on it. + +- `stamp = 0`: Always replace build information by constant values. This gives good build result caching. + +- `stamp = -1`: Embedding of build information is controlled by the \ +[--[no]stamp](https://docs.bazel.build/versions/main/user-manual.html#flag--stamp) flag. + +Stamped targets are not rebuilt unless their dependencies change. + """, + default = -1, + values = [1, 0, -1], + ), + "version": attr.string( + mandatory = True, + doc = """\ +Version number of the package. + +Note that this attribute supports stamp format strings as well as 'make variables'. +For example: + - `version = "1.2.3-{BUILD_TIMESTAMP}"` + - `version = "{BUILD_EMBED_LABEL}"` + - `version = "$(VERSION)"` + +Note that Bazel's output filename cannot include the stamp information, as outputs must be known +during the analysis phase and the stamp data is available only during the action execution. + +The [`py_wheel`](/docs/packaging.md#py_wheel) macro produces a `.dist`-suffix target which creates a +`dist/` folder containing the wheel with the stamped name, suitable for publishing. + +See [`py_wheel_dist`](/docs/packaging.md#py_wheel_dist) for more info. +""", + ), + "_stamp_flag": attr.label( + doc = "A setting used to determine whether or not the `--stamp` flag is enabled", + default = Label("//python/private:stamp"), + ), +} + +_requirement_attrs = { + "extra_requires": attr.string_list_dict( + doc = "List of optional requirements for this package", + ), + "requires": attr.string_list( + doc = ("List of requirements for this package. See the section on " + + "[Declaring required dependency](https://setuptools.readthedocs.io/en/latest/userguide/dependency_management.html#declaring-dependencies) " + + "for details and examples of the format of this argument."), + ), +} + +_entrypoint_attrs = { + "console_scripts": attr.string_dict( + doc = """\ +Deprecated console_script entry points, e.g. `{'main': 'examples.wheel.main:main'}`. + +Deprecated: prefer the `entry_points` attribute, which supports `console_scripts` as well as other entry points. +""", + ), + "entry_points": attr.string_list_dict( + doc = """\ +entry_points, e.g. `{'console_scripts': ['main = examples.wheel.main:main']}`. +""", + ), +} + +_other_attrs = { + "author": attr.string( + doc = "A string specifying the author of the package.", + default = "", + ), + "author_email": attr.string( + doc = "A string specifying the email address of the package author.", + default = "", + ), + "classifiers": attr.string_list( + doc = "A list of strings describing the categories for the package. For valid classifiers see https://pypi.org/classifiers", + ), + "description_file": attr.label( + doc = "A file containing text describing the package.", + allow_single_file = True, + ), + "extra_distinfo_files": attr.label_keyed_string_dict( + doc = "Extra files to add to distinfo directory in the archive.", + allow_files = True, + ), + "homepage": attr.string( + doc = "A string specifying the URL for the package homepage.", + default = "", + ), + "license": attr.string( + doc = "A string specifying the license of the package.", + default = "", + ), + "python_requires": attr.string( + doc = ( + "Python versions required by this distribution, e.g. '>=3.5,<3.7'" + ), + default = "", + ), + "strip_path_prefixes": attr.string_list( + default = [], + doc = "path prefixes to strip from files added to the generated package", + ), +} + +def _escape_filename_segment(segment): + """Escape a segment of the wheel filename. + + See https://www.python.org/dev/peps/pep-0427/#escaping-and-unicode + """ + + # TODO: this is wrong, isalnum replaces non-ascii letters, while we should + # not replace them. + # TODO: replace this with a regexp once starlark supports them. + escaped = "" + for character in segment.elems(): + # isalnum doesn't handle unicode characters properly. + if character.isalnum() or character == ".": + escaped += character + elif not escaped.endswith("_"): + escaped += "_" + return escaped + +def _replace_make_variables(flag, ctx): + """Replace $(VERSION) etc make variables in flag""" + if "$" in flag: + for varname, varsub in ctx.var.items(): + flag = flag.replace("$(%s)" % varname, varsub) + return flag + +def _input_file_to_arg(input_file): + """Converts a File object to string for --input_file argument to wheelmaker""" + return "%s;%s" % (py_package_lib.path_inside_wheel(input_file), input_file.path) + +def _py_wheel_impl(ctx): + abi = _replace_make_variables(ctx.attr.abi, ctx) + python_tag = _replace_make_variables(ctx.attr.python_tag, ctx) + version = _replace_make_variables(ctx.attr.version, ctx) + + outfile = ctx.actions.declare_file("-".join([ + _escape_filename_segment(ctx.attr.distribution), + _escape_filename_segment(version), + _escape_filename_segment(python_tag), + _escape_filename_segment(abi), + _escape_filename_segment(ctx.attr.platform), + ]) + ".whl") + + name_file = ctx.actions.declare_file(ctx.label.name + ".name") + + inputs_to_package = depset( + direct = ctx.files.deps, + ) + + # Inputs to this rule which are not to be packaged. + # Currently this is only the description file (if used). + other_inputs = [] + + # Wrap the inputs into a file to reduce command line length. + packageinputfile = ctx.actions.declare_file(ctx.attr.name + "_target_wrapped_inputs.txt") + content = "" + for input_file in inputs_to_package.to_list(): + content += _input_file_to_arg(input_file) + "\n" + ctx.actions.write(output = packageinputfile, content = content) + other_inputs.append(packageinputfile) + + args = ctx.actions.args() + args.add("--name", ctx.attr.distribution) + args.add("--version", version) + args.add("--python_tag", python_tag) + args.add("--abi", abi) + args.add("--platform", ctx.attr.platform) + args.add("--out", outfile) + args.add("--name_file", name_file) + args.add_all(ctx.attr.strip_path_prefixes, format_each = "--strip_path_prefix=%s") + + # Pass workspace status files if stamping is enabled + if is_stamping_enabled(ctx.attr): + args.add("--volatile_status_file", ctx.version_file) + args.add("--stable_status_file", ctx.info_file) + other_inputs.extend([ctx.version_file, ctx.info_file]) + + args.add("--input_file_list", packageinputfile) + + # Note: Description file and version are not embedded into metadata.txt yet, + # it will be done later by wheelmaker script. + metadata_file = ctx.actions.declare_file(ctx.attr.name + ".metadata.txt") + metadata_contents = ["Metadata-Version: 2.1"] + metadata_contents.append("Name: %s" % ctx.attr.distribution) + + if ctx.attr.author: + metadata_contents.append("Author: %s" % ctx.attr.author) + if ctx.attr.author_email: + metadata_contents.append("Author-email: %s" % ctx.attr.author_email) + if ctx.attr.homepage: + metadata_contents.append("Home-page: %s" % ctx.attr.homepage) + if ctx.attr.license: + metadata_contents.append("License: %s" % ctx.attr.license) + + for c in ctx.attr.classifiers: + metadata_contents.append("Classifier: %s" % c) + + if ctx.attr.python_requires: + metadata_contents.append("Requires-Python: %s" % ctx.attr.python_requires) + for requirement in ctx.attr.requires: + metadata_contents.append("Requires-Dist: %s" % requirement) + + for option, option_requirements in sorted(ctx.attr.extra_requires.items()): + metadata_contents.append("Provides-Extra: %s" % option) + for requirement in option_requirements: + metadata_contents.append( + "Requires-Dist: %s; extra == '%s'" % (requirement, option), + ) + ctx.actions.write( + output = metadata_file, + content = "\n".join(metadata_contents) + "\n", + ) + other_inputs.append(metadata_file) + args.add("--metadata_file", metadata_file) + + # Merge console_scripts into entry_points. + entrypoints = dict(ctx.attr.entry_points) # Copy so we can mutate it + if ctx.attr.console_scripts: + # Copy a console_scripts group that may already exist, so we can mutate it. + console_scripts = list(entrypoints.get("console_scripts", [])) + entrypoints["console_scripts"] = console_scripts + for name, ref in ctx.attr.console_scripts.items(): + console_scripts.append("{name} = {ref}".format(name = name, ref = ref)) + + # If any entry_points are provided, construct the file here and add it to the files to be packaged. + # see: https://packaging.python.org/specifications/entry-points/ + if entrypoints: + lines = [] + for group, entries in sorted(entrypoints.items()): + if lines: + # Blank line between groups + lines.append("") + lines.append("[{group}]".format(group = group)) + lines += sorted(entries) + entry_points_file = ctx.actions.declare_file(ctx.attr.name + "_entry_points.txt") + content = "\n".join(lines) + ctx.actions.write(output = entry_points_file, content = content) + other_inputs.append(entry_points_file) + args.add("--entry_points_file", entry_points_file) + + if ctx.attr.description_file: + description_file = ctx.file.description_file + args.add("--description_file", description_file) + other_inputs.append(description_file) + + for target, filename in ctx.attr.extra_distinfo_files.items(): + target_files = target.files.to_list() + if len(target_files) != 1: + fail( + "Multi-file target listed in extra_distinfo_files %s", + filename, + ) + other_inputs.extend(target_files) + args.add( + "--extra_distinfo_file", + filename + ";" + target_files[0].path, + ) + + ctx.actions.run( + inputs = depset(direct = other_inputs, transitive = [inputs_to_package]), + outputs = [outfile, name_file], + arguments = [args], + executable = ctx.executable._wheelmaker, + progress_message = "Building wheel {}".format(ctx.label), + ) + return [ + DefaultInfo( + files = depset([outfile]), + runfiles = ctx.runfiles(files = [outfile]), + ), + PyWheelInfo( + wheel = outfile, + name_file = name_file, + ), + ] + +def _concat_dicts(*dicts): + result = {} + for d in dicts: + result.update(d) + return result + +py_wheel_lib = struct( + implementation = _py_wheel_impl, + attrs = _concat_dicts( + { + "deps": attr.label_list( + doc = """\ +Targets to be included in the distribution. + +The targets to package are usually `py_library` rules or filesets (for packaging data files). + +Note it's usually better to package `py_library` targets and use +`entry_points` attribute to specify `console_scripts` than to package +`py_binary` rules. `py_binary` targets would wrap a executable script that +tries to locate `.runfiles` directory which is not packaged in the wheel. +""", + ), + "_wheelmaker": attr.label( + executable = True, + cfg = "exec", + default = "//tools:wheelmaker", + ), + }, + _distribution_attrs, + _requirement_attrs, + _entrypoint_attrs, + _other_attrs, + ), +) + +py_wheel = rule( + implementation = py_wheel_lib.implementation, + doc = """\ +Internal rule used by the [py_wheel macro](/docs/packaging.md#py_wheel). + +These intentionally have the same name to avoid sharp edges with Bazel macros. +For example, a `bazel query` for a user's `py_wheel` macro expands to `py_wheel` targets, +in the way they expect. +""", + attrs = py_wheel_lib.attrs, +) diff --git a/python/private/reexports.bzl b/python/private/reexports.bzl index 6ad9e0cdcc..a300a20365 100644 --- a/python/private/reexports.bzl +++ b/python/private/reexports.bzl @@ -37,18 +37,6 @@ different name. Then we can load it from defs.bzl and export it there under the original name. """ -# The implementation of the macros and tagging mechanism follows the example -# set by rules_cc and rules_java. - -_MIGRATION_TAG = "__PYTHON_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__" - -def _add_tags(attrs): - if "tags" in attrs and attrs["tags"] != None: - attrs["tags"] = attrs["tags"] + [_MIGRATION_TAG] - else: - attrs["tags"] = [_MIGRATION_TAG] - return attrs - # Don't use underscore prefix, since that would make the symbol local to this # file only. Use a non-conventional name to emphasize that this is not a public # symbol. @@ -57,43 +45,3 @@ internal_PyInfo = PyInfo # buildifier: disable=name-conventions internal_PyRuntimeInfo = PyRuntimeInfo - -def py_library(**attrs): - """See the Bazel core [py_library](https://docs.bazel.build/versions/master/be/python.html#py_library) documentation. - - Args: - **attrs: Rule attributes - """ - - # buildifier: disable=native-python - native.py_library(**_add_tags(attrs)) - -def py_binary(**attrs): - """See the Bazel core [py_binary](https://docs.bazel.build/versions/master/be/python.html#py_binary) documentation. - - Args: - **attrs: Rule attributes - """ - - # buildifier: disable=native-python - native.py_binary(**_add_tags(attrs)) - -def py_test(**attrs): - """See the Bazel core [py_test](https://docs.bazel.build/versions/master/be/python.html#py_test) documentation. - - Args: - **attrs: Rule attributes - """ - - # buildifier: disable=native-python - native.py_test(**_add_tags(attrs)) - -def py_runtime(**attrs): - """See the Bazel core [py_runtime](https://docs.bazel.build/versions/master/be/python.html#py_runtime) documentation. - - Args: - **attrs: Rule attributes - """ - - # buildifier: disable=native-python - native.py_runtime(**_add_tags(attrs)) diff --git a/python/private/stamp.bzl b/python/private/stamp.bzl index 6d0e54977c..6bc0cd9d23 100644 --- a/python/private/stamp.bzl +++ b/python/private/stamp.bzl @@ -1,3 +1,17 @@ +# 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. + """A small utility module dedicated to detecting whether or not the `--stamp` flag is enabled This module can be removed likely after the following PRs ar addressed: diff --git a/python/private/toolchains_repo.bzl b/python/private/toolchains_repo.bzl index 14576bcf58..9bed73e55c 100644 --- a/python/private/toolchains_repo.bzl +++ b/python/private/toolchains_repo.bzl @@ -31,9 +31,18 @@ load( "WINDOWS_NAME", ) +def get_repository_name(repository_workspace): + dummy_label = "//:_" + return str(repository_workspace.relative(dummy_label))[:-len(dummy_label)] or "@" + def _toolchains_repo_impl(rctx): + python_version_constraint = "{rules_python}//python/config_settings:is_python_{python_version}".format( + rules_python = get_repository_name(rctx.attr._rules_python_workspace), + python_version = rctx.attr.python_version, + ) + build_content = """\ -# Generated by toolchains_repo.bzl +# Generated by python/private/toolchains_repo.bzl # # These can be registered in the workspace file or passed to --extra_toolchains # flag. By default all these toolchains are registered by the @@ -48,15 +57,18 @@ def _toolchains_repo_impl(rctx): # for executing build actions. toolchain( name = "{platform}_toolchain", - exec_compatible_with = {compatible_with}, + target_compatible_with = {compatible_with}, + target_settings = ["{python_version_constraint}"] if {set_python_version_constraint} else [], toolchain = "@{user_repository_name}_{platform}//:python_runtimes", toolchain_type = "@bazel_tools//tools/python:toolchain_type", ) """.format( - platform = platform, + compatible_with = meta.compatible_with, name = rctx.attr.name, + platform = platform, + python_version_constraint = python_version_constraint, + set_python_version_constraint = rctx.attr.set_python_version_constraint, user_repository_name = rctx.attr.user_repository_name, - compatible_with = meta.compatible_with, ) rctx.file("BUILD.bazel", build_content) @@ -66,43 +78,41 @@ toolchains_repo = repository_rule( doc = "Creates a repository with toolchain definitions for all known platforms " + "which can be registered or selected.", attrs = { + "python_version": attr.string(doc = "The Python version."), + "set_python_version_constraint": attr.bool(doc = "if target_compatible_with for the toolchain should set the version constraint"), "user_repository_name": attr.string(doc = "what the user chose for the base name"), + "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")), }, ) -def _resolved_interpreter_os_alias_impl(rctx): - (os_name, arch) = _host_os_arch(rctx) +def _toolchain_aliases_impl(rctx): + (os_name, arch) = get_host_os_arch(rctx) - host_platform = None - for platform, meta in PLATFORMS.items(): - if meta.os_name == os_name and meta.arch == arch: - host_platform = platform - if not host_platform: - fail("No platform declared for host OS {} on arch {}".format(os_name, arch)) + host_platform = get_host_platform(os_name, arch) is_windows = (os_name == WINDOWS_NAME) python3_binary_path = "python.exe" if is_windows else "bin/python3" # Base BUILD file for this repository. build_contents = """\ -# Generated by python/repositories.bzl +# Generated by python/private/toolchains_repo.bzl package(default_visibility = ["//visibility:public"]) +load("@rules_python//python:versions.bzl", "PLATFORMS", "gen_python_config_settings") +gen_python_config_settings() exports_files(["defs.bzl"]) -alias(name = "files", actual = "@{py_repository}_{host_platform}//:files") -alias(name = "includes", actual = "@{py_repository}_{host_platform}//:includes") -alias(name = "libpython", actual = "@{py_repository}_{host_platform}//:libpython") -alias(name = "py3_runtime", actual = "@{py_repository}_{host_platform}//:py3_runtime") -alias(name = "python_headers", actual = "@{py_repository}_{host_platform}//:python_headers") -alias(name = "python_runtimes", actual = "@{py_repository}_{host_platform}//:python_runtimes") -alias(name = "python3", actual = "@{py_repository}_{host_platform}//:{python3_binary_path}") +alias(name = "files", actual = select({{":" + item: "@{py_repository}_" + item + "//:files" for item in PLATFORMS.keys()}})) +alias(name = "includes", actual = select({{":" + item: "@{py_repository}_" + item + "//:includes" for item in PLATFORMS.keys()}})) +alias(name = "libpython", actual = select({{":" + item: "@{py_repository}_" + item + "//:libpython" for item in PLATFORMS.keys()}})) +alias(name = "py3_runtime", actual = select({{":" + item: "@{py_repository}_" + item + "//:py3_runtime" for item in PLATFORMS.keys()}})) +alias(name = "python_headers", actual = select({{":" + item: "@{py_repository}_" + item + "//:python_headers" for item in PLATFORMS.keys()}})) +alias(name = "python_runtimes", actual = select({{":" + item: "@{py_repository}_" + item + "//:python_runtimes" for item in PLATFORMS.keys()}})) +alias(name = "python3", actual = select({{":" + item: "@{py_repository}_" + item + "//:" + ("python.exe" if "windows" in item else "bin/python3") for item in PLATFORMS.keys()}})) """.format( py_repository = rctx.attr.user_repository_name, - host_platform = host_platform, - python3_binary_path = python3_binary_path, ) if not is_windows: build_contents += """\ -alias(name = "pip", actual = "@{py_repository}_{host_platform}//:bin/pip") +alias(name = "pip", actual = select({{":" + item: "@{py_repository}_" + item + "//:python_runtimes" for item in PLATFORMS.keys() if "windows" not in item}})) """.format( py_repository = rctx.attr.user_repository_name, host_platform = host_platform, @@ -112,29 +122,135 @@ alias(name = "pip", actual = "@{py_repository}_{host_platform}//:bin # Expose a Starlark file so rules can know what host platform we used and where to find an interpreter # when using repository_ctx.path, which doesn't understand aliases. rctx.file("defs.bzl", content = """\ -# Generated by python/repositories.bzl +# Generated by python/private/toolchains_repo.bzl + +load("{rules_python}//python/config_settings:transition.bzl", _py_binary = "py_binary", _py_test = "py_test") +load("{rules_python}//python:pip.bzl", _compile_pip_requirements = "compile_pip_requirements") + host_platform = "{host_platform}" interpreter = "@{py_repository}_{host_platform}//:{python3_binary_path}" + +def py_binary(name, **kwargs): + return _py_binary( + name = name, + python_version = "{python_version}", + **kwargs + ) + +def py_test(name, **kwargs): + return _py_test( + name = name, + python_version = "{python_version}", + **kwargs + ) + +def compile_pip_requirements(name, **kwargs): + return _compile_pip_requirements( + name = name, + py_binary = py_binary, + py_test = py_test, + **kwargs + ) + """.format( - py_repository = rctx.attr.user_repository_name, host_platform = host_platform, + py_repository = rctx.attr.user_repository_name, + python_version = rctx.attr.python_version, python3_binary_path = python3_binary_path, + rules_python = get_repository_name(rctx.attr._rules_python_workspace), )) -resolved_interpreter_os_alias = repository_rule( - _resolved_interpreter_os_alias_impl, +toolchain_aliases = repository_rule( + _toolchain_aliases_impl, doc = """Creates a repository with a shorter name meant for the host platform, which contains a BUILD.bazel file declaring aliases to the host platform's targets. """, attrs = { + "python_version": attr.string(doc = "The Python version."), "user_repository_name": attr.string( mandatory = True, doc = "The base name for all created repositories, like 'python38'.", ), + "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")), + }, +) + +def _multi_toolchain_aliases_impl(rctx): + rules_python = rctx.attr._rules_python_workspace.workspace_name + + for python_version, repository_name in rctx.attr.python_versions.items(): + file = "{}/defs.bzl".format(python_version) + rctx.file(file, content = """\ +# Generated by python/private/toolchains_repo.bzl + +load( + "@{repository_name}//:defs.bzl", + _compile_pip_requirements = "compile_pip_requirements", + _host_platform = "host_platform", + _interpreter = "interpreter", + _py_binary = "py_binary", + _py_test = "py_test", +) + +compile_pip_requirements = _compile_pip_requirements +host_platform = _host_platform +interpreter = _interpreter +py_binary = _py_binary +py_test = _py_test +""".format( + repository_name = repository_name, + )) + rctx.file("{}/BUILD.bazel".format(python_version), "") + + pip_bzl = """\ +# Generated by python/private/toolchains_repo.bzl + +load("@{rules_python}//python:pip.bzl", "pip_parse", _multi_pip_parse = "multi_pip_parse") + +def multi_pip_parse(name, requirements_lock, **kwargs): + return _multi_pip_parse( + name = name, + python_versions = {python_versions}, + requirements_lock = requirements_lock, + **kwargs + ) + +""".format( + python_versions = rctx.attr.python_versions.keys(), + rules_python = rules_python, + ) + rctx.file("pip.bzl", content = pip_bzl) + rctx.file("BUILD.bazel", "") + +multi_toolchain_aliases = repository_rule( + _multi_toolchain_aliases_impl, + attrs = { + "python_versions": attr.string_dict(doc = "The Python versions."), + "_rules_python_workspace": attr.label(default = Label("//:WORKSPACE")), }, ) -def _host_os_arch(rctx): +def sanitize_platform_name(platform): + return platform.replace("-", "_") + +def get_host_platform(os_name, arch): + """Gets the host platform. + + Args: + os_name: the host OS name. + arch: the host arch. + Returns: + The host platform. + """ + host_platform = None + for platform, meta in PLATFORMS.items(): + if meta.os_name == os_name and meta.arch == arch: + host_platform = platform + if not host_platform: + fail("No platform declared for host OS {} on arch {}".format(os_name, arch)) + return host_platform + +def get_host_os_arch(rctx): """Infer the host OS name and arch from a repository context. Args: diff --git a/python/private/util.bzl b/python/private/util.bzl new file mode 100644 index 0000000000..f0d43737a0 --- /dev/null +++ b/python/private/util.bzl @@ -0,0 +1,60 @@ +"""Functionality shared by multiple pieces of code.""" + +load("@bazel_skylib//lib:types.bzl", "types") + +def copy_propagating_kwargs(from_kwargs, into_kwargs = None): + """Copies args that must be compatible between two targets with a dependency relationship. + + This is intended for when one target depends on another, so they must have + compatible settings such as `testonly` and `compatible_with`. This usually + happens when a macro generates multiple targets, some of which depend + on one another, so their settings must be compatible. + + Args: + from_kwargs: keyword args dict whose common kwarg will be copied. + into_kwargs: optional keyword args dict that the values from `from_kwargs` + will be copied into. The values in this dict will take precedence + over the ones in `from_kwargs` (i.e., if this has `testonly` already + set, then it won't be overwritten). + NOTE: THIS WILL BE MODIFIED IN-PLACE. + + Returns: + Keyword args to use for the depender target derived from the dependency + target. If `into_kwargs` was passed in, then that same object is + returned; this is to facilitate easy `**` expansion. + """ + if into_kwargs == None: + into_kwargs = {} + + # Include tags because people generally expect tags to propagate. + for attr in ("testonly", "tags", "compatible_with", "restricted_to"): + if attr in from_kwargs and attr not in into_kwargs: + into_kwargs[attr] = from_kwargs[attr] + return into_kwargs + +# The implementation of the macros and tagging mechanism follows the example +# set by rules_cc and rules_java. + +_MIGRATION_TAG = "__PYTHON_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__" + +def add_migration_tag(attrs): + """Add a special tag to `attrs` to aid migration off native rles. + + Args: + attrs: dict of keyword args. The `tags` key will be modified in-place. + + Returns: + The same `attrs` object, but modified. + """ + if "tags" in attrs and attrs["tags"] != None: + tags = attrs["tags"] + + # Preserve the input type: this allows a test verifying the underlying + # rule can accept the tuple for the tags argument. + if types.is_tuple(tags): + attrs["tags"] = tags + (_MIGRATION_TAG,) + else: + attrs["tags"] = tags + [_MIGRATION_TAG] + else: + attrs["tags"] = [_MIGRATION_TAG] + return attrs diff --git a/python/proto.bzl b/python/proto.bzl new file mode 100644 index 0000000000..3f455aee58 --- /dev/null +++ b/python/proto.bzl @@ -0,0 +1,21 @@ +# Copyright 2022 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. + +""" +Python proto library. +""" + +load("//python/private/proto:py_proto_library.bzl", _py_proto_library = "py_proto_library") + +py_proto_library = _py_proto_library diff --git a/python/py_binary.bzl b/python/py_binary.bzl new file mode 100644 index 0000000000..6b6f7e0f8a --- /dev/null +++ b/python/py_binary.bzl @@ -0,0 +1,31 @@ +# 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. + +"""Public entry point for py_binary.""" + +load("//python/private:util.bzl", "add_migration_tag") + +def py_binary(**attrs): + """See the Bazel core [py_binary](https://docs.bazel.build/versions/master/be/python.html#py_binary) documentation. + + Args: + **attrs: Rule attributes + """ + if attrs.get("python_version") == "PY2": + fail("Python 2 is no longer supported: https://github.com/bazelbuild/rules_python/issues/886") + if attrs.get("srcs_version") in ("PY2", "PY2ONLY"): + fail("Python 2 is no longer supported: https://github.com/bazelbuild/rules_python/issues/886") + + # buildifier: disable=native-python + native.py_binary(**add_migration_tag(attrs)) diff --git a/python/py_cc_link_params_info.bzl b/python/py_cc_link_params_info.bzl new file mode 100644 index 0000000000..0ebd64b208 --- /dev/null +++ b/python/py_cc_link_params_info.bzl @@ -0,0 +1,3 @@ +"""Public entry point for PyCcLinkParamsInfo.""" + +PyCcLinkParamsInfo = PyCcLinkParamsProvider diff --git a/python/py_import.bzl b/python/py_import.bzl new file mode 100644 index 0000000000..c9284121d6 --- /dev/null +++ b/python/py_import.bzl @@ -0,0 +1,67 @@ +# 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. + +"""Public entry point for py_import rule.""" + +load(":py_info.bzl", "PyInfo") + +def _py_import_impl(ctx): + # See https://github.com/bazelbuild/bazel/blob/0.24.0/src/main/java/com/google/devtools/build/lib/bazel/rules/python/BazelPythonSemantics.java#L104 . + import_paths = [ + "/".join([ctx.workspace_name, x.short_path]) + for x in ctx.files.srcs + ] + + return [ + DefaultInfo( + default_runfiles = ctx.runfiles(ctx.files.srcs, collect_default = True), + ), + PyInfo( + transitive_sources = depset(transitive = [ + dep[PyInfo].transitive_sources + for dep in ctx.attr.deps + ]), + imports = depset(direct = import_paths, transitive = [ + dep[PyInfo].imports + for dep in ctx.attr.deps + ]), + ), + ] + +py_import = rule( + doc = """This rule allows the use of Python packages as dependencies. + + It imports the given `.egg` file(s), which might be checked in source files, + fetched externally as with `http_file`, or produced as outputs of other rules. + + It may be used like a `py_library`, in the `deps` of other Python rules. + + This is similar to [java_import](https://docs.bazel.build/versions/master/be/java.html#java_import). + """, + implementation = _py_import_impl, + attrs = { + "deps": attr.label_list( + doc = "The list of other libraries to be linked in to the " + + "binary target.", + providers = [PyInfo], + ), + "srcs": attr.label_list( + doc = "The list of Python package files provided to Python targets " + + "that depend on this target. Note that currently only the .egg " + + "format is accepted. For .whl files, try the whl_library rule. " + + "We accept contributions to extend py_import to handle .whl.", + allow_files = [".egg"], + ), + }, +) diff --git a/python/py_info.bzl b/python/py_info.bzl new file mode 100644 index 0000000000..2c3997dee2 --- /dev/null +++ b/python/py_info.bzl @@ -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. + +"""Public entry point for PyInfo.""" + +load("//python/private:reexports.bzl", "internal_PyInfo") + +PyInfo = internal_PyInfo diff --git a/python/py_library.bzl b/python/py_library.bzl new file mode 100644 index 0000000000..d54cbb2958 --- /dev/null +++ b/python/py_library.bzl @@ -0,0 +1,29 @@ +# 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. + +"""Public entry point for py_library.""" + +load("//python/private:util.bzl", "add_migration_tag") + +def py_library(**attrs): + """See the Bazel core [py_library](https://docs.bazel.build/versions/master/be/python.html#py_library) documentation. + + Args: + **attrs: Rule attributes + """ + if attrs.get("srcs_version") in ("PY2", "PY2ONLY"): + fail("Python 2 is no longer supported: https://github.com/bazelbuild/rules_python/issues/886") + + # buildifier: disable=native-python + native.py_library(**add_migration_tag(attrs)) diff --git a/python/py_runtime.bzl b/python/py_runtime.bzl new file mode 100644 index 0000000000..b70f9d4ec4 --- /dev/null +++ b/python/py_runtime.bzl @@ -0,0 +1,29 @@ +# 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. + +"""Public entry point for py_runtime.""" + +load("//python/private:util.bzl", "add_migration_tag") + +def py_runtime(**attrs): + """See the Bazel core [py_runtime](https://docs.bazel.build/versions/master/be/python.html#py_runtime) documentation. + + Args: + **attrs: Rule attributes + """ + if attrs.get("python_version") == "PY2": + fail("Python 2 is no longer supported: see https://github.com/bazelbuild/rules_python/issues/886") + + # buildifier: disable=native-python + native.py_runtime(**add_migration_tag(attrs)) diff --git a/python/py_runtime_info.bzl b/python/py_runtime_info.bzl new file mode 100644 index 0000000000..15598ee903 --- /dev/null +++ b/python/py_runtime_info.bzl @@ -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. + +"""Public entry point for PyRuntimeInfo.""" + +load("//python/private:reexports.bzl", "internal_PyRuntimeInfo") + +PyRuntimeInfo = internal_PyRuntimeInfo diff --git a/python/py_runtime_pair.bzl b/python/py_runtime_pair.bzl new file mode 100644 index 0000000000..951c606f4a --- /dev/null +++ b/python/py_runtime_pair.bzl @@ -0,0 +1,87 @@ +# 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. + +"""Public entry point for py_runtime_pair.""" + +load("@bazel_tools//tools/python:toolchain.bzl", _py_runtime_pair = "py_runtime_pair") + +# NOTE: This doc is copy/pasted from the builtin py_runtime_pair rule so our +# doc generator gives useful API docs. +def py_runtime_pair(name, py2_runtime = None, py3_runtime = None, **attrs): + """A toolchain rule for Python. + + This used to wrap up to two Python runtimes, one for Python 2 and one for Python 3. + However, Python 2 is no longer supported, so it now only wraps a single Python 3 + runtime. + + Usually the wrapped runtimes are declared using the `py_runtime` rule, but any + rule returning a `PyRuntimeInfo` provider may be used. + + This rule returns a `platform_common.ToolchainInfo` provider with the following + schema: + + ```python + platform_common.ToolchainInfo( + py2_runtime = None, + py3_runtime = , + ) + ``` + + Example usage: + + ```python + # In your BUILD file... + + load("@rules_python//python:defs.bzl", "py_runtime_pair") + + py_runtime( + name = "my_py3_runtime", + interpreter_path = "/system/python3", + python_version = "PY3", + ) + + py_runtime_pair( + name = "my_py_runtime_pair", + py3_runtime = ":my_py3_runtime", + ) + + toolchain( + name = "my_toolchain", + target_compatible_with = <...>, + toolchain = ":my_py_runtime_pair", + toolchain_type = "@rules_python//python:toolchain_type", + ) + ``` + + ```python + # In your WORKSPACE... + + register_toolchains("//my_pkg:my_toolchain") + ``` + + Args: + name: str, the name of the target + py2_runtime: optional Label; must be unset or None; an error is raised + otherwise. + py3_runtime: Label; a target with `PyRuntimeInfo` for Python 3. + **attrs: Extra attrs passed onto the native rule + """ + if attrs.get("py2_runtime"): + fail("PYthon 2 is no longer supported: see https://github.com/bazelbuild/rules_python/issues/886") + _py_runtime_pair( + name = name, + py2_runtime = py2_runtime, + py3_runtime = py3_runtime, + **attrs + ) diff --git a/python/py_test.bzl b/python/py_test.bzl new file mode 100644 index 0000000000..09580c01c4 --- /dev/null +++ b/python/py_test.bzl @@ -0,0 +1,31 @@ +# 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. + +"""Public entry point for py_test.""" + +load("//python/private:util.bzl", "add_migration_tag") + +def py_test(**attrs): + """See the Bazel core [py_test](https://docs.bazel.build/versions/master/be/python.html#py_test) documentation. + + Args: + **attrs: Rule attributes + """ + if attrs.get("python_version") == "PY2": + fail("Python 2 is no longer supported: https://github.com/bazelbuild/rules_python/issues/886") + if attrs.get("srcs_version") in ("PY2", "PY2ONLY"): + fail("Python 2 is no longer supported: https://github.com/bazelbuild/rules_python/issues/886") + + # buildifier: disable=native-python + native.py_test(**add_migration_tag(attrs)) diff --git a/python/repositories.bzl b/python/repositories.bzl index 1441432547..2429d7e026 100644 --- a/python/repositories.bzl +++ b/python/repositories.bzl @@ -17,19 +17,41 @@ For historic reasons, pip_repositories() is defined in //python:pip.bzl. """ -load("//python/private:toolchains_repo.bzl", "resolved_interpreter_os_alias", "toolchains_repo") +load("@bazel_tools//tools/build_defs/repo:http.bzl", _http_archive = "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") +load("//python/private:coverage_deps.bzl", "coverage_dep") +load( + "//python/private:toolchains_repo.bzl", + "multi_toolchain_aliases", + "toolchain_aliases", + "toolchains_repo", +) load( ":versions.bzl", "DEFAULT_RELEASE_BASE_URL", "MINOR_MAPPING", "PLATFORMS", "TOOL_VERSIONS", - "get_release_url", + "get_release_info", ) +def http_archive(**kwargs): + maybe(_http_archive, **kwargs) + def py_repositories(): - # buildifier: disable=print - print("py_repositories is a no-op and is deprecated. You can remove this from your WORKSPACE file") + """Runtime dependencies that users must install. + + This function should be loaded and called in the user's WORKSPACE. + With bzlmod enabled, this function is not needed since MODULE.bazel handles transitive deps. + """ + http_archive( + name = "bazel_skylib", + sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz", + ], + ) ######## # Remaining content of the file is only used to support toolchains. @@ -37,15 +59,54 @@ def py_repositories(): STANDALONE_INTERPRETER_FILENAME = "STANDALONE_INTERPRETER" +def get_interpreter_dirname(rctx, python_interpreter_target): + """Get a python interpreter target dirname. + + Args: + rctx (repository_ctx): The repository rule's context object. + python_interpreter_target (Target): A target representing a python interpreter. + + Returns: + str: The Python interpreter directory. + """ + + return rctx.path(Label("{}//:WORKSPACE".format(str(python_interpreter_target).split("//")[0]))).dirname + +def is_standalone_interpreter(rctx, python_interpreter_target): + """Query a python interpreter target for whether or not it's a rules_rust provided toolchain + + Args: + rctx (repository_ctx): The repository rule's context object. + python_interpreter_target (Target): A target representing a python interpreter. + + Returns: + bool: Whether or not the target is from a rules_python generated toolchain. + """ + + # Only update the location when using a hermetic toolchain. + if not python_interpreter_target: + return False + + # This is a rules_python provided toolchain. + return rctx.execute([ + "ls", + "{}/{}".format( + get_interpreter_dirname(rctx, python_interpreter_target), + STANDALONE_INTERPRETER_FILENAME, + ), + ]).return_code == 0 + def _python_repository_impl(rctx): if rctx.attr.distutils and rctx.attr.distutils_content: fail("Only one of (distutils, distutils_content) should be set.") + if bool(rctx.attr.url) == bool(rctx.attr.urls): + fail("Exactly one of (url, urls) must be set.") platform = rctx.attr.platform python_version = rctx.attr.python_version python_short_version = python_version.rpartition(".")[0] release_filename = rctx.attr.release_filename - url = rctx.attr.url + url = rctx.attr.urls or [rctx.attr.url] if release_filename.endswith(".zst"): rctx.download( @@ -97,6 +158,12 @@ def _python_repository_impl(rctx): stripPrefix = rctx.attr.strip_prefix, ) + patches = rctx.attr.patches + if patches: + for patch in patches: + # Should take the strip as an attr, but this is fine for the moment + rctx.patch(patch, strip = 1) + # Write distutils.cfg to the Python installation. if "windows" in rctx.os.name: distutils_path = "Lib/distutils/distutils.cfg" @@ -108,16 +175,91 @@ def _python_repository_impl(rctx): rctx.file(distutils_path, rctx.attr.distutils_content) # Make the Python installation read-only. - if "windows" not in rctx.os.name: - exec_result = rctx.execute(["chmod", "-R", "ugo-w", "lib"]) - if exec_result.return_code: - fail_msg = "Failed to make interpreter installation read-only. 'chmod' error msg: {}".format( - exec_result.stderr, - ) - fail(fail_msg) + if not rctx.attr.ignore_root_user_error: + if "windows" not in rctx.os.name: + lib_dir = "lib" if "windows" not in platform else "Lib" + exec_result = rctx.execute(["chmod", "-R", "ugo-w", lib_dir]) + if exec_result.return_code != 0: + fail_msg = "Failed to make interpreter installation read-only. 'chmod' error msg: {}".format( + exec_result.stderr, + ) + fail(fail_msg) + exec_result = rctx.execute(["touch", "{}/.test".format(lib_dir)]) + if exec_result.return_code == 0: + exec_result = rctx.execute(["id", "-u"]) + if exec_result.return_code != 0: + fail("Could not determine current user ID. 'id -u' error msg: {}".format( + exec_result.stderr, + )) + uid = int(exec_result.stdout.strip()) + if uid == 0: + fail("The current user is root, please run as non-root when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.") + else: + fail("The current user has CAP_DAC_OVERRIDE set, please drop this capability when using the hermetic Python interpreter. See https://github.com/bazelbuild/rules_python/pull/713.") python_bin = "python.exe" if ("windows" in platform) else "bin/python3" + glob_include = [] + glob_exclude = [ + "**/* *", # Bazel does not support spaces in file names. + # Unused shared libraries. `python` executable and the `:libpython` target + # depend on `libpython{python_version}.so.1.0`. + "lib/libpython{python_version}.so", + # static libraries + "lib/**/*.a", + # tests for the standard libraries. + "lib/python{python_version}/**/test/**", + "lib/python{python_version}/**/tests/**", + ] + + if rctx.attr.ignore_root_user_error: + glob_exclude += [ + # These pycache files are created on first use of the associated python files. + # Exclude them from the glob because otherwise between the first time and second time a python toolchain is used," + # the definition of this filegroup will change, and depending rules will get invalidated." + # See https://github.com/bazelbuild/rules_python/issues/1008 for unconditionally adding these to toolchains so we can stop ignoring them." + "**/__pycache__/*.pyc", + "**/__pycache__/*.pyo", + ] + + if "windows" in platform: + glob_include += [ + "*.exe", + "*.dll", + "bin/**", + "DLLs/**", + "extensions/**", + "include/**", + "Lib/**", + "libs/**", + "Scripts/**", + "share/**", + ] + else: + glob_include += [ + "bin/**", + "extensions/**", + "include/**", + "lib/**", + "libs/**", + "share/**", + ] + + if rctx.attr.coverage_tool: + if "windows" in rctx.os.name: + coverage_tool = None + else: + coverage_tool = '"{}"'.format(rctx.attr.coverage_tool) + + coverage_attr_text = """\ + coverage_tool = select({{ + ":coverage_enabled": {coverage_tool}, + "//conditions:default": None + }}), +""".format(coverage_tool = coverage_tool) + else: + coverage_attr_text = " # coverage_tool attribute not supported by this Bazel version" + build_content = """\ # Generated by python/repositories.bzl @@ -128,24 +270,19 @@ package(default_visibility = ["//visibility:public"]) filegroup( name = "files", srcs = glob( - include = [ - "*.exe", - "*.dll", - "bin/**", - "DLLs/**", - "extensions/**", - "include/**", - "lib/**", - "libs/**", - "Scripts/**", - "share/**", - ], - exclude = [ - "**/* *", # Bazel does not support spaces in file names. - ], + include = {glob_include}, + # Platform-agnostic filegroup can't match on all patterns. + allow_empty = True, + exclude = {glob_exclude}, ), ) +cc_import( + name = "interface", + interface_library = "libs/python{python_version_nodot}.lib", + system_provided = True, +) + filegroup( name = "includes", srcs = glob(["include/**/*.h"]), @@ -153,6 +290,10 @@ filegroup( cc_library( name = "python_headers", + deps = select({{ + "@bazel_tools//src/conditions:windows": [":interface"], + "//conditions:default": None, + }}), hdrs = [":includes"], includes = [ "include", @@ -161,21 +302,30 @@ cc_library( ], ) -cc_import( +cc_library( name = "libpython", hdrs = [":includes"], - shared_library = select({{ - "@platforms//os:windows": "python3.dll", - "@platforms//os:macos": "lib/libpython{python_version}.dylib", - "@platforms//os:linux": "lib/libpython{python_version}.so.1.0", + srcs = select({{ + "@platforms//os:windows": ["python3.dll", "libs/python{python_version_nodot}.lib"], + "@platforms//os:macos": ["lib/libpython{python_version}.dylib"], + "@platforms//os:linux": ["lib/libpython{python_version}.so", "lib/libpython{python_version}.so.1.0"], }}), ) -exports_files(["{python_path}"]) +exports_files(["python", "{python_path}"]) + +# Used to only download coverage toolchain when the coverage is collected by +# bazel. +config_setting( + name = "coverage_enabled", + values = {{"collect_code_coverage": "true"}}, + visibility = ["//visibility:private"], +) py_runtime( name = "py3_runtime", files = [":files"], +{coverage_attr} interpreter = "{python_path}", python_version = "PY3", ) @@ -186,16 +336,25 @@ py_runtime_pair( py3_runtime = ":py3_runtime", ) """.format( + glob_exclude = repr(glob_exclude), + glob_include = repr(glob_include), python_path = python_bin, python_version = python_short_version, + python_version_nodot = python_short_version.replace(".", ""), + coverage_attr = coverage_attr_text, ) + rctx.delete("python") + rctx.symlink(python_bin, "python") rctx.file(STANDALONE_INTERPRETER_FILENAME, "# File intentionally left blank. Indicates that this is an interpreter repo created by rules_python.") rctx.file("BUILD.bazel", build_content) return { + "coverage_tool": rctx.attr.coverage_tool, "distutils": rctx.attr.distutils, "distutils_content": rctx.attr.distutils_content, + "ignore_root_user_error": rctx.attr.ignore_root_user_error, "name": rctx.attr.name, + "patches": rctx.attr.patches, "platform": platform, "python_version": python_version, "release_filename": release_filename, @@ -208,6 +367,28 @@ python_repository = repository_rule( _python_repository_impl, doc = "Fetches the external tools needed for the Python toolchain.", attrs = { + "coverage_tool": attr.label( + # Mirrors the definition at + # https://github.com/bazelbuild/bazel/blob/master/src/main/starlark/builtins_bzl/common/python/py_runtime_rule.bzl + allow_files = False, + doc = """ +This is a target to use for collecting code coverage information from `py_binary` +and `py_test` targets. + +If set, the target must either produce a single file or be an executable target. +The path to the single file, or the executable if the target is executable, +determines the entry point for the python coverage tool. The target and its +runfiles will be added to the runfiles when coverage is enabled. + +The entry point for the tool must be loadable by a Python interpreter (e.g. a +`.py` or `.pyc` file). It must accept the command line arguments +of coverage.py (https://coverage.readthedocs.io), at least including +the `run` and `lcov` subcommands. + +For more information see the official bazel docs +(https://bazel.build/reference/be/python#py_runtime.coverage_tool). +""", + ), "distutils": attr.label( allow_single_file = True, doc = "A distutils.cfg file to be included in the Python installation. " + @@ -219,6 +400,15 @@ python_repository = repository_rule( "Either distutils or distutils_content can be specified, but not both.", mandatory = False, ), + "ignore_root_user_error": attr.bool( + default = False, + doc = "Whether the check for root should be ignored or not. This causes cache misses with .pyc files.", + mandatory = False, + ), + "patches": attr.label_list( + doc = "A list of patch files to apply to the unpacked interpreter", + mandatory = False, + ), "platform": attr.string( doc = "The platform name for the Python interpreter tarball.", mandatory = True, @@ -238,11 +428,12 @@ python_repository = repository_rule( ), "strip_prefix": attr.string( doc = "A directory prefix to strip from the extracted files.", - mandatory = True, ), "url": attr.string( - doc = "The URL of the interpreter to download", - mandatory = True, + doc = "The URL of the interpreter to download. Exactly one of url and urls must be set.", + ), + "urls": attr.string_list( + doc = "The URL of the interpreter to download. Exactly one of url and urls must be set.", ), "zstd_sha256": attr.string( default = "7c42d56fac126929a6a85dbc73ff1db2411d04f104fae9bdea51305663a83fd0", @@ -263,7 +454,10 @@ def python_register_toolchains( distutils = None, distutils_content = None, register_toolchains = True, + register_coverage_tool = False, + set_python_version_constraint = False, tool_versions = TOOL_VERSIONS, + bzlmod = False, **kwargs): """Convenience macro for users which does typical setup. @@ -280,8 +474,13 @@ def python_register_toolchains( distutils: see the distutils attribute in the python_repository repository rule. distutils_content: see the distutils_content attribute in the python_repository repository rule. register_toolchains: Whether or not to register the downloaded toolchains. + register_coverage_tool: Whether or not to register the downloaded coverage tool to the toolchains. + NOTE: Coverage support using the toolchain is only supported in Bazel 6 and higher. + + set_python_version_constraint: When set to true, target_compatible_with for the toolchains will include a version constraint. tool_versions: a dict containing a mapping of version with SHASUM and platform info. If not supplied, the defaults - in python/versions.bzl will be used + in python/versions.bzl will be used. + bzlmod: Whether this rule is being run under a bzlmod module extension. **kwargs: passed to each python_repositories call. """ base_url = kwargs.pop("base_url", DEFAULT_RELEASE_BASE_URL) @@ -289,12 +488,47 @@ def python_register_toolchains( if python_version in MINOR_MAPPING: python_version = MINOR_MAPPING[python_version] + toolchain_repo_name = "{name}_toolchains".format(name = name) + + # When using unreleased Bazel versions, the version is an empty string + if native.bazel_version: + bazel_major = int(native.bazel_version.split(".")[0]) + if bazel_major < 6: + if register_coverage_tool: + # buildifier: disable=print + print(( + "WARNING: ignoring register_coverage_tool=True when " + + "registering @{name}: Bazel 6+ required, got {version}" + ).format( + name = name, + version = native.bazel_version, + )) + register_coverage_tool = False + for platform in PLATFORMS.keys(): sha256 = tool_versions[python_version]["sha256"].get(platform, None) if not sha256: continue - (release_filename, url, strip_prefix) = get_release_url(platform, python_version, base_url, tool_versions) + (release_filename, urls, strip_prefix, patches) = get_release_info(platform, python_version, base_url, tool_versions) + + # allow passing in a tool version + coverage_tool = None + coverage_tool = tool_versions[python_version].get("coverage_tool", {}).get(platform, None) + if register_coverage_tool and coverage_tool == None: + coverage_tool = coverage_dep( + name = "{name}_{platform}_coverage".format( + name = name, + platform = platform, + ), + python_version = python_version, + platform = platform, + visibility = ["@@{name}_{platform}//:__subpackages__".format( + name = name, + platform = platform, + )], + install = not bzlmod, + ) python_repository( name = "{name}_{platform}".format( @@ -302,27 +536,79 @@ def python_register_toolchains( platform = platform, ), sha256 = sha256, + patches = patches, platform = platform, python_version = python_version, release_filename = release_filename, - url = url, + urls = urls, distutils = distutils, distutils_content = distutils_content, strip_prefix = strip_prefix, + coverage_tool = coverage_tool, **kwargs ) if register_toolchains: - native.register_toolchains("@{name}_toolchains//:{platform}_toolchain".format( - name = name, + native.register_toolchains("@{toolchain_repo_name}//:{platform}_toolchain".format( + toolchain_repo_name = toolchain_repo_name, platform = platform, )) - resolved_interpreter_os_alias( - name = name, + toolchains_repo( + name = toolchain_repo_name, + python_version = python_version, + set_python_version_constraint = set_python_version_constraint, user_repository_name = name, ) - toolchains_repo( - name = "{name}_toolchains".format(name = name), + toolchain_aliases( + name = name, + python_version = python_version, user_repository_name = name, ) + +def python_register_multi_toolchains( + name, + python_versions, + default_version = None, + **kwargs): + """Convenience macro for registering multiple Python toolchains. + + Args: + name: base name for each name in python_register_toolchains call. + python_versions: the Python version. + default_version: the default Python version. If not set, the first version in + python_versions is used. + **kwargs: passed to each python_register_toolchains call. + """ + if len(python_versions) == 0: + fail("python_versions must not be empty") + + if not default_version: + default_version = python_versions.pop(0) + for python_version in python_versions: + if python_version == default_version: + # We register the default version lastly so that it's not picked first when --platforms + # is set with a constraint during toolchain resolution. This is due to the fact that + # Bazel will match the unconstrained toolchain if we register it before the constrained + # ones. + continue + python_register_toolchains( + name = name + "_" + python_version.replace(".", "_"), + python_version = python_version, + set_python_version_constraint = True, + **kwargs + ) + python_register_toolchains( + name = name + "_" + default_version.replace(".", "_"), + python_version = default_version, + set_python_version_constraint = False, + **kwargs + ) + + multi_toolchain_aliases( + name = name, + python_versions = { + python_version: name + "_" + python_version.replace(".", "_") + for python_version in (python_versions + [default_version]) + }, + ) diff --git a/python/runfiles/BUILD b/python/runfiles/BUILD deleted file mode 100644 index fa824ada0e..0000000000 --- a/python/runfiles/BUILD +++ /dev/null @@ -1,44 +0,0 @@ -# Copyright 2019 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. - -# We'd like to alias the runfiles target @bazel_tools//tools/python/runfiles. -# However, we need its source file to exist in the runfiles tree under this -# repo's name, so that it can be imported as -# -# from rules_python.python.runfiles import runfiles -# -# in user code. This requires either adding a symlink to runfiles or copying -# the file with an action. -# -# Both solutions are made more difficult by the fact that runfiles.py is not -# directly exported by its package. We could try to get a handle on its File -# object by unpacking the runfiles target's providers, but this seems hacky -# and is probably more effort than it's worth. Also, it's not trivial to copy -# files in a cross-platform (i.e. Windows-friendly) way. -# -# So instead, we just vendor in runfiles.py here. - -load("//python:defs.bzl", "py_library") - -filegroup( - name = "distribution", - srcs = glob(["**"]), - visibility = ["//python:__pkg__"], -) - -py_library( - name = "runfiles", - srcs = ["runfiles.py"], - visibility = ["//visibility:public"], -) diff --git a/python/runfiles/BUILD.bazel b/python/runfiles/BUILD.bazel new file mode 100644 index 0000000000..3a93d40f32 --- /dev/null +++ b/python/runfiles/BUILD.bazel @@ -0,0 +1,53 @@ +# Copyright 2019 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. + +load("//python:defs.bzl", "py_library") +load("//python:packaging.bzl", "py_wheel") + +filegroup( + name = "distribution", + srcs = glob(["**"]), + visibility = ["//python:__pkg__"], +) + +py_library( + name = "runfiles", + srcs = [ + "__init__.py", + "runfiles.py", + ], + visibility = ["//visibility:public"], +) + +# This can be manually tested by running tests/runfiles/runfiles_wheel_integration_test.sh +# We ought to have an automated integration test for it, too. +# see https://github.com/bazelbuild/rules_python/issues/1002 +py_wheel( + name = "wheel", + # From https://pypi.org/classifiers/ + classifiers = [ + "Development Status :: 5 - Production/Stable", + "License :: OSI Approved :: Apache Software License", + ], + description_file = "README.rst", + dist_folder = "dist", + distribution = "bazel_runfiles", + homepage = "https://github.com/bazelbuild/rules_python", + strip_path_prefixes = ["python"], + twine = "@publish_deps_twine//:pkg", + # this can be replaced by building with --stamp --embed_label=1.2.3 + version = "{BUILD_EMBED_LABEL}", + visibility = ["//visibility:public"], + deps = [":runfiles"], +) diff --git a/python/runfiles/README.rst b/python/runfiles/README.rst new file mode 100644 index 0000000000..ac61d2dd80 --- /dev/null +++ b/python/runfiles/README.rst @@ -0,0 +1,56 @@ +bazel-runfiles library +====================== + +This is a Bazel Runfiles lookup library for Bazel-built Python binaries and tests. + +Learn about runfiles: read `Runfiles guide `_ +or watch `Fabian's BazelCon talk `_. + +Typical Usage +------------- + +1. Add the 'bazel-runfiles' dependency along with other third-party dependencies, for example in your + ``requirements.txt`` file. + +2. Depend on this runfiles library from your build rule, like you would other third-party libraries:: + + py_binary( + name = "my_binary", + ... + deps = [requirement("bazel-runfiles")], + ) + +3. Import the runfiles library:: + + import runfiles # not "from runfiles import runfiles" + +4. Create a Runfiles object and use rlocation to look up runfile paths:: + + r = runfiles.Create() + ... + with open(r.Rlocation("my_workspace/path/to/my/data.txt"), "r") as f: + contents = f.readlines() + ... + + The code above creates a manifest- or directory-based implementations based + on the environment variables in os.environ. See `Create()` for more info. + + If you want to explicitly create a manifest- or directory-based + implementations, you can do so as follows:: + + r1 = runfiles.CreateManifestBased("path/to/foo.runfiles_manifest") + + r2 = runfiles.CreateDirectoryBased("path/to/foo.runfiles/") + + If you want to start subprocesses, and the subprocess can't automatically + find the correct runfiles directory, you can explicitly set the right + environment variables for them:: + + import subprocess + import runfiles + + r = runfiles.Create() + env = {} + ... + env.update(r.EnvVars()) + p = subprocess.Popen([r.Rlocation("path/to/binary")], env, ...) \ No newline at end of file diff --git a/python/runfiles/__init__.py b/python/runfiles/__init__.py new file mode 100644 index 0000000000..3dc4141749 --- /dev/null +++ b/python/runfiles/__init__.py @@ -0,0 +1,15 @@ +# 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 .runfiles import * diff --git a/python/runfiles/runfiles.py b/python/runfiles/runfiles.py index f11613d321..9bdb61b56a 100644 --- a/python/runfiles/runfiles.py +++ b/python/runfiles/runfiles.py @@ -12,72 +12,33 @@ # See the License for the specific language governing permissions and # limitations under the License. -############################################################################### -# Vendored in from bazelbuild/bazel (tools/python/runfiles/runfiles.py) at # -# commit 6c60a8ec049b6b8540c473969dd7bd1dad46acb9 (2019-07-19). See # -# //python/runfiles:BUILD for details. # -############################################################################### - """Runfiles lookup library for Bazel-built Python binaries and tests. -USAGE: - -1. Depend on this runfiles library from your build rule: - - py_binary( - name = "my_binary", - ... - deps = ["@bazel_tools//tools/python/runfiles"], - ) - -2. Import the runfiles library. - - from bazel_tools.tools.python.runfiles import runfiles - -3. Create a Runfiles object and use rlocation to look up runfile paths: - - r = runfiles.Create() - ... - with open(r.Rlocation("my_workspace/path/to/my/data.txt"), "r") as f: - contents = f.readlines() - ... - - The code above creates a manifest- or directory-based implementations based - on the environment variables in os.environ. See `Create()` for more info. - - If you want to explicitly create a manifest- or directory-based - implementations, you can do so as follows: - - r1 = runfiles.CreateManifestBased("path/to/foo.runfiles_manifest") - - r2 = runfiles.CreateDirectoryBased("path/to/foo.runfiles/") - - If you want to start subprocesses that also need runfiles, you need to set - the right environment variables for them: - - import subprocess - from bazel_tools.tools.python.runfiles import runfiles - - r = runfiles.Create() - env = {} - ... - env.update(r.EnvVars()) - p = subprocess.Popen([r.Rlocation("path/to/binary")], env, ...) +See README.md for usage instructions. """ - +import inspect import os import posixpath +import sys + +if False: + # Mypy needs these symbols imported, but since they only exist in python 3.5+, + # this import may fail at runtime. Luckily mypy can follow this conditional import. + from typing import Callable, Dict, Optional, Tuple, Union def CreateManifestBased(manifest_path): + # type: (str) -> _Runfiles return _Runfiles(_ManifestBased(manifest_path)) def CreateDirectoryBased(runfiles_dir_path): + # type: (str) -> _Runfiles return _Runfiles(_DirectoryBased(runfiles_dir_path)) def Create(env=None): + # type: (Optional[Dict[str, str]]) -> Optional[_Runfiles] """Returns a new `Runfiles` instance. The returned object is either: @@ -120,9 +81,15 @@ class _Runfiles(object): """ def __init__(self, strategy): + # type: (Union[_ManifestBased, _DirectoryBased]) -> None self._strategy = strategy + self._python_runfiles_root = _FindPythonRunfilesRoot() + self._repo_mapping = _ParseRepoMapping( + strategy.RlocationChecked("_repo_mapping") + ) - def Rlocation(self, path): + def Rlocation(self, path, source_repo=None): + # type: (str, Optional[str]) -> Optional[str] """Returns the runtime path of a runfile. Runfiles are data-dependencies of Bazel-built binaries and tests. @@ -135,6 +102,13 @@ def Rlocation(self, path): Args: path: string; runfiles-root-relative path of the runfile + source_repo: string; optional; the canonical name of the repository + whose repository mapping should be used to resolve apparent to + canonical repository names in `path`. If `None` (default), the + repository mapping of the repository containing the caller of this + method is used. Explicitly setting this parameter should only be + necessary for libraries that want to wrap the runfiles library. Use + `CurrentRepository` to obtain canonical repository names. Returns: the path to the runfile, which the caller should check for existence, or None if the method doesn't know about this runfile @@ -159,9 +133,34 @@ def Rlocation(self, path): raise ValueError('path is absolute without a drive letter: "%s"' % path) if os.path.isabs(path): return path - return self._strategy.RlocationChecked(path) + + if source_repo is None and self._repo_mapping: + # Look up runfiles using the repository mapping of the caller of the + # current method. If the repo mapping is empty, determining this + # name is not necessary. + source_repo = self.CurrentRepository(frame=2) + + # Split off the first path component, which contains the repository + # name (apparent or canonical). + target_repo, _, remainder = path.partition("/") + if not remainder or (source_repo, target_repo) not in self._repo_mapping: + # One of the following is the case: + # - not using Bzlmod, so the repository mapping is empty and + # apparent and canonical repository names are the same + # - target_repo is already a canonical repository name and does not + # have to be mapped. + # - path did not contain a slash and referred to a root symlink, + # which also should not be mapped. + return self._strategy.RlocationChecked(path) + + # target_repo is an apparent repository name. Look up the corresponding + # canonical repository name with respect to the current repository, + # identified by its canonical name. + target_canonical = self._repo_mapping[(source_repo, target_repo)] + return self._strategy.RlocationChecked(target_canonical + "/" + remainder) def EnvVars(self): + # type: () -> Dict[str, str] """Returns environment variables for subprocesses. The caller should set the returned key-value pairs in the environment of @@ -174,11 +173,109 @@ def EnvVars(self): """ return self._strategy.EnvVars() + def CurrentRepository(self, frame=1): + # type: (int) -> str + """Returns the canonical name of the caller's Bazel repository. + + For example, this function returns '' (the empty string) when called + from the main repository and a string of the form + 'rules_python~0.13.0` when called from code in the repository + corresponding to the rules_python Bazel module. + + More information about the difference between canonical repository + names and the `@repo` part of labels is available at: + https://bazel.build/build/bzlmod#repository-names + + NOTE: This function inspects the callstack to determine where in the + runfiles the caller is located to determine which repository it came + from. This may fail or produce incorrect results depending on who the + caller is, for example if it is not represented by a Python source + file. Use the `frame` argument to control the stack lookup. + + Args: + frame: int; the stack frame to return the repository name for. + Defaults to 1, the caller of the CurrentRepository function. + + Returns: + The canonical name of the Bazel repository containing the file + containing the frame-th caller of this function + + Raises: + ValueError: if the caller cannot be determined or the caller's file + path is not contained in the Python runfiles tree + """ + # pylint:disable=protected-access # for sys._getframe + # pylint:disable=raise-missing-from # we're still supporting Python 2 + try: + caller_path = inspect.getfile(sys._getframe(frame)) + except (TypeError, ValueError): + raise ValueError("failed to determine caller's file path") + caller_runfiles_path = os.path.relpath(caller_path, self._python_runfiles_root) + if caller_runfiles_path.startswith(".." + os.path.sep): + raise ValueError( + "{} does not lie under the runfiles root {}".format( + caller_path, self._python_runfiles_root + ) + ) + + caller_runfiles_directory = caller_runfiles_path[ + : caller_runfiles_path.find(os.path.sep) + ] + # With Bzlmod, the runfiles directory of the main repository is always + # named "_main". Without Bzlmod, the value returned by this function is + # never used, so we just assume Bzlmod is enabled. + if caller_runfiles_directory == "_main": + # The canonical name of the main repository (also known as the + # workspace) is the empty string. + return "" + # For all other repositories, the name of the runfiles directory is the + # canonical name. + return caller_runfiles_directory + + +def _FindPythonRunfilesRoot(): + # type: () -> str + """Finds the root of the Python runfiles tree.""" + root = __file__ + # Walk up our own runfiles path to the root of the runfiles tree from which + # the current file is being run. This path coincides with what the Bazel + # Python stub sets up as sys.path[0]. Since that entry can be changed at + # runtime, we rederive it here. + for _ in range("rules_python/python/runfiles/runfiles.py".count("/") + 1): + root = os.path.dirname(root) + return root + + +def _ParseRepoMapping(repo_mapping_path): + # type: (Optional[str]) -> Dict[Tuple[str, str], str] + """Parses the repository mapping manifest.""" + # If the repository mapping file can't be found, that is not an error: We + # might be running without Bzlmod enabled or there may not be any runfiles. + # In this case, just apply an empty repo mapping. + if not repo_mapping_path: + return {} + try: + with open(repo_mapping_path, "r") as f: + content = f.read() + except FileNotFoundError: + return {} + + repo_mapping = {} + for line in content.split("\n"): + if not line: + # Empty line following the last line break + break + current_canonical, target_local, target_canonical = line.split(",") + repo_mapping[(current_canonical, target_local)] = target_canonical + + return repo_mapping + class _ManifestBased(object): """`Runfiles` strategy that parses a runfiles-manifest to look up runfiles.""" def __init__(self, path): + # type: (str) -> None if not path: raise ValueError() if not isinstance(path, str): @@ -187,10 +284,27 @@ def __init__(self, path): self._runfiles = _ManifestBased._LoadRunfiles(path) def RlocationChecked(self, path): - return self._runfiles.get(path) + # type: (str) -> Optional[str] + """Returns the runtime path of a runfile.""" + exact_match = self._runfiles.get(path) + if exact_match: + return exact_match + # If path references a runfile that lies under a directory that + # itself is a runfile, then only the directory is listed in the + # manifest. Look up all prefixes of path in the manifest and append + # the relative path from the prefix to the looked up path. + prefix_end = len(path) + while True: + prefix_end = path.rfind("/", 0, prefix_end - 1) + if prefix_end == -1: + return None + prefix_match = self._runfiles.get(path[0:prefix_end]) + if prefix_match: + return prefix_match + "/" + path[prefix_end + 1 :] @staticmethod def _LoadRunfiles(path): + # type: (str) -> Dict[str, str] """Loads the runfiles manifest.""" result = {} with open(path, "r") as f: @@ -205,6 +319,7 @@ def _LoadRunfiles(path): return result def _GetRunfilesDir(self): + # type: () -> str if self._path.endswith("/MANIFEST") or self._path.endswith("\\MANIFEST"): return self._path[: -len("/MANIFEST")] elif self._path.endswith(".runfiles_manifest"): @@ -213,6 +328,7 @@ def _GetRunfilesDir(self): return "" def EnvVars(self): + # type: () -> Dict[str, str] directory = self._GetRunfilesDir() return { "RUNFILES_MANIFEST_FILE": self._path, @@ -227,6 +343,7 @@ class _DirectoryBased(object): """`Runfiles` strategy that appends runfiles paths to the runfiles root.""" def __init__(self, path): + # type: (str) -> None if not path: raise ValueError() if not isinstance(path, str): @@ -234,67 +351,18 @@ def __init__(self, path): self._runfiles_root = path def RlocationChecked(self, path): + # type: (str) -> str + # Use posixpath instead of os.path, because Bazel only creates a runfiles # tree on Unix platforms, so `Create()` will only create a directory-based # runfiles strategy on those platforms. return posixpath.join(self._runfiles_root, path) def EnvVars(self): + # type: () -> Dict[str, str] return { "RUNFILES_DIR": self._runfiles_root, # TODO(laszlocsomor): remove JAVA_RUNFILES once the Java launcher can # pick up RUNFILES_DIR. "JAVA_RUNFILES": self._runfiles_root, } - - -def _PathsFrom( - argv0, runfiles_mf, runfiles_dir, is_runfiles_manifest, is_runfiles_directory -): - """Discover runfiles manifest and runfiles directory paths. - - Args: - argv0: string; the value of sys.argv[0] - runfiles_mf: string; the value of the RUNFILES_MANIFEST_FILE environment - variable - runfiles_dir: string; the value of the RUNFILES_DIR environment variable - is_runfiles_manifest: lambda(string):bool; returns true if the argument is - the path of a runfiles manifest file - is_runfiles_directory: lambda(string):bool; returns true if the argument is - the path of a runfiles directory - - Returns: - (string, string) pair, first element is the path to the runfiles manifest, - second element is the path to the runfiles directory. If the first element - is non-empty, then is_runfiles_manifest returns true for it. Same goes for - the second element and is_runfiles_directory respectively. If both elements - are empty, then this function could not find a manifest or directory for - which is_runfiles_manifest or is_runfiles_directory returns true. - """ - mf_alid = is_runfiles_manifest(runfiles_mf) - dir_valid = is_runfiles_directory(runfiles_dir) - - if not mf_alid and not dir_valid: - runfiles_mf = argv0 + ".runfiles/MANIFEST" - runfiles_dir = argv0 + ".runfiles" - mf_alid = is_runfiles_manifest(runfiles_mf) - dir_valid = is_runfiles_directory(runfiles_dir) - if not mf_alid: - runfiles_mf = argv0 + ".runfiles_manifest" - mf_alid = is_runfiles_manifest(runfiles_mf) - - if not mf_alid and not dir_valid: - return ("", "") - - if not mf_alid: - runfiles_mf = runfiles_dir + "/MANIFEST" - mf_alid = is_runfiles_manifest(runfiles_mf) - if not mf_alid: - runfiles_mf = runfiles_dir + "_manifest" - mf_alid = is_runfiles_manifest(runfiles_mf) - - if not dir_valid: - runfiles_dir = runfiles_mf[:-9] # "_manifest" or "/MANIFEST" - dir_valid = is_runfiles_directory(runfiles_dir) - - return (runfiles_mf if mf_alid else "", runfiles_dir if dir_valid else "") diff --git a/python/tests/toolchains/defs.bzl b/python/tests/toolchains/defs.bzl index 8c07d23885..653cde6657 100644 --- a/python/tests/toolchains/defs.bzl +++ b/python/tests/toolchains/defs.bzl @@ -39,10 +39,6 @@ def _acceptance_test_impl(ctx): ) python_version_test = ctx.actions.declare_file("/".join([ctx.attr.python_version, "python_version_test.py"])) - - # With the current approach in the run_acceptance_test.sh, we use this - # symlink to find the absolute path to the rules_python to be passed to the - # --override_repository rules_python=. ctx.actions.symlink( target_file = ctx.file._python_version_test, output = python_version_test, @@ -92,7 +88,7 @@ def _acceptance_test_impl(ctx): python_version_test, run_acceptance_test_py, workspace, - ] + ] + ctx.files._distribution return [DefaultInfo( executable = executable, files = depset( @@ -126,6 +122,10 @@ _acceptance_test = rule( allow_single_file = True, default = Label("//python/tests/toolchains/workspace_template:BUILD.bazel.tmpl"), ), + "_distribution": attr.label( + doc = "The rules_python source distribution.", + default = Label("//:distribution"), + ), "_python_version_test": attr.label( doc = "The python_version_test.py used to test the Python version.", allow_single_file = True, @@ -172,4 +172,5 @@ def acceptance_tests(): ), python_version = python_version, target_compatible_with = meta.compatible_with, + tags = ["acceptance-test"], ) diff --git a/python/tests/toolchains/run_acceptance_test.py.tmpl b/python/tests/toolchains/run_acceptance_test.py.tmpl index 51eba3da3f..150e1a99df 100644 --- a/python/tests/toolchains/run_acceptance_test.py.tmpl +++ b/python/tests/toolchains/run_acceptance_test.py.tmpl @@ -21,16 +21,10 @@ class TestPythonVersion(unittest.TestCase): @classmethod def setUpClass(cls): os.chdir("%test_location%") - python_version_test_dirname = os.path.dirname( - os.path.realpath("python_version_test.py") - ) - rules_python_path = os.path.normpath( - os.path.join(python_version_test_dirname, "..", "..", "..", "..") - ) + rules_python_path = os.path.join(os.environ["TEST_SRCDIR"], "rules_python") + test_tmpdir = os.environ["TEST_TMPDIR"] if %is_windows%: - test_tmpdir = os.environ["TEST_TMPDIR"] - home = os.path.join(test_tmpdir, "HOME") os.mkdir(home) os.environ["HOME"] = home @@ -39,6 +33,16 @@ class TestPythonVersion(unittest.TestCase): os.mkdir(local_app_data) os.environ["LocalAppData"] = local_app_data + # Bazelisk requires a cache directory be set + os.environ["XDG_CACHE_HOME"] = os.path.join(test_tmpdir, "xdg-cache-home") + + # Unset this so this works when called by Bazel's latest Bazel build + # pipeline. It sets the following combination, which interfere with each other: + # * --sandbox_tmpfs_path=/tmp + # * --test_env=USE_BAZEL_VERSION + # * USE_BAZEL_VERSION=/tmp/ + os.environ.pop("USE_BAZEL_VERSION", None) + with open(".bazelrc", "w") as bazelrc: bazelrc.write( os.linesep.join( @@ -52,8 +56,11 @@ class TestPythonVersion(unittest.TestCase): ) def test_match_toolchain(self): - stream = os.popen("bazel run @python//:python3 -- --version") - output = stream.read().strip() + output = subprocess.check_output( + f"bazel run @python//:python3 -- --version", + shell = True, # Shell needed to look up via PATH + text=True, + ).strip() self.assertEqual(output, "Python %python_version%") subprocess.run("bazel test //...", shell=True, check=True) diff --git a/python/tests/toolchains/workspace_template/WORKSPACE.tmpl b/python/tests/toolchains/workspace_template/WORKSPACE.tmpl index d0aa700928..973e020c1e 100644 --- a/python/tests/toolchains/workspace_template/WORKSPACE.tmpl +++ b/python/tests/toolchains/workspace_template/WORKSPACE.tmpl @@ -25,3 +25,15 @@ python_register_toolchains( name = "python", python_version = "%python_version%", ) + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +http_archive( + name = "bazel_skylib", + urls = [ + "https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", + "https://github.com/bazelbuild/bazel-skylib/releases/download/1.1.1/bazel-skylib-1.1.1.tar.gz", + ], + sha256 = "c6966ec828da198c5d9adbaa94c05e3a1c7f21bd012a0b29ba8ddbccb2c93b0d", +) +load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace") +bazel_skylib_workspace() diff --git a/python/versions.bzl b/python/versions.bzl index 2d05e1638d..baf6e33a06 100644 --- a/python/versions.bzl +++ b/python/versions.bzl @@ -26,6 +26,23 @@ DEFAULT_RELEASE_BASE_URL = "https://github.com/indygreg/python-build-standalone/ # the hashes: # bazel run //python/private:print_toolchains_checksums # +# Note, to users looking at how to specify their tool versions, coverage_tool version for each +# interpreter can be specified by: +# "3.8.10": { +# "url": "20210506/cpython-{python_version}-{platform}-pgo+lto-20210506T0943.tar.zst", +# "sha256": { +# "x86_64-apple-darwin": "8d06bec08db8cdd0f64f4f05ee892cf2fcbc58cfb1dd69da2caab78fac420238", +# "x86_64-unknown-linux-gnu": "aec8c4c53373b90be7e2131093caa26063be6d9d826f599c935c0e1042af3355", +# }, +# "coverage_tool": { +# "x86_64-apple-darwin": """, +# "x86_64-unknown-linux-gnu": """, +# }, +# "strip_prefix": "python", +# }, +# +# It is possible to provide lists in "url". +# # buildifier: disable=unsorted-dict-items TOOL_VERSIONS = { "3.8.10": { @@ -48,13 +65,35 @@ TOOL_VERSIONS = { "strip_prefix": "python", }, "3.8.13": { - "url": "20220502/cpython-{python_version}+20220502-{platform}-{build}.tar.gz", + "url": "20220802/cpython-{python_version}+20220802-{platform}-{build}.tar.gz", "sha256": { - "aarch64-apple-darwin": "ba604867d8c6e0a1a85b1be789cad4c69af8a1699043f51e8a85998b55979127", + "aarch64-apple-darwin": "ae4131253d890b013171cb5f7b03cadc585ae263719506f7b7e063a7cf6fde76", # no aarch64-unknown-linux-gnu build available for 3.8.13 - "x86_64-apple-darwin": "52e3541f41d165002e19a60e11dcc145e90ffe1fe8a6f94b17d5b72a674674ea", - "x86_64-pc-windows-msvc": "1435b77b4d89f2a99719918fcf917a67cd711cad46f67516bed2462d18fbefb3", - "x86_64-unknown-linux-gnu": "884c6c4605c11685164237bad5f8f2773edcf3abb0637a83efa7912a54f658b3", + "x86_64-apple-darwin": "cd6e7c0a27daf7df00f6882eaba01490dd963f698e99aeee9706877333e0df69", + "x86_64-pc-windows-msvc": "f20643f1b3e263a56287319aea5c3888530c09ad9de3a5629b1a5d207807e6b9", + "x86_64-unknown-linux-gnu": "fb566629ccb5f76ef56d275a3f8017d683f1c20c5beb5d5f38b155ed11e16187", + }, + "strip_prefix": "python", + }, + "3.8.15": { + "url": "20221106/cpython-{python_version}+20221106-{platform}-{build}.tar.gz", + "sha256": { + "aarch64-apple-darwin": "1e0a92d1a4f5e6d4a99f86b1cbf9773d703fe7fd032590f3e9c285c7a5eeb00a", + "aarch64-unknown-linux-gnu": "886ab33ced13c84bf59ce8ff79eba6448365bfcafea1bf415bd1d75e21b690aa", + "x86_64-apple-darwin": "70b57f28c2b5e1e3dd89f0d30edd5bc414e8b20195766cf328e1b26bed7890e1", + "x86_64-pc-windows-msvc": "2fdc3fa1c95f982179bbbaedae2b328197658638799b6dcb63f9f494b0de59e2", + "x86_64-unknown-linux-gnu": "e47edfb2ceaf43fc699e20c179ec428b6f3e497cf8e2dcd8e9c936d4b96b1e56", + }, + "strip_prefix": "python", + }, + "3.8.16": { + "url": "20230116/cpython-{python_version}+20230116-{platform}-{build}.tar.gz", + "sha256": { + "aarch64-apple-darwin": "d1f408569d8807c1053939d7822b082a17545e363697e1ce3cfb1ee75834c7be", + "aarch64-unknown-linux-gnu": "15d00bc8400ed6d94c665a797dc8ed7a491ae25c5022e738dcd665cd29beec42", + "x86_64-apple-darwin": "484ba901f64fc7888bec5994eb49343dc3f9d00ed43df17ee9c40935aad4aa18", + "x86_64-pc-windows-msvc": "b446bec833eaba1bac9063bb9b4aeadfdf67fa81783b4487a90c56d408fb7994", + "x86_64-unknown-linux-gnu": "c890de112f1ae31283a31fefd2061d5c97bdd4d1bdd795552c7abddef2697ea1", }, "strip_prefix": "python", }, @@ -80,6 +119,39 @@ TOOL_VERSIONS = { }, "strip_prefix": "python", }, + "3.9.13": { + "url": "20220802/cpython-{python_version}+20220802-{platform}-{build}.tar.gz", + "sha256": { + "aarch64-apple-darwin": "d9603edc296a2dcbc59d7ada780fd12527f05c3e0b99f7545112daf11636d6e5", + "aarch64-unknown-linux-gnu": "80415aac1b96255b9211f6a4c300f31e9940c7e07a23d0dec12b53aa52c0d25e", + "x86_64-apple-darwin": "9540a7efb7c8a54a48aff1cb9480e49588d9c0a3f934ad53f5b167338174afa3", + "x86_64-pc-windows-msvc": "b538127025a467c64b3351babca2e4d2ea7bdfb7867d5febb3529c34456cdcd4", + "x86_64-unknown-linux-gnu": "ce1cfca2715e7e646dd618a8cb9baff93000e345ccc979b801fc6ccde7ce97df", + }, + "strip_prefix": "python", + }, + "3.9.15": { + "url": "20221106/cpython-{python_version}+20221106-{platform}-{build}.tar.gz", + "sha256": { + "aarch64-apple-darwin": "64dc7e1013481c9864152c3dd806c41144c79d5e9cd3140e185c6a5060bdc9ab", + "aarch64-unknown-linux-gnu": "52a8c0a67fb919f80962d992da1bddb511cdf92faf382701ce7673e10a8ff98f", + "x86_64-apple-darwin": "f2bcade6fc976c472f18f2b3204d67202d43ae55cf6f9e670f95e488f780da08", + "x86_64-pc-windows-msvc": "022daacab215679b87f0d200d08b9068a721605fa4721ebeda38220fc641ccf6", + "x86_64-unknown-linux-gnu": "cdc3a4cfddcd63b6cebdd75b14970e02d8ef0ac5be4d350e57ab5df56c19e85e", + }, + "strip_prefix": "python", + }, + "3.9.16": { + "url": "20230116/cpython-{python_version}+20230116-{platform}-{build}.tar.gz", + "sha256": { + "aarch64-apple-darwin": "d732d212d42315ac27c6da3e0b69636737a8d72086c980daf844344c010cab80", + "aarch64-unknown-linux-gnu": "1ba520c0db431c84305677f56eb9a4254f5097430ed443e92fc8617f8fba973d", + "x86_64-apple-darwin": "3948384af5e8d4ee7e5ccc648322b99c1c5cf4979954ed5e6b3382c69d6db71e", + "x86_64-pc-windows-msvc": "5274afd6b7ff2bddbd8306385ffb2336764b0e58535db968daeac656246f59a8", + "x86_64-unknown-linux-gnu": "7ba397787932393e65fc2fb9fcfabf54f2bb6751d5da2b45913cb25b2d493758", + }, + "strip_prefix": "python", + }, "3.10.2": { "url": "20220227/cpython-{python_version}+20220227-{platform}-{build}.tar.gz", "sha256": { @@ -102,13 +174,58 @@ TOOL_VERSIONS = { }, "strip_prefix": "python", }, + "3.10.6": { + "url": "20220802/cpython-{python_version}+20220802-{platform}-{build}.tar.gz", + "sha256": { + "aarch64-apple-darwin": "efaf66acdb9a4eb33d57702607d2e667b1a319d58c167a43c96896b97419b8b7", + "aarch64-unknown-linux-gnu": "81625f5c97f61e2e3d7e9f62c484b1aa5311f21bd6545451714b949a29da5435", + "x86_64-apple-darwin": "7718411adf3ea1480f3f018a643eb0550282aefe39e5ecb3f363a4a566a9398c", + "x86_64-pc-windows-msvc": "91889a7dbdceea585ff4d3b7856a6bb8f8a4eca83a0ff52a73542c2e67220eaa", + "x86_64-unknown-linux-gnu": "55aa2190d28dcfdf414d96dc5dcea9fe048fadcd583dc3981fec020869826111", + }, + "strip_prefix": "python", + }, + "3.10.8": { + "url": "20221106/cpython-{python_version}+20221106-{platform}-{build}.tar.gz", + "sha256": { + "aarch64-apple-darwin": "d52b03817bd245d28e0a8b2f715716cd0fcd112820ccff745636932c76afa20a", + "aarch64-unknown-linux-gnu": "33170bef18c811906b738be530f934640491b065bf16c4d276c6515321918132", + "x86_64-apple-darwin": "525b79c7ce5de90ab66bd07b0ac1008bafa147ddc8a41bef15ffb7c9c1e9e7c5", + "x86_64-pc-windows-msvc": "f2b6d2f77118f06dd2ca04dae1175e44aaa5077a5ed8ddc63333c15347182bfe", + "x86_64-unknown-linux-gnu": "6c8db44ae0e18e320320bbaaafd2d69cde8bfea171ae2d651b7993d1396260b7", + }, + "strip_prefix": "python", + }, + "3.10.9": { + "url": "20230116/cpython-{python_version}+20230116-{platform}-{build}.tar.gz", + "sha256": { + "aarch64-apple-darwin": "018d05a779b2de7a476f3b3ff2d10f503d69d14efcedd0774e6dab8c22ef84ff", + "aarch64-unknown-linux-gnu": "2003750f40cd09d4bf7a850342613992f8d9454f03b3c067989911fb37e7a4d1", + "x86_64-apple-darwin": "0e685f98dce0e5bc8da93c7081f4e6c10219792e223e4b5886730fd73a7ba4c6", + "x86_64-pc-windows-msvc": "59c6970cecb357dc1d8554bd0540eb81ee7f6d16a07acf3d14ed294ece02c035", + "x86_64-unknown-linux-gnu": "d196347aeb701a53fe2bb2b095abec38d27d0fa0443f8a1c2023a1bed6e18cdf", + }, + "strip_prefix": "python", + }, + "3.11.1": { + "url": "20230116/cpython-{python_version}+20230116-{platform}-{build}.tar.gz", + "sha256": { + "aarch64-apple-darwin": "4918cdf1cab742a90f85318f88b8122aeaa2d04705803c7b6e78e81a3dd40f80", + "aarch64-unknown-linux-gnu": "debf15783bdcb5530504f533d33fda75a7b905cec5361ae8f33da5ba6599f8b4", + "x86_64-apple-darwin": "20a4203d069dc9b710f70b09e7da2ce6f473d6b1110f9535fb6f4c469ed54733", + "x86_64-pc-windows-msvc": "edc08979cb0666a597466176511529c049a6f0bba8adf70df441708f766de5bf", + "x86_64-unknown-linux-gnu": "02a551fefab3750effd0e156c25446547c238688a32fabde2995c941c03a6423", + }, + "strip_prefix": "python", + }, } # buildifier: disable=unsorted-dict-items MINOR_MAPPING = { - "3.8": "3.8.13", - "3.9": "3.9.12", - "3.10": "3.10.4", + "3.8": "3.8.15", + "3.9": "3.9.16", + "3.10": "3.10.9", + "3.11": "3.11.1", } PLATFORMS = { @@ -159,7 +276,7 @@ PLATFORMS = { ), } -def get_release_url(platform, python_version, base_url = DEFAULT_RELEASE_BASE_URL, tool_versions = TOOL_VERSIONS): +def get_release_info(platform, python_version, base_url = DEFAULT_RELEASE_BASE_URL, tool_versions = TOOL_VERSIONS): """Resolve the release URL for the requested interpreter version Args: @@ -177,17 +294,37 @@ def get_release_url(platform, python_version, base_url = DEFAULT_RELEASE_BASE_UR if type(url) == type({}): url = url[platform] + if type(url) != type([]): + url = [url] + strip_prefix = tool_versions[python_version].get("strip_prefix", None) if type(strip_prefix) == type({}): strip_prefix = strip_prefix[platform] - release_filename = url.format( - platform = platform, - python_version = python_version, - build = "shared-install_only" if (WINDOWS_NAME in platform) else "install_only", - ) - url = "/".join([base_url, release_filename]) - return (release_filename, url, strip_prefix) + release_filename = None + rendered_urls = [] + for u in url: + release_filename = u.format( + platform = platform, + python_version = python_version, + build = "shared-install_only" if (WINDOWS_NAME in platform) else "install_only", + ) + if "://" in release_filename: # is absolute url? + rendered_urls.append(release_filename) + else: + rendered_urls.append("/".join([base_url, release_filename])) + + if release_filename == None: + fail("release_filename should be set by now; were any download URLs given?") + + patches = tool_versions[python_version].get("patches", []) + if type(patches) == type({}): + if platform in patches.keys(): + patches = patches[platform] + else: + patches = [] + + return (release_filename, rendered_urls, strip_prefix, patches) def print_toolchains_checksums(name): native.genrule( @@ -218,10 +355,11 @@ def _commands_for_version(python_version): "echo \"{python_version}: {platform}: $$(curl --location --fail {release_url_sha256} 2>/dev/null || curl --location --fail {release_url} 2>/dev/null | shasum -a 256 | awk '{{ print $$1 }}')\"".format( python_version = python_version, platform = platform, - release_url = get_release_url(platform, python_version)[1], - release_url_sha256 = get_release_url(platform, python_version)[1] + ".sha256", + release_url = release_url, + release_url_sha256 = release_url + ".sha256", ) for platform in TOOL_VERSIONS[python_version]["sha256"].keys() + for release_url in get_release_info(platform, python_version)[1] ]) def gen_python_config_settings(name = ""): diff --git a/tests/BUILD b/tests/BUILD deleted file mode 100644 index 46d8739622..0000000000 --- a/tests/BUILD +++ /dev/null @@ -1,15 +0,0 @@ -load("//tools/bazel_integration_test:bazel_integration_test.bzl", "bazel_integration_test") - -package(default_visibility = ["//visibility:public"]) - -licenses(["notice"]) # Apache 2.0 - -bazel_integration_test( - name = "pip_repository_entry_points_example", - timeout = "long", -) - -bazel_integration_test( - name = "pip_deps_example", - timeout = "long", -) diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel new file mode 100644 index 0000000000..abbe62ddff --- /dev/null +++ b/tests/BUILD.bazel @@ -0,0 +1,32 @@ +load("@bazel_skylib//rules:build_test.bzl", "build_test") +load("//tools/bazel_integration_test:bazel_integration_test.bzl", "bazel_integration_test") + +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +bazel_integration_test( + name = "pip_repository_entry_points_example", + timeout = "long", + # The dependencies needed for this test are not cross-platform: https://github.com/bazelbuild/rules_python/issues/260 + tags = ["fix-windows"], +) + +build_test( + name = "bzl_libraries_build_test", + targets = [ + # keep sorted + "//python:current_py_toolchain_bzl", + "//python:defs_bzl", + "//python:proto_bzl", + "//python:py_binary_bzl", + "//python:py_cc_link_params_info_bzl", + "//python:py_import_bzl", + "//python:py_info_bzl", + "//python:py_library_bzl", + "//python:py_runtime_bzl", + "//python:py_runtime_info_bzl", + "//python:py_runtime_pair_bzl", + "//python:py_test_bzl", + ], +) diff --git a/tests/compile_pip_requirements/.bazelrc b/tests/compile_pip_requirements/.bazelrc new file mode 100644 index 0000000000..f23315a7a1 --- /dev/null +++ b/tests/compile_pip_requirements/.bazelrc @@ -0,0 +1,5 @@ +test --test_output=errors + +# Windows requires these for multi-python support: +build --enable_runfiles +startup --windows_enable_symlinks diff --git a/tests/compile_pip_requirements/.gitignore b/tests/compile_pip_requirements/.gitignore new file mode 100644 index 0000000000..ac51a054d2 --- /dev/null +++ b/tests/compile_pip_requirements/.gitignore @@ -0,0 +1 @@ +bazel-* diff --git a/tests/compile_pip_requirements/BUILD.bazel b/tests/compile_pip_requirements/BUILD.bazel new file mode 100644 index 0000000000..d6ac0086ab --- /dev/null +++ b/tests/compile_pip_requirements/BUILD.bazel @@ -0,0 +1,34 @@ +load("@rules_python//python:pip.bzl", "compile_pip_requirements") + +genrule( + name = "generate_requirements_extra_in", + srcs = [], + outs = ["requirements_extra.in"], + cmd = "echo 'setuptools~=65.6.3' > $@", +) + +genrule( + name = "generate_requirements_in", + srcs = [], + outs = ["requirements.in"], + cmd = """ +cat > $@ <=1.28.0 # Last avialable for ubuntu python3.6 setuptools==59.6.0 diff --git a/tests/pip_repository_entry_points/requirements.txt b/tests/pip_repository_entry_points/requirements.txt index 02bc8751b0..a93facc03b 100644 --- a/tests/pip_repository_entry_points/requirements.txt +++ b/tests/pip_repository_entry_points/requirements.txt @@ -1,6 +1,6 @@ # -# This file is autogenerated by pip-compile with python 3.10 -# To update, run: +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: # # bazel run //:requirements.update # @@ -166,6 +166,13 @@ requests==2.27.1 \ --hash=sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61 \ --hash=sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d # via sphinx +setuptools==59.6.0 \ + --hash=sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373 \ + --hash=sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e + # via + # -r requirements.in + # sphinx + # yamllint snowballstemmer==2.2.0 \ --hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \ --hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a @@ -202,15 +209,7 @@ urllib3==1.26.7 \ --hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece \ --hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844 # via requests -yamllint==1.26.3 \ - --hash=sha256:3934dcde484374596d6b52d8db412929a169f6d9e52e20f9ade5bf3523d9b96e +yamllint==1.28.0 \ + --hash=sha256:89bb5b5ac33b1ade059743cf227de73daa34d5e5a474b06a5e17fc16583b0cf2 \ + --hash=sha256:9e3d8ddd16d0583214c5fdffe806c9344086721f107435f68bad990e5a88826b # via -r requirements.in - -# The following packages are considered to be unsafe in a requirements file: -setuptools==59.6.0 \ - --hash=sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373 \ - --hash=sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e - # via - # -r requirements.in - # sphinx - # yamllint diff --git a/tests/pip_repository_entry_points/requirements_windows.txt b/tests/pip_repository_entry_points/requirements_windows.txt new file mode 100644 index 0000000000..651e2b5e56 --- /dev/null +++ b/tests/pip_repository_entry_points/requirements_windows.txt @@ -0,0 +1,219 @@ +# +# This file is autogenerated by pip-compile with Python 3.10 +# by the following command: +# +# bazel run //:requirements.update +# +alabaster==0.7.12 \ + --hash=sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359 \ + --hash=sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02 + # via sphinx +babel==2.9.1 \ + --hash=sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9 \ + --hash=sha256:bc0c176f9f6a994582230df350aa6e05ba2ebe4b3ac317eab29d9be5d2768da0 + # via sphinx +certifi==2021.10.8 \ + --hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872 \ + --hash=sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 + # via requests +charset-normalizer==2.0.10 \ + --hash=sha256:876d180e9d7432c5d1dfd4c5d26b72f099d503e8fcc0feb7532c9289be60fcbd \ + --hash=sha256:cb957888737fc0bbcd78e3df769addb41fd1ff8cf950dc9e7ad7793f1bf44455 + # via requests +colorama==0.4.6 \ + --hash=sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44 \ + --hash=sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6 + # via sphinx +docutils==0.17.1 \ + --hash=sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125 \ + --hash=sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61 + # via sphinx +idna==3.3 \ + --hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \ + --hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d + # via requests +imagesize==1.3.0 \ + --hash=sha256:1db2f82529e53c3e929e8926a1fa9235aa82d0bd0c580359c67ec31b2fddaa8c \ + --hash=sha256:cd1750d452385ca327479d45b64d9c7729ecf0b3969a58148298c77092261f9d + # via sphinx +jinja2==3.0.3 \ + --hash=sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8 \ + --hash=sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7 + # via sphinx +markupsafe==2.0.1 \ + --hash=sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298 \ + --hash=sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64 \ + --hash=sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b \ + --hash=sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194 \ + --hash=sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567 \ + --hash=sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff \ + --hash=sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724 \ + --hash=sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74 \ + --hash=sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646 \ + --hash=sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35 \ + --hash=sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6 \ + --hash=sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a \ + --hash=sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6 \ + --hash=sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad \ + --hash=sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26 \ + --hash=sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38 \ + --hash=sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac \ + --hash=sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7 \ + --hash=sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6 \ + --hash=sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047 \ + --hash=sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75 \ + --hash=sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f \ + --hash=sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b \ + --hash=sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135 \ + --hash=sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8 \ + --hash=sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a \ + --hash=sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a \ + --hash=sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1 \ + --hash=sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9 \ + --hash=sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864 \ + --hash=sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914 \ + --hash=sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee \ + --hash=sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f \ + --hash=sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18 \ + --hash=sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8 \ + --hash=sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2 \ + --hash=sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d \ + --hash=sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b \ + --hash=sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b \ + --hash=sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86 \ + --hash=sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6 \ + --hash=sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f \ + --hash=sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb \ + --hash=sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833 \ + --hash=sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28 \ + --hash=sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e \ + --hash=sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415 \ + --hash=sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902 \ + --hash=sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f \ + --hash=sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d \ + --hash=sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9 \ + --hash=sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d \ + --hash=sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145 \ + --hash=sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066 \ + --hash=sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c \ + --hash=sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1 \ + --hash=sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a \ + --hash=sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207 \ + --hash=sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f \ + --hash=sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53 \ + --hash=sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd \ + --hash=sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134 \ + --hash=sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85 \ + --hash=sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9 \ + --hash=sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5 \ + --hash=sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94 \ + --hash=sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509 \ + --hash=sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51 \ + --hash=sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872 + # via jinja2 +packaging==21.3 \ + --hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \ + --hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522 + # via sphinx +pathspec==0.9.0 \ + --hash=sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a \ + --hash=sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1 + # via yamllint +pygments==2.11.2 \ + --hash=sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65 \ + --hash=sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a + # via sphinx +pyparsing==3.0.6 \ + --hash=sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4 \ + --hash=sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81 + # via packaging +pytz==2021.3 \ + --hash=sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c \ + --hash=sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326 + # via babel +pyyaml==6.0 \ + --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:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0 \ + --hash=sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92 \ + --hash=sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f \ + --hash=sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 \ + --hash=sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc \ + --hash=sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c \ + --hash=sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86 \ + --hash=sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4 \ + --hash=sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c \ + --hash=sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34 \ + --hash=sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b \ + --hash=sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c \ + --hash=sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb \ + --hash=sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737 \ + --hash=sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3 \ + --hash=sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d \ + --hash=sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53 \ + --hash=sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78 \ + --hash=sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803 \ + --hash=sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a \ + --hash=sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174 \ + --hash=sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5 + # via yamllint +requests==2.27.1 \ + --hash=sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61 \ + --hash=sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d + # via sphinx +setuptools==59.6.0 \ + --hash=sha256:22c7348c6d2976a52632c67f7ab0cdf40147db7789f9aed18734643fe9cf3373 \ + --hash=sha256:4ce92f1e1f8f01233ee9952c04f6b81d1e02939d6e1b488428154974a4d0783e + # via + # -r requirements.in + # sphinx + # yamllint +snowballstemmer==2.2.0 \ + --hash=sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1 \ + --hash=sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a + # via sphinx +sphinx==4.3.2 \ + --hash=sha256:0a8836751a68306b3fe97ecbe44db786f8479c3bf4b80e3a7f5c838657b4698c \ + --hash=sha256:6a11ea5dd0bdb197f9c2abc2e0ce73e01340464feaece525e64036546d24c851 + # via -r requirements.in +sphinxcontrib-applehelp==1.0.2 \ + --hash=sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a \ + --hash=sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58 + # via sphinx +sphinxcontrib-devhelp==1.0.2 \ + --hash=sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e \ + --hash=sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4 + # via sphinx +sphinxcontrib-htmlhelp==2.0.0 \ + --hash=sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07 \ + --hash=sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2 + # via sphinx +sphinxcontrib-jsmath==1.0.1 \ + --hash=sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178 \ + --hash=sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8 + # via sphinx +sphinxcontrib-qthelp==1.0.3 \ + --hash=sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72 \ + --hash=sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6 + # via sphinx +sphinxcontrib-serializinghtml==1.1.5 \ + --hash=sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd \ + --hash=sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952 + # via sphinx +urllib3==1.26.7 \ + --hash=sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece \ + --hash=sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844 + # via requests +yamllint==1.28.0 \ + --hash=sha256:89bb5b5ac33b1ade059743cf227de73daa34d5e5a474b06a5e17fc16583b0cf2 \ + --hash=sha256:9e3d8ddd16d0583214c5fdffe806c9344086721f107435f68bad990e5a88826b + # via -r requirements.in diff --git a/tests/runfiles/BUILD.bazel b/tests/runfiles/BUILD.bazel new file mode 100644 index 0000000000..d62e179211 --- /dev/null +++ b/tests/runfiles/BUILD.bazel @@ -0,0 +1,7 @@ +load("@rules_python//python:defs.bzl", "py_test") + +py_test( + name = "runfiles_test", + srcs = ["runfiles_test.py"], + deps = ["//python/runfiles"], +) diff --git a/tests/runfiles/runfiles_test.py b/tests/runfiles/runfiles_test.py new file mode 100644 index 0000000000..3a1f49201b --- /dev/null +++ b/tests/runfiles/runfiles_test.py @@ -0,0 +1,558 @@ +# pylint: disable=g-bad-file-header +# Copyright 2018 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 tempfile +import unittest + +from python.runfiles import runfiles + + +class RunfilesTest(unittest.TestCase): + # """Unit tests for `runfiles.Runfiles`.""" + + def testRlocationArgumentValidation(self): + r = runfiles.Create({"RUNFILES_DIR": "whatever"}) + self.assertRaises(ValueError, lambda: r.Rlocation(None)) + self.assertRaises(ValueError, lambda: r.Rlocation("")) + self.assertRaises(TypeError, lambda: r.Rlocation(1)) + self.assertRaisesRegex( + ValueError, "is not normalized", lambda: r.Rlocation("../foo") + ) + self.assertRaisesRegex( + ValueError, "is not normalized", lambda: r.Rlocation("foo/..") + ) + self.assertRaisesRegex( + ValueError, "is not normalized", lambda: r.Rlocation("foo/../bar") + ) + self.assertRaisesRegex( + ValueError, "is not normalized", lambda: r.Rlocation("./foo") + ) + self.assertRaisesRegex( + ValueError, "is not normalized", lambda: r.Rlocation("foo/.") + ) + self.assertRaisesRegex( + ValueError, "is not normalized", lambda: r.Rlocation("foo/./bar") + ) + self.assertRaisesRegex( + ValueError, "is not normalized", lambda: r.Rlocation("//foobar") + ) + self.assertRaisesRegex( + ValueError, "is not normalized", lambda: r.Rlocation("foo//") + ) + self.assertRaisesRegex( + ValueError, "is not normalized", lambda: r.Rlocation("foo//bar") + ) + self.assertRaisesRegex( + ValueError, + "is absolute without a drive letter", + lambda: r.Rlocation("\\foo"), + ) + + def testCreatesManifestBasedRunfiles(self): + with _MockFile(contents=["a/b c/d"]) as mf: + r = runfiles.Create( + { + "RUNFILES_MANIFEST_FILE": mf.Path(), + "RUNFILES_DIR": "ignored when RUNFILES_MANIFEST_FILE has a value", + "TEST_SRCDIR": "always ignored", + } + ) + self.assertEqual(r.Rlocation("a/b"), "c/d") + self.assertIsNone(r.Rlocation("foo")) + + def testManifestBasedRunfilesEnvVars(self): + with _MockFile(name="MANIFEST") as mf: + r = runfiles.Create( + { + "RUNFILES_MANIFEST_FILE": mf.Path(), + "TEST_SRCDIR": "always ignored", + } + ) + self.assertDictEqual( + r.EnvVars(), + { + "RUNFILES_MANIFEST_FILE": mf.Path(), + "RUNFILES_DIR": mf.Path()[: -len("/MANIFEST")], + "JAVA_RUNFILES": mf.Path()[: -len("/MANIFEST")], + }, + ) + + with _MockFile(name="foo.runfiles_manifest") as mf: + r = runfiles.Create( + { + "RUNFILES_MANIFEST_FILE": mf.Path(), + "TEST_SRCDIR": "always ignored", + } + ) + self.assertDictEqual( + r.EnvVars(), + { + "RUNFILES_MANIFEST_FILE": mf.Path(), + "RUNFILES_DIR": ( + mf.Path()[: -len("foo.runfiles_manifest")] + "foo.runfiles" + ), + "JAVA_RUNFILES": ( + mf.Path()[: -len("foo.runfiles_manifest")] + "foo.runfiles" + ), + }, + ) + + with _MockFile(name="x_manifest") as mf: + r = runfiles.Create( + { + "RUNFILES_MANIFEST_FILE": mf.Path(), + "TEST_SRCDIR": "always ignored", + } + ) + self.assertDictEqual( + r.EnvVars(), + { + "RUNFILES_MANIFEST_FILE": mf.Path(), + "RUNFILES_DIR": "", + "JAVA_RUNFILES": "", + }, + ) + + def testCreatesDirectoryBasedRunfiles(self): + r = runfiles.Create( + { + "RUNFILES_DIR": "runfiles/dir", + "TEST_SRCDIR": "always ignored", + } + ) + self.assertEqual(r.Rlocation("a/b"), "runfiles/dir/a/b") + self.assertEqual(r.Rlocation("foo"), "runfiles/dir/foo") + + def testDirectoryBasedRunfilesEnvVars(self): + r = runfiles.Create( + { + "RUNFILES_DIR": "runfiles/dir", + "TEST_SRCDIR": "always ignored", + } + ) + self.assertDictEqual( + r.EnvVars(), + { + "RUNFILES_DIR": "runfiles/dir", + "JAVA_RUNFILES": "runfiles/dir", + }, + ) + + def testFailsToCreateManifestBasedBecauseManifestDoesNotExist(self): + def _Run(): + runfiles.Create({"RUNFILES_MANIFEST_FILE": "non-existing path"}) + + self.assertRaisesRegex(IOError, "non-existing path", _Run) + + def testFailsToCreateAnyRunfilesBecauseEnvvarsAreNotDefined(self): + with _MockFile(contents=["a b"]) as mf: + runfiles.Create( + { + "RUNFILES_MANIFEST_FILE": mf.Path(), + "RUNFILES_DIR": "whatever", + "TEST_SRCDIR": "always ignored", + } + ) + runfiles.Create( + { + "RUNFILES_DIR": "whatever", + "TEST_SRCDIR": "always ignored", + } + ) + self.assertIsNone(runfiles.Create({"TEST_SRCDIR": "always ignored"})) + self.assertIsNone(runfiles.Create({"FOO": "bar"})) + + def testManifestBasedRlocation(self): + with _MockFile( + contents=[ + "Foo/runfile1", + "Foo/runfile2 C:/Actual Path\\runfile2", + "Foo/Bar/runfile3 D:\\the path\\run file 3.txt", + "Foo/Bar/Dir E:\\Actual Path\\Directory", + ] + ) as mf: + r = runfiles.CreateManifestBased(mf.Path()) + self.assertEqual(r.Rlocation("Foo/runfile1"), "Foo/runfile1") + self.assertEqual(r.Rlocation("Foo/runfile2"), "C:/Actual Path\\runfile2") + self.assertEqual( + r.Rlocation("Foo/Bar/runfile3"), "D:\\the path\\run file 3.txt" + ) + self.assertEqual( + r.Rlocation("Foo/Bar/Dir/runfile4"), + "E:\\Actual Path\\Directory/runfile4", + ) + self.assertEqual( + r.Rlocation("Foo/Bar/Dir/Deeply/Nested/runfile4"), + "E:\\Actual Path\\Directory/Deeply/Nested/runfile4", + ) + self.assertIsNone(r.Rlocation("unknown")) + if RunfilesTest.IsWindows(): + self.assertEqual(r.Rlocation("c:/foo"), "c:/foo") + self.assertEqual(r.Rlocation("c:\\foo"), "c:\\foo") + else: + self.assertEqual(r.Rlocation("/foo"), "/foo") + + def testManifestBasedRlocationWithRepoMappingFromMain(self): + with _MockFile( + contents=[ + ",config.json,config.json~1.2.3", + ",my_module,_main", + ",my_protobuf,protobuf~3.19.2", + ",my_workspace,_main", + "protobuf~3.19.2,config.json,config.json~1.2.3", + "protobuf~3.19.2,protobuf,protobuf~3.19.2", + ] + ) as rm, _MockFile( + contents=[ + "_repo_mapping " + rm.Path(), + "config.json /etc/config.json", + "protobuf~3.19.2/foo/runfile C:/Actual Path\\protobuf\\runfile", + "_main/bar/runfile /the/path/./to/other//other runfile.txt", + "protobuf~3.19.2/bar/dir E:\\Actual Path\\Directory", + ], + ) as mf: + r = runfiles.CreateManifestBased(mf.Path()) + + self.assertEqual( + r.Rlocation("my_module/bar/runfile", ""), + "/the/path/./to/other//other runfile.txt", + ) + self.assertEqual( + r.Rlocation("my_workspace/bar/runfile", ""), + "/the/path/./to/other//other runfile.txt", + ) + self.assertEqual( + r.Rlocation("my_protobuf/foo/runfile", ""), + "C:/Actual Path\\protobuf\\runfile", + ) + self.assertEqual( + r.Rlocation("my_protobuf/bar/dir", ""), "E:\\Actual Path\\Directory" + ) + self.assertEqual( + r.Rlocation("my_protobuf/bar/dir/file", ""), + "E:\\Actual Path\\Directory/file", + ) + self.assertEqual( + r.Rlocation("my_protobuf/bar/dir/de eply/nes ted/fi~le", ""), + "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", + ) + + self.assertIsNone(r.Rlocation("protobuf/foo/runfile")) + self.assertIsNone(r.Rlocation("protobuf/bar/dir")) + self.assertIsNone(r.Rlocation("protobuf/bar/dir/file")) + self.assertIsNone(r.Rlocation("protobuf/bar/dir/dir/de eply/nes ted/fi~le")) + + self.assertEqual( + r.Rlocation("_main/bar/runfile", ""), + "/the/path/./to/other//other runfile.txt", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/foo/runfile", ""), + "C:/Actual Path\\protobuf\\runfile", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/bar/dir", ""), "E:\\Actual Path\\Directory" + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/bar/dir/file", ""), + "E:\\Actual Path\\Directory/file", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", ""), + "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", + ) + + self.assertEqual(r.Rlocation("config.json", ""), "/etc/config.json") + self.assertIsNone(r.Rlocation("_main", "")) + self.assertIsNone(r.Rlocation("my_module", "")) + self.assertIsNone(r.Rlocation("protobuf", "")) + + def testManifestBasedRlocationWithRepoMappingFromOtherRepo(self): + with _MockFile( + contents=[ + ",config.json,config.json~1.2.3", + ",my_module,_main", + ",my_protobuf,protobuf~3.19.2", + ",my_workspace,_main", + "protobuf~3.19.2,config.json,config.json~1.2.3", + "protobuf~3.19.2,protobuf,protobuf~3.19.2", + ] + ) as rm, _MockFile( + contents=[ + "_repo_mapping " + rm.Path(), + "config.json /etc/config.json", + "protobuf~3.19.2/foo/runfile C:/Actual Path\\protobuf\\runfile", + "_main/bar/runfile /the/path/./to/other//other runfile.txt", + "protobuf~3.19.2/bar/dir E:\\Actual Path\\Directory", + ], + ) as mf: + r = runfiles.CreateManifestBased(mf.Path()) + + self.assertEqual( + r.Rlocation("protobuf/foo/runfile", "protobuf~3.19.2"), + "C:/Actual Path\\protobuf\\runfile", + ) + self.assertEqual( + r.Rlocation("protobuf/bar/dir", "protobuf~3.19.2"), + "E:\\Actual Path\\Directory", + ) + self.assertEqual( + r.Rlocation("protobuf/bar/dir/file", "protobuf~3.19.2"), + "E:\\Actual Path\\Directory/file", + ) + self.assertEqual( + r.Rlocation( + "protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" + ), + "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", + ) + + self.assertIsNone(r.Rlocation("my_module/bar/runfile", "protobuf~3.19.2")) + self.assertIsNone(r.Rlocation("my_protobuf/foo/runfile", "protobuf~3.19.2")) + self.assertIsNone(r.Rlocation("my_protobuf/bar/dir", "protobuf~3.19.2")) + self.assertIsNone( + r.Rlocation("my_protobuf/bar/dir/file", "protobuf~3.19.2") + ) + self.assertIsNone( + r.Rlocation( + "my_protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" + ) + ) + + self.assertEqual( + r.Rlocation("_main/bar/runfile", "protobuf~3.19.2"), + "/the/path/./to/other//other runfile.txt", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/foo/runfile", "protobuf~3.19.2"), + "C:/Actual Path\\protobuf\\runfile", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/bar/dir", "protobuf~3.19.2"), + "E:\\Actual Path\\Directory", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/bar/dir/file", "protobuf~3.19.2"), + "E:\\Actual Path\\Directory/file", + ) + self.assertEqual( + r.Rlocation( + "protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" + ), + "E:\\Actual Path\\Directory/de eply/nes ted/fi~le", + ) + + self.assertEqual( + r.Rlocation("config.json", "protobuf~3.19.2"), "/etc/config.json" + ) + self.assertIsNone(r.Rlocation("_main", "protobuf~3.19.2")) + self.assertIsNone(r.Rlocation("my_module", "protobuf~3.19.2")) + self.assertIsNone(r.Rlocation("protobuf", "protobuf~3.19.2")) + + def testDirectoryBasedRlocation(self): + # The _DirectoryBased strategy simply joins the runfiles directory and the + # runfile's path on a "/". This strategy does not perform any normalization, + # nor does it check that the path exists. + r = runfiles.CreateDirectoryBased("foo/bar baz//qux/") + self.assertEqual(r.Rlocation("arg"), "foo/bar baz//qux/arg") + if RunfilesTest.IsWindows(): + self.assertEqual(r.Rlocation("c:/foo"), "c:/foo") + self.assertEqual(r.Rlocation("c:\\foo"), "c:\\foo") + else: + self.assertEqual(r.Rlocation("/foo"), "/foo") + + def testDirectoryBasedRlocationWithRepoMappingFromMain(self): + with _MockFile( + name="_repo_mapping", + contents=[ + "_,config.json,config.json~1.2.3", + ",my_module,_main", + ",my_protobuf,protobuf~3.19.2", + ",my_workspace,_main", + "protobuf~3.19.2,config.json,config.json~1.2.3", + "protobuf~3.19.2,protobuf,protobuf~3.19.2", + ], + ) as rm: + dir = os.path.dirname(rm.Path()) + r = runfiles.CreateDirectoryBased(dir) + + self.assertEqual( + r.Rlocation("my_module/bar/runfile", ""), dir + "/_main/bar/runfile" + ) + self.assertEqual( + r.Rlocation("my_workspace/bar/runfile", ""), dir + "/_main/bar/runfile" + ) + self.assertEqual( + r.Rlocation("my_protobuf/foo/runfile", ""), + dir + "/protobuf~3.19.2/foo/runfile", + ) + self.assertEqual( + r.Rlocation("my_protobuf/bar/dir", ""), dir + "/protobuf~3.19.2/bar/dir" + ) + self.assertEqual( + r.Rlocation("my_protobuf/bar/dir/file", ""), + dir + "/protobuf~3.19.2/bar/dir/file", + ) + self.assertEqual( + r.Rlocation("my_protobuf/bar/dir/de eply/nes ted/fi~le", ""), + dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", + ) + + self.assertEqual( + r.Rlocation("protobuf/foo/runfile", ""), dir + "/protobuf/foo/runfile" + ) + self.assertEqual( + r.Rlocation("protobuf/bar/dir/dir/de eply/nes ted/fi~le", ""), + dir + "/protobuf/bar/dir/dir/de eply/nes ted/fi~le", + ) + + self.assertEqual( + r.Rlocation("_main/bar/runfile", ""), dir + "/_main/bar/runfile" + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/foo/runfile", ""), + dir + "/protobuf~3.19.2/foo/runfile", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/bar/dir", ""), + dir + "/protobuf~3.19.2/bar/dir", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/bar/dir/file", ""), + dir + "/protobuf~3.19.2/bar/dir/file", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", ""), + dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", + ) + + self.assertEqual(r.Rlocation("config.json", ""), dir + "/config.json") + + def testDirectoryBasedRlocationWithRepoMappingFromOtherRepo(self): + with _MockFile( + name="_repo_mapping", + contents=[ + "_,config.json,config.json~1.2.3", + ",my_module,_main", + ",my_protobuf,protobuf~3.19.2", + ",my_workspace,_main", + "protobuf~3.19.2,config.json,config.json~1.2.3", + "protobuf~3.19.2,protobuf,protobuf~3.19.2", + ], + ) as rm: + dir = os.path.dirname(rm.Path()) + r = runfiles.CreateDirectoryBased(dir) + + self.assertEqual( + r.Rlocation("protobuf/foo/runfile", "protobuf~3.19.2"), + dir + "/protobuf~3.19.2/foo/runfile", + ) + self.assertEqual( + r.Rlocation("protobuf/bar/dir", "protobuf~3.19.2"), + dir + "/protobuf~3.19.2/bar/dir", + ) + self.assertEqual( + r.Rlocation("protobuf/bar/dir/file", "protobuf~3.19.2"), + dir + "/protobuf~3.19.2/bar/dir/file", + ) + self.assertEqual( + r.Rlocation( + "protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" + ), + dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", + ) + + self.assertEqual( + r.Rlocation("my_module/bar/runfile", "protobuf~3.19.2"), + dir + "/my_module/bar/runfile", + ) + self.assertEqual( + r.Rlocation( + "my_protobuf/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" + ), + dir + "/my_protobuf/bar/dir/de eply/nes ted/fi~le", + ) + + self.assertEqual( + r.Rlocation("_main/bar/runfile", "protobuf~3.19.2"), + dir + "/_main/bar/runfile", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/foo/runfile", "protobuf~3.19.2"), + dir + "/protobuf~3.19.2/foo/runfile", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/bar/dir", "protobuf~3.19.2"), + dir + "/protobuf~3.19.2/bar/dir", + ) + self.assertEqual( + r.Rlocation("protobuf~3.19.2/bar/dir/file", "protobuf~3.19.2"), + dir + "/protobuf~3.19.2/bar/dir/file", + ) + self.assertEqual( + r.Rlocation( + "protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", "protobuf~3.19.2" + ), + dir + "/protobuf~3.19.2/bar/dir/de eply/nes ted/fi~le", + ) + + self.assertEqual( + r.Rlocation("config.json", "protobuf~3.19.2"), dir + "/config.json" + ) + + def testCurrentRepository(self): + # This test assumes that it is running without --enable_bzlmod as the + # correct result with Bzlmod would be the empty string - the canonical + # name # of the main repository. Without Bzlmod, the main repository is + # treated just like any other repository and has the name of its + # runfiles directory returned, which coincides with the name specified + # in the WORKSPACE file. + # + # Specify a fake runfiles directory to verify that its value isn't used + # by the function. + self.assertEqual( + runfiles.Create({"RUNFILES_DIR": "whatever"}).CurrentRepository(), + "rules_python", + ) + + @staticmethod + def IsWindows(): + return os.name == "nt" + + +class _MockFile(object): + def __init__(self, name=None, contents=None): + self._contents = contents or [] + self._name = name or "x" + self._path = None + + def __enter__(self): + tmpdir = os.environ.get("TEST_TMPDIR") + self._path = os.path.join(tempfile.mkdtemp(dir=tmpdir), self._name) + with open(self._path, "wt") as f: + f.writelines(l + "\n" for l in self._contents) + return self + + def __exit__(self, exc_type, exc_value, traceback): + os.remove(self._path) + os.rmdir(os.path.dirname(self._path)) + + def Path(self): + return self._path + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/runfiles/runfiles_wheel_integration_test.sh b/tests/runfiles/runfiles_wheel_integration_test.sh new file mode 100755 index 0000000000..8e9c6082a5 --- /dev/null +++ b/tests/runfiles/runfiles_wheel_integration_test.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# 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. + +# Manual test, run outside of Bazel, to check that our runfiles wheel should be functional +# for users who install it from pypi. +set -o errexit + +SCRIPTPATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" + +bazel 2>/dev/null build --stamp --embed_label=1.2.3 //python/runfiles:wheel +wheelpath=$SCRIPTPATH/../../$(bazel 2>/dev/null cquery --output=files //python/runfiles:wheel) +PYTHONPATH=$wheelpath python3 -c 'import importlib;print(importlib.import_module("runfiles"))' diff --git a/third_party/github.com/bazelbuild/bazel-skylib/README.md b/third_party/github.com/bazelbuild/bazel-skylib/README.md deleted file mode 100644 index 5ed93ff6d1..0000000000 --- a/third_party/github.com/bazelbuild/bazel-skylib/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# vendored copy of skylib - -This exists so that users of rules_python don't have to install bazel-skylib -copied from https://github.com/bazelbuild/bazel-skylib/blob/1.0.3 \ No newline at end of file diff --git a/third_party/github.com/bazelbuild/bazel-skylib/lib/BUILD b/third_party/github.com/bazelbuild/bazel-skylib/lib/BUILD deleted file mode 100644 index 9560aed406..0000000000 --- a/third_party/github.com/bazelbuild/bazel-skylib/lib/BUILD +++ /dev/null @@ -1,22 +0,0 @@ -load("@bazel_skylib//:bzl_library.bzl", "bzl_library") - -licenses(["notice"]) - -package(default_visibility = ["//visibility:public"]) - -# export bzl files for the documentation -exports_files( - glob(["*.bzl"]), - visibility = ["//:__subpackages__"], -) - -filegroup( - name = "distribution", - srcs = glob(["**"]), - visibility = ["//:__pkg__"], -) - -bzl_library( - name = "versions", - srcs = ["versions.bzl"], -) diff --git a/third_party/github.com/bazelbuild/bazel-skylib/lib/versions.bzl b/third_party/github.com/bazelbuild/bazel-skylib/lib/versions.bzl deleted file mode 100644 index 3cd60197aa..0000000000 --- a/third_party/github.com/bazelbuild/bazel-skylib/lib/versions.bzl +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright 2018 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. - -"""Skylib module containing functions for checking Bazel versions.""" - -def _get_bazel_version(): - """Returns the current Bazel version""" - - return native.bazel_version - -def _extract_version_number(bazel_version): - """Extracts the semantic version number from a version string - - Args: - bazel_version: the version string that begins with the semantic version - e.g. "1.2.3rc1 abc1234" where "abc1234" is a commit hash. - - Returns: - The semantic version string, like "1.2.3". - """ - for i in range(len(bazel_version)): - c = bazel_version[i] - if not (c.isdigit() or c == "."): - return bazel_version[:i] - return bazel_version - -# Parse the bazel version string from `native.bazel_version`. -# e.g. -# "0.10.0rc1 abc123d" => (0, 10, 0) -# "0.3.0" => (0, 3, 0) -def _parse_bazel_version(bazel_version): - """Parses a version string into a 3-tuple of ints - - int tuples can be compared directly using binary operators (<, >). - - Args: - bazel_version: the Bazel version string - - Returns: - An int 3-tuple of a (major, minor, patch) version. - """ - - version = _extract_version_number(bazel_version) - return tuple([int(n) for n in version.split(".")]) - -def _is_at_most(threshold, version): - """Check that a version is lower or equals to a threshold. - - Args: - threshold: the maximum version string - version: the version string to be compared to the threshold - - Returns: - True if version <= threshold. - """ - return _parse_bazel_version(version) <= _parse_bazel_version(threshold) - -def _is_at_least(threshold, version): - """Check that a version is higher or equals to a threshold. - - Args: - threshold: the minimum version string - version: the version string to be compared to the threshold - - Returns: - True if version >= threshold. - """ - - return _parse_bazel_version(version) >= _parse_bazel_version(threshold) - -def _check_bazel_version(minimum_bazel_version, maximum_bazel_version = None, bazel_version = None): - """Check that the version of Bazel is valid within the specified range. - - Args: - minimum_bazel_version: minimum version of Bazel expected - maximum_bazel_version: maximum version of Bazel expected - bazel_version: the version of Bazel to check. Used for testing, defaults to native.bazel_version - """ - if not bazel_version: - if "bazel_version" not in dir(native): - fail("Bazel version cannot be determined; expected at least {}".format( - minimum_bazel_version, - )) - elif not native.bazel_version: - # Using a non-release version, assume it is good. - return - else: - bazel_version = native.bazel_version - - if not _is_at_least( - threshold = minimum_bazel_version, - version = bazel_version, - ): - fail("Current Bazel version is {}; expected at least {}".format( - bazel_version, - minimum_bazel_version, - )) - - if maximum_bazel_version: - if not _is_at_most( - threshold = maximum_bazel_version, - version = bazel_version, - ): - fail("Current Bazel version is {}; expected at most {}".format( - bazel_version, - maximum_bazel_version, - )) - - pass - -versions = struct( - get = _get_bazel_version, - parse = _parse_bazel_version, - check = _check_bazel_version, - is_at_most = _is_at_most, - is_at_least = _is_at_least, -) diff --git a/third_party/github.com/bazelbuild/bazel-skylib/rules/BUILD b/third_party/github.com/bazelbuild/bazel-skylib/rules/BUILD deleted file mode 100644 index 6857449878..0000000000 --- a/third_party/github.com/bazelbuild/bazel-skylib/rules/BUILD +++ /dev/null @@ -1,36 +0,0 @@ -load("@bazel_skylib//:bzl_library.bzl", "bzl_library") - -licenses(["notice"]) - -package(default_visibility = ["//visibility:public"]) - -bzl_library( - name = "copy_file", - srcs = ["copy_file.bzl"], - deps = ["//third_party/github.com/bazelbuild/bazel-skylib/rules/private:copy_file_private"], -) - -filegroup( - name = "test_deps", - testonly = True, - srcs = [ - "BUILD", - ] + glob(["*.bzl"]), -) - -# The files needed for distribution -filegroup( - name = "distribution", - srcs = [ - "BUILD", - ] + glob(["*.bzl"]), - visibility = [ - "//:__pkg__", - ], -) - -# export bzl files for the documentation -exports_files( - glob(["*.bzl"]), - visibility = ["//:__subpackages__"], -) diff --git a/third_party/github.com/bazelbuild/bazel-skylib/rules/private/BUILD b/third_party/github.com/bazelbuild/bazel-skylib/rules/private/BUILD deleted file mode 100644 index a1aeb39914..0000000000 --- a/third_party/github.com/bazelbuild/bazel-skylib/rules/private/BUILD +++ /dev/null @@ -1,18 +0,0 @@ -load("@bazel_skylib//:bzl_library.bzl", "bzl_library") - -licenses(["notice"]) - -bzl_library( - name = "copy_file_private", - srcs = ["copy_file_private.bzl"], - visibility = ["//third_party/github.com/bazelbuild/bazel-skylib/rules:__pkg__"], -) - -# The files needed for distribution -filegroup( - name = "distribution", - srcs = glob(["*"]), - visibility = [ - "//:__subpackages__", - ], -) diff --git a/third_party/github.com/bazelbuild/bazel-skylib/rules/private/copy_file_private.bzl b/third_party/github.com/bazelbuild/bazel-skylib/rules/private/copy_file_private.bzl deleted file mode 100644 index d044c9767e..0000000000 --- a/third_party/github.com/bazelbuild/bazel-skylib/rules/private/copy_file_private.bzl +++ /dev/null @@ -1,141 +0,0 @@ -# Copyright 2019 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. - -"""Implementation of copy_file macro and underlying rules. - -These rules copy a file to another location using Bash (on Linux/macOS) or -cmd.exe (on Windows). '_copy_xfile' marks the resulting file executable, -'_copy_file' does not. -""" - -def copy_cmd(ctx, src, dst): - # Most Windows binaries built with MSVC use a certain argument quoting - # scheme. Bazel uses that scheme too to quote arguments. However, - # cmd.exe uses different semantics, so Bazel's quoting is wrong here. - # To fix that we write the command to a .bat file so no command line - # quoting or escaping is required. - bat = ctx.actions.declare_file(ctx.label.name + "-cmd.bat") - ctx.actions.write( - output = bat, - # Do not use lib/shell.bzl's shell.quote() method, because that uses - # Bash quoting syntax, which is different from cmd.exe's syntax. - content = "@copy /Y \"%s\" \"%s\" >NUL" % ( - src.path.replace("/", "\\"), - dst.path.replace("/", "\\"), - ), - is_executable = True, - ) - ctx.actions.run( - inputs = [src], - tools = [bat], - outputs = [dst], - executable = "cmd.exe", - arguments = ["/C", bat.path.replace("/", "\\")], - mnemonic = "CopyFile", - progress_message = "Copying files", - use_default_shell_env = True, - ) - -def copy_bash(ctx, src, dst): - ctx.actions.run_shell( - tools = [src], - outputs = [dst], - command = "cp -f \"$1\" \"$2\"", - arguments = [src.path, dst.path], - mnemonic = "CopyFile", - progress_message = "Copying files", - use_default_shell_env = True, - ) - -def _copy_file_impl(ctx): - if ctx.attr.allow_symlink: - ctx.actions.symlink( - output = ctx.outputs.out, - target_file = ctx.file.src, - is_executable = ctx.attr.is_executable, - ) - elif ctx.attr.is_windows: - copy_cmd(ctx, ctx.file.src, ctx.outputs.out) - else: - copy_bash(ctx, ctx.file.src, ctx.outputs.out) - - files = depset(direct = [ctx.outputs.out]) - runfiles = ctx.runfiles(files = [ctx.outputs.out]) - if ctx.attr.is_executable: - return [DefaultInfo(files = files, runfiles = runfiles, executable = ctx.outputs.out)] - else: - return [DefaultInfo(files = files, runfiles = runfiles)] - -_ATTRS = { - "allow_symlink": attr.bool(mandatory = True), - "is_executable": attr.bool(mandatory = True), - "is_windows": attr.bool(mandatory = True), - "out": attr.output(mandatory = True), - "src": attr.label(mandatory = True, allow_single_file = True), -} - -_copy_file = rule( - implementation = _copy_file_impl, - provides = [DefaultInfo], - attrs = _ATTRS, -) - -_copy_xfile = rule( - implementation = _copy_file_impl, - executable = True, - provides = [DefaultInfo], - attrs = _ATTRS, -) - -def copy_file(name, src, out, is_executable = False, allow_symlink = False, **kwargs): - """Copies a file to another location. - - `native.genrule()` is sometimes used to copy files (often wishing to rename them). The 'copy_file' rule does this with a simpler interface than genrule. - - This rule uses a Bash command on Linux/macOS/non-Windows, and a cmd.exe command on Windows (no Bash is required). - - Args: - name: Name of the rule. - src: A Label. The file to make a copy of. (Can also be the label of a rule - that generates a file.) - out: Path of the output file, relative to this package. - is_executable: A boolean. Whether to make the output file executable. When - True, the rule's output can be executed using `bazel run` and can be - in the srcs of binary and test rules that require executable sources. - WARNING: If `allow_symlink` is True, `src` must also be executable. - allow_symlink: A boolean. Whether to allow symlinking instead of copying. - When False, the output is always a hard copy. When True, the output - *can* be a symlink, but there is no guarantee that a symlink is - created (i.e., at the time of writing, we don't create symlinks on - Windows). Set this to True if you need fast copying and your tools can - handle symlinks (which most UNIX tools can). - **kwargs: further keyword arguments, e.g. `visibility` - """ - - copy_file_impl = _copy_file - if is_executable: - copy_file_impl = _copy_xfile - - copy_file_impl( - name = name, - src = src, - out = out, - is_windows = select({ - "@bazel_tools//src/conditions:host_windows": True, - "//conditions:default": False, - }), - is_executable = is_executable, - allow_symlink = allow_symlink, - **kwargs - ) diff --git a/tools/BUILD b/tools/BUILD.bazel similarity index 94% rename from tools/BUILD rename to tools/BUILD.bazel index 789bc2b53f..fd951d9086 100644 --- a/tools/BUILD +++ b/tools/BUILD.bazel @@ -15,7 +15,7 @@ load("//python:defs.bzl", "py_binary") package(default_visibility = ["//visibility:public"]) -licenses(["notice"]) # Apache 2.0 +licenses(["notice"]) # Implementation detail of py_wheel rule. py_binary( @@ -26,7 +26,7 @@ py_binary( filegroup( name = "distribution", srcs = [ - "BUILD", + "BUILD.bazel", "wheelmaker.py", ], visibility = ["//:__pkg__"], diff --git a/tools/bazel_integration_test/BUILD b/tools/bazel_integration_test/BUILD.bazel similarity index 100% rename from tools/bazel_integration_test/BUILD rename to tools/bazel_integration_test/BUILD.bazel diff --git a/tools/bazel_integration_test/bazel_integration_test.bzl b/tools/bazel_integration_test/bazel_integration_test.bzl index 92d64e5dfa..c016551319 100644 --- a/tools/bazel_integration_test/bazel_integration_test.bzl +++ b/tools/bazel_integration_test/bazel_integration_test.bzl @@ -1,9 +1,23 @@ +# 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. + "Define a rule for running bazel test under Bazel" -load("//:version.bzl", "SUPPORTED_BAZEL_VERSIONS") +load("//:version.bzl", "SUPPORTED_BAZEL_VERSIONS", "bazel_version_to_binary_label") load("//python:defs.bzl", "py_test") -BAZEL_BINARY = "@build_bazel_bazel_%s//:bazel_binary" % SUPPORTED_BAZEL_VERSIONS[0].replace(".", "_") +BAZEL_BINARY = bazel_version_to_binary_label(SUPPORTED_BAZEL_VERSIONS[0]) _ATTRS = { "bazel_binary": attr.label( @@ -19,6 +33,10 @@ It is assumed by the test runner that the bazel binary is found at label_workspa Note that if a command contains a bare `--` argument, the --test_arg passed to Bazel will appear before it. """, ), + "bzlmod": attr.bool( + default = False, + doc = """Whether the test uses bzlmod.""", + ), "workspace_files": attr.label( doc = """A filegroup of all files in the workspace-under-test necessary to run the test.""", ), @@ -44,12 +62,14 @@ You probably need to run {{ "workspaceRoot": "{TMPL_workspace_root}", "bazelBinaryWorkspace": "{TMPL_bazel_binary_workspace}", - "bazelCommands": [ {TMPL_bazel_commands} ] + "bazelCommands": [ {TMPL_bazel_commands} ], + "bzlmod": {TMPL_bzlmod} }} """.format( TMPL_workspace_root = ctx.files.workspace_files[0].dirname, TMPL_bazel_binary_workspace = ctx.attr.bazel_binary.label.workspace_name, TMPL_bazel_commands = ", ".join(["\"%s\"" % s for s in ctx.attr.bazel_commands]), + TMPL_bzlmod = str(ctx.attr.bzlmod).lower(), ), ) @@ -64,16 +84,19 @@ _config = rule( attrs = _ATTRS, ) -def bazel_integration_test(name, **kwargs): +def bazel_integration_test(name, override_bazel_version = None, bzlmod = False, dirname = None, **kwargs): """Wrapper macro to set default srcs and run a py_test with config Args: name: name of the resulting py_test + override_bazel_version: bazel version to use in test + bzlmod: whether the test uses bzlmod + dirname: the directory name of the test. Defaults to value of `name` after trimming the `_example` suffix. **kwargs: additional attributes like timeout and visibility """ # By default, we assume sources for "pip_example" are in examples/pip/**/* - dirname = name[:-len("_example")] + dirname = dirname or name[:-len("_example")] native.filegroup( name = "_%s_sources" % name, srcs = native.glob( @@ -83,11 +106,17 @@ def bazel_integration_test(name, **kwargs): ) workspace_files = kwargs.pop("workspace_files", "_%s_sources" % name) + bazel_binary = BAZEL_BINARY if not override_bazel_version else bazel_version_to_binary_label(override_bazel_version) _config( name = "_%s_config" % name, workspace_files = workspace_files, + bazel_binary = bazel_binary, + bzlmod = bzlmod, ) + tags = kwargs.pop("tags", []) + tags.append("integration-test") + py_test( name = name, srcs = [Label("//tools/bazel_integration_test:test_runner.py")], @@ -95,10 +124,11 @@ def bazel_integration_test(name, **kwargs): args = [native.package_name() + "/_%s_config.json" % name], deps = [Label("//python/runfiles")], data = [ - BAZEL_BINARY, + bazel_binary, "//:distribution", "_%s_config" % name, workspace_files, ], + tags = tags, **kwargs ) diff --git a/tools/bazel_integration_test/test_runner.py b/tools/bazel_integration_test/test_runner.py index ce81274d5e..03599fbd0e 100644 --- a/tools/bazel_integration_test/test_runner.py +++ b/tools/bazel_integration_test/test_runner.py @@ -1,3 +1,17 @@ +# 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 json import os import platform @@ -52,6 +66,19 @@ def main(conf_file): "--override_repository=rules_python=%s/rules_python" % os.environ["TEST_SRCDIR"] ) + bazel_args.append( + "--override_repository=rules_python_gazelle_plugin=%s/rules_python_gazelle_plugin" + % os.environ["TEST_SRCDIR"] + ) + + # TODO: --override_module isn't supported in the current BAZEL_VERSION (5.2.0) + # This condition and attribute can be removed when bazel is updated for + # the rest of rules_python. + if config["bzlmod"]: + bazel_args.append( + "--override_module=rules_python=%s/rules_python" + % os.environ["TEST_SRCDIR"] + ) # Bazel's wrapper script needs this or you get # 2020/07/13 21:58:11 could not get the user's cache directory: $HOME is not defined diff --git a/tools/bazel_integration_test/update_deleted_packages.sh b/tools/bazel_integration_test/update_deleted_packages.sh index 8a215c6d4a..54db02630f 100755 --- a/tools/bazel_integration_test/update_deleted_packages.sh +++ b/tools/bazel_integration_test/update_deleted_packages.sh @@ -1,11 +1,39 @@ #!/usr/bin/env bash +# 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. + # For integration tests, we want to be able to glob() up the sources inside a nested package # See explanation in .bazelrc +# +# This script ensures that we only delete subtrees that have something a file +# signifying a new bazel workspace, whether it be bzlmod or classic. Generic +# algorithm: +# 1. Get all directories where a WORKSPACE or MODULE.bazel exists. +# 2. For each of the directories, get all directories that contains a BUILD.bazel file. +# 3. Sort and remove duplicates. -set -eux +set -euxo pipefail DIR="$(dirname $0)/../.." +cd $DIR + # The sed -i.bak pattern is compatible between macos and linux sed -i.bak "/^[^#].*--deleted_packages/s#=.*#=$(\ - find examples/*/* tests/*/* \( -name BUILD -or -name BUILD.bazel \) | xargs -n 1 dirname | paste -sd, -\ + find examples/*/* tests/*/* \( -name WORKSPACE -or -name MODULE.bazel \) | + xargs -n 1 dirname | + xargs -n 1 -I{} find {} \( -name BUILD -or -name BUILD.bazel \) | + xargs -n 1 dirname | + sort -u | + paste -sd, -\ )#" $DIR/.bazelrc && rm .bazelrc.bak diff --git a/tools/build_defs/python/BUILD.bazel b/tools/build_defs/python/BUILD.bazel new file mode 100644 index 0000000000..aa21042e25 --- /dev/null +++ b/tools/build_defs/python/BUILD.bazel @@ -0,0 +1,13 @@ +# 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. diff --git a/tools/build_defs/python/tests/BUILD.bazel b/tools/build_defs/python/tests/BUILD.bazel new file mode 100644 index 0000000000..92bdc5c396 --- /dev/null +++ b/tools/build_defs/python/tests/BUILD.bazel @@ -0,0 +1,79 @@ +# 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. + +load("@rules_cc//cc:defs.bzl", "cc_toolchain", "cc_toolchain_suite") +load(":fake_cc_toolchain_config.bzl", "fake_cc_toolchain_config") + +platform( + name = "mac", + constraint_values = [ + "@platforms//os:macos", + ], +) + +platform( + name = "linux", + constraint_values = [ + "@platforms//os:linux", + ], +) + +cc_toolchain_suite( + name = "cc_toolchain_suite", + tags = ["manual"], + toolchains = { + "darwin_x86_64": ":mac_toolchain", + "k8": ":linux_toolchain", + }, +) + +filegroup(name = "empty") + +cc_toolchain( + name = "mac_toolchain", + all_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 0, + toolchain_config = ":mac_toolchain_config", + toolchain_identifier = "mac-toolchain", +) + +fake_cc_toolchain_config( + name = "mac_toolchain_config", + target_cpu = "darwin_x86_64", + toolchain_identifier = "mac-toolchain", +) + +cc_toolchain( + name = "linux_toolchain", + all_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 0, + toolchain_config = ":linux_toolchain_config", + toolchain_identifier = "linux-toolchain", +) + +fake_cc_toolchain_config( + name = "linux_toolchain_config", + target_cpu = "k8", + toolchain_identifier = "linux-toolchain", +) diff --git a/tools/build_defs/python/tests/base_tests.bzl b/tools/build_defs/python/tests/base_tests.bzl new file mode 100644 index 0000000000..467611fcd8 --- /dev/null +++ b/tools/build_defs/python/tests/base_tests.bzl @@ -0,0 +1,124 @@ +# 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. +"""Tests common to py_test, py_binary, and py_library rules.""" + +load("@rules_testing//lib:analysis_test.bzl", "analysis_test") +load("@rules_testing//lib:truth.bzl", "matching") +load("@rules_testing//lib:util.bzl", "PREVENT_IMPLICIT_BUILDING_TAGS", rt_util = "util") +load("//python:defs.bzl", "PyInfo") +load("//tools/build_defs/python/tests:py_info_subject.bzl", "py_info_subject") +load("//tools/build_defs/python/tests:util.bzl", pt_util = "util") + +_tests = [] + +def _produces_py_info_impl(ctx): + return [PyInfo(transitive_sources = depset(ctx.files.srcs))] + +_produces_py_info = rule( + implementation = _produces_py_info_impl, + attrs = {"srcs": attr.label_list(allow_files = True)}, +) + +def _test_consumes_provider(name, config): + rt_util.helper_target( + config.base_test_rule, + name = name + "_subject", + deps = [name + "_produces_py_info"], + ) + rt_util.helper_target( + _produces_py_info, + name = name + "_produces_py_info", + srcs = [rt_util.empty_file(name + "_produce.py")], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_consumes_provider_impl, + ) + +def _test_consumes_provider_impl(env, target): + env.expect.that_target(target).provider( + PyInfo, + factory = py_info_subject, + ).transitive_sources().contains("{package}/{test_name}_produce.py") + +_tests.append(_test_consumes_provider) + +def _test_requires_provider(name, config): + rt_util.helper_target( + config.base_test_rule, + name = name + "_subject", + deps = [name + "_nopyinfo"], + ) + rt_util.helper_target( + native.filegroup, + name = name + "_nopyinfo", + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_requires_provider_impl, + expect_failure = True, + ) + +def _test_requires_provider_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("mandatory*PyInfo"), + ) + +_tests.append(_test_requires_provider) + +def _test_data_sets_uses_shared_library(name, config): + rt_util.helper_target( + config.base_test_rule, + name = name + "_subject", + data = [rt_util.empty_file(name + "_dso.so")], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_data_sets_uses_shared_library_impl, + ) + +def _test_data_sets_uses_shared_library_impl(env, target): + env.expect.that_target(target).provider( + PyInfo, + factory = py_info_subject, + ).uses_shared_libraries().equals(True) + +_tests.append(_test_data_sets_uses_shared_library) + +def _test_tags_can_be_tuple(name, config): + # We don't use a helper because we want to ensure that value passed is + # a tuple. + config.base_test_rule( + name = name + "_subject", + tags = ("one", "two") + tuple(PREVENT_IMPLICIT_BUILDING_TAGS), + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_tags_can_be_tuple_impl, + ) + +def _test_tags_can_be_tuple_impl(env, target): + env.expect.that_target(target).tags().contains_at_least([ + "one", + "two", + ]) + +_tests.append(_test_tags_can_be_tuple) + +def create_base_tests(config): + return pt_util.create_tests(_tests, config = config) diff --git a/tools/build_defs/python/tests/fake_cc_toolchain_config.bzl b/tools/build_defs/python/tests/fake_cc_toolchain_config.bzl new file mode 100644 index 0000000000..b3214a61ba --- /dev/null +++ b/tools/build_defs/python/tests/fake_cc_toolchain_config.bzl @@ -0,0 +1,37 @@ +# 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. + +"""Fake for providing CcToolchainConfigInfo.""" + +def _impl(ctx): + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + toolchain_identifier = ctx.attr.toolchain_identifier, + host_system_name = "local", + target_system_name = "local", + target_cpu = ctx.attr.target_cpu, + target_libc = "unknown", + compiler = "clang", + abi_version = "unknown", + abi_libc_version = "unknown", + ) + +fake_cc_toolchain_config = rule( + implementation = _impl, + attrs = { + "target_cpu": attr.string(), + "toolchain_identifier": attr.string(), + }, + provides = [CcToolchainConfigInfo], +) diff --git a/tools/build_defs/python/tests/py_binary/BUILD.bazel b/tools/build_defs/python/tests/py_binary/BUILD.bazel new file mode 100644 index 0000000000..17a6690b82 --- /dev/null +++ b/tools/build_defs/python/tests/py_binary/BUILD.bazel @@ -0,0 +1,17 @@ +# 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. + +load(":py_binary_tests.bzl", "py_binary_test_suite") + +py_binary_test_suite(name = "py_binary_tests") diff --git a/tools/build_defs/python/tests/py_binary/py_binary_tests.bzl b/tools/build_defs/python/tests/py_binary/py_binary_tests.bzl new file mode 100644 index 0000000000..8d32632610 --- /dev/null +++ b/tools/build_defs/python/tests/py_binary/py_binary_tests.bzl @@ -0,0 +1,28 @@ +# 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. +"""Tests for py_binary.""" + +load("//python:defs.bzl", "py_binary") +load( + "//tools/build_defs/python/tests:py_executable_base_tests.bzl", + "create_executable_tests", +) + +def py_binary_test_suite(name): + config = struct(rule = py_binary) + + native.test_suite( + name = name, + tests = create_executable_tests(config), + ) diff --git a/tools/build_defs/python/tests/py_executable_base_tests.bzl b/tools/build_defs/python/tests/py_executable_base_tests.bzl new file mode 100644 index 0000000000..c66ea11e00 --- /dev/null +++ b/tools/build_defs/python/tests/py_executable_base_tests.bzl @@ -0,0 +1,272 @@ +# 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. +"""Tests common to py_binary and py_test (executable rules).""" + +load("@rules_testing//lib:analysis_test.bzl", "analysis_test") +load("@rules_testing//lib:truth.bzl", "matching") +load("@rules_testing//lib:util.bzl", rt_util = "util") +load("//tools/build_defs/python/tests:base_tests.bzl", "create_base_tests") +load("//tools/build_defs/python/tests:util.bzl", "WINDOWS_ATTR", pt_util = "util") + +_tests = [] + +def _test_executable_in_runfiles(name, config): + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [name + "_subject.py"], + ) + analysis_test( + name = name, + impl = _test_executable_in_runfiles_impl, + target = name + "_subject", + attrs = WINDOWS_ATTR, + ) + +_tests.append(_test_executable_in_runfiles) + +def _test_executable_in_runfiles_impl(env, target): + if pt_util.is_windows(env): + exe = ".exe" + else: + exe = "" + + env.expect.that_target(target).runfiles().contains_at_least([ + "{workspace}/{package}/{test_name}_subject" + exe, + ]) + +def _test_default_main_can_be_generated(name, config): + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [rt_util.empty_file(name + "_subject.py")], + ) + analysis_test( + name = name, + impl = _test_default_main_can_be_generated_impl, + target = name + "_subject", + ) + +_tests.append(_test_default_main_can_be_generated) + +def _test_default_main_can_be_generated_impl(env, target): + env.expect.that_target(target).default_outputs().contains( + "{package}/{test_name}_subject.py", + ) + +def _test_default_main_can_have_multiple_path_segments(name, config): + rt_util.helper_target( + config.rule, + name = name + "/subject", + srcs = [name + "/subject.py"], + ) + analysis_test( + name = name, + impl = _test_default_main_can_have_multiple_path_segments_impl, + target = name + "/subject", + ) + +_tests.append(_test_default_main_can_have_multiple_path_segments) + +def _test_default_main_can_have_multiple_path_segments_impl(env, target): + env.expect.that_target(target).default_outputs().contains( + "{package}/{test_name}/subject.py", + ) + +def _test_default_main_must_be_in_srcs(name, config): + # Bazel 5 will crash with a Java stacktrace when the native Python + # rules have an error. + if not pt_util.is_bazel_6_or_higher(): + rt_util.skip_test(name = name) + return + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = ["other.py"], + ) + analysis_test( + name = name, + impl = _test_default_main_must_be_in_srcs_impl, + target = name + "_subject", + expect_failure = True, + ) + +_tests.append(_test_default_main_must_be_in_srcs) + +def _test_default_main_must_be_in_srcs_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("default*does not appear in srcs"), + ) + +def _test_default_main_cannot_be_ambiguous(name, config): + # Bazel 5 will crash with a Java stacktrace when the native Python + # rules have an error. + if not pt_util.is_bazel_6_or_higher(): + rt_util.skip_test(name = name) + return + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [name + "_subject.py", "other/{}_subject.py".format(name)], + ) + analysis_test( + name = name, + impl = _test_default_main_cannot_be_ambiguous_impl, + target = name + "_subject", + expect_failure = True, + ) + +_tests.append(_test_default_main_cannot_be_ambiguous) + +def _test_default_main_cannot_be_ambiguous_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("default main*matches multiple files"), + ) + +def _test_explicit_main(name, config): + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = ["custom.py"], + main = "custom.py", + ) + analysis_test( + name = name, + impl = _test_explicit_main_impl, + target = name + "_subject", + ) + +_tests.append(_test_explicit_main) + +def _test_explicit_main_impl(env, target): + # There isn't a direct way to ask what main file was selected, so we + # rely on it being in the default outputs. + env.expect.that_target(target).default_outputs().contains( + "{package}/custom.py", + ) + +def _test_explicit_main_cannot_be_ambiguous(name, config): + # Bazel 5 will crash with a Java stacktrace when the native Python + # rules have an error. + if not pt_util.is_bazel_6_or_higher(): + rt_util.skip_test(name = name) + return + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = ["x/foo.py", "y/foo.py"], + main = "foo.py", + ) + analysis_test( + name = name, + impl = _test_explicit_main_cannot_be_ambiguous_impl, + target = name + "_subject", + expect_failure = True, + ) + +_tests.append(_test_explicit_main_cannot_be_ambiguous) + +def _test_explicit_main_cannot_be_ambiguous_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("foo.py*matches multiple"), + ) + +def _test_files_to_build(name, config): + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [name + "_subject.py"], + ) + analysis_test( + name = name, + impl = _test_files_to_build_impl, + target = name + "_subject", + attrs = WINDOWS_ATTR, + ) + +_tests.append(_test_files_to_build) + +def _test_files_to_build_impl(env, target): + default_outputs = env.expect.that_target(target).default_outputs() + if pt_util.is_windows(env): + default_outputs.contains("{package}/{test_name}_subject.exe") + else: + default_outputs.contains_exactly([ + "{package}/{test_name}_subject", + "{package}/{test_name}_subject.py", + ]) + +def _test_name_cannot_end_in_py(name, config): + # Bazel 5 will crash with a Java stacktrace when the native Python + # rules have an error. + if not pt_util.is_bazel_6_or_higher(): + rt_util.skip_test(name = name) + return + rt_util.helper_target( + config.rule, + name = name + "_subject.py", + srcs = ["main.py"], + ) + analysis_test( + name = name, + impl = _test_name_cannot_end_in_py_impl, + target = name + "_subject.py", + expect_failure = True, + ) + +_tests.append(_test_name_cannot_end_in_py) + +def _test_name_cannot_end_in_py_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("name must not end in*.py"), + ) + +# Can't test this -- mandatory validation happens before analysis test +# can intercept it +# TODO(#1069): Once re-implemented in Starlark, modify rule logic to make this +# testable. +# def _test_srcs_is_mandatory(name, config): +# rt_util.helper_target( +# config.rule, +# name = name + "_subject", +# ) +# analysis_test( +# name = name, +# impl = _test_srcs_is_mandatory, +# target = name + "_subject", +# expect_failure = True, +# ) +# +# _tests.append(_test_srcs_is_mandatory) +# +# def _test_srcs_is_mandatory_impl(env, target): +# env.expect.that_target(target).failures().contains_predicate( +# matching.str_matches("mandatory*srcs"), +# ) + +# ===== +# You were gonna add a test at the end, weren't you? +# Nope. Please keep them sorted; put it in its alphabetical location. +# Here's the alphabet so you don't have to sing that song in your head: +# A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +# ===== + +def create_executable_tests(config): + def _executable_with_srcs_wrapper(name, **kwargs): + if not kwargs.get("srcs"): + kwargs["srcs"] = [name + ".py"] + config.rule(name = name, **kwargs) + + config = pt_util.struct_with(config, base_test_rule = _executable_with_srcs_wrapper) + return pt_util.create_tests(_tests, config = config) + create_base_tests(config = config) diff --git a/tools/build_defs/python/tests/py_info_subject.bzl b/tools/build_defs/python/tests/py_info_subject.bzl new file mode 100644 index 0000000000..20185e55e4 --- /dev/null +++ b/tools/build_defs/python/tests/py_info_subject.bzl @@ -0,0 +1,95 @@ +# 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. +"""PyInfo testing subject.""" + +load("@rules_testing//lib:truth.bzl", "subjects") + +def py_info_subject(info, *, meta): + """Creates a new `PyInfoSubject` for a PyInfo provider instance. + + Method: PyInfoSubject.new + + Args: + info: The PyInfo object + meta: ExpectMeta object. + + Returns: + A `PyInfoSubject` struct + """ + + # buildifier: disable=uninitialized + public = struct( + # go/keep-sorted start + has_py2_only_sources = lambda *a, **k: _py_info_subject_has_py2_only_sources(self, *a, **k), + has_py3_only_sources = lambda *a, **k: _py_info_subject_has_py3_only_sources(self, *a, **k), + imports = lambda *a, **k: _py_info_subject_imports(self, *a, **k), + transitive_sources = lambda *a, **k: _py_info_subject_transitive_sources(self, *a, **k), + uses_shared_libraries = lambda *a, **k: _py_info_subject_uses_shared_libraries(self, *a, **k), + # go/keep-sorted end + ) + self = struct( + actual = info, + meta = meta, + ) + return public + +def _py_info_subject_has_py2_only_sources(self): + """Returns a `BoolSubject` for the `has_py2_only_sources` attribute. + + Method: PyInfoSubject.has_py2_only_sources + """ + return subjects.bool( + self.actual.has_py2_only_sources, + meta = self.meta.derive("has_py2_only_sources()"), + ) + +def _py_info_subject_has_py3_only_sources(self): + """Returns a `BoolSubject` for the `has_py3_only_sources` attribute. + + Method: PyInfoSubject.has_py3_only_sources + """ + return subjects.bool( + self.actual.has_py3_only_sources, + meta = self.meta.derive("has_py3_only_sources()"), + ) + +def _py_info_subject_imports(self): + """Returns a `CollectionSubject` for the `imports` attribute. + + Method: PyInfoSubject.imports + """ + return subjects.collection( + self.actual.imports, + meta = self.meta.derive("imports()"), + ) + +def _py_info_subject_transitive_sources(self): + """Returns a `DepsetFileSubject` for the `transitive_sources` attribute. + + Method: PyInfoSubject.transitive_sources + """ + return subjects.depset_file( + self.actual.transitive_sources, + meta = self.meta.derive("transitive_sources()"), + ) + +def _py_info_subject_uses_shared_libraries(self): + """Returns a `BoolSubject` for the `uses_shared_libraries` attribute. + + Method: PyInfoSubject.uses_shared_libraries + """ + return subjects.bool( + self.actual.uses_shared_libraries, + meta = self.meta.derive("uses_shared_libraries()"), + ) diff --git a/tools/build_defs/python/tests/py_library/BUILD.bazel b/tools/build_defs/python/tests/py_library/BUILD.bazel new file mode 100644 index 0000000000..9de414b31b --- /dev/null +++ b/tools/build_defs/python/tests/py_library/BUILD.bazel @@ -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. +"""Tests for py_library.""" + +load(":py_library_tests.bzl", "py_library_test_suite") + +py_library_test_suite(name = "py_library_tests") diff --git a/tools/build_defs/python/tests/py_library/py_library_tests.bzl b/tools/build_defs/python/tests/py_library/py_library_tests.bzl new file mode 100644 index 0000000000..1fcb0c19b9 --- /dev/null +++ b/tools/build_defs/python/tests/py_library/py_library_tests.bzl @@ -0,0 +1,148 @@ +"""Test for py_library.""" + +load("@rules_testing//lib:analysis_test.bzl", "analysis_test") +load("@rules_testing//lib:truth.bzl", "matching") +load("@rules_testing//lib:util.bzl", rt_util = "util") +load("//python:defs.bzl", "PyRuntimeInfo", "py_library") +load("//tools/build_defs/python/tests:base_tests.bzl", "create_base_tests") +load("//tools/build_defs/python/tests:util.bzl", pt_util = "util") + +_tests = [] + +def _test_py_runtime_info_not_present(name, config): + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = ["lib.py"], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_py_runtime_info_not_present_impl, + ) + +def _test_py_runtime_info_not_present_impl(env, target): + env.expect.that_bool(PyRuntimeInfo in target).equals(False) + +_tests.append(_test_py_runtime_info_not_present) + +def _test_files_to_build(name, config): + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = ["lib.py"], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_files_to_build_impl, + ) + +def _test_files_to_build_impl(env, target): + env.expect.that_target(target).default_outputs().contains_exactly([ + "{package}/lib.py", + ]) + +_tests.append(_test_files_to_build) + +def _test_srcs_can_contain_rule_generating_py_and_nonpy_files(name, config): + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = ["lib.py", name + "_gensrcs"], + ) + rt_util.helper_target( + native.genrule, + name = name + "_gensrcs", + cmd = "touch $(OUTS)", + outs = [name + "_gen.py", name + "_gen.cc"], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_srcs_can_contain_rule_generating_py_and_nonpy_files_impl, + ) + +def _test_srcs_can_contain_rule_generating_py_and_nonpy_files_impl(env, target): + env.expect.that_target(target).default_outputs().contains_exactly([ + "{package}/{test_name}_gen.py", + "{package}/lib.py", + ]) + +_tests.append(_test_srcs_can_contain_rule_generating_py_and_nonpy_files) + +def _test_srcs_generating_no_py_files_is_error(name, config): + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [name + "_gen"], + ) + rt_util.helper_target( + native.genrule, + name = name + "_gen", + cmd = "touch $(OUTS)", + outs = [name + "_gen.cc"], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_srcs_generating_no_py_files_is_error_impl, + expect_failure = True, + ) + +def _test_srcs_generating_no_py_files_is_error_impl(env, target): + env.expect.that_target(target).failures().contains_predicate( + matching.str_matches("does not produce*srcs files"), + ) + +_tests.append(_test_srcs_generating_no_py_files_is_error) + +def _test_files_to_compile(name, config): + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = ["lib1.py"], + deps = [name + "_lib2"], + ) + rt_util.helper_target( + config.rule, + name = name + "_lib2", + srcs = ["lib2.py"], + deps = [name + "_lib3"], + ) + rt_util.helper_target( + config.rule, + name = name + "_lib3", + srcs = ["lib3.py"], + ) + analysis_test( + name = name, + target = name + "_subject", + impl = _test_files_to_compile_impl, + ) + +def _test_files_to_compile_impl(env, target): + target = env.expect.that_target(target) + target.output_group( + "compilation_prerequisites_INTERNAL_", + ).contains_exactly([ + "{package}/lib1.py", + "{package}/lib2.py", + "{package}/lib3.py", + ]) + target.output_group( + "compilation_outputs", + ).contains_exactly([ + "{package}/lib1.py", + "{package}/lib2.py", + "{package}/lib3.py", + ]) + +_tests.append(_test_files_to_compile) + +def py_library_test_suite(name): + config = struct(rule = py_library, base_test_rule = py_library) + native.test_suite( + name = name, + tests = pt_util.create_tests(_tests, config = config) + create_base_tests(config), + ) diff --git a/tools/build_defs/python/tests/py_test/BUILD.bazel b/tools/build_defs/python/tests/py_test/BUILD.bazel new file mode 100644 index 0000000000..2dc0e5b51d --- /dev/null +++ b/tools/build_defs/python/tests/py_test/BUILD.bazel @@ -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. +"""Tests for py_test.""" + +load(":py_test_tests.bzl", "py_test_test_suite") + +py_test_test_suite(name = "py_test_tests") diff --git a/tools/build_defs/python/tests/py_test/py_test_tests.bzl b/tools/build_defs/python/tests/py_test/py_test_tests.bzl new file mode 100644 index 0000000000..8bb2fc2af0 --- /dev/null +++ b/tools/build_defs/python/tests/py_test/py_test_tests.bzl @@ -0,0 +1,104 @@ +# 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. +"""Tests for py_test.""" + +load("@rules_testing//lib:analysis_test.bzl", "analysis_test") +load("@rules_testing//lib:util.bzl", rt_util = "util") +load("//python:defs.bzl", "py_test") +load( + "//tools/build_defs/python/tests:py_executable_base_tests.bzl", + "create_executable_tests", +) +load("//tools/build_defs/python/tests:util.bzl", pt_util = "util") + +# Explicit Label() calls are required so that it resolves in @rules_python context instead of +# @rules_testing context. +_FAKE_CC_TOOLCHAIN = Label("//tools/build_defs/python/tests:cc_toolchain_suite") +_PLATFORM_MAC = Label("//tools/build_defs/python/tests:mac") +_PLATFORM_LINUX = Label("//tools/build_defs/python/tests:linux") + +_tests = [] + +def _test_mac_requires_darwin_for_execution(name, config): + # Bazel 5.4 has a bug where every access of testing.ExecutionInfo is + # a different object that isn't equal to any other, which prevents + # rules_testing from detecting it properly and fails with an error. + # This is fixed in Bazel 6+. + if not pt_util.is_bazel_6_or_higher(): + rt_util.skip_test(name = name) + return + + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [name + "_subject.py"], + ) + analysis_test( + name = name, + impl = _test_mac_requires_darwin_for_execution_impl, + target = name + "_subject", + config_settings = { + "//command_line_option:cpu": "darwin_x86_64", + "//command_line_option:crosstool_top": _FAKE_CC_TOOLCHAIN, + "//command_line_option:platforms": [_PLATFORM_MAC], + }, + ) + +def _test_mac_requires_darwin_for_execution_impl(env, target): + env.expect.that_target(target).provider( + testing.ExecutionInfo, + ).requirements().keys().contains("requires-darwin") + +_tests.append(_test_mac_requires_darwin_for_execution) + +def _test_non_mac_doesnt_require_darwin_for_execution(name, config): + # Bazel 5.4 has a bug where every access of testing.ExecutionInfo is + # a different object that isn't equal to any other, which prevents + # rules_testing from detecting it properly and fails with an error. + # This is fixed in Bazel 6+. + if not pt_util.is_bazel_6_or_higher(): + rt_util.skip_test(name = name) + return + rt_util.helper_target( + config.rule, + name = name + "_subject", + srcs = [name + "_subject.py"], + ) + analysis_test( + name = name, + impl = _test_non_mac_doesnt_require_darwin_for_execution_impl, + target = name + "_subject", + config_settings = { + "//command_line_option:cpu": "k8", + "//command_line_option:crosstool_top": _FAKE_CC_TOOLCHAIN, + "//command_line_option:platforms": [_PLATFORM_LINUX], + }, + ) + +def _test_non_mac_doesnt_require_darwin_for_execution_impl(env, target): + # Non-mac builds don't have the provider at all. + if testing.ExecutionInfo not in target: + return + env.expect.that_target(target).provider( + testing.ExecutionInfo, + ).requirements().keys().not_contains("requires-darwin") + +_tests.append(_test_non_mac_doesnt_require_darwin_for_execution) + +def py_test_test_suite(name): + config = struct(rule = py_test) + native.test_suite( + name = name, + tests = pt_util.create_tests(_tests, config = config) + create_executable_tests(config), + ) diff --git a/tools/build_defs/python/tests/util.bzl b/tools/build_defs/python/tests/util.bzl new file mode 100644 index 0000000000..9b386ca3bd --- /dev/null +++ b/tools/build_defs/python/tests/util.bzl @@ -0,0 +1,78 @@ +# 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. +"""Helpers and utilities multiple tests re-use.""" + +load("@bazel_skylib//lib:structs.bzl", "structs") + +# Use this with is_windows() +WINDOWS_ATTR = {"windows": attr.label(default = "@platforms//os:windows")} + +def _create_tests(tests, **kwargs): + test_names = [] + for func in tests: + test_name = _test_name_from_function(func) + func(name = test_name, **kwargs) + test_names.append(test_name) + return test_names + +def _test_name_from_function(func): + """Derives the name of the given rule implementation function. + + Args: + func: the function whose name to extract + + Returns: + The name of the given function. Note it will have leading and trailing + "_" stripped -- this allows passing a private function and having the + name of the test not start with "_". + """ + + # Starlark currently stringifies a function as "", so we use + # that knowledge to parse the "NAME" portion out. + # NOTE: This is relying on an implementation detail of Bazel + func_name = str(func) + func_name = func_name.partition("")[0] + func_name = func_name.partition(" ")[0] + return func_name.strip("_") + +def _struct_with(s, **kwargs): + struct_dict = structs.to_dict(s) + struct_dict.update(kwargs) + return struct(**struct_dict) + +def _is_bazel_6_or_higher(): + # Bazel 5.4 has a bug where every access of testing.ExecutionInfo is a + # different object that isn't equal to any other. This is fixed in bazel 6+. + return testing.ExecutionInfo == testing.ExecutionInfo + +def _is_windows(env): + """Tell if the target platform is windows. + + This assumes the `WINDOWS_ATTR` attribute was added. + + Args: + env: The test env struct + Returns: + True if the target is Windows, False if not. + """ + constraint = env.ctx.attr.windows[platform_common.ConstraintValueInfo] + return env.ctx.target_platform_has_constraint(constraint) + +util = struct( + create_tests = _create_tests, + struct_with = _struct_with, + is_bazel_6_or_higher = _is_bazel_6_or_higher, + is_windows = _is_windows, +) diff --git a/tools/publish/BUILD.bazel b/tools/publish/BUILD.bazel new file mode 100644 index 0000000000..065e56bd69 --- /dev/null +++ b/tools/publish/BUILD.bazel @@ -0,0 +1,7 @@ +load("//python:pip.bzl", "compile_pip_requirements") + +compile_pip_requirements( + name = "requirements", + requirements_darwin = "requirements_darwin.txt", + requirements_windows = "requirements_windows.txt", +) diff --git a/tools/publish/README.md b/tools/publish/README.md new file mode 100644 index 0000000000..6f1e54901b --- /dev/null +++ b/tools/publish/README.md @@ -0,0 +1,6 @@ +# Publish to pypi with twine + +https://packaging.python.org/en/latest/tutorials/packaging-projects/ indicates that the twine +package is used to publish wheels to pypi. + +See more: https://twine.readthedocs.io/en/stable/ diff --git a/tools/publish/requirements.in b/tools/publish/requirements.in new file mode 100644 index 0000000000..af996cf7e2 --- /dev/null +++ b/tools/publish/requirements.in @@ -0,0 +1 @@ +twine diff --git a/tools/publish/requirements.txt b/tools/publish/requirements.txt new file mode 100644 index 0000000000..858fc51e4c --- /dev/null +++ b/tools/publish/requirements.txt @@ -0,0 +1,297 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# bazel run //tools/publish:requirements.update +# +bleach==6.0.0 \ + --hash=sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414 \ + --hash=sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4 + # via readme-renderer +certifi==2022.12.7 \ + --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ + --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 + # via requests +cffi==1.15.1 \ + --hash=sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5 \ + --hash=sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef \ + --hash=sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104 \ + --hash=sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426 \ + --hash=sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405 \ + --hash=sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375 \ + --hash=sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a \ + --hash=sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e \ + --hash=sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc \ + --hash=sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf \ + --hash=sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185 \ + --hash=sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497 \ + --hash=sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3 \ + --hash=sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35 \ + --hash=sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c \ + --hash=sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83 \ + --hash=sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21 \ + --hash=sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca \ + --hash=sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984 \ + --hash=sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac \ + --hash=sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd \ + --hash=sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee \ + --hash=sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a \ + --hash=sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2 \ + --hash=sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192 \ + --hash=sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7 \ + --hash=sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585 \ + --hash=sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f \ + --hash=sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e \ + --hash=sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27 \ + --hash=sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b \ + --hash=sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e \ + --hash=sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e \ + --hash=sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d \ + --hash=sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c \ + --hash=sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415 \ + --hash=sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82 \ + --hash=sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02 \ + --hash=sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314 \ + --hash=sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325 \ + --hash=sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c \ + --hash=sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3 \ + --hash=sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914 \ + --hash=sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045 \ + --hash=sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d \ + --hash=sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9 \ + --hash=sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5 \ + --hash=sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2 \ + --hash=sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c \ + --hash=sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3 \ + --hash=sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2 \ + --hash=sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8 \ + --hash=sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d \ + --hash=sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d \ + --hash=sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9 \ + --hash=sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162 \ + --hash=sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76 \ + --hash=sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4 \ + --hash=sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e \ + --hash=sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9 \ + --hash=sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6 \ + --hash=sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b \ + --hash=sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01 \ + --hash=sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0 + # via cryptography +charset-normalizer==3.0.1 \ + --hash=sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b \ + --hash=sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42 \ + --hash=sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d \ + --hash=sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b \ + --hash=sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a \ + --hash=sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59 \ + --hash=sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154 \ + --hash=sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1 \ + --hash=sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c \ + --hash=sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a \ + --hash=sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d \ + --hash=sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6 \ + --hash=sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b \ + --hash=sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b \ + --hash=sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783 \ + --hash=sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5 \ + --hash=sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918 \ + --hash=sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555 \ + --hash=sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639 \ + --hash=sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786 \ + --hash=sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e \ + --hash=sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed \ + --hash=sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820 \ + --hash=sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8 \ + --hash=sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3 \ + --hash=sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541 \ + --hash=sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14 \ + --hash=sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be \ + --hash=sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e \ + --hash=sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76 \ + --hash=sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b \ + --hash=sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c \ + --hash=sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b \ + --hash=sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3 \ + --hash=sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc \ + --hash=sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6 \ + --hash=sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59 \ + --hash=sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4 \ + --hash=sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d \ + --hash=sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d \ + --hash=sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3 \ + --hash=sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a \ + --hash=sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea \ + --hash=sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6 \ + --hash=sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e \ + --hash=sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603 \ + --hash=sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24 \ + --hash=sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a \ + --hash=sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58 \ + --hash=sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678 \ + --hash=sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a \ + --hash=sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c \ + --hash=sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6 \ + --hash=sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18 \ + --hash=sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174 \ + --hash=sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317 \ + --hash=sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f \ + --hash=sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc \ + --hash=sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837 \ + --hash=sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41 \ + --hash=sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c \ + --hash=sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579 \ + --hash=sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753 \ + --hash=sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8 \ + --hash=sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291 \ + --hash=sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087 \ + --hash=sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866 \ + --hash=sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3 \ + --hash=sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d \ + --hash=sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1 \ + --hash=sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca \ + --hash=sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e \ + --hash=sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db \ + --hash=sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72 \ + --hash=sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d \ + --hash=sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc \ + --hash=sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539 \ + --hash=sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d \ + --hash=sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af \ + --hash=sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b \ + --hash=sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602 \ + --hash=sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f \ + --hash=sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478 \ + --hash=sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c \ + --hash=sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e \ + --hash=sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479 \ + --hash=sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7 \ + --hash=sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8 + # via requests +cryptography==39.0.0 \ + --hash=sha256:1a6915075c6d3a5e1215eab5d99bcec0da26036ff2102a1038401d6ef5bef25b \ + --hash=sha256:1ee1fd0de9851ff32dbbb9362a4d833b579b4a6cc96883e8e6d2ff2a6bc7104f \ + --hash=sha256:407cec680e811b4fc829de966f88a7c62a596faa250fc1a4b520a0355b9bc190 \ + --hash=sha256:50386acb40fbabbceeb2986332f0287f50f29ccf1497bae31cf5c3e7b4f4b34f \ + --hash=sha256:6f97109336df5c178ee7c9c711b264c502b905c2d2a29ace99ed761533a3460f \ + --hash=sha256:754978da4d0457e7ca176f58c57b1f9de6556591c19b25b8bcce3c77d314f5eb \ + --hash=sha256:76c24dd4fd196a80f9f2f5405a778a8ca132f16b10af113474005635fe7e066c \ + --hash=sha256:7dacfdeee048814563eaaec7c4743c8aea529fe3dd53127313a792f0dadc1773 \ + --hash=sha256:80ee674c08aaef194bc4627b7f2956e5ba7ef29c3cc3ca488cf15854838a8f72 \ + --hash=sha256:844ad4d7c3850081dffba91cdd91950038ee4ac525c575509a42d3fc806b83c8 \ + --hash=sha256:875aea1039d78557c7c6b4db2fe0e9d2413439f4676310a5f269dd342ca7a717 \ + --hash=sha256:887cbc1ea60786e534b00ba8b04d1095f4272d380ebd5f7a7eb4cc274710fad9 \ + --hash=sha256:ad04f413436b0781f20c52a661660f1e23bcd89a0e9bb1d6d20822d048cf2856 \ + --hash=sha256:bae6c7f4a36a25291b619ad064a30a07110a805d08dc89984f4f441f6c1f3f96 \ + --hash=sha256:c52a1a6f81e738d07f43dab57831c29e57d21c81a942f4602fac7ee21b27f288 \ + --hash=sha256:e0a05aee6a82d944f9b4edd6a001178787d1546ec7c6223ee9a848a7ade92e39 \ + --hash=sha256:e324de6972b151f99dc078defe8fb1b0a82c6498e37bff335f5bc6b1e3ab5a1e \ + --hash=sha256:e5d71c5d5bd5b5c3eebcf7c5c2bb332d62ec68921a8c593bea8c394911a005ce \ + --hash=sha256:f3ed2d864a2fa1666e749fe52fb8e23d8e06b8012e8bd8147c73797c506e86f1 \ + --hash=sha256:f671c1bb0d6088e94d61d80c606d65baacc0d374e67bf895148883461cd848de \ + --hash=sha256:f6c0db08d81ead9576c4d94bbb27aed8d7a430fa27890f39084c2d0e2ec6b0df \ + --hash=sha256:f964c7dcf7802d133e8dbd1565914fa0194f9d683d82411989889ecd701e8adf \ + --hash=sha256:fec8b932f51ae245121c4671b4bbc030880f363354b2f0e0bd1366017d891458 + # via secretstorage +docutils==0.19 \ + --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \ + --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc + # via readme-renderer +idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 + # via requests +importlib-metadata==6.0.0 \ + --hash=sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad \ + --hash=sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d + # via + # keyring + # twine +jaraco-classes==3.2.3 \ + --hash=sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158 \ + --hash=sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a + # via keyring +jeepney==0.8.0 \ + --hash=sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806 \ + --hash=sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755 + # via + # keyring + # secretstorage +keyring==23.13.1 \ + --hash=sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd \ + --hash=sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678 + # via twine +markdown-it-py==2.1.0 \ + --hash=sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27 \ + --hash=sha256:cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da + # via rich +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +more-itertools==9.0.0 \ + --hash=sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41 \ + --hash=sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab + # via jaraco-classes +pkginfo==1.9.6 \ + --hash=sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546 \ + --hash=sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046 + # via twine +pycparser==2.21 \ + --hash=sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9 \ + --hash=sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206 + # via cffi +pygments==2.14.0 \ + --hash=sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297 \ + --hash=sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717 + # via + # readme-renderer + # rich +readme-renderer==37.3 \ + --hash=sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273 \ + --hash=sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343 + # via twine +requests==2.28.2 \ + --hash=sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa \ + --hash=sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf + # via + # requests-toolbelt + # twine +requests-toolbelt==0.10.1 \ + --hash=sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7 \ + --hash=sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d + # via twine +rfc3986==2.0.0 \ + --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \ + --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c + # via twine +rich==13.2.0 \ + --hash=sha256:7c963f0d03819221e9ac561e1bc866e3f95a02248c1234daa48954e6d381c003 \ + --hash=sha256:f1a00cdd3eebf999a15d85ec498bfe0b1a77efe9b34f645768a54132ef444ac5 + # via twine +secretstorage==3.3.3 \ + --hash=sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77 \ + --hash=sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99 + # via keyring +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 + # via bleach +twine==4.0.2 \ + --hash=sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8 \ + --hash=sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8 + # via -r tools/publish/requirements.in +urllib3==1.26.14 \ + --hash=sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72 \ + --hash=sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1 + # via + # requests + # twine +webencodings==0.5.1 \ + --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ + --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923 + # via bleach +zipp==3.11.0 \ + --hash=sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa \ + --hash=sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766 + # via importlib-metadata diff --git a/tools/publish/requirements_darwin.txt b/tools/publish/requirements_darwin.txt new file mode 100644 index 0000000000..cb35e69c97 --- /dev/null +++ b/tools/publish/requirements_darwin.txt @@ -0,0 +1,192 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# bazel run //tools/publish:requirements.update +# +bleach==6.0.0 \ + --hash=sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414 \ + --hash=sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4 + # via readme-renderer +certifi==2022.12.7 \ + --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ + --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 + # via requests +charset-normalizer==3.0.1 \ + --hash=sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b \ + --hash=sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42 \ + --hash=sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d \ + --hash=sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b \ + --hash=sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a \ + --hash=sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59 \ + --hash=sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154 \ + --hash=sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1 \ + --hash=sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c \ + --hash=sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a \ + --hash=sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d \ + --hash=sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6 \ + --hash=sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b \ + --hash=sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b \ + --hash=sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783 \ + --hash=sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5 \ + --hash=sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918 \ + --hash=sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555 \ + --hash=sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639 \ + --hash=sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786 \ + --hash=sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e \ + --hash=sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed \ + --hash=sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820 \ + --hash=sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8 \ + --hash=sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3 \ + --hash=sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541 \ + --hash=sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14 \ + --hash=sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be \ + --hash=sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e \ + --hash=sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76 \ + --hash=sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b \ + --hash=sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c \ + --hash=sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b \ + --hash=sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3 \ + --hash=sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc \ + --hash=sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6 \ + --hash=sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59 \ + --hash=sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4 \ + --hash=sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d \ + --hash=sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d \ + --hash=sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3 \ + --hash=sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a \ + --hash=sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea \ + --hash=sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6 \ + --hash=sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e \ + --hash=sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603 \ + --hash=sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24 \ + --hash=sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a \ + --hash=sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58 \ + --hash=sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678 \ + --hash=sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a \ + --hash=sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c \ + --hash=sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6 \ + --hash=sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18 \ + --hash=sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174 \ + --hash=sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317 \ + --hash=sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f \ + --hash=sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc \ + --hash=sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837 \ + --hash=sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41 \ + --hash=sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c \ + --hash=sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579 \ + --hash=sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753 \ + --hash=sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8 \ + --hash=sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291 \ + --hash=sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087 \ + --hash=sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866 \ + --hash=sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3 \ + --hash=sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d \ + --hash=sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1 \ + --hash=sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca \ + --hash=sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e \ + --hash=sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db \ + --hash=sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72 \ + --hash=sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d \ + --hash=sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc \ + --hash=sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539 \ + --hash=sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d \ + --hash=sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af \ + --hash=sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b \ + --hash=sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602 \ + --hash=sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f \ + --hash=sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478 \ + --hash=sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c \ + --hash=sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e \ + --hash=sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479 \ + --hash=sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7 \ + --hash=sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8 + # via requests +docutils==0.19 \ + --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \ + --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc + # via readme-renderer +idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 + # via requests +importlib-metadata==6.0.0 \ + --hash=sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad \ + --hash=sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d + # via + # keyring + # twine +jaraco-classes==3.2.3 \ + --hash=sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158 \ + --hash=sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a + # via keyring +keyring==23.13.1 \ + --hash=sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd \ + --hash=sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678 + # via twine +markdown-it-py==2.1.0 \ + --hash=sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27 \ + --hash=sha256:cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da + # via rich +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +more-itertools==9.0.0 \ + --hash=sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41 \ + --hash=sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab + # via jaraco-classes +pkginfo==1.9.6 \ + --hash=sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546 \ + --hash=sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046 + # via twine +pygments==2.14.0 \ + --hash=sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297 \ + --hash=sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717 + # via + # readme-renderer + # rich +readme-renderer==37.3 \ + --hash=sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273 \ + --hash=sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343 + # via twine +requests==2.28.2 \ + --hash=sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa \ + --hash=sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf + # via + # requests-toolbelt + # twine +requests-toolbelt==0.10.1 \ + --hash=sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7 \ + --hash=sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d + # via twine +rfc3986==2.0.0 \ + --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \ + --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c + # via twine +rich==13.2.0 \ + --hash=sha256:7c963f0d03819221e9ac561e1bc866e3f95a02248c1234daa48954e6d381c003 \ + --hash=sha256:f1a00cdd3eebf999a15d85ec498bfe0b1a77efe9b34f645768a54132ef444ac5 + # via twine +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 + # via bleach +twine==4.0.2 \ + --hash=sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8 \ + --hash=sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8 + # via -r tools/publish/requirements.in +urllib3==1.26.14 \ + --hash=sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72 \ + --hash=sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1 + # via + # requests + # twine +webencodings==0.5.1 \ + --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ + --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923 + # via bleach +zipp==3.11.0 \ + --hash=sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa \ + --hash=sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766 + # via importlib-metadata diff --git a/tools/publish/requirements_windows.txt b/tools/publish/requirements_windows.txt new file mode 100644 index 0000000000..cd175c68ef --- /dev/null +++ b/tools/publish/requirements_windows.txt @@ -0,0 +1,196 @@ +# +# This file is autogenerated by pip-compile with Python 3.11 +# by the following command: +# +# bazel run //tools/publish:requirements.update +# +bleach==6.0.0 \ + --hash=sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414 \ + --hash=sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4 + # via readme-renderer +certifi==2022.12.7 \ + --hash=sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3 \ + --hash=sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18 + # via requests +charset-normalizer==3.0.1 \ + --hash=sha256:00d3ffdaafe92a5dc603cb9bd5111aaa36dfa187c8285c543be562e61b755f6b \ + --hash=sha256:024e606be3ed92216e2b6952ed859d86b4cfa52cd5bc5f050e7dc28f9b43ec42 \ + --hash=sha256:0298eafff88c99982a4cf66ba2efa1128e4ddaca0b05eec4c456bbc7db691d8d \ + --hash=sha256:02a51034802cbf38db3f89c66fb5d2ec57e6fe7ef2f4a44d070a593c3688667b \ + --hash=sha256:083c8d17153ecb403e5e1eb76a7ef4babfc2c48d58899c98fcaa04833e7a2f9a \ + --hash=sha256:0a11e971ed097d24c534c037d298ad32c6ce81a45736d31e0ff0ad37ab437d59 \ + --hash=sha256:0bf2dae5291758b6f84cf923bfaa285632816007db0330002fa1de38bfcb7154 \ + --hash=sha256:0c0a590235ccd933d9892c627dec5bc7511ce6ad6c1011fdf5b11363022746c1 \ + --hash=sha256:0f438ae3532723fb6ead77e7c604be7c8374094ef4ee2c5e03a3a17f1fca256c \ + --hash=sha256:109487860ef6a328f3eec66f2bf78b0b72400280d8f8ea05f69c51644ba6521a \ + --hash=sha256:11b53acf2411c3b09e6af37e4b9005cba376c872503c8f28218c7243582df45d \ + --hash=sha256:12db3b2c533c23ab812c2b25934f60383361f8a376ae272665f8e48b88e8e1c6 \ + --hash=sha256:14e76c0f23218b8f46c4d87018ca2e441535aed3632ca134b10239dfb6dadd6b \ + --hash=sha256:16a8663d6e281208d78806dbe14ee9903715361cf81f6d4309944e4d1e59ac5b \ + --hash=sha256:292d5e8ba896bbfd6334b096e34bffb56161c81408d6d036a7dfa6929cff8783 \ + --hash=sha256:2c03cc56021a4bd59be889c2b9257dae13bf55041a3372d3295416f86b295fb5 \ + --hash=sha256:2e396d70bc4ef5325b72b593a72c8979999aa52fb8bcf03f701c1b03e1166918 \ + --hash=sha256:2edb64ee7bf1ed524a1da60cdcd2e1f6e2b4f66ef7c077680739f1641f62f555 \ + --hash=sha256:31a9ddf4718d10ae04d9b18801bd776693487cbb57d74cc3458a7673f6f34639 \ + --hash=sha256:356541bf4381fa35856dafa6a965916e54bed415ad8a24ee6de6e37deccf2786 \ + --hash=sha256:358a7c4cb8ba9b46c453b1dd8d9e431452d5249072e4f56cfda3149f6ab1405e \ + --hash=sha256:37f8febc8ec50c14f3ec9637505f28e58d4f66752207ea177c1d67df25da5aed \ + --hash=sha256:39049da0ffb96c8cbb65cbf5c5f3ca3168990adf3551bd1dee10c48fce8ae820 \ + --hash=sha256:39cf9ed17fe3b1bc81f33c9ceb6ce67683ee7526e65fde1447c772afc54a1bb8 \ + --hash=sha256:3ae1de54a77dc0d6d5fcf623290af4266412a7c4be0b1ff7444394f03f5c54e3 \ + --hash=sha256:3b590df687e3c5ee0deef9fc8c547d81986d9a1b56073d82de008744452d6541 \ + --hash=sha256:3e45867f1f2ab0711d60c6c71746ac53537f1684baa699f4f668d4c6f6ce8e14 \ + --hash=sha256:3fc1c4a2ffd64890aebdb3f97e1278b0cc72579a08ca4de8cd2c04799a3a22be \ + --hash=sha256:4457ea6774b5611f4bed5eaa5df55f70abde42364d498c5134b7ef4c6958e20e \ + --hash=sha256:44ba614de5361b3e5278e1241fda3dc1838deed864b50a10d7ce92983797fa76 \ + --hash=sha256:4a8fcf28c05c1f6d7e177a9a46a1c52798bfe2ad80681d275b10dcf317deaf0b \ + --hash=sha256:4b0d02d7102dd0f997580b51edc4cebcf2ab6397a7edf89f1c73b586c614272c \ + --hash=sha256:502218f52498a36d6bf5ea77081844017bf7982cdbe521ad85e64cabee1b608b \ + --hash=sha256:503e65837c71b875ecdd733877d852adbc465bd82c768a067badd953bf1bc5a3 \ + --hash=sha256:5995f0164fa7df59db4746112fec3f49c461dd6b31b841873443bdb077c13cfc \ + --hash=sha256:59e5686dd847347e55dffcc191a96622f016bc0ad89105e24c14e0d6305acbc6 \ + --hash=sha256:601f36512f9e28f029d9481bdaf8e89e5148ac5d89cffd3b05cd533eeb423b59 \ + --hash=sha256:608862a7bf6957f2333fc54ab4399e405baad0163dc9f8d99cb236816db169d4 \ + --hash=sha256:62595ab75873d50d57323a91dd03e6966eb79c41fa834b7a1661ed043b2d404d \ + --hash=sha256:70990b9c51340e4044cfc394a81f614f3f90d41397104d226f21e66de668730d \ + --hash=sha256:71140351489970dfe5e60fc621ada3e0f41104a5eddaca47a7acb3c1b851d6d3 \ + --hash=sha256:72966d1b297c741541ca8cf1223ff262a6febe52481af742036a0b296e35fa5a \ + --hash=sha256:74292fc76c905c0ef095fe11e188a32ebd03bc38f3f3e9bcb85e4e6db177b7ea \ + --hash=sha256:761e8904c07ad053d285670f36dd94e1b6ab7f16ce62b9805c475b7aa1cffde6 \ + --hash=sha256:772b87914ff1152b92a197ef4ea40efe27a378606c39446ded52c8f80f79702e \ + --hash=sha256:79909e27e8e4fcc9db4addea88aa63f6423ebb171db091fb4373e3312cb6d603 \ + --hash=sha256:7e189e2e1d3ed2f4aebabd2d5b0f931e883676e51c7624826e0a4e5fe8a0bf24 \ + --hash=sha256:7eb33a30d75562222b64f569c642ff3dc6689e09adda43a082208397f016c39a \ + --hash=sha256:81d6741ab457d14fdedc215516665050f3822d3e56508921cc7239f8c8e66a58 \ + --hash=sha256:8499ca8f4502af841f68135133d8258f7b32a53a1d594aa98cc52013fff55678 \ + --hash=sha256:84c3990934bae40ea69a82034912ffe5a62c60bbf6ec5bc9691419641d7d5c9a \ + --hash=sha256:87701167f2a5c930b403e9756fab1d31d4d4da52856143b609e30a1ce7160f3c \ + --hash=sha256:88600c72ef7587fe1708fd242b385b6ed4b8904976d5da0893e31df8b3480cb6 \ + --hash=sha256:8ac7b6a045b814cf0c47f3623d21ebd88b3e8cf216a14790b455ea7ff0135d18 \ + --hash=sha256:8b8af03d2e37866d023ad0ddea594edefc31e827fee64f8de5611a1dbc373174 \ + --hash=sha256:8c7fe7afa480e3e82eed58e0ca89f751cd14d767638e2550c77a92a9e749c317 \ + --hash=sha256:8eade758719add78ec36dc13201483f8e9b5d940329285edcd5f70c0a9edbd7f \ + --hash=sha256:911d8a40b2bef5b8bbae2e36a0b103f142ac53557ab421dc16ac4aafee6f53dc \ + --hash=sha256:93ad6d87ac18e2a90b0fe89df7c65263b9a99a0eb98f0a3d2e079f12a0735837 \ + --hash=sha256:95dea361dd73757c6f1c0a1480ac499952c16ac83f7f5f4f84f0658a01b8ef41 \ + --hash=sha256:9ab77acb98eba3fd2a85cd160851816bfce6871d944d885febf012713f06659c \ + --hash=sha256:9cb3032517f1627cc012dbc80a8ec976ae76d93ea2b5feaa9d2a5b8882597579 \ + --hash=sha256:9cf4e8ad252f7c38dd1f676b46514f92dc0ebeb0db5552f5f403509705e24753 \ + --hash=sha256:9d9153257a3f70d5f69edf2325357251ed20f772b12e593f3b3377b5f78e7ef8 \ + --hash=sha256:a152f5f33d64a6be73f1d30c9cc82dfc73cec6477ec268e7c6e4c7d23c2d2291 \ + --hash=sha256:a16418ecf1329f71df119e8a65f3aa68004a3f9383821edcb20f0702934d8087 \ + --hash=sha256:a60332922359f920193b1d4826953c507a877b523b2395ad7bc716ddd386d866 \ + --hash=sha256:a8d0fc946c784ff7f7c3742310cc8a57c5c6dc31631269876a88b809dbeff3d3 \ + --hash=sha256:ab5de034a886f616a5668aa5d098af2b5385ed70142090e2a31bcbd0af0fdb3d \ + --hash=sha256:c22d3fe05ce11d3671297dc8973267daa0f938b93ec716e12e0f6dee81591dc1 \ + --hash=sha256:c2ac1b08635a8cd4e0cbeaf6f5e922085908d48eb05d44c5ae9eabab148512ca \ + --hash=sha256:c512accbd6ff0270939b9ac214b84fb5ada5f0409c44298361b2f5e13f9aed9e \ + --hash=sha256:c75ffc45f25324e68ab238cb4b5c0a38cd1c3d7f1fb1f72b5541de469e2247db \ + --hash=sha256:c95a03c79bbe30eec3ec2b7f076074f4281526724c8685a42872974ef4d36b72 \ + --hash=sha256:cadaeaba78750d58d3cc6ac4d1fd867da6fc73c88156b7a3212a3cd4819d679d \ + --hash=sha256:cd6056167405314a4dc3c173943f11249fa0f1b204f8b51ed4bde1a9cd1834dc \ + --hash=sha256:db72b07027db150f468fbada4d85b3b2729a3db39178abf5c543b784c1254539 \ + --hash=sha256:df2c707231459e8a4028eabcd3cfc827befd635b3ef72eada84ab13b52e1574d \ + --hash=sha256:e62164b50f84e20601c1ff8eb55620d2ad25fb81b59e3cd776a1902527a788af \ + --hash=sha256:e696f0dd336161fca9adbb846875d40752e6eba585843c768935ba5c9960722b \ + --hash=sha256:eaa379fcd227ca235d04152ca6704c7cb55564116f8bc52545ff357628e10602 \ + --hash=sha256:ebea339af930f8ca5d7a699b921106c6e29c617fe9606fa7baa043c1cdae326f \ + --hash=sha256:f4c39b0e3eac288fedc2b43055cfc2ca7a60362d0e5e87a637beac5d801ef478 \ + --hash=sha256:f5057856d21e7586765171eac8b9fc3f7d44ef39425f85dbcccb13b3ebea806c \ + --hash=sha256:f6f45710b4459401609ebebdbcfb34515da4fc2aa886f95107f556ac69a9147e \ + --hash=sha256:f97e83fa6c25693c7a35de154681fcc257c1c41b38beb0304b9c4d2d9e164479 \ + --hash=sha256:f9d0c5c045a3ca9bedfc35dca8526798eb91a07aa7a2c0fee134c6c6f321cbd7 \ + --hash=sha256:ff6f3db31555657f3163b15a6b7c6938d08df7adbfc9dd13d9d19edad678f1e8 + # via requests +docutils==0.19 \ + --hash=sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6 \ + --hash=sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc + # via readme-renderer +idna==3.4 \ + --hash=sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4 \ + --hash=sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2 + # via requests +importlib-metadata==6.0.0 \ + --hash=sha256:7efb448ec9a5e313a57655d35aa54cd3e01b7e1fbcf72dce1bf06119420f5bad \ + --hash=sha256:e354bedeb60efa6affdcc8ae121b73544a7aa74156d047311948f6d711cd378d + # via + # keyring + # twine +jaraco-classes==3.2.3 \ + --hash=sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158 \ + --hash=sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a + # via keyring +keyring==23.13.1 \ + --hash=sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd \ + --hash=sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678 + # via twine +markdown-it-py==2.1.0 \ + --hash=sha256:93de681e5c021a432c63147656fe21790bc01231e0cd2da73626f1aa3ac0fe27 \ + --hash=sha256:cf7e59fed14b5ae17c0006eff14a2d9a00ed5f3a846148153899a0224e2c07da + # via rich +mdurl==0.1.2 \ + --hash=sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8 \ + --hash=sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba + # via markdown-it-py +more-itertools==9.0.0 \ + --hash=sha256:250e83d7e81d0c87ca6bd942e6aeab8cc9daa6096d12c5308f3f92fa5e5c1f41 \ + --hash=sha256:5a6257e40878ef0520b1803990e3e22303a41b5714006c32a3fd8304b26ea1ab + # via jaraco-classes +pkginfo==1.9.6 \ + --hash=sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546 \ + --hash=sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046 + # via twine +pygments==2.14.0 \ + --hash=sha256:b3ed06a9e8ac9a9aae5a6f5dbe78a8a58655d17b43b93c078f094ddc476ae297 \ + --hash=sha256:fa7bd7bd2771287c0de303af8bfdfc731f51bd2c6a47ab69d117138893b82717 + # via + # readme-renderer + # rich +pywin32-ctypes==0.2.0 \ + --hash=sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942 \ + --hash=sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98 + # via keyring +readme-renderer==37.3 \ + --hash=sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273 \ + --hash=sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343 + # via twine +requests==2.28.2 \ + --hash=sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa \ + --hash=sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf + # via + # requests-toolbelt + # twine +requests-toolbelt==0.10.1 \ + --hash=sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7 \ + --hash=sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d + # via twine +rfc3986==2.0.0 \ + --hash=sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd \ + --hash=sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c + # via twine +rich==13.2.0 \ + --hash=sha256:7c963f0d03819221e9ac561e1bc866e3f95a02248c1234daa48954e6d381c003 \ + --hash=sha256:f1a00cdd3eebf999a15d85ec498bfe0b1a77efe9b34f645768a54132ef444ac5 + # via twine +six==1.16.0 \ + --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \ + --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 + # via bleach +twine==4.0.2 \ + --hash=sha256:929bc3c280033347a00f847236564d1c52a3e61b1ac2516c97c48f3ceab756d8 \ + --hash=sha256:9e102ef5fdd5a20661eb88fad46338806c3bd32cf1db729603fe3697b1bc83c8 + # via -r tools/publish/requirements.in +urllib3==1.26.14 \ + --hash=sha256:076907bf8fd355cde77728471316625a4d2f7e713c125f51953bb5b3eecf4f72 \ + --hash=sha256:75edcdc2f7d85b137124a6c3c9fc3933cdeaa12ecb9a6a959f22797a0feca7e1 + # via + # requests + # twine +webencodings==0.5.1 \ + --hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \ + --hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923 + # via bleach +zipp==3.11.0 \ + --hash=sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa \ + --hash=sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766 + # via importlib-metadata diff --git a/tools/update_coverage_deps.py b/tools/update_coverage_deps.py new file mode 100755 index 0000000000..4cf1e94232 --- /dev/null +++ b/tools/update_coverage_deps.py @@ -0,0 +1,258 @@ +#!/usr/bin/python3 -B +# 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. + +"""A small script to update bazel files within the repo. + +We are not running this with 'bazel run' to keep the dependencies minimal +""" + +# NOTE @aignas 2023-01-09: We should only depend on core Python 3 packages. +import argparse +import difflib +import json +import pathlib +import sys +import textwrap +from collections import defaultdict +from dataclasses import dataclass +from typing import Any +from urllib import request + +# This should be kept in sync with //python:versions.bzl +_supported_platforms = { + # Windows is unsupported right now + # "win_amd64": "x86_64-pc-windows-msvc", + "manylinux2014_x86_64": "x86_64-unknown-linux-gnu", + "manylinux2014_aarch64": "aarch64-unknown-linux-gnu", + "macosx_11_0_arm64": "aarch64-apple-darwin", + "macosx_10_9_x86_64": "x86_64-apple-darwin", +} + + +@dataclass +class Dep: + name: str + platform: str + python: str + url: str + sha256: str + + @property + def repo_name(self): + return f"pypi__{self.name}_{self.python}_{self.platform}" + + def __repr__(self): + return "\n".join( + [ + "(", + f' "{self.url}",', + f' "{self.sha256}",', + ")", + ] + ) + + +@dataclass +class Deps: + deps: list[Dep] + + def __repr__(self): + deps = defaultdict(dict) + for d in self.deps: + deps[d.python][d.platform] = d + + parts = [] + for python, contents in deps.items(): + inner = textwrap.indent( + "\n".join([f'"{platform}": {d},' for platform, d in contents.items()]), + prefix=" ", + ) + parts.append('"{}": {{\n{}\n}},'.format(python, inner)) + return "{{\n{}\n}}".format(textwrap.indent("\n".join(parts), prefix=" ")) + + +def _get_platforms(filename: str, name: str, version: str, python_version: str): + return filename[ + len(f"{name}-{version}-{python_version}-{python_version}-") : -len(".whl") + ].split(".") + + +def _map( + name: str, + filename: str, + python_version: str, + url: str, + digests: list, + platform: str, + **kwargs: Any, +): + if platform not in _supported_platforms: + return None + + return Dep( + name=name, + platform=_supported_platforms[platform], + python=python_version, + url=url, + sha256=digests["sha256"], + ) + + +def _writelines(path: pathlib.Path, lines: list[str]): + with open(path, "w") as f: + f.writelines(lines) + + +def _difflines(path: pathlib.Path, lines: list[str]): + with open(path) as f: + input = f.readlines() + + rules_python = pathlib.Path(__file__).parent.parent + p = path.relative_to(rules_python) + + print(f"Diff of the changes that would be made to '{p}':") + for line in difflib.unified_diff( + input, + lines, + fromfile=f"a/{p}", + tofile=f"b/{p}", + ): + print(line, end="") + + # Add an empty line at the end of the diff + print() + + +def _update_file( + path: pathlib.Path, + snippet: str, + start_marker: str, + end_marker: str, + dry_run: bool = True, +): + with open(path) as f: + input = f.readlines() + + out = [] + skip = False + for line in input: + if skip: + if not line.startswith(end_marker): + continue + + skip = False + + out.append(line) + + if not line.startswith(start_marker): + continue + + skip = True + out.extend([f"{line}\n" for line in snippet.splitlines()]) + + if dry_run: + _difflines(path, out) + else: + _writelines(path, out) + + +def _parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(__doc__) + parser.add_argument( + "--name", + default="coverage", + type=str, + help="The name of the package", + ) + parser.add_argument( + "version", + type=str, + help="The version of the package to download", + ) + parser.add_argument( + "--py", + nargs="+", + type=str, + default=["cp38", "cp39", "cp310", "cp311"], + help="Supported python versions", + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Wether to write to files", + ) + return parser.parse_args() + + +def main(): + args = _parse_args() + + api_url = f"https://pypi.python.org/pypi/{args.name}/{args.version}/json" + req = request.Request(api_url) + with request.urlopen(req) as response: + data = json.loads(response.read().decode("utf-8")) + + urls = [] + for u in data["urls"]: + if u["yanked"]: + continue + + if not u["filename"].endswith(".whl"): + continue + + if u["python_version"] not in args.py: + continue + + if f'_{u["python_version"]}m_' in u["filename"]: + continue + + platforms = _get_platforms( + u["filename"], + args.name, + args.version, + u["python_version"], + ) + + result = [_map(name=args.name, platform=p, **u) for p in platforms] + urls.extend(filter(None, result)) + + urls.sort(key=lambda x: f"{x.python}_{x.platform}") + + rules_python = pathlib.Path(__file__).parent.parent + + # Update the coverage_deps, which are used to register deps + _update_file( + path=rules_python / "python" / "private" / "coverage_deps.bzl", + snippet=f"_coverage_deps = {repr(Deps(urls))}\n", + start_marker="#START: managed by update_coverage_deps.py script", + end_marker="#END: managed by update_coverage_deps.py script", + dry_run=args.dry_run, + ) + + # Update the MODULE.bazel, which needs to expose the dependencies to the toolchain + # repositories + _update_file( + path=rules_python / "MODULE.bazel", + snippet="".join(sorted([f' "{u.repo_name}",\n' for u in urls])), + start_marker=" # coverage_deps managed by running", + end_marker=")", + dry_run=args.dry_run, + ) + + return + + +if __name__ == "__main__": + main() diff --git a/tools/wheelmaker.py b/tools/wheelmaker.py index 0bd585f3bb..6138c934d5 100644 --- a/tools/wheelmaker.py +++ b/tools/wheelmaker.py @@ -146,7 +146,7 @@ def arcname_from(name): size = 0 with open(real_filename, "rb") as f: while True: - block = f.read(2 ** 20) + block = f.read(2**20) if not block: break hash.update(block) @@ -167,39 +167,12 @@ def add_wheelfile(self): wheel_contents += "Tag: %s\n" % tag self.add_string(self.distinfo_path("WHEEL"), wheel_contents) - def add_metadata( - self, - extra_headers, - description, - classifiers, - python_requires, - requires, - extra_requires, - ): + def add_metadata(self, metadata, description, version): """Write METADATA file to the distribution.""" # https://www.python.org/dev/peps/pep-0566/ # https://packaging.python.org/specifications/core-metadata/ - metadata = [] - metadata.append("Metadata-Version: 2.1") - metadata.append("Name: %s" % self._name) - metadata.append("Version: %s" % self._version) - metadata.extend(extra_headers) - for classifier in classifiers: - metadata.append("Classifier: %s" % classifier) - if python_requires: - metadata.append("Requires-Python: %s" % python_requires) - for requirement in requires: - metadata.append("Requires-Dist: %s" % requirement) - - extra_requires = sorted(extra_requires.items()) - for option, option_requires in extra_requires: - metadata.append("Provides-Extra: %s" % option) - for requirement in option_requires: - metadata.append( - "Requires-Dist: %s; extra == '%s'" % (requirement, option) - ) - - metadata = "\n".join(metadata) + "\n\n" + metadata += "Version: " + version + metadata += "\n\n" # setuptools seems to insert UNKNOWN as description when none is # provided. metadata += description if description else "UNKNOWN" @@ -214,7 +187,7 @@ def add_recordfile(self): contents = b"" for filename, digest, size in entries: if sys.version_info[0] > 2 and isinstance(filename, str): - filename = filename.encode("utf-8", "surrogateescape") + filename = filename.lstrip("/").encode("utf-8", "surrogateescape") contents += b"%s,%s,%s\n" % (filename, digest, size) self.add_string(record_path, contents) @@ -303,25 +276,15 @@ def parse_args() -> argparse.Namespace: action="append", default=[], help="Path prefix to be stripped from input package files' path. " - "Can be supplied multiple times. " - "Evaluated in order.", + "Can be supplied multiple times. Evaluated in order.", ) wheel_group = parser.add_argument_group("Wheel metadata") wheel_group.add_argument( - "--header", - action="append", - help="Additional headers to be embedded in the package metadata. " - "Can be supplied multiple times.", - ) - wheel_group.add_argument( - "--classifier", - action="append", - help="Classifiers to embed in package metadata. " - "Can be supplied multiple times", - ) - wheel_group.add_argument( - "--python_requires", help="Version of python that the wheel will work with" + "--metadata_file", + type=Path, + help="Contents of the METADATA file (before appending contents of " + "--description_file)", ) wheel_group.add_argument( "--description_file", help="Path to the file with package description" @@ -342,22 +305,14 @@ def parse_args() -> argparse.Namespace: contents_group.add_argument( "--input_file_list", action="append", - help="A file that has all the input files defined as a list to avoid the long command", + help="A file that has all the input files defined as a list to avoid " + "the long command", ) - - requirements_group = parser.add_argument_group("Package requirements") - requirements_group.add_argument( - "--requires", - type=str, - action="append", - help="List of package requirements. Can be supplied multiple times.", - ) - requirements_group.add_argument( - "--extra_requires", - type=str, + contents_group.add_argument( + "--extra_distinfo_file", action="append", - help="List of optional requirements in a 'requirement;option name'. " - "Can be supplied multiple times.", + help="'filename;real_path' pairs listing extra files to include in" + "dist-info directory. Can be supplied multiple times.", ) build_group = parser.add_argument_group("Building requirements") @@ -383,6 +338,11 @@ def main() -> None: else: input_files = [] + if arguments.extra_distinfo_file: + extra_distinfo_file = [i.split(";") for i in arguments.extra_distinfo_file] + else: + extra_distinfo_file = [] + if arguments.input_file_list: for input_file in arguments.input_file_list: with open(input_file) as _file: @@ -430,30 +390,25 @@ def main() -> None: ) as description_file: description = description_file.read() - extra_requires = collections.defaultdict(list) - if arguments.extra_requires: - for extra in arguments.extra_requires: - req, option = extra.rsplit(";", 1) - extra_requires[option].append(req) - classifiers = arguments.classifier or [] - python_requires = arguments.python_requires or "" - requires = arguments.requires or [] - extra_headers = arguments.header or [] - - maker.add_metadata( - extra_headers=extra_headers, - description=description, - classifiers=classifiers, - python_requires=python_requires, - requires=requires, - extra_requires=extra_requires, - ) + metadata = None + if sys.version_info[0] == 2: + with open(arguments.metadata_file, "rt") as metadata_file: + metadata = metadata_file.read() + else: + with open(arguments.metadata_file, "rt", encoding="utf-8") as metadata_file: + metadata = metadata_file.read() + + maker.add_metadata(metadata=metadata, description=description, version=version) if arguments.entry_points_file: maker.add_file( maker.distinfo_path("entry_points.txt"), arguments.entry_points_file ) + # Sort the files for reproducible order in the archive. + for filename, real_path in sorted(extra_distinfo_file): + maker.add_file(maker.distinfo_path(filename), real_path) + maker.add_recordfile() # Since stamping may otherwise change the target name of the diff --git a/version.bzl b/version.bzl index ac1dabb473..8c7f01cd19 100644 --- a/version.bzl +++ b/version.bzl @@ -17,8 +17,11 @@ # against. # This version should be updated together with the version of Bazel # in .bazelversion. -# TODO(alexeagle): assert this is the case in a test -BAZEL_VERSION = "5.2.0" +BAZEL_VERSION = "6.0.0" + +# NOTE: Keep in sync with .bazelci/presubmit.yml +# This is the minimum supported bazel version, that we have some tests for. +MINIMUM_BAZEL_VERSION = "5.4.0" # Versions of Bazel which users should be able to use. # Ensures we don't break backwards-compatibility, @@ -26,6 +29,11 @@ BAZEL_VERSION = "5.2.0" # These are the versions used when testing nested workspaces with # bazel_integration_test. SUPPORTED_BAZEL_VERSIONS = [ - # TODO: add LTS versions of bazel like 1.0.0, 2.0.0 BAZEL_VERSION, + # TODO @aignas 2023-02-15: the integration tests currently support + # only a single element in this array. + #MINIMUM_BAZEL_VERSION, ] + +def bazel_version_to_binary_label(version): + return "@build_bazel_bazel_%s//:bazel_binary" % version.replace(".", "_")