From 55e4d13fa00490b55a18d946496532fe823273b0 Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Sun, 23 Jun 2024 14:59:35 -0700 Subject: [PATCH] feat(gazelle): generate __test__ target --- gazelle/python/generate.go | 26 +++++++++++++++++++ gazelle/python/kinds.go | 16 +++++++++--- gazelle/python/target.go | 14 +++++++++- .../testdata/annotation_include_dep/BUILD.out | 6 +++++ .../annotation_include_dep/subpkg/BUILD.out | 6 +++++ .../monorepo/coarse_grained/BUILD.out | 6 +++++ .../python_target_with_test_in_name/BUILD.out | 6 +++++ gazelle/pythonconfig/pythonconfig.go | 9 +++++++ 8 files changed, 85 insertions(+), 4 deletions(-) diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go index ef49dd74c4..9ce0cda827 100644 --- a/gazelle/python/generate.go +++ b/gazelle/python/generate.go @@ -93,6 +93,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes actualPyBinaryKind := GetActualKindName(pyBinaryKind, args) actualPyLibraryKind := GetActualKindName(pyLibraryKind, args) actualPyTestKind := GetActualKindName(pyTestKind, args) + actualPyTestMainKind := GetActualKindName(pyTestMainKind, args) pythonProjectRoot := cfg.PythonProjectRoot() @@ -476,6 +477,31 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes result.Imports = append(result.Imports, pyTest.PrivateAttr(config.GazelleImportsKey)) } + if len(pyTestTargets) > 0 { + if m := cfg.GetGazelleManifest(); m != nil { + pipName := m.PipDepsRepositoryName + if m.PipRepository != nil { + pipName = m.PipRepository.Name + } + pyPyTestMain := createPyTestMainTarget(pipName) + + // 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 err := ensureNoCollision(args.File, pyPyTestMain.Name(), actualPyTestMainKind); err != nil { + fqTarget := label.New("", args.Rel, pyPyTestMain.Name()) + err := fmt.Errorf("failed to generate target %q of kind %q: %w. "+ + "Use the '# gazelle:%s' directive to change the naming convention.", + fqTarget.String(), actualPyTestMainKind, err, pythonconfig.TestNamingConvention) + collisionErrors.Add(err) + } + + result.Gen = append(result.Gen, pyPyTestMain) + result.Imports = append(result.Imports, pyPyTestMain.PrivateAttr(config.GazelleImportsKey)) + } + } + if !collisionErrors.Empty() { it := collisionErrors.Iterator() for it.Next() { diff --git a/gazelle/python/kinds.go b/gazelle/python/kinds.go index a9483372e2..2e558f2b55 100644 --- a/gazelle/python/kinds.go +++ b/gazelle/python/kinds.go @@ -19,9 +19,10 @@ import ( ) const ( - pyBinaryKind = "py_binary" - pyLibraryKind = "py_library" - pyTestKind = "py_test" + pyBinaryKind = "py_binary" + pyLibraryKind = "py_library" + pyTestKind = "py_test" + pyTestMainKind = "py_pytest_main" ) // Kinds returns a map that maps rule names (kinds) and information on how to @@ -79,6 +80,9 @@ var pyKinds = map[string]rule.KindInfo{ "deps": true, }, }, + pyTestMainKind: { + MatchAny: true, + }, } // Loads returns .bzl files and symbols they define. Every rule generated by @@ -97,4 +101,10 @@ var pyLoads = []rule.LoadInfo{ pyTestKind, }, }, + { + Name: "@aspect_rules_py//py:defs.bzl", + Symbols: []string{ + pyTestMainKind, + }, + }, } diff --git a/gazelle/python/target.go b/gazelle/python/target.go index c40d6fb3b7..64c27009ec 100644 --- a/gazelle/python/target.go +++ b/gazelle/python/target.go @@ -15,11 +15,13 @@ package python import ( + "path/filepath" + "github.com/bazelbuild/bazel-gazelle/config" + "github.com/bazelbuild/bazel-gazelle/label" "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. @@ -171,3 +173,13 @@ func (t *targetBuilder) build() *rule.Rule { r.SetPrivateAttr(resolvedDepsKey, t.resolvedDeps) return r } + +func createPyTestMainTarget(pipName string) *rule.Rule { + resolvedDeps := treeset.NewWith(godsutils.StringComparator) + resolvedDeps.Add(label.New(pipName, "pytest", "pytest").String()) + + r := rule.NewRule(pyTestMainKind, "__test__") + r.SetPrivateAttr(resolvedDepsKey, resolvedDeps) + r.SetPrivateAttr(config.GazelleImportsKey, treeset.NewWith(moduleComparator)) + return r +} diff --git a/gazelle/python/testdata/annotation_include_dep/BUILD.out b/gazelle/python/testdata/annotation_include_dep/BUILD.out index 1cff8f4676..9217e4458b 100644 --- a/gazelle/python/testdata/annotation_include_dep/BUILD.out +++ b/gazelle/python/testdata/annotation_include_dep/BUILD.out @@ -1,4 +1,5 @@ load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test") +load("@aspect_rules_py//py:defs.bzl", "py_pytest_main") # gazelle:python_generation_mode file @@ -51,3 +52,8 @@ py_test( "//checking/py_test/works:too", ], ) + +py_pytest_main( + name = "__test__", + deps = ["@gazelle_python_test//pytest"], +) diff --git a/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out b/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out index 921c892889..ee81808f23 100644 --- a/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out +++ b/gazelle/python/testdata/annotation_include_dep/subpkg/BUILD.out @@ -1,4 +1,5 @@ load("@rules_python//python:defs.bzl", "py_library", "py_test") +load("@aspect_rules_py//py:defs.bzl", "py_pytest_main") # gazelle:python_generation_mode package @@ -27,3 +28,8 @@ py_test( "//:bagel_from_include_dep_in_module1_test", ], ) + +py_pytest_main( + name = "__test__", + deps = ["@gazelle_python_test//pytest"], +) diff --git a/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out b/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out index af01460694..fdf44e7782 100644 --- a/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out +++ b/gazelle/python/testdata/monorepo/coarse_grained/BUILD.out @@ -1,3 +1,4 @@ +load("@aspect_rules_py//py:defs.bzl", "py_pytest_main") load("@rules_python//python:defs.bzl", "py_library", "py_test") # gazelle:python_extension enabled @@ -26,3 +27,8 @@ py_test( "foo/bar/bar_test.py", ], ) + +py_pytest_main( + name = "__test__", + deps = ["@root_pip_deps//pytest"], +) 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 index 32e899b9e8..099a6d771e 100644 --- a/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out +++ b/gazelle/python/testdata/python_target_with_test_in_name/BUILD.out @@ -1,4 +1,5 @@ load("@rules_python//python:defs.bzl", "py_library", "py_test") +load("@aspect_rules_py//py:defs.bzl", "py_pytest_main") py_library( name = "python_target_with_test_in_name", @@ -20,3 +21,8 @@ py_test( srcs = ["test_reality.py"], deps = [":python_target_with_test_in_name"], ) + +py_pytest_main( + name = "__test__", + deps = ["@gazelle_python_test//pytest"], +) diff --git a/gazelle/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go index 41a470a940..b7219f7399 100644 --- a/gazelle/pythonconfig/pythonconfig.go +++ b/gazelle/pythonconfig/pythonconfig.go @@ -268,6 +268,15 @@ func (c *Config) SetGazelleManifest(gazelleManifest *manifest.Manifest) { c.gazelleManifest = gazelleManifest } +func (c *Config) GetGazelleManifest() *manifest.Manifest { + for currentCfg := c; currentCfg != nil; currentCfg = currentCfg.parent { + if currentCfg.gazelleManifest != nil { + return currentCfg.gazelleManifest + } + } + return nil +} + // FindThirdPartyDependency scans the gazelle manifests for the current config // and the parent configs up to the root finding if it can resolve the module // name.