Nix flake에서 Python 환경 관리하기
목표
Node와 Python으로 짠 스크립트가 어지럽게 널려있는 환경에서 두세번 스크립트 실행을 실패하면서
pip install ...
혹은 npm install ...
을 하다보니, 왠지 그냥 nix run .#asdf args...
로
스크립트를 실행할 수 있으면 조금이나마 편하지 않을까…? 라는 생각을 하게 되었다. 일단 node 스크립트는
잠깐 제쳐두고, python 스크립트를 바로 실행할 수 있는 flake 환경을 만들어보았다.
문제 1 - 스크립트의 의존성 관리
그냥 requirements.txt
파일로 관리하던 의존성을 어딘가 nix가 알아먹을 수 있는 포맷으로 옮겨야 하는데,
이거 하자고 flake.nix에 또다시 의존성 목록을 쓰는 건… 배보다 배꼽이 크다는 느낌이다. 절충해서 의존성
목록을 pyproject.toml 파일에 넣고, 해당 파일을 읽어서 의존성을 관리할 수 있는 pyproject-nix flake를
사용해보기로 했다.
문제 2 - nixpkgs에 없는 의존성
다만 모든 pypi 패키지를 사용할 수 있는 게 아니라, nixpkgs에 등록된 파이썬 패키지만 쓸 수 있다는 문제가 있었다. 몇몇 아주 오래되고 인기 없는 패키지의 경우 nixpkgs에 없어서, 내가 직접 등록하던가 아니면 로컬에서 바로 패키지를 빌드해서 써야 할 필요가 있었다.
결론
되면 한다.
{
description = "A flake for python package";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
flake-utils.url = "github:numtide/flake-utils";
pyproject-nix.url = "github:pyproject-nix/pyproject.nix";
pyproject-nix.inputs.nixpkgs.follows = "nixpkgs";
};
outputs =
{
self,
nixpkgs,
flake-utils,
pyproject-nix,
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
# Python 패키지 googledrivedownloader를 따로 빌드한다.
googledrivedownloader = pkgs.python3Packages.buildPythonPackage rec {
pname = "googledrivedownloader";
version = "1.1.0";
format = "pyproject"; # 이 패키지는 setup.py 대신 pyproject.toml을 사용한다.
propagatedBuildInputs = with pkgs.python3Packages; [ # 이 패키지가 의존하는 패키지들
hatchling
requests
];
src = pkgs.python3Packages.fetchPypi {
inherit version;
inherit pname;
hash = "sha256-EAkg606QabSLwvBEXt8uvFKBS8jUEDhdCyyQLK1kzJs="; # 해시는 빌드 과정에서 알려준다.
};
};
# Python 패키지 pimento를 따로 빌드한다.
pimento = pkgs.python3Packages.buildPythonPackage rec {
pname = "pimento";
version = "0.5.1";
# 이번엔 setup.py를 사용하기 때문에 별도의 옵션이 없다.
src = pkgs.python3Packages.fetchPypi {
inherit version;
inherit pname;
hash = "sha256-psWe9vYH+HzxNjKRpXVGz8uxwbiwUahWIyiDnvxMciY=";
};
};
myPython =
let
packageOverrides = self: super: {
# 새로운 패키지들을 packageOverrides를 통해 추가한다.
inherit googledrivedownloader;
inherit pimento;
};
in
pkgs.python3.override {
inherit packageOverrides;
self = myPython;
};
# pyproject-nix를 사용해서 pyproject.toml을 분석한다.
project = pyproject-nix.lib.project.loadPyproject {
projectRoot = ./.;
};
in
{
devShells.default =
let
arg = project.renderers.withPackages { python = myPython; }; # pyproject.toml에서 요구하는 의존성 목록을 제공한다.
pythonEnv = myPython.withPackages arg; # 해당 의존성 목록을 제공하는 파이썬 환경을 만든다.
in
pkgs.mkShell {
packages = [
pythonEnv # 해당 파이썬 환경을 바로 사용할 수 있는 shell을 만든다.
];
};
# flake.nix 포맷용 스크립트
formatter = pkgs.writeScriptBin "format" ''
#!${pkgs.bash}/bin/bash
${pkgs.nixfmt-rfc-style}/bin/nixfmt flake.nix
'';
}
);
}