fix(gazelle): Do not create invalid py_test rules in `project` generation mode (#1809)
Since https://github.com/bazelbuild/rules_python/pull/1538, when using
`gazelle:python_generation_mode project`, a `py_test` rule is created
even when there are no test files in the project.
fb673ee47b3268a65a18a154edd574b6509c38c7 (first commit on the PR branch)
reproduces this issue - it shows that a `py_test` rule is created even
when there is no test entrypoint file (`__test__.py`) nor any test file
in the entire project. Additionally, test rules created on `project` or
`package` generation mode will always set `main = "__test__.py"`, even
when that file doesn't exist.
This PR fixes it by only generating a `py_test` rule if it can find some
test file - either an explicit `__test__.py`, or any other file prefixed
with `test_` or suffixed with `_test.py`. It also changes the generated
test rule to only add `main = "__test__.py"` if there is an actual
`__test__.py` file. Note that, in the case where a `__test__.py` file
doesn't exist, the generated `py_test` rule is likely invalid as-is due
to the lack of `main`; this can be fixed by mapping to some other
`py_test` implementation, and I believe the new behavior makes more
sense than pointing `main` to a non-existing file.
It may be useful to review per-commit (all tests pass on each commit):
- First commit reproduces the issue;
- Second commit fixes the issue and the corresponding tests;
- Third commit adds additional test cases.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9b7ab25..af4c108 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -26,6 +26,9 @@
* (whl_library): Fix the experimental_target_platforms overriding for platform
specific wheels when the wheels are for any python interpreter version. Fixes
[#1810](https://github.com/bazelbuild/rules_python/issues/1810).
+* (gazelle) In `project` or `package` generation modes, do not generate `py_test`
+ rules when there are no test files and do not set `main = "__test__.py"` when
+ that file doesn't exist.
### Added
diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go
index 400c25e..673076d 100644
--- a/gazelle/python/generate.go
+++ b/gazelle/python/generate.go
@@ -393,20 +393,22 @@
// the file exists on disk.
pyTestFilenames.Add(pyTestEntrypointFilename)
}
- pyTestTargetName := cfg.RenderTestName(packageName)
- pyTestTarget := newPyTestTargetBuilder(pyTestFilenames, pyTestTargetName)
+ if (hasPyTestEntryPointTarget || !pyTestFilenames.Empty()) {
+ pyTestTargetName := cfg.RenderTestName(packageName)
+ pyTestTarget := newPyTestTargetBuilder(pyTestFilenames, pyTestTargetName)
- if hasPyTestEntryPointTarget {
- entrypointTarget := fmt.Sprintf(":%s", pyTestEntrypointTargetname)
- main := fmt.Sprintf(":%s", pyTestEntrypointFilename)
- pyTestTarget.
- addSrc(entrypointTarget).
- addResolvedDependency(entrypointTarget).
- setMain(main)
- } else {
- pyTestTarget.setMain(pyTestEntrypointFilename)
+ if hasPyTestEntryPointTarget {
+ entrypointTarget := fmt.Sprintf(":%s", pyTestEntrypointTargetname)
+ main := fmt.Sprintf(":%s", pyTestEntrypointFilename)
+ pyTestTarget.
+ addSrc(entrypointTarget).
+ addResolvedDependency(entrypointTarget).
+ setMain(main)
+ } else if hasPyTestEntryPointFile {
+ pyTestTarget.setMain(pyTestEntrypointFilename)
+ }
+ pyTestTargets = append(pyTestTargets, pyTestTarget)
}
- pyTestTargets = append(pyTestTargets, pyTestTarget)
} else {
// Create one py_test target per file
pyTestFilenames.Each(func(index int, testFile interface{}) {
diff --git a/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out b/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out
index 3a33111..af01460 100644
--- a/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out
+++ b/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out
@@ -25,5 +25,4 @@
"bar/bar_test.py",
"foo/bar/bar_test.py",
],
- main = "__test__.py",
)
diff --git a/gazelle/python/testdata/project_generation_mode/BUILD.in b/gazelle/python/testdata/project_generation_mode/BUILD.in
new file mode 100644
index 0000000..130a625
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/BUILD.in
@@ -0,0 +1,2 @@
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
diff --git a/gazelle/python/testdata/project_generation_mode/BUILD.out b/gazelle/python/testdata/project_generation_mode/BUILD.out
new file mode 100644
index 0000000..1f30b6d
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/BUILD.out
@@ -0,0 +1,14 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
+
+py_library(
+ name = "project_generation_mode",
+ srcs = [
+ "__init__.py",
+ "bar/bar.py",
+ "foo/foo.py",
+ ],
+ visibility = ["//:__subpackages__"],
+)
diff --git a/gazelle/python/testdata/project_generation_mode/README.md b/gazelle/python/testdata/project_generation_mode/README.md
new file mode 100644
index 0000000..6d8f138
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/README.md
@@ -0,0 +1,3 @@
+# Project generation mode
+
+Simple example using `gazelle:python_generation_mode project` in a project with no tests.
diff --git a/gazelle/python/testdata/project_generation_mode/WORKSPACE b/gazelle/python/testdata/project_generation_mode/WORKSPACE
new file mode 100644
index 0000000..faff6af
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/WORKSPACE
@@ -0,0 +1 @@
+# This is a Bazel workspace for the Gazelle test data.
diff --git a/gazelle/python/testdata/project_generation_mode/__init__.py b/gazelle/python/testdata/project_generation_mode/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/__init__.py
diff --git a/gazelle/python/testdata/project_generation_mode/bar/bar.py b/gazelle/python/testdata/project_generation_mode/bar/bar.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/bar/bar.py
diff --git a/gazelle/python/testdata/project_generation_mode/foo/foo.py b/gazelle/python/testdata/project_generation_mode/foo/foo.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/foo/foo.py
diff --git a/gazelle/python/testdata/project_generation_mode/test.yaml b/gazelle/python/testdata/project_generation_mode/test.yaml
new file mode 100644
index 0000000..fcea777
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode/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/project_generation_mode_with_test_entrypoint/BUILD.in b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.in
new file mode 100644
index 0000000..130a625
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.in
@@ -0,0 +1,2 @@
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.out b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.out
new file mode 100644
index 0000000..05cf353
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/BUILD.out
@@ -0,0 +1,19 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
+
+py_library(
+ name = "project_generation_mode_with_test_entrypoint",
+ srcs = ["__init__.py"],
+ visibility = ["//:__subpackages__"],
+)
+
+py_test(
+ name = "project_generation_mode_with_test_entrypoint_test",
+ srcs = [
+ "__test__.py",
+ "foo/foo_test.py",
+ ],
+ main = "__test__.py",
+)
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/README.md b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/README.md
new file mode 100644
index 0000000..8db5728
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/README.md
@@ -0,0 +1,3 @@
+# Project generation mode with test entrypoint
+
+Example using `gazelle:python_generation_mode project` in a project with tests that use an explicit `__test__.py` entrypoint.
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/WORKSPACE b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/WORKSPACE
new file mode 100644
index 0000000..faff6af
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/WORKSPACE
@@ -0,0 +1 @@
+# This is a Bazel workspace for the Gazelle test data.
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__init__.py b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__init__.py
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__test__.py b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__test__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/__test__.py
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/foo/foo_test.py b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/foo/foo_test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/foo/foo_test.py
diff --git a/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/test.yaml b/gazelle/python/testdata/project_generation_mode_with_test_entrypoint/test.yaml
new file mode 100644
index 0000000..fcea777
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_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/python/testdata/project_generation_mode_with_tests/BUILD.in b/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.in
new file mode 100644
index 0000000..130a625
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.in
@@ -0,0 +1,2 @@
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.out b/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.out
new file mode 100644
index 0000000..8756978
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/BUILD.out
@@ -0,0 +1,15 @@
+load("@rules_python//python:defs.bzl", "py_library", "py_test")
+
+# gazelle:python_extension enabled
+# gazelle:python_generation_mode project
+
+py_library(
+ name = "project_generation_mode_with_tests",
+ srcs = ["__init__.py"],
+ visibility = ["//:__subpackages__"],
+)
+
+py_test(
+ name = "project_generation_mode_with_tests_test",
+ srcs = ["foo/foo_test.py"],
+)
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/README.md b/gazelle/python/testdata/project_generation_mode_with_tests/README.md
new file mode 100644
index 0000000..4a5f012
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/README.md
@@ -0,0 +1,7 @@
+# Project generation mode with tests
+
+Example using `gazelle:python_generation_mode project` in a project with tests, but no `__test__.py` entrypoint.
+
+Note that, in this mode, the `py_test` rule will have no `main` set, which will fail to run with the standard
+`py_test` rule. However, this can be used in conjunction with `gazelle:map_kind` to use some other implementation
+of `py_test` that is able to handle this sitation (such as `rules_python_pytest`).
\ No newline at end of file
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/WORKSPACE b/gazelle/python/testdata/project_generation_mode_with_tests/WORKSPACE
new file mode 100644
index 0000000..faff6af
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/WORKSPACE
@@ -0,0 +1 @@
+# This is a Bazel workspace for the Gazelle test data.
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/__init__.py b/gazelle/python/testdata/project_generation_mode_with_tests/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/__init__.py
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/foo/foo_test.py b/gazelle/python/testdata/project_generation_mode_with_tests/foo/foo_test.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/foo/foo_test.py
diff --git a/gazelle/python/testdata/project_generation_mode_with_tests/test.yaml b/gazelle/python/testdata/project_generation_mode_with_tests/test.yaml
new file mode 100644
index 0000000..fcea777
--- /dev/null
+++ b/gazelle/python/testdata/project_generation_mode_with_tests/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.
+
+---