pants: `--use-first-matching-interpreter` shebang doesn't play nicely with macOS Python 3.7 install
pants fmt failure on master as of master plus just some typing changes in https://github.com/pantsbuild/pants/pull/10647 : env: python3.7: No such file or directory
OS is macOS 10.15.6. which python3 reveals interpreter in a venv that is v3.8.5.
$ ./pants fmt src/python/pants::
Compiling engine v0.0.1 (XXX)
Finished release [optimized + debuginfo] target(s) in 2m 49s
12:11:49 [INFO] initializing pantsd...
12:12:00 [INFO] pantsd initialized.
12:12:08.46 [INFO] Completed: Building black.pex with 2 requirements: black==19.10b0, setuptools
12:12:08 [WARN] /Users/tdyas/TC/pants/src/python/pants/base/exception_sink.py:359: DeprecationWarning: PY_SSIZE_T_CLEAN will be required for '#' formats
process_title=setproctitle.getproctitle(),
12:12:08 [ERROR] 1 Exception encountered:
Engine traceback:
in select
in `fmt` goal
in pants.backend.python.lint.python_fmt.format_python_target
in Format with Black
in pants.engine.process.fallible_to_exec_result_or_raise
Traceback (most recent call last):
File "XXX/pants/src/python/pants/engine/process.py", line 229, in fallible_to_exec_result_or_raise
raise ProcessExecutionFailure(
pants.engine.process.ProcessExecutionFailure: Process 'Run Black on 413 files.' failed with exit code 127.
stdout:
stderr:
env: python3.7: No such file or directory
Traceback (most recent call last):
File "XXX/pants/src/python/pants/bin/local_pants_runner.py", line 255, in run
engine_result = self._run_v2()
File "XXX/pants/src/python/pants/bin/local_pants_runner.py", line 166, in _run_v2
return self._maybe_run_v2_body(goals, poll=False)
File "XXX/pants/src/python/pants/bin/local_pants_runner.py", line 183, in _maybe_run_v2_body
return self.graph_session.run_goal_rules(
File "XXX/pants/src/python/pants/init/engine_initializer.py", line 117, in run_goal_rules
exit_code = self.scheduler_session.run_goal_rule(
File "XXX/pants/src/python/pants/engine/internals/scheduler.py", line 552, in run_goal_rule
self._raise_on_error([t for _, t in throws])
File "XXX/pants/src/python/pants/engine/internals/scheduler.py", line 511, in _raise_on_error
raise ExecutionError(
pants.engine.internals.scheduler.ExecutionError: 1 Exception encountered:
Engine traceback:
in select
in `fmt` goal
in pants.backend.python.lint.python_fmt.format_python_target
in Format with Black
in pants.engine.process.fallible_to_exec_result_or_raise
Traceback (most recent call last):
File "XXX/pants/src/python/pants/engine/process.py", line 229, in fallible_to_exec_result_or_raise
raise ProcessExecutionFailure(
pants.engine.process.ProcessExecutionFailure: Process 'Run Black on 413 files.' failed with exit code 127.
stdout:
stderr:
env: python3.7: No such file or directory
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 40 (40 by maintainers)
Commits related to this issue
- Run internal only pexes via discovered python. (#10779) Since shebangs record information about the host system it is incorrect to ever rely on these when present in a binary stored in the CAS. A s... — committed to pantsbuild/pants by jsirois 4 years ago
- Revert "Run internal only pexes via discovered python. (#10779)" (#10785) This reverts commit ca7e2c268cbf9857110069dcc2b9ccf6feb97bcc. Unfortunately, this is resulting in Pex using macOS's System... — committed to pantsbuild/pants by Eric-Arellano 4 years ago
This is false. The
BinaryPathsdiscovery only finds a bootstrap interpreter to run the Pex CLI. The Pex CLI just needs some python 2.7 or python 3.5+. The interpreter used to build any individual PEX file is determined by the Pex CLI from interpreter constraints and platforms and the vagaries of what the Pex CLI happens to find on the system. This process is opaque to Pants today, although #10779 fixes that.Noting another problem - this time on the Pants side - with using absolute path shebangs related to #10751. If a PEX file gets a statically determined python baked in it becomes a time bomb in the CAS. It will get plucked out to run next week when that interpreter is removed and go boom. The only remedy at that point will be to nuke the local lmdb.
It seems in general for local execution that relies on local binaries (not a problem for remote executiuon where images are static) Pants has this problem. We always need to check a cached binary exists and then re-run discovery if it does not before attempting to finally run the desired Process that uses that binary as argv0. In the case of a
whichstyle discovery process, this could be baked into the local command runner on the Rust side. In more complex cases like Pex interpreter discovery where the interpreter must be found, executed, and data extracted from the runtime environment to check applicability, it is alot easier to approach from the Python rule side of things.In the case of PEX files, we could have a rule that uses a
./my.pex -c ''fallible Process to run the cached pex noop which should always exit 0 if an appropriate interpreter is still found. This would mean, once per pantsd lifecycle we’d have to no-op execute each cached pex tool using an absolute path shebang to verify it does not need to be rebuilt much like #10751 does.True, but it’s a less aggressive breakage. But yeah, for right now the precise shebang is probably the way to go. I’ll put a change up.
I had tried creating that symlink in
/usr/binbut macOS security disallowed that even when running as root.The platforms thing alone doesn’t solve the issue, except on MacOS (assuming you’re not remoting to a MacOS cluster). But you could still get into trouble creating a pex with a full-path shebang on your linux desktop and then trying to use it remotely.
But then, that’s likely true even now, albeit maybe less often.
I recommend Pants/Pex should not assume that the interpreter name is the same in both environments. They are different environments. As an example outside of Pants, Bazel’s “toolchain” concept models this and I believe a local toolchain and a remote toolchain could resolve different interpreter names (although worth looking to see how Bazel does Python toolchains).
https://github.com/pantsbuild/pex/blob/436dc554c1ac234e01529f9f275f7bd01130a51b/pex/interpreter.py#L197-L201 - pex assumes that it can form a binary name of
pythonMAJOR.MINOReven if that is not actually the Python binary that it found when searching for an interpreter.