Skip to content

Commit 9910e6e

Browse files
committed
add alias kind support
1 parent 6dac0f6 commit 9910e6e

16 files changed

Lines changed: 218 additions & 25 deletions

File tree

gazelle/python/generate.go

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,14 @@ var (
4848
buildFilenames = []string{"BUILD", "BUILD.bazel"}
4949
)
5050

51-
func GetActualKindName(kind string, args language.GenerateArgs) string {
52-
if kindOverride, ok := args.Config.KindMap[kind]; ok {
53-
return kindOverride.KindName
54-
}
55-
return kind
51+
// kindMatches returns whether r matches the canonical Python rule kind `expected`, respecting `# gazelle:map_kind` and
52+
// `# gazelle:alias_kind` directives in the config.Config c.
53+
func kindMatches(r *rule.Rule, expected string, c *config.Config) bool {
54+
k := r.Kind()
55+
if mapped, ok := c.KindMap[expected]; ok {
56+
return k == mapped.KindName || c.AliasMap[k] == expected
57+
}
58+
return k == expected || c.AliasMap[k] == expected
5659
}
5760

5861
func matchesAnyGlob(s string, globs []string) bool {
@@ -112,10 +115,6 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
112115
}
113116
}
114117

115-
actualPyBinaryKind := GetActualKindName(pyBinaryKind, args)
116-
actualPyLibraryKind := GetActualKindName(pyLibraryKind, args)
117-
actualPyTestKind := GetActualKindName(pyTestKind, args)
118-
119118
pythonProjectRoot := cfg.PythonProjectRoot()
120119

121120
packageName := filepath.Base(args.Dir)
@@ -283,10 +282,10 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
283282
sort.Strings(mainFileNames)
284283
for _, filename := range mainFileNames {
285284
pyBinaryTargetName := strings.TrimSuffix(filepath.Base(filename), ".py")
286-
if err := ensureNoCollision(args.File, pyBinaryTargetName, actualPyBinaryKind); err != nil {
285+
if err := ensureNoCollision(args.Config, args.File, pyBinaryTargetName, pyBinaryKind); err != nil {
287286
fqTarget := label.New("", args.Rel, pyBinaryTargetName)
288287
log.Printf("failed to generate target %q of kind %q: %v",
289-
fqTarget.String(), actualPyBinaryKind, err)
288+
fqTarget.String(), pyBinaryKind, err)
290289
continue
291290
}
292291

@@ -316,7 +315,7 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
316315
}
317316
generateEmptyLibrary := false
318317
for _, r := range args.File.Rules {
319-
if r.Kind() == actualPyLibraryKind && r.Name() == pyLibraryTargetName {
318+
if r.Name() == pyLibraryTargetName && kindMatches(r, pyLibraryKind, args.Config) {
320319
generateEmptyLibrary = true
321320
}
322321
}
@@ -332,11 +331,11 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
332331
// exists, and if it is of a different kind from the one we are
333332
// generating. If so, we have to throw an error since Gazelle won't
334333
// generate it correctly.
335-
if err := ensureNoCollision(args.File, pyLibraryTargetName, actualPyLibraryKind); err != nil {
334+
if err := ensureNoCollision(args.Config, args.File, pyLibraryTargetName, pyLibraryKind); err != nil {
336335
fqTarget := label.New("", args.Rel, pyLibraryTargetName)
337336
err := fmt.Errorf("failed to generate target %q of kind %q: %w. "+
338337
"Use the '# gazelle:%s' directive to change the naming convention.",
339-
fqTarget.String(), actualPyLibraryKind, err, pythonconfig.LibraryNamingConvention)
338+
fqTarget.String(), pyLibraryKind, err, pythonconfig.LibraryNamingConvention)
340339
collisionErrors.Add(err)
341340
}
342341

@@ -386,11 +385,11 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
386385
// exists, and if it is of a different kind from the one we are
387386
// generating. If so, we have to throw an error since Gazelle won't
388387
// generate it correctly.
389-
if err := ensureNoCollision(args.File, pyBinaryTargetName, actualPyBinaryKind); err != nil {
388+
if err := ensureNoCollision(args.Config, args.File, pyBinaryTargetName, pyBinaryKind); err != nil {
390389
fqTarget := label.New("", args.Rel, pyBinaryTargetName)
391390
err := fmt.Errorf("failed to generate target %q of kind %q: %w. "+
392391
"Use the '# gazelle:%s' directive to change the naming convention.",
393-
fqTarget.String(), actualPyBinaryKind, err, pythonconfig.BinaryNamingConvention)
392+
fqTarget.String(), pyBinaryKind, err, pythonconfig.BinaryNamingConvention)
394393
collisionErrors.Add(err)
395394
}
396395

@@ -425,10 +424,10 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
425424
// exists, and if it is of a different kind from the one we are
426425
// generating. If so, we have to throw an error since Gazelle won't
427426
// generate it correctly.
428-
if err := ensureNoCollision(args.File, conftestTargetname, actualPyLibraryKind); err != nil {
427+
if err := ensureNoCollision(args.Config, args.File, conftestTargetname, pyLibraryKind); err != nil {
429428
fqTarget := label.New("", args.Rel, conftestTargetname)
430429
err := fmt.Errorf("failed to generate target %q of kind %q: %w. ",
431-
fqTarget.String(), actualPyLibraryKind, err)
430+
fqTarget.String(), pyLibraryKind, err)
432431
collisionErrors.Add(err)
433432
}
434433

@@ -462,11 +461,11 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
462461
// exists, and if it is of a different kind from the one we are
463462
// generating. If so, we have to throw an error since Gazelle won't
464463
// generate it correctly.
465-
if err := ensureNoCollision(args.File, pyTestTargetName, actualPyTestKind); err != nil {
464+
if err := ensureNoCollision(args.Config, args.File, pyTestTargetName, pyTestKind); err != nil {
466465
fqTarget := label.New("", args.Rel, pyTestTargetName)
467466
err := fmt.Errorf("failed to generate target %q of kind %q: %w. "+
468467
"Use the '# gazelle:%s' directive to change the naming convention.",
469-
fqTarget.String(), actualPyTestKind, err, pythonconfig.TestNamingConvention)
468+
fqTarget.String(), pyTestKind, err, pythonconfig.TestNamingConvention)
470469
collisionErrors.Add(err)
471470
}
472471

@@ -575,8 +574,7 @@ func (py *Python) getRulesWithInvalidSrcs(args language.GenerateArgs, validFiles
575574
return strings.HasPrefix(src, "@") || strings.HasPrefix(src, "//") || strings.HasPrefix(src, ":")
576575
}
577576
for _, existingRule := range args.File.Rules {
578-
actualPyBinaryKind := GetActualKindName(pyBinaryKind, args)
579-
if existingRule.Kind() != actualPyBinaryKind {
577+
if !kindMatches(existingRule, pyBinaryKind, args.Config) {
580578
continue
581579
}
582580
var hasValidSrcs bool
@@ -652,12 +650,12 @@ func isEntrypointFile(path string) bool {
652650
}
653651
}
654652

655-
func ensureNoCollision(file *rule.File, targetName, kind string) error {
653+
func ensureNoCollision(c *config.Config, file *rule.File, targetName, kind string) error {
656654
if file == nil {
657655
return nil
658656
}
659657
for _, t := range file.Rules {
660-
if t.Name() == targetName && t.Kind() != kind {
658+
if t.Name() == targetName && !kindMatches(t, kind, c) {
661659
return fmt.Errorf("a target of kind %q with the same name already exists", t.Kind())
662660
}
663661
}
@@ -680,7 +678,7 @@ func generateProtoLibraries(args language.GenerateArgs, cfg *pythonconfig.Config
680678
pyProtoRulesForProto := map[string]string{}
681679
if args.File != nil {
682680
for _, r := range args.File.Rules {
683-
if r.Kind() == "py_proto_library" {
681+
if kindMatches(r, pyProtoLibraryKind, args.Config) {
684682
pyProtoRules[r.Name()] = false
685683

686684
protos := r.AttrStrings("deps")
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
load(":mylib.bzl", "my_py_library")
2+
3+
# gazelle:alias_kind my_py_library py_library
4+
# gazelle:map_kind py_test my_test :mytest.bzl
5+
6+
my_py_library(
7+
name = "respect_alias_and_map_kind",
8+
srcs = ["__init__.py"],
9+
)
10+
11+
my_test(
12+
name = "respect_alias_and_map_kind_test",
13+
srcs = ["__test__.py"],
14+
main = "__test__.py",
15+
deps = [],
16+
)
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
load(":mylib.bzl", "my_py_library")
2+
load(":mytest.bzl", "my_test")
3+
4+
# gazelle:alias_kind my_py_library py_library
5+
# gazelle:map_kind py_test my_test :mytest.bzl
6+
7+
my_py_library(
8+
name = "respect_alias_and_map_kind",
9+
srcs = [
10+
"__init__.py",
11+
"foo.py",
12+
],
13+
visibility = ["//:__subpackages__"],
14+
)
15+
16+
my_test(
17+
name = "respect_alias_and_map_kind_test",
18+
srcs = ["__test__.py"],
19+
main = "__test__.py",
20+
deps = [":respect_alias_and_map_kind"],
21+
)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Respect alias_kind and map_kind
2+
3+
This test case asserts that `# gazelle:alias_kind` and `# gazelle:map_kind` are
4+
both respected as the expected kind at once and do not collide.
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: 17 additions & 0 deletions
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+
from foo import foo
16+
17+
_ = foo
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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+
import unittest
16+
17+
from __init__ import foo
18+
19+
20+
class FooTest(unittest.TestCase):
21+
def test_foo(self):
22+
self.assertEqual("foo", foo())
23+
24+
25+
if __name__ == "__main__":
26+
unittest.main()
Lines changed: 17 additions & 0 deletions
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+
def foo():
17+
return "foo"
Lines changed: 17 additions & 0 deletions
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
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
load(":mylib.bzl", "my_py_library")
2+
3+
# gazelle:alias_kind my_py_library py_library
4+
5+
my_py_library(
6+
name = "respect_alias_kind",
7+
srcs = ["__init__.py"],
8+
)

0 commit comments

Comments
 (0)