Skip to content

Commit 990a053

Browse files
linzhpaignas
andauthored
feat!: Following generation mode when generating test targets (#2044)
When `python_generation_mode` is `project` or `file` , the generated `py_test` targets are consistent with `py_library`. However, when `python_generation_mode` is `package`, it becomes inconsistent: it requires either `__test__` target or `__test__.py` file to generate `py_test` in package mode, otherwise it will fall back to file mode. This PR relaxes this requirement with a new directive `gazelle:python_generation_mode_per_package_require_test_entry_point`. Whent it's set to false, Gazelle and generates one `py_test` target per package in package mode even without entry points. This allows people to use `gazelle:map_kind` to map `py_test` to a macro that sets a default test runner, such as [rules_python_pytest](https://github.com/caseyduquettesc/rules_python_pytest) or [pytest-bazel](https://pypi.org/project/pytest-bazel/), and generate one test target per package. The behavior when `gazelle:python_generation_mode` is "file" or "project" remains the same. This fixes #1972 for supporting pytest from Gazelle. With this approach, people can define a thin macro like this to use py_pytest_main: ``` load("@aspect_rules_py//py:defs.bzl", "py_pytest_main") def py_test(name, **kwargs): py_pytest_main( name = "__test__", deps = ["@pip_pytest//:pkg"], # change this to the pytest target in your repo. ) deps = kwargs.pop("deps", []) deps.append(":__test__") py_test( name = name, main = ":__test__.py", deps = deps, **kwargs, ) ``` BREAKING CHANGES: Without `gazelle:map_kind` or `__test__` target or `__test__.py`, the package mode will now generate `py_test` without `main` attribute, which may not be runnable. However, this is already an issue with "python_generation_mode:project" before this PR. The default value of `gazelle:python_generation_mode_per_package_require_test_entry_point` is true to preserve the current behavior. We will flip that default value in the future. --------- Co-authored-by: aignas <[email protected]>
1 parent 9ff6ab7 commit 990a053

File tree

13 files changed

+235
-81
lines changed

13 files changed

+235
-81
lines changed

CHANGELOG.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,16 @@ A brief description of the categories of changes:
3939
containing ">" sign
4040

4141
### Added
42-
* Nothing yet
42+
* (gazelle) Added `python_generation_mode_per_package_require_test_entry_point`
43+
in order to better accommodate users who use a custom macro,
44+
[`pytest-bazel`][pytest_bazel], [rules_python_pytest] or `rules_py`
45+
[py_test_main] in order to integrate with `pytest`. Currently the default
46+
flag value is set to `true` for backwards compatible behaviour, but in the
47+
future the flag will be flipped be `false` by default.
48+
49+
[rules_python_pytest]: https://github.com/caseyduquettesc/rules_python_pytest
50+
[py_test_main]: https://docs.aspect.build/rulesets/aspect_rules_py/docs/rules/#py_pytest_main
51+
[pytest_bazel]: https://pypi.org/project/pytest-bazel
4352

4453
### Removed
4554
* Nothing yet

gazelle/README.md

Lines changed: 64 additions & 35 deletions
Large diffs are not rendered by default.

gazelle/python/configure.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func (py *Configurer) KnownDirectives() []string {
6161
pythonconfig.ValidateImportStatementsDirective,
6262
pythonconfig.GenerationMode,
6363
pythonconfig.GenerationModePerFileIncludeInit,
64+
pythonconfig.GenerationModePerPackageRequireTestEntryPoint,
6465
pythonconfig.LibraryNamingConvention,
6566
pythonconfig.BinaryNamingConvention,
6667
pythonconfig.TestNamingConvention,
@@ -163,6 +164,14 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
163164
log.Fatal(err)
164165
}
165166
config.SetPerFileGenerationIncludeInit(v)
167+
case pythonconfig.GenerationModePerPackageRequireTestEntryPoint:
168+
v, err := strconv.ParseBool(strings.TrimSpace(d.Value))
169+
if err != nil {
170+
log.Printf("invalid value for gazelle:%s in %q: %q",
171+
pythonconfig.GenerationModePerPackageRequireTestEntryPoint, rel, d.Value)
172+
} else {
173+
config.SetPerPackageGenerationRequireTestEntryPoint(v)
174+
}
166175
case pythonconfig.LibraryNamingConvention:
167176
config.SetLibraryNamingConvention(strings.TrimSpace(d.Value))
168177
case pythonconfig.BinaryNamingConvention:

gazelle/python/generate.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
421421
addResolvedDependencies(annotations.includeDeps).
422422
generateImportsAttribute()
423423
}
424-
if (hasPyTestEntryPointFile || hasPyTestEntryPointTarget || cfg.CoarseGrainedGeneration()) && !cfg.PerFileGeneration() {
424+
if (!cfg.PerPackageGenerationRequireTestEntryPoint() || hasPyTestEntryPointFile || hasPyTestEntryPointTarget || cfg.CoarseGrainedGeneration()) && !cfg.PerFileGeneration() {
425425
// Create one py_test target per package
426426
if hasPyTestEntryPointFile {
427427
// Only add the pyTestEntrypointFilename to the pyTestFilenames if
@@ -441,7 +441,10 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
441441
setMain(main)
442442
} else if hasPyTestEntryPointFile {
443443
pyTestTarget.setMain(pyTestEntrypointFilename)
444-
}
444+
} /* else:
445+
main is not set, assuming there is a test file with the same name
446+
as the target name, or there is a macro wrapping py_test and setting its main attribute.
447+
*/
445448
pyTestTargets = append(pyTestTargets, pyTestTarget)
446449
}
447450
} else {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# gazelle:python_generation_mode package
2+
# gazelle:python_generation_mode_per_package_require_test_entry_point false
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
load("@rules_python//python:defs.bzl", "py_library", "py_test")
2+
3+
# gazelle:python_generation_mode package
4+
# gazelle:python_generation_mode_per_package_require_test_entry_point false
5+
6+
py_library(
7+
name = "per_package_test_target_without_entry_point",
8+
srcs = ["__init__.py"],
9+
visibility = ["//:__subpackages__"],
10+
)
11+
12+
py_test(
13+
name = "per_package_test_target_without_entry_point_test",
14+
srcs = [
15+
"bar_test.py",
16+
"foo_test.py",
17+
],
18+
)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# One test target per package without entry point
2+
3+
This test case asserts that one test target is generated per package without entry point when `gazelle:python_generation_mode_per_package_require_test_entry_point false`
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# This is a Bazel workspace for the Gazelle test data.

gazelle/python/testdata/per_package_test_target_without_entry_point/__init__.py

Whitespace-only changes.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2023 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import unittest
16+
17+
18+
class BarTest(unittest.TestCase):
19+
def test_foo(self):
20+
pass
21+
22+
23+
if __name__ == "__main__":
24+
unittest.main()

0 commit comments

Comments
 (0)