Skip to content

fix(gazelle) Delete python targets with invalid srcs #3046

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 31 additions & 2 deletions gazelle/python/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes

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

collisionErrors := singlylinkedlist.New()

appendPyLibrary := func(srcs *treeset.Set, pyLibraryTargetName string) {
Expand Down Expand Up @@ -473,7 +472,10 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
result.Gen = append(result.Gen, pyTest)
result.Imports = append(result.Imports, pyTest.PrivateAttr(config.GazelleImportsKey))
}

if !cfg.CoarseGrainedGeneration() {
emptyRules := py.getRulesWithInvalidSrcs(args)
result.Empty = append(result.Empty, emptyRules...)
}
if !collisionErrors.Empty() {
it := collisionErrors.Iterator()
for it.Next() {
Expand All @@ -485,6 +487,33 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes
return result
}

// getRulesWithInvalidSrcs checks existing Python rules in the BUILD file and return the rules with invalid srcs.
func (py *Python) getRulesWithInvalidSrcs(args language.GenerateArgs) (invalidRules []*rule.Rule) {
if args.File == nil {
return
}
regularFiles := args.RegularFiles
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we can inline this variable because it's only used once

regularFilesMap := make(map[string]struct{})
for _, file := range regularFiles {
regularFilesMap[file] = struct{}{}
}
for _, existingRule := range args.File.Rules {
if _, ok := py.Kinds()[existingRule.Kind()]; !ok {
continue
}
allInvalidSrcs := true
for _, src := range existingRule.AttrStrings("srcs") {
if _, ok := regularFilesMap[src]; ok {
Copy link
Contributor

@linzhp linzhp Jul 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need to check:

  1. whether it's in args.GenFiles
  2. whether it's a target. If it starts with either @, // or :, we can assume it's a target. If not, we can see if it ends with ".py"
  3. whether it is a glob

allInvalidSrcs = false
break
}
}
if allInvalidSrcs {
invalidRules = append(invalidRules, newTargetBuilder(existingRule.Kind(), existingRule.Name(), args.Config.RepoRoot, args.Rel, nil).build())
}
}
return invalidRules
}
// isBazelPackage determines if the directory is a Bazel package by probing for
// the existence of a known BUILD file name.
func isBazelPackage(dir string) bool {
Expand Down
7 changes: 7 additions & 0 deletions gazelle/python/testdata/remove_invalid_binary/BUILD.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
load("@rules_python//python:defs.bzl", "py_binary")

py_binary(
name = "remove_invalid_binary",
srcs = ["__main__.py"],
visibility = ["//:__subpackages__"],
)
Empty file.
3 changes: 3 additions & 0 deletions gazelle/python/testdata/remove_invalid_binary/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Remove invalid binary

This test case asserts that `py_binary` should be deleted if invalid (no source files).
1 change: 1 addition & 0 deletions gazelle/python/testdata/remove_invalid_binary/WORKSPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
workspace(name = "remove_invalid_binary")
15 changes: 15 additions & 0 deletions gazelle/python/testdata/remove_invalid_binary/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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.

---
10 changes: 0 additions & 10 deletions gazelle/python/testdata/remove_invalid_library/BUILD.out
Original file line number Diff line number Diff line change
@@ -1,10 +0,0 @@
load("@rules_python//python:defs.bzl", "py_library")

py_library(
name = "deps_with_no_srcs_library",
deps = [
"//:remove_invalid_library",
"@pypi//bar",
"@pypi//foo",
],
)
17 changes: 17 additions & 0 deletions gazelle/python/testdata/remove_invalid_pytest/BUILD.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
load("@rules_python//python:defs.bzl", "py_test")

py_test(
name = "remove_invalid_test",
srcs = ["__test__.py"],
visibility = ["//:__subpackages__"],
)

py_test(
name = "test_with_deps",
srcs = ["test_main.py"],
deps = [
"//:remove_invalid_test",
"@pypi//bar",
"@pypi//foo",
],
)
Empty file.
3 changes: 3 additions & 0 deletions gazelle/python/testdata/remove_invalid_pytest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Remove invalid pytest

This test case asserts that `py_test` should be deleted if invalid (no source files).
Empty file.
15 changes: 15 additions & 0 deletions gazelle/python/testdata/remove_invalid_pytest/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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.

---