bazel-contrib / bazel-contrib/rules_python
Gazelle: Unintuitive error message for 'multiple targets' in package generation mode with `py_binary` targets
- 主要言語
- Starlark
- スター
- 688
- フォーク
- 721
- 平均マージ
- 15時間 7分
- マージ済み PR(30日)
- 76
説明
As the title states, this report highlights an unintuitive error message.
The problem presents itself when 1) `# gazelle:python_generation_mode package` is used and 2) a python file is used both as a library (imported by other python files) and a binary (containing a `main` method). Gazelle then tries to generate 2 targets, one for the package `py_library` target and another `py_binary` target for the python file. When this happens, an error message like the following is produced (see below for the MRE):
```
2024/12/26 08:39:05 ERROR: failed to validate dependencies for target "//app:app": multiple targets (//lib:a, //lib) may be imported with "lib.a" at line 1 in "app/app.py" - this must be fixed using the "gazelle:resolve" directive
"lib" at line 1 from "app/app.py" 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.
3. Ignore it with a comment '# gazelle:ignore lib' in the Python file.
```
This error message is unintuitive because it is not clear why there are multiple targets, especially for engineers who aren't part of the infra team maintaining Gazelle and don't fully understand its internals. Also, none of the 3 solutions recommended by the error message are correct; the real solution is to split the `main` method from `lib/a.py` into its own file (e.g. `lib/a_main.py`) which wouldn't be imported by anything else.
It seems to me that python files which serve as both libraries and binaries are fundamentally incompatible with `package` generation mode, but I'm still new to Gazelle so I might be missing something.
### Proposed Solution
Gazelle already has all the information it needs to provide a better error message. Some ideas for improving the DX:
1. Fail hard when in `package` mode and any python file has a dependency on another file with a `main` function. This would be my preferred solution because its the most explicit, but not sure if it will break other setups.
1. In the case where the above message would be printed, include a 4th option to the suggested solutions in the error message: `4. Split the 'if '__name__' == '__main__' entrypoint function into a separate '.py' file, to avoid including its other contents in multiple targets.`
### MRE
```
# BUILD.bazel
# gazelle:python_generation_mode package
```
```python
# lib/a.py
def add(a: int, b: int) -> int:
return a + b
if __name__ == "__main__":
print(add(1, 2))
```
```python
# app/app.py
import lib.a as a
if __name__ == "__main__":
print(a.add(1, 2))
```
### Bonus
There might be some relation between this request and #2443. After resolving by splitting out the `main` method, e.g. into `a_main.py`, the generated targets for the `lib` package look like this:
```
load("@aspect_rules_py//py:defs.bzl", "py_binary", "py_library")
py_binary(
name = "a",
srcs = ["a_main.py"],
visibility = ["//:__subpackages__"],
deps = [":lib"],
)
py_library(
name = "lib",
srcs = [
"a.py",
"a_main.py", # <-- unnecessary src
],
visibility = ["//:__subpackages__"],
)
```
Importantly, `a_main.py` gets included in the package-level `py_library` target even though it will never be used. If it was used, we would run into the error above.
コントリビューションガイド
調査の方向性
BUILD.bazel、lib/a.py、app/app.pyを使用し、パッケージ生成モードでMREを再現してから、Gazelleの依存関係の検証とエラー報告の経路を調査します。生成されたターゲットを比較し、診断でライブラリとバイナリの競合を説明すべきか、それとも拒否すべきかを判断します。完了条件は、3つの誤った修正を提案しない、明確でテスト済みの診断がこのケースにあることです。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- build-system, tooling
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 静か
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 45/100