feat: generate py_library per file (#1398)
fixes #1150
fixes #1323
you can no longer pre-define the name of the target by creating an empty
`py_library` (see 3c84655). I don't think this was being used and it's
straightforward to rename the generated per-project or per-package
target if you want
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed78e62..c380066 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,8 @@
the `py_binary` rule used to build it.
* New Python versions available: `3.8.17`, `3.9.18`, `3.10.13`, `3.11.5` using
https://github.com/indygreg/python-build-standalone/releases/tag/20230826.
+* (gazelle) New `# gazelle:python_generation_mode file` directive to support
+ generating one `py_library` per file.
### Removed
diff --git a/gazelle/README.md b/gazelle/README.md
index ba8520d..4728e4c 100644
--- a/gazelle/README.md
+++ b/gazelle/README.md
@@ -189,9 +189,9 @@
| `# gazelle:python_validate_import_statements`| `true` |
| Controls whether the Python import statements should be validated. Can be "true" or "false" | |
| `# gazelle:python_generation_mode`| `package` |
-| Controls the target generation mode. Can be "package" or "project" | |
+| Controls the target generation mode. Can be "file", "package", or "project" | |
| `# gazelle:python_library_naming_convention`| `$package_name$` |
-| Controls the `py_library` naming convention. It interpolates $package_name$ with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | |
+| Controls the `py_library` naming convention. It interpolates \$package_name\$ with the Bazel package name. E.g. if the Bazel package name is `foo`, setting this to `$package_name$_my_lib` would result in a generated target named `foo_my_lib`. | |
| `# gazelle:python_binary_naming_convention` | `$package_name$_bin` |
| Controls the `py_binary` naming convention. Follows the same interpolation rules as `python_library_naming_convention`. | |
| `# gazelle:python_test_naming_convention` | `$package_name$_test` |
@@ -206,11 +206,15 @@
First, we look for the nearest ancestor BUILD file starting from the folder
containing the Python source file.
-If there is no `py_library` in this BUILD file, one is created, using the
-package name as the target's name. This makes it the default target in the
-package.
+In package generation mode, if there is no `py_library` in this BUILD file, one
+is created using the package name as the target's name. This makes it the
+default target in the package. Next, all source files are collected into the
+`srcs` of the `py_library`.
-Next, all source files are collected into the `srcs` of the `py_library`.
+In project generation mode, all source files in subdirectories (that don't have
+BUILD files) are also collected.
+
+In file generation mode, each file is given its own target.
Finally, the `import` statements in the source files are parsed, and
dependencies are added to the `deps` attribute.
diff --git a/gazelle/python/configure.go b/gazelle/python/configure.go
index 32f9ab0..2d38805 100644
--- a/gazelle/python/configure.go
+++ b/gazelle/python/configure.go
@@ -137,8 +137,13 @@
switch pythonconfig.GenerationModeType(strings.TrimSpace(d.Value)) {
case pythonconfig.GenerationModePackage:
config.SetCoarseGrainedGeneration(false)
+ config.SetPerFileGeneration(false)
+ case pythonconfig.GenerationModeFile:
+ config.SetCoarseGrainedGeneration(false)
+ config.SetPerFileGeneration(true)
case pythonconfig.GenerationModeProject:
config.SetCoarseGrainedGeneration(true)
+ config.SetPerFileGeneration(false)
default:
err := fmt.Errorf("invalid value for directive %q: %s",
pythonconfig.GenerationMode, d.Value)
diff --git a/gazelle/python/generate.go b/gazelle/python/generate.go
index fb41324..ede4d2a 100644
--- a/gazelle/python/generate.go
+++ b/gazelle/python/generate.go
@@ -153,12 +153,17 @@
if entry.IsDir() {
// If we are visiting a directory, we determine if we should
// halt digging the tree based on a few criterias:
- // 1. The directory has a BUILD or BUILD.bazel files. Then
+ // 1. We are using per-file generation.
+ // 2. The directory has a BUILD or BUILD.bazel files. Then
// it doesn't matter at all what it has since it's a
// separate Bazel package.
- // 2. (only for fine-grained generation) The directory has
- // an __init__.py, __main__.py or __test__.py, meaning
- // a BUILD file will be generated.
+ // 3. (only for package generation) The directory has an
+ // __init__.py, __main__.py or __test__.py, meaning a
+ // BUILD file will be generated.
+ if cfg.PerFileGeneration() {
+ return fs.SkipDir
+ }
+
if isBazelPackage(path) {
boundaryPackages[path] = struct{}{}
return nil
@@ -213,15 +218,12 @@
collisionErrors := singlylinkedlist.New()
- var pyLibrary *rule.Rule
- if !pyLibraryFilenames.Empty() {
- deps, err := parser.parse(pyLibraryFilenames)
+ appendPyLibrary := func(srcs *treeset.Set, pyLibraryTargetName string) {
+ deps, err := parser.parse(srcs)
if err != nil {
log.Fatalf("ERROR: %v\n", err)
}
- pyLibraryTargetName := cfg.RenderLibraryName(packageName)
-
// Check if a target with the same name we are generating already
// exists, and if it is of a different kind from the one we are
// generating. If so, we have to throw an error since Gazelle won't
@@ -239,9 +241,9 @@
}
}
- pyLibrary = newTargetBuilder(pyLibraryKind, pyLibraryTargetName, pythonProjectRoot, args.Rel, pyFileNames).
+ pyLibrary := newTargetBuilder(pyLibraryKind, pyLibraryTargetName, pythonProjectRoot, args.Rel, pyFileNames).
addVisibility(visibility).
- addSrcs(pyLibraryFilenames).
+ addSrcs(srcs).
addModuleDependencies(deps).
generateImportsAttribute().
build()
@@ -249,6 +251,24 @@
result.Gen = append(result.Gen, pyLibrary)
result.Imports = append(result.Imports, pyLibrary.PrivateAttr(config.GazelleImportsKey))
}
+ if cfg.PerFileGeneration() {
+ pyLibraryFilenames.Each(func(index int, filename interface{}) {
+ if filename == pyLibraryEntrypointFilename {
+ stat, err := os.Stat(filepath.Join(args.Dir, filename.(string)))
+ if err != nil {
+ log.Fatalf("ERROR: %v\n", err)
+ }
+ if stat.Size() == 0 {
+ return // ignore empty __init__.py
+ }
+ }
+ srcs := treeset.NewWith(godsutils.StringComparator, filename)
+ pyLibraryTargetName := strings.TrimSuffix(filepath.Base(filename.(string)), ".py")
+ appendPyLibrary(srcs, pyLibraryTargetName)
+ })
+ } else if !pyLibraryFilenames.Empty() {
+ appendPyLibrary(pyLibraryFilenames, cfg.RenderLibraryName(packageName))
+ }
if hasPyBinary {
deps, err := parser.parseSingle(pyBinaryEntrypointFilename)
diff --git a/gazelle/python/kinds.go b/gazelle/python/kinds.go
index ab1afb7..941b45b 100644
--- a/gazelle/python/kinds.go
+++ b/gazelle/python/kinds.go
@@ -49,7 +49,8 @@
},
},
pyLibraryKind: {
- MatchAny: true,
+ MatchAny: false,
+ MatchAttrs: []string{"srcs"},
NonEmptyAttrs: map[string]bool{
"deps": true,
"srcs": true,
diff --git a/gazelle/python/resolve.go b/gazelle/python/resolve.go
index 46014e5..87eed76 100644
--- a/gazelle/python/resolve.go
+++ b/gazelle/python/resolve.go
@@ -151,10 +151,10 @@
for len(moduleParts) > 1 {
// Iterate back through the possible imports until
// a match is found.
- // For example, "from foo.bar import baz" where bar is a variable, we should try
- // `foo.bar.baz` first, then `foo.bar`, then `foo`. In the first case, the import could be file `baz.py`
- // in the directory `foo/bar`.
- // Or, the import could be variable `bar` in file `foo/bar.py`.
+ // For example, "from foo.bar import baz" where baz is a module, we should try `foo.bar.baz` first, then
+ // `foo.bar`, then `foo`.
+ // In the first case, the import could be file `baz.py` in the directory `foo/bar`.
+ // Or, the import could be variable `baz` in file `foo/bar.py`.
// The import could also be from a standard module, e.g. `six.moves`, where
// the dependency is actually `six`.
moduleParts = moduleParts[:len(moduleParts)-1]
diff --git a/gazelle/python/testdata/dont_rename_target/BUILD.in b/gazelle/python/testdata/dont_rename_target/BUILD.in
index 33e8ec2..e9bc0e6 100644
--- a/gazelle/python/testdata/dont_rename_target/BUILD.in
+++ b/gazelle/python/testdata/dont_rename_target/BUILD.in
@@ -2,4 +2,5 @@
py_library(
name = "my_custom_target",
+ srcs = ["__init__.py"],
)
diff --git a/gazelle/python/testdata/per_file/BUILD.in b/gazelle/python/testdata/per_file/BUILD.in
new file mode 100644
index 0000000..01b0904
--- /dev/null
+++ b/gazelle/python/testdata/per_file/BUILD.in
@@ -0,0 +1,11 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+# gazelle:python_generation_mode file
+
+# This target should be kept unmodified by Gazelle.
+py_library(
+ name = "custom",
+ srcs = ["bar.py"],
+ visibility = ["//visibility:private"],
+ tags = ["cant_touch_this"],
+)
diff --git a/gazelle/python/testdata/per_file/BUILD.out b/gazelle/python/testdata/per_file/BUILD.out
new file mode 100644
index 0000000..2ec825b
--- /dev/null
+++ b/gazelle/python/testdata/per_file/BUILD.out
@@ -0,0 +1,24 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+# gazelle:python_generation_mode file
+
+# This target should be kept unmodified by Gazelle.
+py_library(
+ name = "custom",
+ srcs = ["bar.py"],
+ tags = ["cant_touch_this"],
+ visibility = ["//visibility:private"],
+)
+
+py_library(
+ name = "baz",
+ srcs = ["baz.py"],
+ visibility = ["//:__subpackages__"],
+)
+
+py_library(
+ name = "foo",
+ srcs = ["foo.py"],
+ visibility = ["//:__subpackages__"],
+ deps = [":custom"],
+)
diff --git a/gazelle/python/testdata/per_file/README.md b/gazelle/python/testdata/per_file/README.md
new file mode 100644
index 0000000..3ddeb21
--- /dev/null
+++ b/gazelle/python/testdata/per_file/README.md
@@ -0,0 +1,5 @@
+# Per-file generation
+
+This test case generates one `py_library` per file.
+
+`__init__.py` is left empty so no target is generated for it.
diff --git a/gazelle/python/testdata/per_file/WORKSPACE b/gazelle/python/testdata/per_file/WORKSPACE
new file mode 100644
index 0000000..faff6af
--- /dev/null
+++ b/gazelle/python/testdata/per_file/WORKSPACE
@@ -0,0 +1 @@
+# This is a Bazel workspace for the Gazelle test data.
diff --git a/gazelle/python/testdata/per_file/__init__.py b/gazelle/python/testdata/per_file/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/per_file/__init__.py
diff --git a/gazelle/python/testdata/per_file/bar.py b/gazelle/python/testdata/per_file/bar.py
new file mode 100644
index 0000000..7307559
--- /dev/null
+++ b/gazelle/python/testdata/per_file/bar.py
@@ -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.
+
+# For test purposes only.
diff --git a/gazelle/python/testdata/per_file/baz.py b/gazelle/python/testdata/per_file/baz.py
new file mode 100644
index 0000000..7307559
--- /dev/null
+++ b/gazelle/python/testdata/per_file/baz.py
@@ -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.
+
+# For test purposes only.
diff --git a/gazelle/python/testdata/per_file/foo.py b/gazelle/python/testdata/per_file/foo.py
new file mode 100644
index 0000000..c000990
--- /dev/null
+++ b/gazelle/python/testdata/per_file/foo.py
@@ -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.
+
+import bar
diff --git a/gazelle/python/testdata/per_file/test.yaml b/gazelle/python/testdata/per_file/test.yaml
new file mode 100644
index 0000000..fcea777
--- /dev/null
+++ b/gazelle/python/testdata/per_file/test.yaml
@@ -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.
+
+---
diff --git a/gazelle/python/testdata/per_file_non_empty_init/BUILD.in b/gazelle/python/testdata/per_file_non_empty_init/BUILD.in
new file mode 100644
index 0000000..a5853f6
--- /dev/null
+++ b/gazelle/python/testdata/per_file_non_empty_init/BUILD.in
@@ -0,0 +1,3 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+# gazelle:python_generation_mode file
diff --git a/gazelle/python/testdata/per_file_non_empty_init/BUILD.out b/gazelle/python/testdata/per_file_non_empty_init/BUILD.out
new file mode 100644
index 0000000..8733dbd
--- /dev/null
+++ b/gazelle/python/testdata/per_file_non_empty_init/BUILD.out
@@ -0,0 +1,16 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+# gazelle:python_generation_mode file
+
+py_library(
+ name = "__init__",
+ srcs = ["__init__.py"],
+ visibility = ["//:__subpackages__"],
+ deps = [":foo"],
+)
+
+py_library(
+ name = "foo",
+ srcs = ["foo.py"],
+ visibility = ["//:__subpackages__"],
+)
diff --git a/gazelle/python/testdata/per_file_non_empty_init/README.md b/gazelle/python/testdata/per_file_non_empty_init/README.md
new file mode 100644
index 0000000..6e6e9e2
--- /dev/null
+++ b/gazelle/python/testdata/per_file_non_empty_init/README.md
@@ -0,0 +1,3 @@
+# Per-file generation
+
+This test case generates one `py_library` per file, including `__init__.py`.
diff --git a/gazelle/python/testdata/per_file_non_empty_init/WORKSPACE b/gazelle/python/testdata/per_file_non_empty_init/WORKSPACE
new file mode 100644
index 0000000..faff6af
--- /dev/null
+++ b/gazelle/python/testdata/per_file_non_empty_init/WORKSPACE
@@ -0,0 +1 @@
+# This is a Bazel workspace for the Gazelle test data.
diff --git a/gazelle/python/testdata/per_file_non_empty_init/__init__.py b/gazelle/python/testdata/per_file_non_empty_init/__init__.py
new file mode 100644
index 0000000..492cbc0
--- /dev/null
+++ b/gazelle/python/testdata/per_file_non_empty_init/__init__.py
@@ -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.
+
+import foo
diff --git a/gazelle/python/testdata/per_file_non_empty_init/foo.py b/gazelle/python/testdata/per_file_non_empty_init/foo.py
new file mode 100644
index 0000000..7307559
--- /dev/null
+++ b/gazelle/python/testdata/per_file_non_empty_init/foo.py
@@ -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.
+
+# For test purposes only.
diff --git a/gazelle/python/testdata/per_file_non_empty_init/test.yaml b/gazelle/python/testdata/per_file_non_empty_init/test.yaml
new file mode 100644
index 0000000..fcea777
--- /dev/null
+++ b/gazelle/python/testdata/per_file_non_empty_init/test.yaml
@@ -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.
+
+---
diff --git a/gazelle/python/testdata/per_file_subdirs/BUILD.in b/gazelle/python/testdata/per_file_subdirs/BUILD.in
new file mode 100644
index 0000000..a5853f6
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/BUILD.in
@@ -0,0 +1,3 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+# gazelle:python_generation_mode file
diff --git a/gazelle/python/testdata/per_file_subdirs/BUILD.out b/gazelle/python/testdata/per_file_subdirs/BUILD.out
new file mode 100644
index 0000000..69c42e0
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/BUILD.out
@@ -0,0 +1,10 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+# gazelle:python_generation_mode file
+
+py_library(
+ name = "foo",
+ srcs = ["foo.py"],
+ visibility = ["//:__subpackages__"],
+ deps = ["//bar:__init__"],
+)
diff --git a/gazelle/python/testdata/per_file_subdirs/README.md b/gazelle/python/testdata/per_file_subdirs/README.md
new file mode 100644
index 0000000..9eda2fa
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/README.md
@@ -0,0 +1,3 @@
+# Per-file generation
+
+This test case generates one `py_library` per file in subdirectories.
diff --git a/gazelle/python/testdata/per_file_subdirs/WORKSPACE b/gazelle/python/testdata/per_file_subdirs/WORKSPACE
new file mode 100644
index 0000000..faff6af
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/WORKSPACE
@@ -0,0 +1 @@
+# This is a Bazel workspace for the Gazelle test data.
diff --git a/gazelle/python/testdata/per_file_subdirs/bar/BUILD.in b/gazelle/python/testdata/per_file_subdirs/bar/BUILD.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/bar/BUILD.in
diff --git a/gazelle/python/testdata/per_file_subdirs/bar/BUILD.out b/gazelle/python/testdata/per_file_subdirs/bar/BUILD.out
new file mode 100644
index 0000000..7258d27
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/bar/BUILD.out
@@ -0,0 +1,13 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+py_library(
+ name = "__init__",
+ srcs = ["__init__.py"],
+ visibility = ["//:__subpackages__"],
+)
+
+py_library(
+ name = "foo",
+ srcs = ["foo.py"],
+ visibility = ["//:__subpackages__"],
+)
diff --git a/gazelle/python/testdata/per_file_subdirs/bar/__init__.py b/gazelle/python/testdata/per_file_subdirs/bar/__init__.py
new file mode 100644
index 0000000..5799152
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/bar/__init__.py
@@ -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.
+
+from .foo import func
diff --git a/gazelle/python/testdata/per_file_subdirs/bar/foo.py b/gazelle/python/testdata/per_file_subdirs/bar/foo.py
new file mode 100644
index 0000000..59eb08c
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/bar/foo.py
@@ -0,0 +1,16 @@
+# 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.
+
+def func():
+ pass
diff --git a/gazelle/python/testdata/per_file_subdirs/baz/baz.py b/gazelle/python/testdata/per_file_subdirs/baz/baz.py
new file mode 100644
index 0000000..5256394
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/baz/baz.py
@@ -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.
+
+from bar.foo import func
diff --git a/gazelle/python/testdata/per_file_subdirs/foo.py b/gazelle/python/testdata/per_file_subdirs/foo.py
new file mode 100644
index 0000000..b5e6cff
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/foo.py
@@ -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.
+
+from bar import func
diff --git a/gazelle/python/testdata/per_file_subdirs/test.yaml b/gazelle/python/testdata/per_file_subdirs/test.yaml
new file mode 100644
index 0000000..fcea777
--- /dev/null
+++ b/gazelle/python/testdata/per_file_subdirs/test.yaml
@@ -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.
+
+---
diff --git a/gazelle/pythonconfig/pythonconfig.go b/gazelle/pythonconfig/pythonconfig.go
index c7cd7c1..a266804 100644
--- a/gazelle/pythonconfig/pythonconfig.go
+++ b/gazelle/pythonconfig/pythonconfig.go
@@ -78,6 +78,7 @@
// GenerationModeProject defines the mode in which a coarse-grained target will
// be generated englobing sub-directories containing Python files.
GenerationModeProject GenerationModeType = "project"
+ GenerationModeFile GenerationModeType = "file"
)
const (
@@ -126,6 +127,7 @@
ignoreDependencies map[string]struct{}
validateImportStatements bool
coarseGrainedGeneration bool
+ perFileGeneration bool
libraryNamingConvention string
binaryNamingConvention string
testNamingConvention string
@@ -145,6 +147,7 @@
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: true,
coarseGrainedGeneration: false,
+ perFileGeneration: false,
libraryNamingConvention: packageNameNamingConventionSubstitution,
binaryNamingConvention: fmt.Sprintf("%s_bin", packageNameNamingConventionSubstitution),
testNamingConvention: fmt.Sprintf("%s_test", packageNameNamingConventionSubstitution),
@@ -169,6 +172,7 @@
ignoreDependencies: make(map[string]struct{}),
validateImportStatements: c.validateImportStatements,
coarseGrainedGeneration: c.coarseGrainedGeneration,
+ perFileGeneration: c.perFileGeneration,
libraryNamingConvention: c.libraryNamingConvention,
binaryNamingConvention: c.binaryNamingConvention,
testNamingConvention: c.testNamingConvention,
@@ -327,6 +331,18 @@
return c.coarseGrainedGeneration
}
+// SetPerFileGneration sets whether a separate py_library target should be
+// generated for each file.
+func (c *Config) SetPerFileGeneration(perFile bool) {
+ c.perFileGeneration = perFile
+}
+
+// PerFileGeneration returns whether a separate py_library target should be
+// generated for each file.
+func (c *Config) PerFileGeneration() bool {
+ return c.perFileGeneration
+}
+
// SetLibraryNamingConvention sets the py_library target naming convention.
func (c *Config) SetLibraryNamingConvention(libraryNamingConvention string) {
c.libraryNamingConvention = libraryNamingConvention