Skip to content

Commit 004be45

Browse files
authored
feat(gazelle): python_proto_naming_convention directive controls py_proto_library naming (#3093)
Closes #3081. This adds support in the Gazelle plugin for controlling how the generated `py_proto_library` rules are named; support for these was originally added in #3057. We do this via a new Gazelle directive, `python_proto_naming_convention`, which is similar to `python_library_naming_convention` and the like, except it interpolates `$proto_name$`, which is the `proto_library` rule minus any trailing `_proto`. We default to `$proto_name$_py_pb2`. For instance, for a `proto_library` named `foo_proto`, the default value would generate `foo_py_pb2`, aligning with [the convention stated in the Bazel docs.](https://bazel.build/reference/be/protocol-buffer#py_proto_library)
1 parent f02c9c7 commit 004be45

File tree

20 files changed

+227
-3
lines changed

20 files changed

+227
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ END_UNRELEASED_TEMPLATE
116116
dep is not added to the {obj}`py_test` target.
117117
* (gazelle) New directive `gazelle:python_generate_proto`; when `true`,
118118
Gazelle generates `py_proto_library` rules for `proto_library`. `false` by default.
119+
* (gazelle) New directive `gazelle:python_proto_naming_convention`; controls
120+
naming of `py_proto_library` rules.
119121

120122
{#v0-0-0-removed}
121123
### Removed

gazelle/README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ Python-specific directives are as follows:
208208
| Controls the `py_binary` naming convention. Follows the same interpolation rules as `python_library_naming_convention`. | |
209209
| `# gazelle:python_test_naming_convention` | `$package_name$_test` |
210210
| Controls the `py_test` naming convention. Follows the same interpolation rules as `python_library_naming_convention`. | |
211+
| [`# gazelle:python_proto_naming_convention`](#directive-python_proto_naming_convention) | `$proto_name$_py_pb2` |
212+
| Controls the `py_proto_library` naming convention. It interpolates `$proto_name$` with the proto_library rule name, minus any trailing _proto. E.g. if the proto_library name is `foo_proto`, setting this to `$proto_name$_my_lib` would render to `foo_my_lib`. | |
211213
| `# gazelle:resolve py ...` | n/a |
212214
| 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`. | |
213215
| [`# gazelle:python_default_visibility labels`](#directive-python_default_visibility) | |
@@ -262,6 +264,31 @@ py_libary(
262264

263265
[python-packaging-user-guide]: https://github.com/pypa/packaging.python.org/blob/4c86169a/source/tutorials/packaging-projects.rst
264266

267+
#### Directive: `python_proto_naming_convention`:
268+
269+
Set this directive to a string pattern to control how the generated `py_proto_library` targets are named. When generating new `py_proto_library` rules, Gazelle will replace `$proto_name$` in the pattern with the name of the `proto_library` rule, stripping out a trailing `_proto`. For example:
270+
271+
```starlark
272+
# gazelle:python_generate_proto true
273+
# gazelle:python_proto_naming_convention my_custom_$proto_name$_pattern
274+
275+
proto_library(
276+
name = "foo_proto",
277+
srcs = ["foo.proto"],
278+
)
279+
```
280+
281+
produces the following `py_proto_library` rule:
282+
```starlark
283+
py_proto_library(
284+
name = "my_custom_foo_pattern",
285+
deps = [":foo_proto"],
286+
)
287+
```
288+
289+
The default naming convention is `$proto_name$_pb2_py`, so by default in the above example Gazelle would generate `foo_pb2_py`. Any pre-existing rules are left in place and not renamed.
290+
291+
Note that the Python library will always be imported as `foo_pb2` in Python code, regardless of the naming convention. Also note that Gazelle is currently not able to map said imports, e.g. `import foo_pb2`, to fill in `py_proto_library` targets as dependencies of other rules. See [this issue](https://github.com/bazel-contrib/rules_python/issues/1703).
265292

266293
#### Directive: `python_default_visibility`:
267294

gazelle/python/configure.go

Lines changed: 3 additions & 0 deletions
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.ProtoNamingConvention,
6667
pythonconfig.DefaultVisibilty,
6768
pythonconfig.Visibility,
6869
pythonconfig.TestFilePattern,
@@ -179,6 +180,8 @@ func (py *Configurer) Configure(c *config.Config, rel string, f *rule.File) {
179180
config.SetBinaryNamingConvention(strings.TrimSpace(d.Value))
180181
case pythonconfig.TestNamingConvention:
181182
config.SetTestNamingConvention(strings.TrimSpace(d.Value))
183+
case pythonconfig.ProtoNamingConvention:
184+
config.SetProtoNamingConvention(strings.TrimSpace(d.Value))
182185
case pythonconfig.DefaultVisibilty:
183186
switch directiveArg := strings.TrimSpace(d.Value); directiveArg {
184187
case "NONE":

gazelle/python/generate.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
227227
result.Gen = make([]*rule.Rule, 0)
228228

229229
if cfg.GenerateProto() {
230-
generateProtoLibraries(args, pythonProjectRoot, visibility, &result)
230+
generateProtoLibraries(args, cfg, pythonProjectRoot, visibility, &result)
231231
}
232232

233233
collisionErrors := singlylinkedlist.New()
@@ -569,7 +569,7 @@ func ensureNoCollision(file *rule.File, targetName, kind string) error {
569569
return nil
570570
}
571571

572-
func generateProtoLibraries(args language.GenerateArgs, pythonProjectRoot string, visibility []string, res *language.GenerateResult) {
572+
func generateProtoLibraries(args language.GenerateArgs, cfg *pythonconfig.Config, pythonProjectRoot string, visibility []string, res *language.GenerateResult) {
573573
// First, enumerate all the proto_library in this package.
574574
var protoRuleNames []string
575575
for _, r := range args.OtherGen {
@@ -582,18 +582,29 @@ func generateProtoLibraries(args language.GenerateArgs, pythonProjectRoot string
582582

583583
// Next, enumerate all the pre-existing py_proto_library in this package, so we can delete unnecessary rules later.
584584
pyProtoRules := map[string]bool{}
585+
pyProtoRulesForProto := map[string]string{}
585586
if args.File != nil {
586587
for _, r := range args.File.Rules {
587588
if r.Kind() == "py_proto_library" {
588589
pyProtoRules[r.Name()] = false
590+
591+
protos := r.AttrStrings("deps")
592+
for _, proto := range protos {
593+
pyProtoRulesForProto[strings.TrimPrefix(proto, ":")] = r.Name()
594+
}
589595
}
590596
}
591597
}
592598

593599
emptySiblings := treeset.Set{}
594600
// Generate a py_proto_library for each proto_library.
595601
for _, protoRuleName := range protoRuleNames {
596-
pyProtoLibraryName := strings.TrimSuffix(protoRuleName, "_proto") + "_py_pb2"
602+
pyProtoLibraryName := cfg.RenderProtoName(protoRuleName)
603+
if ruleName, ok := pyProtoRulesForProto[protoRuleName]; ok {
604+
// There exists a pre-existing py_proto_library for this proto. Keep this name.
605+
pyProtoLibraryName = ruleName
606+
}
607+
597608
pyProtoLibrary := newTargetBuilder(pyProtoLibraryKind, pyProtoLibraryName, pythonProjectRoot, args.Rel, &emptySiblings).
598609
addVisibility(visibility).
599610
addResolvedDependency(":" + protoRuleName).
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Directive: `python_proto_naming_convention`
2+
3+
This test case asserts that the `# gazelle:python_proto_naming_convention` directive
4+
correctly:
5+
6+
1. Has no effect on pre-existing `py_proto_library` when `gazelle:python_generate_proto` is disabled.
7+
2. Uses the default value when proto generation is on and `python_proto_naming_convention` is not set.
8+
3. Uses the provided naming convention when proto generation is on and `python_proto_naming_convention` is set.
9+
4. With a pre-existing `py_proto_library` not following a given naming convention, keeps it intact and does not rename it.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# This is a Bazel workspace for the Gazelle test data.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
expect:
3+
exit_code: 0
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
load("@com_google_protobuf//bazel:py_proto_library.bzl", "py_proto_library")
2+
load("@rules_proto//proto:defs.bzl", "proto_library")
3+
4+
# gazelle:python_generate_proto false
5+
# gazelle:python_proto_naming_convention some_$proto_name$_value
6+
7+
proto_library(
8+
name = "foo_proto",
9+
srcs = ["foo.proto"],
10+
visibility = ["//:__subpackages__"],
11+
)
12+
13+
py_proto_library(
14+
name = "foo_proto_custom_name",
15+
visibility = ["//:__subpackages__"],
16+
deps = [":foo_proto"],
17+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
load("@com_google_protobuf//bazel:py_proto_library.bzl", "py_proto_library")
2+
load("@rules_proto//proto:defs.bzl", "proto_library")
3+
4+
# gazelle:python_generate_proto false
5+
# gazelle:python_proto_naming_convention some_$proto_name$_value
6+
7+
proto_library(
8+
name = "foo_proto",
9+
srcs = ["foo.proto"],
10+
visibility = ["//:__subpackages__"],
11+
)
12+
13+
py_proto_library(
14+
name = "foo_proto_custom_name",
15+
visibility = ["//:__subpackages__"],
16+
deps = [":foo_proto"],
17+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
syntax = "proto3";
2+
3+
package foo.bar;
4+
5+
message Foo {
6+
string bar = 1;
7+
}

0 commit comments

Comments
 (0)