-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Neither python_top nor python toolchain works with starlark actions on windows #7947
Comments
cc @brandjon |
Ok, I played around with our windows sandbox vm and think I understand. The most likely situation is that your From my experimentation, it doesn't look like the The error message is a little misleading: Our Python launcher will first check whether the given Python interpreter file exists, and if not, fall back on "python.exe" on its own. This means that the error message you see will always say it can't find "python.exe" even if you specified (Note the "r" raw string literal prefix in that last sentence. Depending on your path to the Python interpreter, it's possible you're using a single backslash before something that's interpreted as a valid escape sequence, producing a different string than you think. When in doubt, it's valid to replace backslashes with slashes in windows paths.) Please let me know if this helps. If nothing else, we'll keep this issue open to track the bad error message in the Python launcher (or the removal of that fallback entirely). |
Actually we are using an in-build runtime from an external dependency and finally I managed to setup a simple example here pziggo/bazel_py_runtime_win_example. I also debugged the issue a bit more and I think I'm getting closer to understand the root cause. Thanks for the link which was a good starting point. The python launcher checks if the given python interpreter exists by converting the
while the following path exists (note the "external" in the path):
So, before handing over the |
Thank you for the investigation and repro! I haven't run your repro yet, but if I understand correctly it sounds like the scope of this problem is anytime the |
Yes, sounds about right. |
But I wonder what would be a good change to solve the problem. Fixing the path could be relatively easy, but that would not work on remote execution if I’m not mistaken. |
+@laszlocsomor FYI. So to summarize: When an in-build interpreter is used on Windows, the Python launcher will take its workspace-relative path and use that in combination with its own working directory to construct the full (non-runfile) path to the interpreter. Due to a bug, this fails when the interpreter is defined in an external repo. I'm not sure what the plan is for remote execution and windows, but there are likely already other blockers within the Python rules. For instance, BazelPythonSemantics checks Aside from that, this behavior is another difference between unix and Windows. On unix, the shebang of the stub script is You can see an analogous version of this bug in google/subpar#98, where in-build interpreters caused the shebang to get a relative path. |
Regarding priority: This issue prevents using many kinds of in-workspace Python runtimes on Windows, including interpreters provided by external repos (e.g. Therefore, a fix for this won't be in 0.27, but it'd be great to have it for 0.28. Now on to the actual design issue. As I alluded to above, on unix we use a two-phase approach to launching Python programs.
This separation into two separate phases is what enables us to use an in-workspace interpreter packaged within a zip file. The zip depends on a system interpreter to bootstrap and extract itself. On Windows, this separation does not currently exist: Regardless of whether you're using a zip file or not, the Python launcher will try to run the stub script with the same interpreter that your toolchain requests to be used for payload user code. This can be an interpreter in runfiles, which haven't been extracted yet. The way we currently get around this is by giving the launcher a path to the interpreter binary on disk, i.e. the actual checked-in source file or the built binary file in the output tree. This is pretty brittle. First, there's the breakage described above, when the path construction doesn't quite work out. But even if we fix that by passing an absolute path to the on-disk file, we'd end up in a situation where the resulting So let's revisit our strategy: There's no reason we need to use the same interpreter for the zip file / stub script that we do for the payload code. We can assume the presence of a system Python interpreter for the purposes of bootstrapping. The launcher can hardcode that assumption, which could take different forms, for example:
It's even possible to let the Python toolchain customize how the bootstrapping interpreter is found, just like you could let it parameterize the shebang string. But I don't see much value in this at the moment. So right now, I think the goal should be to add |
Thanks for the summary!
IIUC we do so because the stub script itself is in Python, so the binary requires a system interpreter even in presence of a bundled interpreter. But isn't part of a bundled interpreter's goal to enable running without a system interpreter? The analogous problem is running Bazel itself. The embedded JVM lets you run Bazel without a system JVM. Its launcher (the bazel client) is a native binary.
Sure, the launcher should begin with extracting the payload. We can fix that.
Doesn't that partially beat the purpose of a bundled interpreter? If the stub weren't a python script but were part of the .exe launcher, we wouldn't need this assumption. My questions:
|
This is true when using python toolchain or |
With #8440, I can run all commands in https://github.com/pziggo/bazel_py_runtime_win_example, but got error like this
But it's caused by the python binary we are using, not a Bazel bug. |
I don't think this is something we need to prioritize, but it'd be nice to have in the future. With a bundled interpreter that requires the system interpreter for bootstrapping, you still get hermeticity once you're past the stub script phase.
Then by that logic we could embed a native Python interpreter (perhaps miniaturized) into each py_binary artifact. But at that point perhaps it'd be worth rewriting the stub script in C++. An alternative to embedding an extra interpreter into the artifact would be to have a way of extracting the payload user code's interpreter from runfiles without using Python... and again the way to do that would be essentially rewriting the stub in C++.
Sounds like we're arriving at similar conclusions.
The stub script:
In principle the stub script should be runnable under any reasonable Python interpreter (with some hand wavyness around "reasonable"). So there shouldn't be a need to customize which system interpreter is used, whether it's However, if we're eliminating the dependency on a system interpreter and just using whatever interpreter is specified for the payload user code (whether system or runfiles based), then we'd just pass that along to the launcher, as we do today. |
Apparently the |
Thanks for confirming. I just wanted to note that requiring an ambient Python interpreter might cause frustration. People were unhappy about Bazel requiring MSYS2 too.
Sounds reasonable.
Agreed. The Bazel client does the same.
Indeed. :)
Thanks. None of that sounds daunting. I implemented the test wrapper in C++. It solves similar problems, we could reuse it. (It's Windows-focused though.) |
That all sounds good to me. I forked off #8446. In the meantime, we should make the launcher better at detecting a system interpreter. We can track that in this bug. |
Let's discuss the merits of the approach of #8440 here. That PR makes it so if a runfiles path is passed to the launcher, it uses rlocation to resolve that to its absolute path. Since when a zip file is used the runfiles are manifest-based rather than directory-based, this doesn't suffer the chicken-and-egg problem of accessing an interpreter in a zip. It does suffer the problem where if the build is cleaned, the .exe artifact is broken, even if it is copied out of the output tree. Currently I believe the .exe only depends on having the .zip in the same directory. I'm ok with settling for this limitation for now as a fix of existing behavior. It'll be moot once the launcher extracts the zip. |
`exit()` doesn't exist if python was invoked without the `site` module. See #7947 (comment) (#7947). RELNOTES: None PiperOrigin-RevId: 249636384
Although, one consequence of this design is that if you have no system Python interpreter, but your build has a checked-in interpreter, it'll work on your development machine since the actual not-in-runfiles interpreter file is available, and then fail on a user's machine. But we don't have a better option before #8446, unless we want to fail fast on the development machine. |
Indeed #8440 is just for making existing behavior that works on Linux also works on Windows. Beyond this, it's not a Windows only problem. Ideally we should move all logic in |
@brandjon , @meteorcloudy : does any of you plan to work on this? |
@brandjon has a change to enable more python toolchain integration tests on Windows, I will submit my fix for this issue afterwards. Yet I have no plan to migrate the |
Thanks. I consider migrating it. |
My pending fix blocked on a failing test over the long weekend, looking at it again today. |
My fix finally went through, merging the fix to this issue now. |
`exit()` doesn't exist if python was invoked without the `site` module. See bazelbuild#7947 (comment) (bazelbuild#7947). RELNOTES: None PiperOrigin-RevId: 249636384
Previously, we assume the python/bash binary path passed to the launcher is an absolute path specified by `--python_path` or `--shell_executable`, but this is not true when using python/shell toolchain. When using the python toolchain, the given python binary path will be a runfile path, we can use `Rlocation` to find the absolute path. Fixes bazelbuild#7947 Closes bazelbuild#8440. PiperOrigin-RevId: 250569171
`exit()` doesn't exist if python was invoked without the `site` module. See bazelbuild#7947 (comment) (bazelbuild#7947). RELNOTES: None PiperOrigin-RevId: 249636384
Previously, we assume the python/bash binary path passed to the launcher is an absolute path specified by `--python_path` or `--shell_executable`, but this is not true when using python/shell toolchain. When using the python toolchain, the given python binary path will be a runfile path, we can use `Rlocation` to find the absolute path. Fixes bazelbuild#7947 Closes bazelbuild#8440. PiperOrigin-RevId: 250569171
Description of the problem / feature request:
Trying to use a
py_binary()
target as executable withactions.run()
fails on windows ifpy_runtime()
is used. Error Message:This issue happens with
python_top
and the new python toolchain feature. It works fine on Linux though.Feature requests: what underlying problem are you trying to solve with this feature?
Executing python actions during the build with a hermetic python environment.
Bugs: what's the simplest, easiest way to reproduce this bug? Please provide a minimal example if possible.
see here
What operating system are you running Bazel on?
Windows 10
What's the output of
bazel info release
?development version
If
bazel info release
returns "development version" or "(@non-git)", tell us how you built Bazel.bazel build //src:bazel_jdk_minimal
on Windows 10What's the output of
git remote get-url origin ; git rev-parse master ; git rev-parse HEAD
?https://github.com/bazelbuild/bazel.git
78d6831
78d6831
Have you found anything relevant by searching the web?
Discussion has been started on Slack: https://bazelbuild.slack.com/archives/CA306CEV6/p1554299740008400
Any other information, logs, or outputs that you want to share?
From the resulting
__main__.py
from the rule definition:
and from the rule implementation
The text was updated successfully, but these errors were encountered: