feat(gazelle): Update resolve.go to provide more human-friendly error output (#2120)
Update the Resolver error messages in resolve.go to be easier to read.
While enabling gazelle in an existing repo, I found it somewhat
difficult to skim the error messages. This PR:
1. Adds a bit of whitespace to spread different pieces of information
out
2. Provides an additional remediation action for ambiguous import
resolution
3. Moves the filename and line number where the error is to the front of
the line.
4. Provides more concrete "gazelle:resolve py" directive examples for
each errored dep resolution.
Additionally, update a testcase to showcase the ambiguous import
resolution error case. See the `test.yaml` file diff for an example of
the old vs new error output.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 91554c4..f703269 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,7 +25,7 @@
[x.x.x]: https://github.com/bazelbuild/rules_python/releases/tag/x.x.x
### Changed
-* Nothing yet
+* (gazelle): Update error messages when unable to resolve a dependency to be more human-friendly.
### Fixed
* (gazelle): Fix incorrect use of `t.Fatal`/`t.Fatalf` in tests.
diff --git a/gazelle/python/resolve.go b/gazelle/python/resolve.go
index ca306c3..a7b716a 100644
--- a/gazelle/python/resolve.go
+++ b/gazelle/python/resolve.go
@@ -206,11 +206,11 @@
continue MODULES_LOOP
} else if cfg.ValidateImportStatements() {
err := fmt.Errorf(
- "%[1]q at line %[2]d from %[3]q is an invalid dependency: possible solutions:\n"+
+ "%[1]q, line %[2]d: %[3]q is an invalid dependency: possible solutions:\n"+
"\t1. Add it as a dependency in the requirements.txt file.\n"+
- "\t2. Instruct Gazelle to resolve to a known dependency using the gazelle:resolve directive.\n"+
- "\t3. Ignore it with a comment '# gazelle:ignore %[1]s' in the Python file.\n",
- moduleName, mod.LineNumber, mod.Filepath,
+ "\t2. Use the '# gazelle:resolve py %[3]s TARGET_LABEL' BUILD file directive to resolve to a known dependency.\n"+
+ "\t3. Ignore it with a comment '# gazelle:ignore %[3]s' in the Python file.\n",
+ mod.Filepath, mod.LineNumber, moduleName,
)
errs = append(errs, err)
continue POSSIBLE_MODULE_LOOP
@@ -236,9 +236,10 @@
}
if len(sameRootMatches) != 1 {
err := fmt.Errorf(
- "multiple targets (%s) may be imported with %q at line %d in %q "+
- "- this must be fixed using the \"gazelle:resolve\" directive",
- targetListFromResults(filteredMatches), moduleName, mod.LineNumber, mod.Filepath)
+ "%[1]q, line %[2]d: multiple targets (%[3]s) may be imported with %[4]q: possible solutions:\n"+
+ "\t1. Disambiguate the above multiple targets by removing duplicate srcs entries.\n"+
+ "\t2. Use the '# gazelle:resolve py %[4]s TARGET_LABEL' BUILD file directive to resolve to one of the above targets.\n",
+ mod.Filepath, mod.LineNumber, targetListFromResults(filteredMatches), moduleName)
errs = append(errs, err)
continue POSSIBLE_MODULE_LOOP
}
@@ -263,7 +264,7 @@
for _, err := range errs {
joinedErrs = fmt.Sprintf("%s%s\n", joinedErrs, err)
}
- log.Printf("ERROR: failed to validate dependencies for target %q: %v\n", from.String(), joinedErrs)
+ log.Printf("ERROR: failed to validate dependencies for target %q:\n\n%v", from.String(), joinedErrs)
hasFatalError = true
}
}
diff --git a/gazelle/python/testdata/invalid_imported_module/__init__.py b/gazelle/python/testdata/invalid_imported_module/__init__.py
index dc6fb85..40b5848 100644
--- a/gazelle/python/testdata/invalid_imported_module/__init__.py
+++ b/gazelle/python/testdata/invalid_imported_module/__init__.py
@@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import foo.bar
+
try:
import grpc
@@ -19,4 +21,4 @@
except ImportError:
grpc_available = False
-_ = grpc
+_ = bar(grpc)
diff --git a/gazelle/python/testdata/invalid_imported_module/foo/BUILD.in b/gazelle/python/testdata/invalid_imported_module/foo/BUILD.in
new file mode 100644
index 0000000..4f598e9
--- /dev/null
+++ b/gazelle/python/testdata/invalid_imported_module/foo/BUILD.in
@@ -0,0 +1,11 @@
+load("@rules_python//python:defs.bzl", "py_library")
+
+py_library(
+ name = "bar_1",
+ srcs = ["bar.py"],
+)
+
+py_library(
+ name = "bar_2",
+ srcs = ["bar.py"],
+)
diff --git a/gazelle/python/testdata/invalid_imported_module/foo/bar.py b/gazelle/python/testdata/invalid_imported_module/foo/bar.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/python/testdata/invalid_imported_module/foo/bar.py
diff --git a/gazelle/python/testdata/invalid_imported_module/test.yaml b/gazelle/python/testdata/invalid_imported_module/test.yaml
index 6bcea39..0085523 100644
--- a/gazelle/python/testdata/invalid_imported_module/test.yaml
+++ b/gazelle/python/testdata/invalid_imported_module/test.yaml
@@ -16,7 +16,20 @@
expect:
exit_code: 1
stderr: |
- gazelle: ERROR: failed to validate dependencies for target "//:invalid_imported_module": "grpc" at line 16 from "__init__.py" is an invalid dependency: possible solutions:
+ gazelle: ERROR: failed to validate dependencies for target "//:invalid_imported_module":
+
+ "__init__.py", line 15: multiple targets (//foo:bar_1, //foo:bar_2) may be imported with "foo.bar": possible solutions:
+ 1. Disambiguate the above multiple targets by removing duplicate srcs entries.
+ 2. Use the '# gazelle:resolve py foo.bar TARGET_LABEL' BUILD file directive to resolve to one of the above targets.
+
+ "__init__.py", line 15: "foo" is an invalid dependency: possible solutions:
1. Add it as a dependency in the requirements.txt file.
- 2. Instruct Gazelle to resolve to a known dependency using the gazelle:resolve directive.
+ 2. Use the '# gazelle:resolve py foo TARGET_LABEL' BUILD file directive to resolve to a known dependency.
+ 3. Ignore it with a comment '# gazelle:ignore foo' in the Python file.
+
+ gazelle: ERROR: failed to validate dependencies for target "//:invalid_imported_module":
+
+ "__init__.py", line 18: "grpc" is an invalid dependency: possible solutions:
+ 1. Add it as a dependency in the requirements.txt file.
+ 2. Use the '# gazelle:resolve py grpc TARGET_LABEL' BUILD file directive to resolve to a known dependency.
3. Ignore it with a comment '# gazelle:ignore grpc' in the Python file.