diff --git a/CHANGELOG.md b/CHANGELOG.md index 272b74db06..db16851959 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -63,7 +63,8 @@ END_UNRELEASED_TEMPLATE {#v0-0-0-fixed} ### Fixed -* Nothing fixed. +* (gazelle) Fixed handling of auto-included `__init__.py` files when generating `py_binary` + targets ([#3729](https://github.com/bazel-contrib/rules_python/issues/3729)). {#v0-0-0-added} ### Added diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go index 2495c42d20..a13d7182e9 100644 --- a/gazelle/python/generate.go +++ b/gazelle/python/generate.go @@ -258,6 +258,15 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes // Create a validFilesMap of mainModules to validate if python macros have valid srcs. validFilesMap := make(map[string]struct{}) + // Determine whether we have an __init__.py file in this package and whether we'll implicitly include it in srcs. + var hasPopulatedInit bool + var autoIncludeInit bool + if cfg.PerFileGeneration() { + var hasInit bool + hasInit, hasPopulatedInit = hasLibraryEntrypointFile(args.Dir) + autoIncludeInit = cfg.PerFileGenerationIncludeInit() && hasInit && hasPopulatedInit + } + appendPyLibrary := func(srcs *treeset.Set, pyLibraryTargetName string) { allDeps, mainModules, annotations, err := parser.parse(srcs) for name := range mainModules { @@ -277,6 +286,10 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes // that we don't also generate a py_library target for it. if cfg.PerFileGeneration() { srcs.Remove(name) + // Also remove the __init__.py that was added earlier. + if autoIncludeInit { + srcs.Remove(pyLibraryEntrypointFilename) + } } } @@ -294,15 +307,20 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes filenames := treeset.NewWith(godsutils.StringComparator, filename) pyiSrcs, _ := getPyiFilenames(filenames, cfg.GeneratePyiSrcs(), args.Dir) - pyBinary := newTargetBuilder(pyBinaryKind, pyBinaryTargetName, pythonProjectRoot, args.Rel, pyFileNames, cfg.ResolveSiblingImports()). + pyBinaryBuilder := newTargetBuilder(pyBinaryKind, pyBinaryTargetName, pythonProjectRoot, args.Rel, pyFileNames, cfg.ResolveSiblingImports()). addVisibility(visibility). addSrc(filename). addPyiSrcs(pyiSrcs). addModuleDependencies(mainModules[filename]). addResolvedDependencies(annotations.includeDeps). generateImportsAttribute(). - setAnnotations(*annotations). - build() + setAnnotations(*annotations) + + if autoIncludeInit { + pyBinaryBuilder.addSrc(pyLibraryEntrypointFilename) + } + + pyBinary := pyBinaryBuilder.build() result.Gen = append(result.Gen, pyBinary) result.Imports = append(result.Imports, pyBinary.PrivateAttr(config.GazelleImportsKey)) } @@ -357,15 +375,15 @@ func (py *Python) GenerateRules(args language.GenerateArgs) language.GenerateRes result.Imports = append(result.Imports, pyLibrary.PrivateAttr(config.GazelleImportsKey)) } } + if cfg.PerFileGeneration() { - hasInit, nonEmptyInit := hasLibraryEntrypointFile(args.Dir) pyLibraryFilenames.Each(func(index int, filename interface{}) { pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py") - if filename == pyLibraryEntrypointFilename && !nonEmptyInit { + if filename == pyLibraryEntrypointFilename && !hasPopulatedInit { return // ignore empty __init__.py. } srcs := treeset.NewWith(godsutils.StringComparator, filename) - if cfg.PerFileGenerationIncludeInit() && hasInit && nonEmptyInit { + if autoIncludeInit { srcs.Add(pyLibraryEntrypointFilename) } appendPyLibrary(srcs, pyLibraryTargetName) diff --git a/gazelle/python/testdata/per_file_non_empty_init/BUILD.out b/gazelle/python/testdata/per_file_non_empty_init/BUILD.out index ee4a417966..38dcaa5ee2 100644 --- a/gazelle/python/testdata/per_file_non_empty_init/BUILD.out +++ b/gazelle/python/testdata/per_file_non_empty_init/BUILD.out @@ -1,4 +1,4 @@ -load("@rules_python//python:defs.bzl", "py_library") +load("@rules_python//python:defs.bzl", "py_binary", "py_library") # gazelle:python_generation_mode file # gazelle:python_generation_mode_per_file_include_init true @@ -18,3 +18,13 @@ py_library( ], visibility = ["//:__subpackages__"], ) + +py_binary( + name = "foobin", + srcs = [ + "__init__.py", + "foobin.py", + ], + visibility = ["//:__subpackages__"], + deps = [":foo"], +) diff --git a/gazelle/python/testdata/per_file_non_empty_init/README.md b/gazelle/python/testdata/per_file_non_empty_init/README.md index 6e6e9e245d..0fad0f3d43 100644 --- a/gazelle/python/testdata/per_file_non_empty_init/README.md +++ b/gazelle/python/testdata/per_file_non_empty_init/README.md @@ -1,3 +1,3 @@ # Per-file generation -This test case generates one `py_library` per file, including `__init__.py`. +This test case generates one `py_library` or `py_binary` per file, including `__init__.py`. diff --git a/gazelle/python/testdata/per_file_non_empty_init/foo.py b/gazelle/python/testdata/per_file_non_empty_init/foo.py index 730755995d..b70cc96b7e 100644 --- a/gazelle/python/testdata/per_file_non_empty_init/foo.py +++ b/gazelle/python/testdata/per_file_non_empty_init/foo.py @@ -13,3 +13,4 @@ # limitations under the License. # For test purposes only. +BAR = "baz" diff --git a/gazelle/python/testdata/per_file_non_empty_init/foobin.py b/gazelle/python/testdata/per_file_non_empty_init/foobin.py new file mode 100644 index 0000000000..6b493b531e --- /dev/null +++ b/gazelle/python/testdata/per_file_non_empty_init/foobin.py @@ -0,0 +1,4 @@ +from foo import BAR + +if __name__ == "__main__": + print(BAR)