master 248e2ea9620f cached
5 files
9.4 KB
3.0k tokens
1 requests
Download .txt
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
              '';
            };
        }
    );
}
Download .txt
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.

Copied to clipboard!