diff --git a/gazelle/README.md b/gazelle/README.md index 117aacbd14..00170345e7 100644 --- a/gazelle/README.md +++ b/gazelle/README.md @@ -198,6 +198,8 @@ Python-specific directives are as follows: | Controls the `py_test` naming convention. Follows the same interpolation rules as `python_library_naming_convention`. | | | `# gazelle:resolve py ...` | n/a | | Instructs the plugin what target to add as a dependency to satisfy a given import statement. The syntax is `# gazelle:resolve py import-string label` where `import-string` is the symbol in the python `import` statement, and `label` is the Bazel label that Gazelle should write in `deps`. | | +| [`# gazelle:python_visibility label`](#directive-python_visibility) | | +| Appends additional visibility labels to each generated target. This directive can be set multiple times. | | #### Directive: `python_root`: @@ -236,6 +238,50 @@ py_libary( [python-packaging-user-guide]: https://github.com/pypa/packaging.python.org/blob/4c86169a/source/tutorials/packaging-projects.rst +#### Directive: `python_visibility`: + +Appends additional `visibility` labels to each generated target. + +This directive can be set multiple times. The generated `visibility` attribute +will include the default visibility and all labels defined by this directive. +All labels will be ordered alphabetically. + +```starlark +# ./BUILD.bazel +# gazelle:python_visibility //tests:__pkg__ +# gazelle:python_visibility //bar:baz + +py_library( + ... + visibility = [ + "//:__subpackages__", # default visibility + "//bar:baz", + "//tests:__pkg__", + ], + ... +) +``` + +Child Bazel packages inherit values from parents: + +```starlark +# ./bar/BUILD.bazel +# gazelle:python_visibility //tests:__subpackages__ + +py_library( + ... + visibility = [ + "//:__subpackages__", # default visibility + "//bar:baz", # defined in ../BUILD.bazel + "//tests:__pkg__", # defined in ../BUILD.bazel + "//tests:__subpackages__", # defined in this ./BUILD.bazel + ], + ... +) + +``` + + ### Libraries Python source files are those ending in `.py` but not ending in `_test.py`. diff --git a/gazelle/python/configure.go b/gazelle/python/configure.go index 69d276266e..431568829b 100644 --- a/gazelle/python/configure.go +++ b/gazelle/python/configure.go @@ -63,6 +63,7 @@ func (py *Configurer) KnownDirectives() []string { pythonconfig.LibraryNamingConvention, pythonconfig.BinaryNamingConvention, pythonconfig.TestNamingConvention, + pythonconfig.Visibility, } } @@ -162,6 +163,8 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) { config.SetBinaryNamingConvention(strings.TrimSpace(d.Value)) case pythonconfig.TestNamingConvention: config.SetTestNamingConvention(strings.TrimSpace(d.Value)) + case pythonconfig.Visibility: + config.AppendVisibility(strings.TrimSpace(d.Value)) } } diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go index ba273be2b7..6973d2ded8 100644 --- a/gazelle/python/generate.go +++ b/gazelle/python/generate.go @@ -212,7 +212,8 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes } parser := newPython3Parser(args.Config.RepoRoot, args.Rel, cfg.IgnoresDependency) - visibility := fmt.Sprintf("//%s:__subpackages__", pythonProjectRoot) + visibility := []string{fmt.Sprintf("//%s:__subpackages__", pythonProjectRoot)} + visibility = append(visibility, cfg.Visibility()...) var result language.GenerateResult result.Gen = make([]*rule.Rule, 0) diff --git a/gazelle/python/target.go b/gazelle/python/target.go index e3104058b2..a941a7cc7e 100644 --- a/gazelle/python/target.go +++ b/gazelle/python/target.go @@ -99,9 +99,11 @@ func (t *targetBuilder) addResolvedDependency(dep string) *targetBuilder { return t } -// addVisibility adds a visibility to the target. -func (t *targetBuilder) addVisibility(visibility string) *targetBuilder { - t.visibility.Add(visibility) +// addVisibility adds visibility labels to the target. +func (t *targetBuilder) addVisibility(visibility []string) *targetBuilder { + for _, item := range visibility { + t.visibility.Add(item) + } return t } diff --git a/gazelle/python/testdata/directive_python_visibility/BUILD.in b/gazelle/python/testdata/directive_python_visibility/BUILD.in new file mode 100644 index 0000000000..c1ba9e455e --- /dev/null +++ b/gazelle/python/testdata/directive_python_visibility/BUILD.in @@ -0,0 +1,4 @@ +# Directives can be added in any order. They will be ordered alphabetically +# when added. +# gazelle:python_visibility //tests:__pkg__ +# gazelle:python_visibility //bar:baz diff --git a/gazelle/python/testdata/directive_python_visibility/BUILD.out b/gazelle/python/testdata/directive_python_visibility/BUILD.out new file mode 100644 index 0000000000..70715e86fa --- /dev/null +++ b/gazelle/python/testdata/directive_python_visibility/BUILD.out @@ -0,0 +1,16 @@ +load("@rules_python//python:defs.bzl", "py_library") + +# Directives can be added in any order. They will be ordered alphabetically +# when added. +# gazelle:python_visibility //tests:__pkg__ +# gazelle:python_visibility //bar:baz + +py_library( + name = "directive_python_visibility", + srcs = ["foo.py"], + visibility = [ + "//:__subpackages__", + "//bar:baz", + "//tests:__pkg__", + ], +) diff --git a/gazelle/python/testdata/directive_python_visibility/README.md b/gazelle/python/testdata/directive_python_visibility/README.md new file mode 100644 index 0000000000..51ab7aef6b --- /dev/null +++ b/gazelle/python/testdata/directive_python_visibility/README.md @@ -0,0 +1,4 @@ +# Directive: `python_visibility` + +This test case asserts that the `# gazelle:python_visibility` directive correctly +appends multiple labels to the target's `visibility` parameter. diff --git a/gazelle/python/testdata/directive_python_visibility/WORKSPACE b/gazelle/python/testdata/directive_python_visibility/WORKSPACE new file mode 100644 index 0000000000..faff6af87a --- /dev/null +++ b/gazelle/python/testdata/directive_python_visibility/WORKSPACE @@ -0,0 +1 @@ +# This is a Bazel workspace for the Gazelle test data. diff --git a/gazelle/python/testdata/directive_python_visibility/foo.py b/gazelle/python/testdata/directive_python_visibility/foo.py new file mode 100644 index 0000000000..98907eb794 --- /dev/null +++ b/gazelle/python/testdata/directive_python_visibility/foo.py @@ -0,0 +1,2 @@ +def func(): + print("library_func") diff --git a/gazelle/python/testdata/directive_python_visibility/subdir/BUILD.in b/gazelle/python/testdata/directive_python_visibility/subdir/BUILD.in new file mode 100644 index 0000000000..5193e69587 --- /dev/null +++ b/gazelle/python/testdata/directive_python_visibility/subdir/BUILD.in @@ -0,0 +1,4 @@ +# python_visibilty directive applies to all child bazel packages. +# Thus, the generated file for this package will also have vis for +# //tests:__pkg__ and //bar:baz in addition to the default. +# gazelle:python_visibility //tests:__subpackages__ diff --git a/gazelle/python/testdata/directive_python_visibility/subdir/BUILD.out b/gazelle/python/testdata/directive_python_visibility/subdir/BUILD.out new file mode 100644 index 0000000000..722c840432 --- /dev/null +++ b/gazelle/python/testdata/directive_python_visibility/subdir/BUILD.out @@ -0,0 +1,20 @@ +load("@rules_python//python:defs.bzl", "py_library") + +# python_visibilty directive applies to all child bazel packages. +# Thus, the generated file for this package will also have vis for +# //tests:__pkg__ and //bar:baz in addition to the default. +# gazelle:python_visibility //tests:__subpackages__ + +py_library( + name = "subdir", + srcs = [ + "__init__.py", + "bar.py", + ], + visibility = [ + "//:__subpackages__", + "//bar:baz", + "//tests:__pkg__", + "//tests:__subpackages__", + ], +) diff --git a/gazelle/python/testdata/directive_python_visibility/subdir/__init__.py b/gazelle/python/testdata/directive_python_visibility/subdir/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/python/testdata/directive_python_visibility/subdir/bar.py b/gazelle/python/testdata/directive_python_visibility/subdir/bar.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/gazelle/python/testdata/directive_python_visibility/test.yaml b/gazelle/python/testdata/directive_python_visibility/test.yaml new file mode 100644 index 0000000000..2410223e59 --- /dev/null +++ b/gazelle/python/testdata/directive_python_visibility/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/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go index cecf9dcea0..e350a7cb60 100644 --- a/gazelle/pythonconfig/pythonconfig.go +++ b/gazelle/pythonconfig/pythonconfig.go @@ -67,6 +67,10 @@ const ( // naming convention. See python_library_naming_convention for more info on // the package name interpolation. TestNamingConvention = "python_test_naming_convention" + // Visibility represents the directive that controls what additional + // visibility labels are added to generated targets. It mimics the behavior + // of the `go_visibility` directive. + Visibility = "python_visibility" ) // GenerationModeType represents one of the generation modes for the Python @@ -136,6 +140,7 @@ type Config struct { libraryNamingConvention string binaryNamingConvention string testNamingConvention string + visibility []string } // New creates a new Config. @@ -157,6 +162,7 @@ func New( libraryNamingConvention: packageNameNamingConventionSubstitution, binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution), testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution), + visibility: []string{}, } } @@ -183,6 +189,7 @@ func (c *Config) NewChild() *Config { libraryNamingConvention: c.libraryNamingConvention, binaryNamingConvention: c.binaryNamingConvention, testNamingConvention: c.testNamingConvention, + visibility: c.visibility, } } @@ -388,3 +395,13 @@ func (c *Config) SetTestNamingConvention(testNamingConvention string) { func (c *Config) RenderTestName(packageName string) string { return strings.ReplaceAll(c.testNamingConvention, packageNameNamingConventionSubstitution, packageName) } + +// AppendVisibility adds additional items to the target's visibility. +func (c *Config) AppendVisibility(visibility string) { + c.visibility = append(c.visibility, visibility) +} + +// Visibility returns the target's visibility. +func (c *Config) Visibility() []string { + return c.visibility +}