Skip to content

Commit

Permalink
feat(gazelle): Add "python_default_visibility" directive (#1787)
Browse files Browse the repository at this point in the history
Fixes #1783. Provides a way to fix #1682.

Add the `python_default_visibility` directive. This directive sets the
`visibility` attribute on all generated `py_*` rules. It accepts a
comma-separated list of labels to add as visibility targets, similar
to how the base `default_visibility` directive works.

Setting this directive multiple times will override previous values.

Two special values are also accepted: `NONE` and `DEFAULT`. See
./gazelle/README.md#directive-python_default_visibility for details.

The directive also accepts a special string `"$python_root"` that gets
replaced with the `python_root` value, if set. If not set,
`"$python_root"`
is replaced with the empty string.

---------

Co-authored-by: Thulio Ferraz Assis <3149049+f0rmiga@users.noreply.github.com>
  • Loading branch information
dougthor42 and f0rmiga committed Mar 17, 2024
1 parent 565a531 commit bdb2aa2
Show file tree
Hide file tree
Showing 55 changed files with 364 additions and 4 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ A brief description of the categories of changes:
https://github.com/indygreg/python-build-standalone/releases/tag/20240224.
* (gazelle) Added a new `python_visibility` directive to control visibility
of generated targets by appending additional visibility labels.
* (gazelle) Added a new `python_default_visibility` directive to control the
_default_ visibility of generated targets. See the [docs][python_default_visibility]
for details.

[0.XX.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.XX.0
[python_default_visibility]: gazelle/README.md#directive-python_default_visibility

### Changed

Expand Down
77 changes: 77 additions & 0 deletions gazelle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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_default_visibility labels`](#directive-python_default_visibility) | |
| Instructs gazelle to use these visibility labels on all python targets. `labels` is a comma-separated list of labels (without spaces). | `//$python_root:__subpackages__` |
| [`# gazelle:python_visibility label`](#directive-python_visibility) | |
| Appends additional visibility labels to each generated target. This directive can be set multiple times. | |

Expand Down Expand Up @@ -238,6 +240,81 @@ py_libary(
[python-packaging-user-guide]: https://github.com/pypa/packaging.python.org/blob/4c86169a/source/tutorials/packaging-projects.rst


#### Directive: `python_default_visibility`:

Instructs gazelle to use these visibility labels on all _python_ targets
(typically `py_*`, but can be modified via the `map_kind` directive). The arg
to this directive is a a comma-separated list (without spaces) of labels.

For example:

```starlark
# gazelle:python_default_visibility //:__subpackages__,//tests:__subpackages__
```

produces the following visibility attribute:

```starlark
py_library(
...,
visibility = [
"//:__subpackages__",
"//tests:__subpackages__",
],
...,
)
```

You can also inject the `python_root` value by using the exact string
`$python_root`. All instances of this string will be replaced by the `python_root`
value.

```starlark
# gazelle:python_default_visibility //$python_root:__pkg__,//foo/$python_root/tests:__subpackages__

# Assuming the "# gazelle:python_root" directive is set in ./py/src/BUILD.bazel,
# the results will be:
py_library(
...,
visibility = [
"//foo/py/src/tests:__subpackages__", # sorted alphabetically
"//py/src:__pkg__",
],
...,
)
```

Two special values are also accepted as an argument to the directive:

+ `NONE`: This removes all default visibility. Labels added by the
`python_visibility` directive are still included.
+ `DEFAULT`: This resets the default visibility.

For example:

```starlark
# gazelle:python_default_visibility NONE

py_library(
name = "...",
srcs = [...],
)
```

```starlark
# gazelle:python_default_visibility //foo:bar
# gazelle:python_default_visibility DEFAULT

py_library(
...,
visibility = ["//:__subpackages__"],
...,
)
```

These special values can be useful for sub-packages.


#### Directive: `python_visibility`:

Appends additional `visibility` labels to each generated target.
Expand Down
16 changes: 16 additions & 0 deletions gazelle/python/configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ func (py *Configurer) KnownDirectives() []string {
pythonconfig.LibraryNamingConvention,
pythonconfig.BinaryNamingConvention,
pythonconfig.TestNamingConvention,
pythonconfig.DefaultVisibilty,
pythonconfig.Visibility,
}
}
Expand Down Expand Up @@ -119,6 +120,7 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
}
case pythonconfig.PythonRootDirective:
config.SetPythonProjectRoot(rel)
config.SetDefaultVisibility([]string{fmt.Sprintf(pythonconfig.DefaultVisibilityFmtString, rel)})
case pythonconfig.PythonManifestFileNameDirective:
gazelleManifestFilename = strings.TrimSpace(d.Value)
case pythonconfig.IgnoreFilesDirective:
Expand Down Expand Up @@ -163,6 +165,20 @@ 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.DefaultVisibilty:
switch directiveArg := strings.TrimSpace(d.Value); directiveArg {
case "NONE":
config.SetDefaultVisibility([]string{})
case "DEFAULT":
pythonProjectRoot := config.PythonProjectRoot()
defaultVisibility := fmt.Sprintf(pythonconfig.DefaultVisibilityFmtString, pythonProjectRoot)
config.SetDefaultVisibility([]string{defaultVisibility})
default:
// Handle injecting the python root. Assume that the user used the
// exact string "$python_root".
labels := strings.ReplaceAll(directiveArg, "$python_root", config.PythonProjectRoot())
config.SetDefaultVisibility(strings.Split(labels, ","))
}
case pythonconfig.Visibility:
config.AppendVisibility(strings.TrimSpace(d.Value))
}
Expand Down
3 changes: 1 addition & 2 deletions gazelle/python/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
}

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

var result language.GenerateResult
result.Gen = make([]*rule.Rule, 0)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Directive: `python_default_visibility`

This test case asserts that the `# gazelle:python_default_visibility` directive
correctly:

1. Uses the default value when `python_default_visibility` is not set.
2. Uses the correct default value when `python_root` is set and
`python_default_visibility` is not set.
3. Supports injecting `python_root`
4. Supports multiple labels
5. Setting the label to "NONE" removes all visibility attibutes.
6. Setting the label to "DEFAULT" reverts to using the default.
7. Adding `python_visibility` directive with `python_default_visibility NONE`
only adds the items listed by `python_visibility`.
8. Multiple `python_root` dirs [GH #1682][gh-1682] uses correct value when
injecting `python_root`.
9. Setting both `python_default_visibility` and `python_visibility` and how
they interact with sub-packages.


[gh-1682]: https://github.com/bazelbuild/rules_python/issues/1682
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This is a Bazel workspace for the Gazelle test data.
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# python_default_visibility is not set.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("@rules_python//python:defs.bzl", "py_library")

# python_default_visibility is not set.

py_library(
name = "test1_default",
srcs = ["test1.py"],
visibility = ["//:__subpackages__"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def func():
print("library_func")
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:python_root
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_root

py_library(
name = "test2_default_with_python_root",
srcs = [
"__init__.py",
"test2.py",
],
visibility = ["//test2_default_with_python_root:__subpackages__"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def func():
print("library_func")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# gazelle:python_root
# gazelle:python_default_visibility //foo/$python_root/bar:__pkg__
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_root
# gazelle:python_default_visibility //foo/$python_root/bar:__pkg__

py_library(
name = "test3_injection",
srcs = [
"__init__.py",
"test3.py",
],
visibility = ["//foo/test3_injection/bar:__pkg__"],
)
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def func():
print("library_func")
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:python_default_visibility //foo/bar:__pkg__,//tests:__subpackages__,//a:b
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_default_visibility //foo/bar:__pkg__,//tests:__subpackages__,//a:b

py_library(
name = "test4_multiple_labels",
srcs = ["test4.py"],
visibility = [
"//a:b",
"//foo/bar:__pkg__",
"//tests:__subpackages__",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def func():
print("library_func")
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:python_default_visibility NONE
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_default_visibility NONE

py_library(
name = "test5_none_label",
srcs = ["test5.py"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def func():
print("library_func")
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:python_default_visibility //foo:bar
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
load("@rules_python//python:defs.bzl", "py_library")

# gazelle:python_default_visibility //foo:bar

py_library(
name = "test6_default_label",
srcs = ["test6.py"],
visibility = ["//foo:bar"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Reset the default visibility to the default for all child packages.
# gazelle:python_default_visibility DEFAULT
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
load("@rules_python//python:defs.bzl", "py_library")

# Reset the default visibility to the default for all child packages.
# gazelle:python_default_visibility DEFAULT

py_library(
name = "subpkg",
srcs = ["test6_sub.py"],
visibility = ["//:__subpackages__"],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def func():
print("library_func")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def func():
print("library_func")
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# python_visibility directives that happen either before _or_ after the
# NONE reset both get applied.
# gazelle:python_visibility //foo:bar
# gazelle:python_default_visibility NONE
# gazelle:python_visibility //bar:baz
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
load("@rules_python//python:defs.bzl", "py_library")

# python_visibility directives that happen either before _or_ after the
# NONE reset both get applied.
# gazelle:python_visibility //foo:bar
# gazelle:python_default_visibility NONE
# gazelle:python_visibility //bar:baz

py_library(
name = "test7_none_label_with_extra_vis",
srcs = ["test7.py"],
visibility = [
"//bar:baz",
"//foo:bar",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def func():
print("library_func")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# For funzies, also throw in some additional visibility.
# gazelle:python_visibility //tests:__pkg__
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# For funzies, also throw in some additional visibility.
# gazelle:python_visibility //tests:__pkg__
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:python_root
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:python_root
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# proj1 depends on proj2
# We can leave the default visibility.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
load("@rules_python//python:defs.bzl", "py_library")

# proj1 depends on proj2
# We can leave the default visibility.

py_library(
name = "pkg1",
srcs = ["file1.py"],
imports = [".."],
visibility = [
"//test8_multiple_python_root_dirs/proj1/src:__subpackages__",
"//tests:__pkg__",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:python_root
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:python_root
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# proj1 depends on proj2
# So we have to make sure that proj2 is visible by proj1
# gazelle:python_default_visibility //$python_root:__subpackages__,//test8_multiple_python_root_dirs/proj1/src:__subpackages__
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
load("@rules_python//python:defs.bzl", "py_library")

# proj1 depends on proj2
# So we have to make sure that proj2 is visible by proj1
# gazelle:python_default_visibility //$python_root:__subpackages__,//test8_multiple_python_root_dirs/proj1/src:__subpackages__

py_library(
name = "pkg2",
srcs = ["file2.py"],
imports = [".."],
visibility = [
"//test8_multiple_python_root_dirs/proj1/src:__subpackages__",
"//test8_multiple_python_root_dirs/proj2/src:__subpackages__",
"//tests:__pkg__",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# gazelle:python_default_visibility //tests:__pkg__
Loading

0 comments on commit bdb2aa2

Please sign in to comment.