diff --git a/CHANGELOG.md b/CHANGELOG.md index 422e399026..71436165c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -104,6 +104,7 @@ END_UNRELEASED_TEMPLATE {#v0-0-0-added} ### Added +* (gazelle) Added Python tags directives: `python_tags`, `python_library_tags`, `python_binary_tags`, and `python_test_tags`. These directives allow adding Bazel tags to generated Python targets for better build control and test categorization. Tags from general and specific directives are combined and sorted alphabetically. * (repl) Default stub now has tab completion, where `readline` support is available, see ([#3114](https://github.com/bazel-contrib/rules_python/pull/3114)). ([#3114](https://github.com/bazel-contrib/rules_python/pull/3114)). diff --git a/gazelle/docs/directives.md b/gazelle/docs/directives.md index 9221c60823..6fad569a74 100644 --- a/gazelle/docs/directives.md +++ b/gazelle/docs/directives.md @@ -145,6 +145,26 @@ The Python-specific directives are: * Allowed Values: `true`, `false` * Allows absolute imports to be resolved to sibling modules (Python 2's behavior without `absolute_import`). +* [`# gazelle:python_tags`](#directive-python_tags) + * Default: n/a + * Allowed Values: A comma-separated list of tags + * Adds tags to all generated Python targets. Multiple tags can be specified + as a comma-separated list. +* [`# gazelle:python_library_tags`](#directive-python_tags) + * Default: n/a + * Allowed Values: A comma-separated list of tags + * Adds tags specific to `py_library` targets. Multiple tags can be specified + as a comma-separated list. +* [`# gazelle:python_binary_tags`](#directive-python_tags) + * Default: n/a + * Allowed Values: A comma-separated list of tags + * Adds tags specific to `py_binary` targets. Multiple tags can be specified + as a comma-separated list. +* [`# gazelle:python_test_tags`](#directive-python_tags) + * Default: n/a + * Allowed Values: A comma-separated list of tags + * Adds tags specific to `py_test` targets. Multiple tags can be specified + as a comma-separated list. ## `python_extension` @@ -645,3 +665,79 @@ previously-generated or hand-created rules. :::{error} Detailed docs are not yet written. ::: + + +## `python_tags` {#directive-python_tags} + +The tag directives allow you to add [Bazel tags](https://bazel.build/reference/be/common-definitions#common-attributes) to generated Python targets. Tags are metadata labels that can be used to control test execution, categorize targets, or influence build behavior. + +There are four tag directives available: + +- `# gazelle:python_tags` - Adds tags to all generated Python targets (`py_library`, `py_binary`, `py_test`) +- `# gazelle:python_library_tags` - Adds tags specifically to `py_library` targets +- `# gazelle:python_binary_tags` - Adds tags specifically to `py_binary` targets +- `# gazelle:python_test_tags` - Adds tags specifically to `py_test` targets + +Tags from general (`python_tags`) and specific directives are combined and sorted alphabetically. + +**Usage:** + +```starlark +# Add tags to all Python targets +# gazelle:python_tags manual,integration + +# Add specific tags to different target types +# gazelle:python_library_tags reusable,shared +# gazelle:python_binary_tags deploy,production +# gazelle:python_test_tags unit,fast +``` + +This generates targets like: + +```starlark +py_library( + name = "mylib", + srcs = ["mylib.py"], + tags = [ + "integration", + "manual", + "reusable", + "shared", + ], +) + +py_binary( + name = "myapp_bin", + srcs = ["__main__.py"], + main = "__main__.py", + tags = [ + "deploy", + "integration", + "manual", + "production", + ], +) + +py_test( + name = "mylib_test", + srcs = ["__test__.py"], + main = "__test__.py", + tags = [ + "fast", + "integration", + "manual", + "unit", + ], +) +``` + +**Common tag examples:** + +- `manual` - Prevents the target from being built by `bazel build //...` +- `integration` - Marks integration tests that may be run separately +- `unit` - Marks unit tests for selective execution +- `exclusive` - Indicates tests that need exclusive resource access +- `deploy` - Marks binaries used for deployment +- `fast` - Indicates fast-running tests + +Multiple tags can be specified as a comma-separated list. Whitespace around commas is automatically trimmed. diff --git a/gazelle/python/configure.go b/gazelle/python/configure.go index 13ba6477cd..bab9b7c5eb 100644 --- a/gazelle/python/configure.go +++ b/gazelle/python/configure.go @@ -69,6 +69,10 @@ func (py *Configurer) KnownDirectives() []string { pythonconfig.TestFilePattern, pythonconfig.LabelConvention, pythonconfig.LabelNormalization, + pythonconfig.Tags, + pythonconfig.LibraryTags, + pythonconfig.BinaryTags, + pythonconfig.TestTags, pythonconfig.GeneratePyiDeps, pythonconfig.ExperimentalAllowRelativeImports, pythonconfig.GenerateProto, @@ -254,6 +258,50 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) { log.Fatal(err) } config.SetResolveSiblingImports(v) + case pythonconfig.Tags: + value := strings.TrimSpace(d.Value) + if value == "" { + config.SetTags([]string{}) + } else { + tags := strings.Split(value, ",") + for i, tag := range tags { + tags[i] = strings.TrimSpace(tag) + } + config.SetTags(tags) + } + case pythonconfig.LibraryTags: + value := strings.TrimSpace(d.Value) + if value == "" { + config.SetLibraryTags([]string{}) + } else { + tags := strings.Split(value, ",") + for i, tag := range tags { + tags[i] = strings.TrimSpace(tag) + } + config.SetLibraryTags(tags) + } + case pythonconfig.BinaryTags: + value := strings.TrimSpace(d.Value) + if value == "" { + config.SetBinaryTags([]string{}) + } else { + tags := strings.Split(value, ",") + for i, tag := range tags { + tags[i] = strings.TrimSpace(tag) + } + config.SetBinaryTags(tags) + } + case pythonconfig.TestTags: + value := strings.TrimSpace(d.Value) + if value == "" { + config.SetTestTags([]string{}) + } else { + tags := strings.Split(value, ",") + for i, tag := range tags { + tags[i] = strings.TrimSpace(tag) + } + config.SetTestTags(tags) + } } } diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go index a180ec527d..ecac9e5488 100644 --- a/gazelle/python/generate.go +++ b/gazelle/python/generate.go @@ -66,6 +66,14 @@ func matchesAnyGlob(s string, globs []string) bool { return false } +// combineTags combines general tags with specific target-type tags +func combineTags(generalTags, specificTags []string) []string { + combined := make([]string, 0, len(generalTags)+len(specificTags)) + combined = append(combined, generalTags...) + combined = append(combined, specificTags...) + return combined +} + // 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. @@ -261,6 +269,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes } pyBinary := newTargetBuilder(pyBinaryKind, pyBinaryTargetName, pythonProjectRoot, args.Rel, pyFileNames, cfg.ResolveSiblingImports()). addVisibility(visibility). + addTags(combineTags(cfg.Tags(), cfg.BinaryTags())). addSrc(filename). addModuleDependencies(mainModules[filename]). addResolvedDependencies(annotations.includeDeps). @@ -303,6 +312,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes pyLibrary := newTargetBuilder(pyLibraryKind, pyLibraryTargetName, pythonProjectRoot, args.Rel, pyFileNames, cfg.ResolveSiblingImports()). addVisibility(visibility). + addTags(combineTags(cfg.Tags(), cfg.LibraryTags())). addSrcs(srcs). addModuleDependencies(allDeps). addResolvedDependencies(annotations.includeDeps). @@ -357,6 +367,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes pyBinaryTarget := newTargetBuilder(pyBinaryKind, pyBinaryTargetName, pythonProjectRoot, args.Rel, pyFileNames, cfg.ResolveSiblingImports()). setMain(pyBinaryEntrypointFilename). addVisibility(visibility). + addTags(combineTags(cfg.Tags(), cfg.BinaryTags())). addSrc(pyBinaryEntrypointFilename). addModuleDependencies(deps). addResolvedDependencies(annotations.includeDeps). @@ -393,6 +404,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes addResolvedDependencies(annotations.includeDeps). setAnnotations(*annotations). addVisibility(visibility). + addTags(combineTags(cfg.Tags(), cfg.LibraryTags())). setTestonly(). generateImportsAttribute() @@ -423,6 +435,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes addSrcs(srcs). addModuleDependencies(deps). addResolvedDependencies(annotations.includeDeps). + addTags(combineTags(cfg.Tags(), cfg.TestTags())). setAnnotations(*annotations). generateImportsAttribute() } diff --git a/gazelle/python/target.go b/gazelle/python/target.go index 3fe5819e00..52e396ee57 100644 --- a/gazelle/python/target.go +++ b/gazelle/python/target.go @@ -16,6 +16,7 @@ package python import ( "path/filepath" + "strings" "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/rule" @@ -37,6 +38,7 @@ type targetBuilder struct { main *string imports []string testonly bool + tags *treeset.Set annotations *annotations resolveSiblingImports bool } @@ -53,6 +55,7 @@ func newTargetBuilder(kind, name, pythonProjectRoot, bzlPackage string, siblingS deps: treeset.NewWith(moduleComparator), resolvedDeps: treeset.NewWith(godsutils.StringComparator), visibility: treeset.NewWith(godsutils.StringComparator), + tags: treeset.NewWith(godsutils.StringComparator), annotations: new(annotations), resolveSiblingImports: resolveSiblingImports, } @@ -122,6 +125,16 @@ func (t *targetBuilder) addVisibility(visibility []string) *targetBuilder { return t } +// addTags adds tags to the target. +func (t *targetBuilder) addTags(tags []string) *targetBuilder { + for _, tag := range tags { + if strings.TrimSpace(tag) != "" { + t.tags.Add(strings.TrimSpace(tag)) + } + } + return t +} + // setMain sets the main file to the target. func (t *targetBuilder) setMain(main string) *targetBuilder { t.main = &main @@ -168,6 +181,9 @@ func (t *targetBuilder) build() *rule.Rule { if !t.visibility.Empty() { r.SetAttr("visibility", t.visibility.Values()) } + if !t.tags.Empty() { + r.SetAttr("tags", t.tags.Values()) + } if t.main != nil { r.SetAttr("main", *t.main) } diff --git a/gazelle/python/testdata/directive_python_tags/README.md b/gazelle/python/testdata/directive_python_tags/README.md new file mode 100644 index 0000000000..64738f5f6d --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/README.md @@ -0,0 +1,11 @@ +# Python tags directive test + +Tests the python_tags, python_library_tags, python_binary_tags, and python_test_tags directives. + +These directives allow adding tags to generated Python targets: +- `python_tags` - Tags added to all Python targets +- `python_library_tags` - Tags specific to py_library targets +- `python_binary_tags` - Tags specific to py_binary targets +- `python_test_tags` - Tags specific to py_test targets + +Tags from general and specific directives are combined. \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/WORKSPACE b/gazelle/python/testdata/directive_python_tags/WORKSPACE new file mode 100644 index 0000000000..6003a32366 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/WORKSPACE @@ -0,0 +1 @@ +workspace(name = "directive_python_tags") \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test.yaml b/gazelle/python/testdata/directive_python_tags/test.yaml new file mode 100644 index 0000000000..95820025c6 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test.yaml @@ -0,0 +1,17 @@ +# Copyright 2024 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test1_general_tags/BUILD.in b/gazelle/python/testdata/directive_python_tags/test1_general_tags/BUILD.in new file mode 100644 index 0000000000..67307d5d79 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test1_general_tags/BUILD.in @@ -0,0 +1,2 @@ +# Test general python_tags directive +# gazelle:python_tags manual,integration \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test1_general_tags/BUILD.out b/gazelle/python/testdata/directive_python_tags/test1_general_tags/BUILD.out new file mode 100644 index 0000000000..c4ad9c668a --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test1_general_tags/BUILD.out @@ -0,0 +1,35 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test") + +# Test general python_tags directive +# gazelle:python_tags manual,integration + +py_library( + name = "test1_general_tags", + srcs = ["__init__.py"], + tags = [ + "integration", + "manual", + ], + visibility = ["//:__subpackages__"], +) + +py_binary( + name = "test1_general_tags_bin", + srcs = ["__main__.py"], + main = "__main__.py", + tags = [ + "integration", + "manual", + ], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "test1_general_tags_test", + srcs = ["__test__.py"], + main = "__test__.py", + tags = [ + "integration", + "manual", + ], +) \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test1_general_tags/__init__.py b/gazelle/python/testdata/directive_python_tags/test1_general_tags/__init__.py new file mode 100644 index 0000000000..fcead4660c --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test1_general_tags/__init__.py @@ -0,0 +1 @@ +# Empty library file \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test1_general_tags/__main__.py b/gazelle/python/testdata/directive_python_tags/test1_general_tags/__main__.py new file mode 100644 index 0000000000..a351b65ac7 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test1_general_tags/__main__.py @@ -0,0 +1 @@ +print("Hello from test1") \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test1_general_tags/__test__.py b/gazelle/python/testdata/directive_python_tags/test1_general_tags/__test__.py new file mode 100644 index 0000000000..1cb87bcd67 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test1_general_tags/__test__.py @@ -0,0 +1 @@ +assert True, "Test passed" \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test2_specific_tags/BUILD.in b/gazelle/python/testdata/directive_python_tags/test2_specific_tags/BUILD.in new file mode 100644 index 0000000000..ccdeb2796a --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test2_specific_tags/BUILD.in @@ -0,0 +1,4 @@ +# Test specific target type tags +# gazelle:python_library_tags reusable,shared +# gazelle:python_binary_tags deploy,production +# gazelle:python_test_tags unit,fast \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test2_specific_tags/BUILD.out b/gazelle/python/testdata/directive_python_tags/test2_specific_tags/BUILD.out new file mode 100644 index 0000000000..fedf3c1b26 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test2_specific_tags/BUILD.out @@ -0,0 +1,37 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test") + +# Test specific target type tags +# gazelle:python_library_tags reusable,shared +# gazelle:python_binary_tags deploy,production +# gazelle:python_test_tags unit,fast + +py_library( + name = "test2_specific_tags", + srcs = ["__init__.py"], + tags = [ + "reusable", + "shared", + ], + visibility = ["//:__subpackages__"], +) + +py_binary( + name = "test2_specific_tags_bin", + srcs = ["__main__.py"], + main = "__main__.py", + tags = [ + "deploy", + "production", + ], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "test2_specific_tags_test", + srcs = ["__test__.py"], + main = "__test__.py", + tags = [ + "fast", + "unit", + ], +) \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test2_specific_tags/__init__.py b/gazelle/python/testdata/directive_python_tags/test2_specific_tags/__init__.py new file mode 100644 index 0000000000..fcead4660c --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test2_specific_tags/__init__.py @@ -0,0 +1 @@ +# Empty library file \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test2_specific_tags/__main__.py b/gazelle/python/testdata/directive_python_tags/test2_specific_tags/__main__.py new file mode 100644 index 0000000000..ae6cf5b544 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test2_specific_tags/__main__.py @@ -0,0 +1 @@ +print("Hello from test2") \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test2_specific_tags/__test__.py b/gazelle/python/testdata/directive_python_tags/test2_specific_tags/__test__.py new file mode 100644 index 0000000000..1cb87bcd67 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test2_specific_tags/__test__.py @@ -0,0 +1 @@ +assert True, "Test passed" \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test3_combined_tags/BUILD.in b/gazelle/python/testdata/directive_python_tags/test3_combined_tags/BUILD.in new file mode 100644 index 0000000000..9c5b080a1b --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test3_combined_tags/BUILD.in @@ -0,0 +1,5 @@ +# Test combining general and specific tags +# gazelle:python_tags manual,integration +# gazelle:python_library_tags reusable +# gazelle:python_binary_tags production +# gazelle:python_test_tags fast \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test3_combined_tags/BUILD.out b/gazelle/python/testdata/directive_python_tags/test3_combined_tags/BUILD.out new file mode 100644 index 0000000000..342adf4bbc --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test3_combined_tags/BUILD.out @@ -0,0 +1,41 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test") + +# Test combining general and specific tags +# gazelle:python_tags manual,integration +# gazelle:python_library_tags reusable +# gazelle:python_binary_tags production +# gazelle:python_test_tags fast + +py_library( + name = "test3_combined_tags", + srcs = ["__init__.py"], + tags = [ + "integration", + "manual", + "reusable", + ], + visibility = ["//:__subpackages__"], +) + +py_binary( + name = "test3_combined_tags_bin", + srcs = ["__main__.py"], + main = "__main__.py", + tags = [ + "integration", + "manual", + "production", + ], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "test3_combined_tags_test", + srcs = ["__test__.py"], + main = "__test__.py", + tags = [ + "fast", + "integration", + "manual", + ], +) \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test3_combined_tags/__init__.py b/gazelle/python/testdata/directive_python_tags/test3_combined_tags/__init__.py new file mode 100644 index 0000000000..fcead4660c --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test3_combined_tags/__init__.py @@ -0,0 +1 @@ +# Empty library file \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test3_combined_tags/__main__.py b/gazelle/python/testdata/directive_python_tags/test3_combined_tags/__main__.py new file mode 100644 index 0000000000..c8b40f37b3 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test3_combined_tags/__main__.py @@ -0,0 +1 @@ +print("Hello from test3") \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test3_combined_tags/__test__.py b/gazelle/python/testdata/directive_python_tags/test3_combined_tags/__test__.py new file mode 100644 index 0000000000..1cb87bcd67 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test3_combined_tags/__test__.py @@ -0,0 +1 @@ +assert True, "Test passed" \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test4_no_tags/BUILD.in b/gazelle/python/testdata/directive_python_tags/test4_no_tags/BUILD.in new file mode 100644 index 0000000000..8ffd6f921e --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test4_no_tags/BUILD.in @@ -0,0 +1 @@ +# Test with no tag directives - targets should have no tags attribute \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test4_no_tags/BUILD.out b/gazelle/python/testdata/directive_python_tags/test4_no_tags/BUILD.out new file mode 100644 index 0000000000..6f4b87310a --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test4_no_tags/BUILD.out @@ -0,0 +1,22 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test") + +# Test with no tag directives - targets should have no tags attribute + +py_library( + name = "test4_no_tags", + srcs = ["__init__.py"], + visibility = ["//:__subpackages__"], +) + +py_binary( + name = "test4_no_tags_bin", + srcs = ["__main__.py"], + main = "__main__.py", + visibility = ["//:__subpackages__"], +) + +py_test( + name = "test4_no_tags_test", + srcs = ["__test__.py"], + main = "__test__.py", +) \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test4_no_tags/__init__.py b/gazelle/python/testdata/directive_python_tags/test4_no_tags/__init__.py new file mode 100644 index 0000000000..fcead4660c --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test4_no_tags/__init__.py @@ -0,0 +1 @@ +# Empty library file \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test4_no_tags/__main__.py b/gazelle/python/testdata/directive_python_tags/test4_no_tags/__main__.py new file mode 100644 index 0000000000..f1f41aef82 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test4_no_tags/__main__.py @@ -0,0 +1 @@ +print("Hello from test4") \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test4_no_tags/__test__.py b/gazelle/python/testdata/directive_python_tags/test4_no_tags/__test__.py new file mode 100644 index 0000000000..1cb87bcd67 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test4_no_tags/__test__.py @@ -0,0 +1 @@ +assert True, "Test passed" \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/BUILD.in b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/BUILD.in new file mode 100644 index 0000000000..026969adc1 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/BUILD.in @@ -0,0 +1,5 @@ +# Test per-file generation with different tag directives +# gazelle:resolve py lib1 lib1_test +# gazelle:python_generation_mode file +# gazelle:python_tags manual,integration +# gazelle:python_library_tags shared diff --git a/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/BUILD.out b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/BUILD.out new file mode 100644 index 0000000000..4f3ec42ff5 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/BUILD.out @@ -0,0 +1,64 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test") + +# Test per-file generation with different tag directives +# gazelle:resolve py lib1 lib1_test +# gazelle:python_generation_mode file +# gazelle:python_tags manual,integration +# gazelle:python_library_tags shared + +py_library( + name = "__init__", + srcs = ["__init__.py"], + tags = [ + "integration", + "manual", + "shared", + ], + visibility = ["//:__subpackages__"], +) + +py_library( + name = "lib1", + srcs = ["lib1.py"], + tags = [ + "integration", + "manual", + "shared", + ], + visibility = ["//:__subpackages__"], +) + +py_library( + name = "lib2", + srcs = ["lib2.py"], + tags = [ + "integration", + "manual", + "shared", + ], + visibility = ["//:__subpackages__"], +) + +py_binary( + name = "test5_per_file_generation_bin", + srcs = ["__main__.py"], + main = "__main__.py", + tags = [ + "integration", + "manual", + ], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "lib1_test", + srcs = [ + "__test__.py", + "lib1_test.py", + ], + main = "__test__.py", + tags = [ + "integration", + "manual", + ], +) diff --git a/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/__init__.py b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/__init__.py new file mode 100644 index 0000000000..fcead4660c --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/__init__.py @@ -0,0 +1 @@ +# Empty library file \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/__main__.py b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/__main__.py new file mode 100644 index 0000000000..60fa88248c --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/__main__.py @@ -0,0 +1 @@ +print("Hello from test5") \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/__test__.py b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/__test__.py new file mode 100644 index 0000000000..1cb87bcd67 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/__test__.py @@ -0,0 +1 @@ +assert True, "Test passed" \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/lib1.py b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/lib1.py new file mode 100644 index 0000000000..d170a570b5 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/lib1.py @@ -0,0 +1,3 @@ +def add_numbers(a, b): + """Simple function that adds two numbers.""" + return a + b \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/lib1_test.py b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/lib1_test.py new file mode 100644 index 0000000000..1589132909 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/lib1_test.py @@ -0,0 +1,9 @@ +import lib1 + +def test_add_numbers(): + result = lib1.add_numbers(2, 3) + assert result == 5, f"Expected 5, got {result}" + +if __name__ == "__main__": + test_add_numbers() + print("lib1 tests passed") \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/lib2.py b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/lib2.py new file mode 100644 index 0000000000..b06a88305c --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test5_per_file_generation/lib2.py @@ -0,0 +1,3 @@ +def multiply_numbers(a, b): + """Simple function that multiplies two numbers.""" + return a * b \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test6_edge_cases/BUILD.in b/gazelle/python/testdata/directive_python_tags/test6_edge_cases/BUILD.in new file mode 100644 index 0000000000..0e0b42607d --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test6_edge_cases/BUILD.in @@ -0,0 +1,4 @@ +# Test edge cases: multiple directives, empty values, whitespace handling +# gazelle:python_tags manual, spaced +# gazelle:python_library_tags +# gazelle:python_test_tags unit, fast,reliable \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test6_edge_cases/BUILD.out b/gazelle/python/testdata/directive_python_tags/test6_edge_cases/BUILD.out new file mode 100644 index 0000000000..b6eddaa054 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test6_edge_cases/BUILD.out @@ -0,0 +1,40 @@ +load("@rules_python//python:defs.bzl", "py_binary", "py_library", "py_test") + +# Test edge cases: multiple directives, empty values, whitespace handling +# gazelle:python_tags manual, spaced +# gazelle:python_library_tags +# gazelle:python_test_tags unit, fast,reliable + +py_library( + name = "test6_edge_cases", + srcs = ["__init__.py"], + tags = [ + "manual", + "spaced", + ], + visibility = ["//:__subpackages__"], +) + +py_binary( + name = "test6_edge_cases_bin", + srcs = ["__main__.py"], + main = "__main__.py", + tags = [ + "manual", + "spaced", + ], + visibility = ["//:__subpackages__"], +) + +py_test( + name = "test6_edge_cases_test", + srcs = ["__test__.py"], + main = "__test__.py", + tags = [ + "fast", + "manual", + "reliable", + "spaced", + "unit", + ], +) \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test6_edge_cases/__init__.py b/gazelle/python/testdata/directive_python_tags/test6_edge_cases/__init__.py new file mode 100644 index 0000000000..fcead4660c --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test6_edge_cases/__init__.py @@ -0,0 +1 @@ +# Empty library file \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test6_edge_cases/__main__.py b/gazelle/python/testdata/directive_python_tags/test6_edge_cases/__main__.py new file mode 100644 index 0000000000..45e6c512cc --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test6_edge_cases/__main__.py @@ -0,0 +1 @@ +print("Hello from test6") \ No newline at end of file diff --git a/gazelle/python/testdata/directive_python_tags/test6_edge_cases/__test__.py b/gazelle/python/testdata/directive_python_tags/test6_edge_cases/__test__.py new file mode 100644 index 0000000000..1cb87bcd67 --- /dev/null +++ b/gazelle/python/testdata/directive_python_tags/test6_edge_cases/__test__.py @@ -0,0 +1 @@ +assert True, "Test passed" \ No newline at end of file diff --git a/gazelle/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go index ed9b914e82..7e4869d237 100644 --- a/gazelle/pythonconfig/pythonconfig.go +++ b/gazelle/pythonconfig/pythonconfig.go @@ -112,6 +112,14 @@ const ( // like "import a" can be resolved to sibling modules. When disabled, they // can only be resolved as an absolute import. PythonResolveSiblingImports = "python_resolve_sibling_imports" + // Tags represents the directive that adds tags to all generated Python targets. + Tags = "python_tags" + // LibraryTags represents the directive that adds tags specifically to py_library targets. + LibraryTags = "python_library_tags" + // BinaryTags represents the directive that adds tags specifically to py_binary targets. + BinaryTags = "python_binary_tags" + // TestTags represents the directive that adds tags specifically to py_test targets. + TestTags = "python_test_tags" ) // GenerationModeType represents one of the generation modes for the Python @@ -204,6 +212,10 @@ type Config struct { generatePyiDeps bool generateProto bool resolveSiblingImports bool + tags []string + libraryTags []string + binaryTags []string + testTags []string } type LabelNormalizationType int @@ -244,6 +256,10 @@ func New( generatePyiDeps: false, generateProto: false, resolveSiblingImports: false, + tags: []string{}, + libraryTags: []string{}, + binaryTags: []string{}, + testTags: []string{}, } } @@ -281,6 +297,10 @@ func (c *Config) NewChild() *Config { generatePyiDeps: c.generatePyiDeps, generateProto: c.generateProto, resolveSiblingImports: c.resolveSiblingImports, + tags: c.tags, + libraryTags: c.libraryTags, + binaryTags: c.binaryTags, + testTags: c.testTags, } } @@ -610,6 +630,46 @@ func (c *Config) ResolveSiblingImports() bool { return c.resolveSiblingImports } +// SetTags sets the tags to add to all generated Python targets. +func (c *Config) SetTags(tags []string) { + c.tags = tags +} + +// Tags returns the tags to add to all generated Python targets. +func (c *Config) Tags() []string { + return c.tags +} + +// SetLibraryTags sets the tags to add specifically to py_library targets. +func (c *Config) SetLibraryTags(tags []string) { + c.libraryTags = tags +} + +// LibraryTags returns the tags to add specifically to py_library targets. +func (c *Config) LibraryTags() []string { + return c.libraryTags +} + +// SetBinaryTags sets the tags to add specifically to py_binary targets. +func (c *Config) SetBinaryTags(tags []string) { + c.binaryTags = tags +} + +// BinaryTags returns the tags to add specifically to py_binary targets. +func (c *Config) BinaryTags() []string { + return c.binaryTags +} + +// SetTestTags sets the tags to add specifically to py_test targets. +func (c *Config) SetTestTags(tags []string) { + c.testTags = tags +} + +// TestTags returns the tags to add specifically to py_test targets. +func (c *Config) TestTags() []string { + return c.testTags +} + // FormatThirdPartyDependency returns a label to a third-party dependency performing all formating and normalization. func (c *Config) FormatThirdPartyDependency(repositoryName string, distributionName string) label.Label { conventionalDistributionName := strings.ReplaceAll(c.labelConvention, distributionNameLabelConventionSubstitution, distributionName)