Skip to content

Commit da10ac4

Browse files
authored
feat(gazelle): Add "python_visibility" directive that appends additional visibility labels (bazel-contrib#1784)
Fixes bazel-contrib#1783. Add a new gazelle directive, `python_visibility`, that allows users to add labels to the `visibility` attribute of generated targets. out by the way, hence this PR), I noticed that the docs were a little This directive acts similar to[^1] the [`go_visibility` directive](https://github.com/bazelbuild/bazel-gazelle#directives). The primary use case is for python projects that separate unit test files from the python packages/modules that they test, like so: ``` packaging_tutorial/ ├── LICENSE ├── pyproject.toml ├── README.md ├── src/ │ └── mypackage/ │ ├── __init__.py │ └── foo.py └── tests/ ├── __init__.py └── test_foo.py ``` A future PR will add an example to the `./examples` directory (issue bazel-contrib#1775). [^1]: At least, similar based on docs. I haven't done any actual comparison.
1 parent 3f40e98 commit da10ac4

File tree

15 files changed

+141
-4
lines changed

15 files changed

+141
-4
lines changed

gazelle/README.md

+46
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ Python-specific directives are as follows:
198198
| Controls the `py_test` naming convention. Follows the same interpolation rules as `python_library_naming_convention`. | |
199199
| `# gazelle:resolve py ...` | n/a |
200200
| 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`. | |
201+
| [`# gazelle:python_visibility label`](#directive-python_visibility) | |
202+
| Appends additional visibility labels to each generated target. This directive can be set multiple times. | |
201203

202204

203205
#### Directive: `python_root`:
@@ -236,6 +238,50 @@ py_libary(
236238
[python-packaging-user-guide]: https://github.com/pypa/packaging.python.org/blob/4c86169a/source/tutorials/packaging-projects.rst
237239

238240

241+
#### Directive: `python_visibility`:
242+
243+
Appends additional `visibility` labels to each generated target.
244+
245+
This directive can be set multiple times. The generated `visibility` attribute
246+
will include the default visibility and all labels defined by this directive.
247+
All labels will be ordered alphabetically.
248+
249+
```starlark
250+
# ./BUILD.bazel
251+
# gazelle:python_visibility //tests:__pkg__
252+
# gazelle:python_visibility //bar:baz
253+
254+
py_library(
255+
...
256+
visibility = [
257+
"//:__subpackages__", # default visibility
258+
"//bar:baz",
259+
"//tests:__pkg__",
260+
],
261+
...
262+
)
263+
```
264+
265+
Child Bazel packages inherit values from parents:
266+
267+
```starlark
268+
# ./bar/BUILD.bazel
269+
# gazelle:python_visibility //tests:__subpackages__
270+
271+
py_library(
272+
...
273+
visibility = [
274+
"//:__subpackages__", # default visibility
275+
"//bar:baz", # defined in ../BUILD.bazel
276+
"//tests:__pkg__", # defined in ../BUILD.bazel
277+
"//tests:__subpackages__", # defined in this ./BUILD.bazel
278+
],
279+
...
280+
)
281+
282+
```
283+
284+
239285
### Libraries
240286

241287
Python source files are those ending in `.py` but not ending in `_test.py`.

gazelle/python/configure.go

+3
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func (py *Configurer) KnownDirectives() []string {
6363
pythonconfig.LibraryNamingConvention,
6464
pythonconfig.BinaryNamingConvention,
6565
pythonconfig.TestNamingConvention,
66+
pythonconfig.Visibility,
6667
}
6768
}
6869

@@ -162,6 +163,8 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
162163
config.SetBinaryNamingConvention(strings.TrimSpace(d.Value))
163164
case pythonconfig.TestNamingConvention:
164165
config.SetTestNamingConvention(strings.TrimSpace(d.Value))
166+
case pythonconfig.Visibility:
167+
config.AppendVisibility(strings.TrimSpace(d.Value))
165168
}
166169
}
167170

gazelle/python/generate.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,8 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
212212
}
213213

214214
parser := newPython3Parser(args.Config.RepoRoot, args.Rel, cfg.IgnoresDependency)
215-
visibility := fmt.Sprintf("//%s:__subpackages__", pythonProjectRoot)
215+
visibility := []string{fmt.Sprintf("//%s:__subpackages__", pythonProjectRoot)}
216+
visibility = append(visibility, cfg.Visibility()...)
216217

217218
var result language.GenerateResult
218219
result.Gen = make([]*rule.Rule, 0)

gazelle/python/target.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,11 @@ func (t *targetBuilder) addResolvedDependency(dep string) *targetBuilder {
9999
return t
100100
}
101101

102-
// addVisibility adds a visibility to the target.
103-
func (t *targetBuilder) addVisibility(visibility string) *targetBuilder {
104-
t.visibility.Add(visibility)
102+
// addVisibility adds visibility labels to the target.
103+
func (t *targetBuilder) addVisibility(visibility []string) *targetBuilder {
104+
for _, item := range visibility {
105+
t.visibility.Add(item)
106+
}
105107
return t
106108
}
107109

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Directives can be added in any order. They will be ordered alphabetically
2+
# when added.
3+
# gazelle:python_visibility //tests:__pkg__
4+
# gazelle:python_visibility //bar:baz
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
load("@rules_python//python:defs.bzl", "py_library")
2+
3+
# Directives can be added in any order. They will be ordered alphabetically
4+
# when added.
5+
# gazelle:python_visibility //tests:__pkg__
6+
# gazelle:python_visibility //bar:baz
7+
8+
py_library(
9+
name = "directive_python_visibility",
10+
srcs = ["foo.py"],
11+
visibility = [
12+
"//:__subpackages__",
13+
"//bar:baz",
14+
"//tests:__pkg__",
15+
],
16+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Directive: `python_visibility`
2+
3+
This test case asserts that the `# gazelle:python_visibility` directive correctly
4+
appends multiple labels to the target's `visibility` parameter.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# This is a Bazel workspace for the Gazelle test data.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def func():
2+
print("library_func")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# python_visibilty directive applies to all child bazel packages.
2+
# Thus, the generated file for this package will also have vis for
3+
# //tests:__pkg__ and //bar:baz in addition to the default.
4+
# gazelle:python_visibility //tests:__subpackages__
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
load("@rules_python//python:defs.bzl", "py_library")
2+
3+
# python_visibilty directive applies to all child bazel packages.
4+
# Thus, the generated file for this package will also have vis for
5+
# //tests:__pkg__ and //bar:baz in addition to the default.
6+
# gazelle:python_visibility //tests:__subpackages__
7+
8+
py_library(
9+
name = "subdir",
10+
srcs = [
11+
"__init__.py",
12+
"bar.py",
13+
],
14+
visibility = [
15+
"//:__subpackages__",
16+
"//bar:baz",
17+
"//tests:__pkg__",
18+
"//tests:__subpackages__",
19+
],
20+
)

gazelle/python/testdata/directive_python_visibility/subdir/__init__.py

Whitespace-only changes.

gazelle/python/testdata/directive_python_visibility/subdir/bar.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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+
---
16+
expect:
17+
exit_code: 0

gazelle/pythonconfig/pythonconfig.go

+17
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ const (
6767
// naming convention. See python_library_naming_convention for more info on
6868
// the package name interpolation.
6969
TestNamingConvention = "python_test_naming_convention"
70+
// Visibility represents the directive that controls what additional
71+
// visibility labels are added to generated targets. It mimics the behavior
72+
// of the `go_visibility` directive.
73+
Visibility = "python_visibility"
7074
)
7175

7276
// GenerationModeType represents one of the generation modes for the Python
@@ -136,6 +140,7 @@ type Config struct {
136140
libraryNamingConvention string
137141
binaryNamingConvention string
138142
testNamingConvention string
143+
visibility []string
139144
}
140145

141146
// New creates a new Config.
@@ -157,6 +162,7 @@ func New(
157162
libraryNamingConvention: packageNameNamingConventionSubstitution,
158163
binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution),
159164
testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution),
165+
visibility: []string{},
160166
}
161167
}
162168

@@ -183,6 +189,7 @@ func (c *Config) NewChild() *Config {
183189
libraryNamingConvention: c.libraryNamingConvention,
184190
binaryNamingConvention: c.binaryNamingConvention,
185191
testNamingConvention: c.testNamingConvention,
192+
visibility: c.visibility,
186193
}
187194
}
188195

@@ -388,3 +395,13 @@ func (c *Config) SetTestNamingConvention(testNamingConvention string) {
388395
func (c *Config) RenderTestName(packageName string) string {
389396
return strings.ReplaceAll(c.testNamingConvention, packageNameNamingConventionSubstitution, packageName)
390397
}
398+
399+
// AppendVisibility adds additional items to the target's visibility.
400+
func (c *Config) AppendVisibility(visibility string) {
401+
c.visibility = append(c.visibility, visibility)
402+
}
403+
404+
// Visibility returns the target's visibility.
405+
func (c *Config) Visibility() []string {
406+
return c.visibility
407+
}

0 commit comments

Comments
 (0)