Repository: GuillaumeDesforges/fix-python
Branch: master
Commit: 248e2ea9620f
Files: 5
Total size: 9.4 KB
Directory structure:
gitextract__09hmi36/
├── .github/
│ └── workflows/
│ └── build.yaml
├── .gitignore
├── README.md
├── fix-python
└── flake.nix
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/build.yaml
================================================
name: Check
on:
push:
branches:
- main
- master
- action
pull_request:
branches:
- main
- master
jobs:
check-numpy-import:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Set up Nix
uses: cachix/install-nix-action@v22
- name: Install Python 3.11 with Nix
run: nix --extra-experimental-features nix-command --extra-experimental-features flakes run nixpkgs#python311 -- -m venv .venv --copies
- name: check that using it on numpy works
run: |
echo "Install numpy"
.venv/bin/pip install numpy
echo "Run fix-python"
./fix-python --venv .venv
echo "Check numpy works"
.venv/bin/python -c 'import numpy'
================================================
FILE: .gitignore
================================================
result
.nix
.venv
libs.nix
================================================
FILE: README.md
================================================
# fix-python
Work with Python "normally" on NixOS in one command!
Tired of all these "*.so not found" errors?
Change the RPATH of all the binaries in your venv!
## Requirements
- Nix
- `nix-command` and `flakes` experimental features must be enabled
## Install
Use temporarily in a shell
```
nix shell github:GuillaumeDesforges/fix-python
```
Or add it to your profile
```
nix profile install github:GuillaumeDesforges/fix-python
```
## Usage
In your Python project, create a virtual environment `.venv` and use your preferred tool (pip, poetry, ...) to install your dependencies.
> [!IMPORTANT]
> `fix-python` will patch binary files in the virtual environment _without_ following symlinks.
>
> For example, if you use the `venv` module from Python's standard library, please make sure you pass `--copies` when creating the environment.
> ```bash
> python -m venv .venv --copies
> ```
By default, `fix-python` patches the packages given in the following expression:
```nix
let pkgs = import (builtins.getFlake "nixpkgs") { };
in [
pkgs.gcc.cc
pkgs.glibc
pkgs.zlib
]
```
> Note: these three packages are fundamental for most Python packages and should never be removed.
If you need to patch packages in addition to these, create a `.nix/libs.nix` file with a structure similar to the above that returns the array of packages that you want binaries to be linked with.
> Note: you may add this `.nix` folder to your project `.gitignore`.
Finally, call `fix-python`.
```console
fix-python --venv .venv [--libs .nix/libs.nix]
```
The list of options is:
```
$ ./fix-python --help
Usage: fix-python --venv .venv [--libs libs.nix] [--no-default-libs]
--help: show this help message
--venv: path to Python virtual environment
--libs: path to a Nix file which returns a list of derivations
--no-default-libs: don't patch C++ standard libraries, glibc, and zlib by default
--gpu: enable GPU support
--with-torch: fix pytorch dependencies issues
--deep: looks for anything executable to patch, very slow but needed sometimes (e.g. PyQt)
--verbose: increase verbosity
```
================================================
FILE: fix-python
================================================
#!/usr/bin/env bash
set -e
# This script fix issues with Python binaries on NixOS
# Usage:
# fix-python --venv .venv [--libs libs.nix] [--no-default-libs]
DEFAULT_LIBS_EXPRESSION="
(
let pkgs = import (builtins.getFlake \"nixpkgs\") { };
in [
pkgs.gcc.cc
pkgs.glibc
pkgs.zlib
]
)
"
# Help
if [ "$1" = "--help" ]; then
echo "Usage: fix-python --venv .venv [--libs libs.nix] [--no-default-libs]" >&2
echo "--help: show this help message" >&2
echo "--venv: path to Python virtual environment" >&2
echo "--libs: path to a Nix file which returns a list of derivations" >&2
echo "--no-default-libs: don't patch C++ standard libraries, glibc, and zlib by default" >&2
echo "--gpu: enable GPU support" >&2
echo "--with-torch: fix pytorch dependencies issues" >&2
echo "--deep: looks for anything executable to patch, very slow but needed sometimes (e.g. PyQt)" >&2
echo "--verbose: increase verbosity" >&2
exit 0
fi
# arguments
while [ $# -gt 0 ]; do
case "$1" in
--venv)
shift
VENV_PATH="$1"
;;
--libs)
shift
LIBS_PATH="$1"
;;
--no-default-libs)
shift
DEFAULT_LIBS_EXPRESSION="[]"
;;
--gpu)
enable_gpu="1"
;;
--with-torch)
enable_torch="1"
;;
--deep)
deep="1"
;;
--verbose)
verbose="1"
;;
*)
echo "Unknown argument: $1" >&2
echo "Usage: fix-python --venv .venv [--libs libs.nix] [--no-default-libs]" >&2
exit 1
;;
esac
shift
done
# check arguments
if [ -z "$VENV_PATH" ]; then
echo "Missing argument: --venv" >&2
echo "Usage: fix-python --venv .venv [--libs libs.nix] [--no-default-libs]" >&2
echo "or set VENV_PATH" >&2
exit 1
fi
# check runtime dependencies are installed
if ! command -v file &> /dev/null
then
echo "Automatically adding \"file\" to PATH." >&2
dep_file_path="$(nix build --no-link --print-out-paths nixpkgs#file.out)/bin"
export PATH="$dep_file_path:$PATH"
if [ "$verbose" ]; then
echo "dep_file_path=$dep_file_path" >&2
fi
fi
if ! command -v patchelf &> /dev/null
then
echo "Automatically adding \"patchelf\" to PATH." >&2
dep_patchelf_path="$(nix build --no-link --print-out-paths nixpkgs#patchelf)/bin"
export PATH="$dep_patchelf_path:$PATH"
if [ "$verbose" ]; then
echo "dep_patchelf_path=$dep_patchelf_path" >&2
fi
fi
# load libs from Nix file
if [ "$LIBS_PATH" ];
then
# if $LIBS_PATH is just a file in the current working directory,
# specified without leading "./", we add "./" so that $LIBS_PATH
# can be interpreted directly as a Nix path in the following
# expression
if [[ ! "$LIBS_PATH" == *"/"* ]];
then
LIBS_PATH="./$LIBS_PATH"
fi
custom_libs_expression="(import $LIBS_PATH)"
mkdir -p .nix/fix-python
nix_libs_build_status=$(
nix build --impure --expr "$custom_libs_expression" -o .nix/fix-python/result
echo $?
)
if [ "$nix_libs_build_status" -eq "1" ];
then
echo "Failed to load libs from Nix file $LIBS_PATH" >&2
echo "" >&2
echo "Try to debug this issue with the command:" >&2
echo " nix build --impure --expr \"import $LIBS_PATH\"" >&2
exit 1
fi
else
custom_libs_expression="[]"
fi
all_nix_libs_expression="($custom_libs_expression ++ $DEFAULT_LIBS_EXPRESSION)"
nixos_python_nix_libs="$(nix eval --impure --expr "let pkgs = import (builtins.getFlake \"nixpkgs\") {}; in pkgs.lib.strings.makeLibraryPath $all_nix_libs_expression" | sed 's/^"\(.*\)"$/\1/')"
if [ "$verbose" ]; then
echo "nixos_python_nix_libs=$nixos_python_nix_libs" >&2
fi
libs="$nixos_python_nix_libs"
# load libs from virtual environment
python_venv_libs=$(echo "$(find "$(realpath "$VENV_PATH")" -name '*.libs'):$(find "$(realpath "$VENV_PATH")" -name 'lib')" | tr '\n' ':')
if [ "$verbose" ]; then
echo "nixos_python_venv_libs=$python_venv_libs" >&2
fi
libs="$libs:$python_venv_libs"
# load libs from NixOS for GPU support if requested
if [ "$enable_gpu" ]; then
nixos_gpu_libs="$(readlink /run/opengl-driver)/lib"
if [ "$verbose" ]; then
echo "nixos_gpu_libs=$nixos_gpu_libs" >&2
fi
libs="$libs:$nixos_gpu_libs"
fi
# put it all together
libs=$(echo "$libs" | sed 's/:\+/:/g' | sed 's/^://' | sed 's/:$//')
if [ "$verbose" ]; then
echo "libs=$libs" >&2
fi
# patch each binary file found in the virtual environment
# shellcheck disable=SC2156
echo "Searching for files to patch in $VENV_PATH" >&2
if [ "$deep" ]; then
echo "Deep search for binary files" >&2
# For context, see #19
binary_files=$(find "$(realpath "$VENV_PATH")" -type f -exec sh -c "file -i '{}' | grep -qE 'application/x-(executable|sharedlib); charset=binary'" \; -print)
else
echo "Fast search for binary files" >&2
binary_files=$(find "$(realpath "$VENV_PATH")" -type f -executable -exec sh -c "file -i '{}' | grep -qE 'x-(.*); charset=binary'" \; -print)
fi
n_binary_files=$(wc -l <<< "$binary_files")
echo "Found $n_binary_files binary files" >&2
cat <<< "$binary_files" \
| while read -r file
do
echo "Patching file: $file" >&2
old_rpath="$(patchelf --print-rpath "$file" || true)"
# prevent duplicates
new_rpath="$(echo "$libs:$old_rpath" | sed 's/:$//' | tr ':' '\n' | sort --unique | tr '\n' ':' | sed 's/^://' | sed 's/:$/\n/')"
patchelf --set-rpath "$new_rpath" "$file" || true
old_interpreter=$(patchelf --print-interpreter "$file" || true)
if [ -n "$old_interpreter" ]; then
interpreter_name="$(basename "$old_interpreter")"
new_interpreter="$(echo "$new_rpath" | tr ':' '\n' | xargs -I {} find {} -name "$interpreter_name" | head -1)"
patchelf --set-interpreter "$new_interpreter" "$file" || true
fi
echo >&2
done
# `libtorch_global_deps.so` depends on libstdc++ but does not properly declare it, fix it manually see
# https://github.com/eth-sri/lmql/blob/main/scripts/flake.d/overrides.nix#L28-L40
if [ "$enable_torch" ]; then
torch_files=$(find "$(realpath "$VENV_PATH")" -name libtorch_global_deps.so)
cat <<< "$torch_files" \
| while read -r file
do
echo "Patching torch file: $file" >&2
patchelf $file --add-needed libstdc++.so
done
fi
================================================
FILE: flake.nix
================================================
{
outputs = { self, flake-utils, nixpkgs, ...}:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in
{
packages.default =
pkgs.stdenv.mkDerivation {
name = "fix-python";
src = self;
phases = [ "unpackPhase" "installPhase" ];
installPhase = ''
mkdir -p $out/bin
cp $src/fix-python $out/bin
'';
};
}
);
}
gitextract__09hmi36/ ├── .github/ │ └── workflows/ │ └── build.yaml ├── .gitignore ├── README.md ├── fix-python └── flake.nix
Condensed preview — 5 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (10K chars).
[
{
"path": ".github/workflows/build.yaml",
"chars": 816,
"preview": "name: Check\n\non:\n push:\n branches:\n - main\n - master\n - action\n pull_request:\n branches:\n - "
},
{
"path": ".gitignore",
"chars": 27,
"preview": "result\n.nix\n.venv\nlibs.nix\n"
},
{
"path": "README.md",
"chars": 2088,
"preview": "# fix-python\n\nWork with Python \"normally\" on NixOS in one command!\n\nTired of all these \"*.so not found\" errors?\nChange t"
},
{
"path": "fix-python",
"chars": 6212,
"preview": "#!/usr/bin/env bash\nset -e\n\n# This script fix issues with Python binaries on NixOS\n# Usage:\n# fix-python --venv .venv [-"
},
{
"path": "flake.nix",
"chars": 523,
"preview": "{\n outputs = { self, flake-utils, nixpkgs, ...}:\n flake-utils.lib.eachDefaultSystem (system:\n let\n pkgs "
}
]
About this extraction
This page contains the full source code of the GuillaumeDesforges/fix-python GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 5 files (9.4 KB), approximately 3.0k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.