Ruff VSCode extension does not work on NixOS
I just installed the Ruff VSCode extension v2024.16.0. However, I'm seeing the following error in Output > Ruff:
2024-04-03 22:32:28.078 [info] Interpreter executable (/nix/store/qzaa6lxhvlbb9nn2v6ssppmqg4c3pb1p-python3-3.11.8/bin/ruff) not found
2024-04-03 22:32:28.078 [info] [Trace - 10:32:28 PM] Received notification 'window/logMessage'.
2024-04-03 22:32:28.078 [info] Falling back to bundled executable: /home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff
2024-04-03 22:32:28.081 [info] 2024-04-03 22:32:28,079 ERROR Exception occurred for message "15": FileNotFoundError: [Errno 2] No such file or directory: '/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff'
Traceback (most recent call last):
File "/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/pygls/protocol/json_rpc.py", line 194, in _execute_request_callback
self._send_response(msg_id, result=future.result())
^^^^^^^^^^^^^^^
File "/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/ruff_lsp/server.py", line 1220, in format_document
return await _format_document_impl(params, None)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/ruff_lsp/server.py", line 1260, in _format_document_impl
result = await _run_format_on_document(document, settings, range)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/ruff_lsp/server.py", line 1912, in _run_format_on_document
executable = _find_ruff_binary(settings, version_requirement)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/ruff_lsp/server.py", line 1750, in _find_ruff_binary
version = _executable_version(path)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/ruff_lsp/server.py", line 1825, in _executable_version
version = utils.version(executable)
^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/ruff_lsp/utils.py", line 96, in version
output = subprocess.check_output([executable, "--version"]).decode().strip()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/nix/store/qzaa6lxhvlbb9nn2v6ssppmqg4c3pb1p-python3-3.11.8/lib/python3.11/subprocess.py", line 466, in check_output
return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/nix/store/qzaa6lxhvlbb9nn2v6ssppmqg4c3pb1p-python3-3.11.8/lib/python3.11/subprocess.py", line 548, in run
with Popen(*popenargs, **kwargs) as process:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/nix/store/qzaa6lxhvlbb9nn2v6ssppmqg4c3pb1p-python3-3.11.8/lib/python3.11/subprocess.py", line 1026, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "/nix/store/qzaa6lxhvlbb9nn2v6ssppmqg4c3pb1p-python3-3.11.8/lib/python3.11/subprocess.py", line 1953, in _execute_child
raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: '/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff'
2024-04-03 22:32:28.082 [info] [Trace - 10:32:28 PM] Received response 'textDocument/formatting - (15)' in 6ms. Request failed: FileNotFoundError: [Errno 2] No such file or directory: '/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff' (-32603).
2024-04-03 22:32:28.082 [info] [Error - 10:32:28 PM] Request textDocument/formatting failed.
2024-04-03 22:32:28.082 [info] Message: FileNotFoundError: [Errno 2] No such file or directory: '/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff'
Code: -32603
[object Object]
Perhaps this is due to a dynamic linking issue on NixOS?
Does the file /home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff exist? I'm not certain what's going on here. Are you able to use other extensions successfully, like Microsoft's official isort extension, as an arbitrary example? https://github.com/microsoft/vscode-isort
the file exists but is not executable:
❯ ls /home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff
/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff
❯ /home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff
zsh: no such file or directory: /home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff
otoh:
[nix-shell:~/dev/bitbop]$ file /home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff
/home/skainswo/.vscode-server/extensions/charliermarsh.ruff-2024.16.0-linux-arm64/bundled/libs/bin/ruff: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 4.10.17, stripped
❯ ls /lib/ld-linux-aarch64.so.1
ls: cannot access '/lib/ld-linux-aarch64.so.1': No such file or directory
I'm guessing this is caused by NixOS's approach to dynamic linking. IIUC the path forward here is to publish a statically linked binary (targeting any Linux), or with patchelf (targeting NixOS).
Or, given that this is a VSCode extension, perhaps it could be packaged as WASM?
Or, given that this is a VSCode extension, perhaps it could be packaged as WASM?
That's a possibility but it would require trampolining into JS for filesystem calls (and isn't possible today because the extension spawns suprocesses). I'm also not sure what the latest status on multithreading support in WASM is (which is important for Ruff's performance).
Can you try installing ruff locally into your activate venv and see if that works?
That's a possibility but it would require trampolining into JS for filesystem calls (and isn't possible today because the extension spawns suprocesses). I'm also not sure what the latest status on multithreading support in WASM is (which is important for Ruff's performance).
All good points!
Can you try installing ruff locally into your activate venv and see if that works?
Running ruff via the nixpkgs ruff works:
❯ nix-shell -p ruff --run ruff
Ruff: An extremely fast Python linter.
...
This brings to mind 2 other ways to solve this issue:
- Detect NixOS (eg via
ls /etc/NIXOS) and runnix-shell -p ruff --run ruffinstead of the baseline linux binary - Detect NixOS and then install a script
#! /usr/bin/env nix-shell
#! nix-shell -p ruff --run ruff
in place of the baseline linux binary.
I think either of these should solve the issue.
FWIW I just noticed that ripgrep publishes statically linked binaries in their VSCode Extension, so there is precedent for such a solution:
/home/skainswo/.vscode-server/bin/863d2581ecda6849923a2118d93a088b0745d9d6/node_modules/@vscode/ripgrep/bin/rg:
ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), statically linked, stripped
By the way, we do publish statically linked binaries. They're just not picked up in the extension build. We could change that.
(I think we should change it to bundle the statically linked binaries, since they have strictly better compatibility.)
Makes sense
I use Nix/NixOS and faced this issue. How to make ruff VSCode extension pick the statically linked binaries of ruff?
I use Nix/NixOS and faced this issue. How to make ruff VSCode extension pick the statically linked binaries of ruff?
This is something we have to change in our release pipeline.
I don't know anything about Nix myself but what I understand from both is that one possibility is to install ruff with the nix package manager and you can then configure the extension to use that binary instead by setting ruff.path to the path of the ruff binary.
Another solution that you could try is to install ruff into a local venv using pip (or uv). the extension should then automatically pick up ruff from the venv.