fix(gazelle): ensure that gazelle helper modules are on PYTHONPATH (#1590)
Before this change there was a bug in how the parsing helpers were being
used in case we were using Python 3.11 toolchain, which is using a more
strict version of the entrypoint template. This change adds `imports =
["."]`
to ensure that the gazelle helper components are on PYTHONPATH and
updates
the non-bzlmod tests to run under 3.11.
We also:
* Change `.bazelrc` to use explicit `__init__.py` definition to avoid
non-reproducible errors in the future.
* Add a dedicated `gazelle_binary` that uses `DEFAULT_LANGUAGES` *and*
`//python`.
Fixes #1589
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 32ab939..ca8276f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,9 +17,14 @@
* Particular sub-systems are identified using parentheses, e.g. `(bzlmod)` or
`(docs)`.
-## Unreleased
+## [0.27.1] - 2023-12-05
-[0.XX.0]: https://github.com/bazelbuild/rules_python/releases/tag/0.XX.0
+[0.27.1]: https://github.com/bazelbuild/rules_python/releases/tag/0.27.1
+
+### Fixed
+
+* (gazelle) The gazelle plugin helper was not working with Python toolchains 3.11
+ and above due to a bug in the helper components not being on `PYTHONPATH`.
## [0.27.0] - 2023-11-16
diff --git a/gazelle/.bazelrc b/gazelle/.bazelrc
index f48d0a9..7a67d3e 100644
--- a/gazelle/.bazelrc
+++ b/gazelle/.bazelrc
@@ -11,3 +11,11 @@
# Windows makes use of runfiles for some rules
build --enable_runfiles
startup --windows_enable_symlinks
+
+# Do NOT implicitly create empty __init__.py files in the runfiles tree.
+# By default, these are created in every directory containing Python source code
+# or shared libraries, and every parent directory of those directories,
+# excluding the repo root directory. With this flag set, we are responsible for
+# creating (possibly empty) __init__.py files and adding them to the srcs of
+# Python targets as required.
+build --incompatible_default_to_explicit_init_py
diff --git a/gazelle/BUILD.bazel b/gazelle/BUILD.bazel
index 6016145..7a4d4c0 100644
--- a/gazelle/BUILD.bazel
+++ b/gazelle/BUILD.bazel
@@ -1,10 +1,18 @@
-load("@bazel_gazelle//:def.bzl", "gazelle")
+load("@bazel_gazelle//:def.bzl", "DEFAULT_LANGUAGES", "gazelle", "gazelle_binary")
# Gazelle configuration options.
# See https://github.com/bazelbuild/bazel-gazelle#running-gazelle-with-bazel
# gazelle:prefix github.com/bazelbuild/rules_python/gazelle
# gazelle:exclude bazel-out
-gazelle(name = "gazelle")
+gazelle(
+ name = "gazelle",
+ gazelle = ":gazelle_binary",
+)
+
+gazelle_binary(
+ name = "gazelle_binary",
+ languages = DEFAULT_LANGUAGES + ["//python"],
+)
gazelle(
name = "gazelle_update_repos",
diff --git a/gazelle/WORKSPACE b/gazelle/WORKSPACE
index fe7ac3e..df2883f 100644
--- a/gazelle/WORKSPACE
+++ b/gazelle/WORKSPACE
@@ -39,8 +39,8 @@
py_repositories()
python_register_toolchains(
- name = "python39",
- python_version = "3.9",
+ name = "python_3_11",
+ python_version = "3.11",
)
load("//:deps.bzl", _py_gazelle_deps = "gazelle_deps")
diff --git a/gazelle/modules_mapping/BUILD.bazel b/gazelle/modules_mapping/BUILD.bazel
index 1855551..d78b1fb 100644
--- a/gazelle/modules_mapping/BUILD.bazel
+++ b/gazelle/modules_mapping/BUILD.bazel
@@ -1,5 +1,7 @@
load("@rules_python//python:defs.bzl", "py_binary")
+# gazelle:exclude *.py
+
py_binary(
name = "generator",
srcs = ["generator.py"],
diff --git a/gazelle/python/BUILD.bazel b/gazelle/python/BUILD.bazel
index e993a14..1d9460c 100644
--- a/gazelle/python/BUILD.bazel
+++ b/gazelle/python/BUILD.bazel
@@ -17,7 +17,15 @@
"std_modules.go",
"target.go",
],
- embedsrcs = [":helper.zip"],
+ # NOTE @aignas 2023-12-03: currently gazelle does not support embedding
+ # generated files, but helper.zip is generated by a build rule.
+ #
+ # You will get a benign error like when running gazelle locally:
+ # > 8 gazelle: .../rules_python/gazelle/python/lifecycle.go:26:3: pattern helper.zip: matched no files
+ #
+ # See following for more info:
+ # https://github.com/bazelbuild/bazel-gazelle/issues/1513
+ embedsrcs = [":helper.zip"], # keep
importpath = "github.com/bazelbuild/rules_python/gazelle/python",
visibility = ["//visibility:public"],
deps = [
@@ -44,6 +52,8 @@
"parse.py",
"std_modules.py",
],
+ # This is to make sure that the current directory is added to PYTHONPATH
+ imports = ["."],
main = "__main__.py",
visibility = ["//visibility:public"],
)
@@ -54,6 +64,8 @@
output_group = "python_zip_file",
)
+# gazelle:exclude testdata/
+
gazelle_test(
name = "python_test",
srcs = ["python_test.go"],
diff --git a/gazelle/python/__main__.py b/gazelle/python/__main__.py
index 2f5a4a1..18bc1ca 100644
--- a/gazelle/python/__main__.py
+++ b/gazelle/python/__main__.py
@@ -16,9 +16,10 @@
# STDIN receives parse requests, one per line. It outputs the parsed modules and
# comments from all the files from each request.
+import sys
+
import parse
import std_modules
-import sys
if __name__ == "__main__":
if len(sys.argv) < 2:
diff --git a/gazelle/python/python_test.go b/gazelle/python/python_test.go
index 74bd85b..617b3f8 100644
--- a/gazelle/python/python_test.go
+++ b/gazelle/python/python_test.go
@@ -162,7 +162,7 @@
cmd.Dir = workspaceRoot
helperScript, err := runfiles.Rlocation("rules_python_gazelle_plugin/python/helper")
if err != nil {
- t.Fatalf("failed to initialize Python heler: %v", err)
+ t.Fatalf("failed to initialize Python helper: %v", err)
}
cmd.Env = append(os.Environ(), "GAZELLE_PYTHON_HELPER="+helperScript)
if err := cmd.Run(); err != nil {
diff --git a/gazelle/pythonconfig/BUILD.bazel b/gazelle/pythonconfig/BUILD.bazel
index d0f1690..d80902e 100644
--- a/gazelle/pythonconfig/BUILD.bazel
+++ b/gazelle/pythonconfig/BUILD.bazel
@@ -18,7 +18,7 @@
go_test(
name = "pythonconfig_test",
srcs = ["pythonconfig_test.go"],
- deps = [":pythonconfig"],
+ embed = [":pythonconfig"],
)
filegroup(
diff --git a/gazelle/pythonconfig/pythonconfig_test.go b/gazelle/pythonconfig/pythonconfig_test.go
index 1512eb9..bf31106 100644
--- a/gazelle/pythonconfig/pythonconfig_test.go
+++ b/gazelle/pythonconfig/pythonconfig_test.go
@@ -2,8 +2,6 @@
import (
"testing"
-
- "github.com/bazelbuild/rules_python/gazelle/pythonconfig"
)
func TestDistributionSanitizing(t *testing.T) {
@@ -19,7 +17,7 @@
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
- got := pythonconfig.SanitizeDistribution(tc.input)
+ got := SanitizeDistribution(tc.input)
if tc.want != got {
t.Fatalf("expected %q, got %q", tc.want, got)
}