跳转到主要内容

一个生成您Python包的nix表达式的工具,因此您不必这样做。

项目描述

pypi2nix 是一个命令行工具,可以从不同的 requirements.txt 生成 Nix表达式。这对于

  • 构建一个用于Python程序的Nix衍生物,作为其打包的一部分。

  • 设置一个用于修改Python程序的开发环境。

    我们能够修复pypi2nix的bug的唯一方法是您报告它们。如果您发现问题,请创建一个问题。

pypi2nix 将(除非另行通知)仅与最新的 不稳定 通道一起工作。这是由于Nixpkgs中Python基础设施的持续变化。

如果您尚未阅读,请阅读 Nixpkgs手册中的Python部分

1. 安装

pypi2nixnixpkgs 的一部分。如果您只想在系统上使用 pypi2nix,建议您通过常规方式安装,例如在 NixOS 上使用 nix-env -iA nixos.pypi2nix 或在其他使用 nix 的系统上使用 nix-env -iA nixpkgs.pypi2nix

系统要求

确保已安装 Nix

% curl https://nixos.org/nix/install | sh

目前,pypi2nix 仅在 linux 系统上进行测试。支持的 nixpkgs 通道是 nixos-19.09nixos-unstable。由于 nixos-unstable 的特性,偶尔 pypi2nix 会出现故障,这是可以预见的。我们努力及时提供相关修复。

临时安装(简单)

如果您只想使用命令安装软件包,请使用 nix-env

nix-env -if https://github.com/nix-community/pypi2nix/tarball/master

声明性安装(高级)

如果您更喜欢在 Nix(OS) 或项目配置中显式声明每个已安装的软件包,您可以执行以下操作

首先,通过使用 pkgs.fetchgit 获取整个 Git 存储库从其 default.nix 导入软件包。之后,您可以将导入的属性添加到已安装软件包的列表中。

以下是一个 NixOS 的 configuration.nix 示例。其他方法,如 home-manager,操作类似。

let
  pypi2nix = import (pkgs.fetchgit {
    url = "https://github.com/nix-community/pypi2nix";
    # adjust rev and sha256 to desired version
    rev = "v2.0.1";
    sha256 = "sha256:0mxh3x8bck3axdfi9vh9mz1m3zvmzqkcgy6gxp8f9hhs6qg5146y";
  }) {};
in
  environment.systemPackages = [
    # your other packages
    pypi2nix
  ];

2. 使用

生成 Nix 表达式最简单的方法是调用

% pypi2nix -e packageA -e packageB==0.1

如果您还有 Python 项目的 requirements.txt 文件,您可以使用 -r 选项。

% pypi2nix -e packageA -e packageB==0.1 \
    -r requirements.txt -r requirements-dev.txt

正在生成什么

选项 -V 告诉 pypi2nix 使用哪个 Python 版本。要查看哪些 Python 版本可用,请运行 pypi2nix --help

生成 Nix 表达式后,您应该能看到 3 个新文件

  • requirements_frozen.txt - 对您的 pypi2nix 调用的完整冻结集。这是您从 pip freeze 预期得到的输出。

  • requirements.nix 是一个包含构建的软件包集的 Nix 表达式的文件。

  • requirements_override.nix - 这是一个空文件,允许您覆盖生成的 Nix 表达式。

构建生成的包

构建单个软件包

% nix build -f requirements.nix packages.empy

构建所有软件包

% nix build -f requirements.nix packages

构建包含所有软件包的 Python 解释器

% nix build -f requirements.nix interpreter
% ./result/bin/python -c "import empy"

进入开发环境

% nix run -f requirements.nix interpreter
[user@localhost:~/dev/nixos/pypi2nix) % python -c "import empy"

使用生成的包

如果您正在处理一个其依赖项在 requirements.txt 中定义的项目,则可以创建一个 default.nix 并将生成的软件包添加为 buildInputs,如下所示

{}:
let
  python = import ./requirements.nix { inherit pkgs; };
in python.mkDerivation {
  name = "ProjectA-1.0.0";
  src = ./.;
  buildInputs = [
    python.packages."coverage"
    python.packages."flake8"
    python.packages."mock"
    python.packages."pytest"
    python.packages."pytest-asyncio"
    python.packages."pytest-cov"
    python.packages."pytest-mock"
    python.packages."pytest-xdist"
    python.packages."virtualenv"
  ];
  propagatedBuildInputs = [
    python.packages."aiohttp"
    python.packages."arrow"
    python.packages."defusedxml"
    python.packages."frozendict"
    python.packages."jsonschema"
    python.packages."taskcluster"
    python.packages."virtualenv"
  ];
  ...
}

如您所见,您可以通过 python.packages."<name>" 访问所有软件包。如果您想要依赖所有软件包,甚至可以这样做

propagatedBuildInputs = builtins.attrValues python.packages;

命令行选项

-v

增加输出给用户的信息量和详细信息。详细程度依次为 ERRORWARNINGINFODEBUG。默认详细程度为 INFO

-q

减少输出给用户的信息量和详细信息。有关更多信息,请参阅 -v

-I/--nix-path TEXT

以类似 -Inix 可执行文件(如 nix-build)的方式将条目添加到 NIX_PATH 环境变量中。这可以用于根据本地系统中使用的不同版本的 nixpkgs 生成包集。

--nix-shell PATH

替代版本 nix-shell 命令的路径。默认是系统当前 PATH 中找到的第一个可执行文件。

--version

显示 pypi2nix 的当前版本

--basename TEXT

此选项确定生成的文件名。因此,使用 --basename environment 将得到 environment.nixenvironment_frozen.nixenvironment_override.nix 等文件。

--extra-build-inputs/-E TEXT

额外的构建输入,这是必需的 Python 包需要运行的,例如 libffilibgl。在这种情况下,您将提供 -E "libffi libgl"。这些 nix 包将在构建环境中的 wheel 中可用。

--emit-extra-build-inputs/--no-emit-extra-build-inputs

这些选项让您控制通过 -E 指定的外部构建依赖是否最终会出现在生成的 nix 包集中。请注意,如果您选择此选项,您需要确保 Python 包找到它们各自的外部依赖。

--extra-env/-N TEXT

传递给构建环境的额外环境变量。注意,您可以使用 nix 表达式在此字符串中,例如 -N 'BERKELEYDB_DIR=${pkgs.db.dev}'

--enable-tests/-T

如果您想启用生成 nix 表达式中的所有包的检查阶段,请指定此标志。请注意,此功能非常实验性,可能不会适用于您的用例。

--python-version/-V

指定您希望要求集使用构建的 Python 版本。默认为 3,对应于 nixpkgspython3 衍生。

--requirements/-r FILE

指定一个要求文件,类似于您使用 pip 时的方法。pypi2nix 尝试与 pip 的文件格式完全兼容。

--editable/-e TEXT

此选项允许您指定要添加到要求集中的单个要求,例如 pypi2nix -e attrspypi2nix -e $HOME/src/myproject#egg=myprojectpypi2nix -e .#egg=myegg

--setup-requires/-s TEXT

允许您指定需要存在于其他包构建环境中的 Python 包,一个好的例子是 setuptools-scm。请注意,pypi2nix 尝试自己检测这些依赖关系。您只需在包作者或维护者忘记在其设置中提及构建时依赖关系,或者既没有使用 setup.cfg 也没有使用 pyproject.toml 的情况下指定此标志。

--overrides/-O URL

允许您指定符合 requirements_override.nix 通用结构的额外覆盖。我们支持带有 httphttps 方案的常规 URL,以及 git。使用 https 的示例是:pypi2nix -O https://myoverrides.test/overrides.nix。从 Git 仓库重用覆盖层的方法如下:pypi2nix -O git+https://github.com/nix-community/pypi2nix.git&path=requirement_override.nix。请注意,这些覆盖将被纳入具有预先计算的哈希值的 nix 表达式中。因此,如果文件在上游发生更改,则无法再构建生成的软件包。

--default-overrides/--no-default-overrides

https://github.com/nix-community/pypi2nix-overrides 拉取覆盖。此功能默认启用。

--wheels-cache/-W TEXT

可以找到预构建 wheel 的位置。此选项最终将传递给 pip --find-links。请仅指向通过 pypi2nix 在您自己的或非常相似的系统上构建的 wheel。

--build-directory TEXT

警告 pypi2nix 中的一个错误目前阻止某些软件包使用此选项进行构建。建议不要使用此标志。

将用于构建 Python 环境以生成所需 nix 表达式的目录。如果未指定,则构建目录将是临时的,并在程序退出前被删除。

3. 当它不起作用时

希望没有人期望 pypi2nix 总是做得完美。在 Python 打包中,有太多不同的案例,我们永远无法涵盖。pypi2nix 尝试做的是让您非常接近。

有时 pypi2nix 完全失败。如果发生这种情况,请打开一个错误报告——这几乎总是 pypi2nix 中的一个错误。然而,有时 pypi2nix 成功,但生成的 requirements.nix 文件在构建您的 Python 软件包时失败。根据问题的性质,本节可能有所帮助。

非Python/系统依赖项

相当多的 Python 软件包需要在构建时存在非 Python 依赖项。这些软件包将无法构建,并显示无法找到 foo.h 或某些 fooconfig 文件的错误消息。为了解决这个问题,pypi2nix-E 选项,可以用于包含额外的非 Python 依赖项。

例如,psycopg2 需要 pg_config 二进制文件在安装时存在

% pypi2nix -v -V 2.7 -e psycopg2 -E postgresql

lxml 需要 libxml2libxslt 系统软件包

% pypi2nix -v -V 2.7 -e lxml -E libxml2 -E libxslt

额外的环境变量

某些软件包期望设置额外的环境变量

% pypi2nix -v -V 2.7 -e bsddb3 -N 'BERKELEYDB_DIR=${pkgs.db.dev}'

使用 requirements_override.nix

一些其他失败可能是因为 pypi2nix 编写的导出不完全。一个非常常见的情况是,pypi2nix 未包括某些软件包的所有依赖项。例如,execnet 依赖于 setuptools-scm,但 pypi2nix 可能无法检测到这一点。

当发生这种情况时,Nix 将无法构建 execnet,可能还会收到来自 distutils/setuptools 的错误消息,抱怨找不到 setuptools-scm 的发行版。这里发生的情况是,通常 execnet 会从 PyPI 获取 setuptools-scm,但 Nix 禁用了网络访问以保证可重现性。因此,当您构建 execnet 时,它将无法找到 setuptools-scm

对于这些情况,pypi2nix 提供了一个 requirements_override.nix 文件,允许您覆盖它生成的任何内容。您甚至可以通过这种方式添加新的软件包到依赖集合中。

例如,让我们将 setuptools-scm 添加为 execnet 的构建时依赖项。以下是 requirements_override.nix

{ pkgs, python }:

self: super: {

  "execnet" = python.overrideDerivation super."execnet" (old: {
    buildInputs = old.buildInputs ++ [ self."setuptools-scm" ];
  });

}

以类似的方式,您可以添加或删除任何 Python 软件包。

共享覆盖

除了自动生成的空 requirements_overrides.nix 文件之外,您还可以包括现有的覆盖文件。这些覆盖文件将以与您的 requirements_overrides.nix 相同的方式包含。

pypi2nix 的作者还维护了一组“默认”覆盖文件,位于 https://github.com/nix-community/pypi2nix-overrides/blob/master/overrides.nix – 您可以通过使用 --default-overrides 参数来包含这些覆盖文件 pypi2nix。这些覆盖文件设计得如此之好,以至于它们只覆盖了在您的 requirements.nix 中已经存在的依赖项。

您还可以使用 -O 命令行参数来包含一个覆盖文件。 pypi2nix 可以从本地文件或某些常见协议中获取这些覆盖文件。

httphttps

pypi2nix -V 3 --overrides https://raw.githubusercontent.com/nix-community/pypi2nix-overrides/master/overrides.nix

请注意,生成的 Nix 表达式将检查覆盖文件的内容是否与构建 Nix 表达式时的内容不同,如果不同(或文件不再存在),则失败。

本地文件

pypi2nix -V 3 --override ../some/relative/path --override /some/absolute/path

Git 仓库

pypi2nix -V 3 --override git+https://github.com/nix-community/pypi2nix.git#path=overrides.nix

如果您想从特定的 Git 仓库导入一个文件,您必须使用 git+ 作为其 URL 的前缀,这与在 piprequirements.txt 文件中执行的方式非常相似。

4. 高级使用

为您的项目创建 default.nix

没有什么比例子更能说明问题了

{ }:

let
  pkgs = import <nixpkgs> {};
  python = import ./requirements.nix { inherit pkgs; };
in python.mkDerivation {
  name = "projectA-1.0.0";
  src = ./.;
  buildInputs = [
    python.packages."coverage"
    python.packages."flake8"
    python.packages."mock"
    python.packages."pytest"
    python.packages."pytest-asyncio"
    python.packages."pytest-cov"
    python.packages."pytest-mock"
    python.packages."pytest-xdist"
  ];
  propagatedBuildInputs = [
    python.packages."aiohttp"
    python.packages."arrow"
    python.packages."defusedxml"
    python.packages."frozendict"
    python.packages."jsonschema"
  ];
  checkPhase = ''
    export NO_TESTS_OVER_WIRE=1
    export PYTHONDONTWRITEBYTECODE=1

    flake8 src/
    py.test --cov=src -cov-report term-missing
    coverage html
  '';
}

重要的是要知道,您将所有生成的软件包实例化为 python = import ./requirements.nix { inherit pkgs; };,这为您提供了一个包含 pypi2nix 生成的所有软件包以及一些常用工具的 Python 环境。

要创建软件包,您使用 python.mkDerivation,它类似于 nixpkgs 中的 pythonPackages.buildPythonPackage 函数。所有生成的软件包都作为 python.packages 下的一个属性集合可用。

pypi2nix 项目的未来目标之一是改进 nixpkgs 中 Python 工具的 UX。虽然这在 nixpkgs 中非常困难,但几乎在 nixpkgs 之外进行实验几乎是微不足道的。

将生成的 requirements.nix 转换为 nixpkgs 混合层

一个好的例子胜过千言万语。

overlay.nix

self: super:
{
  customPython =
      (import ./requirements.nix { pkgs = self; });
}

shell.nix

with (import <nixpkgs> { overlays = [ (import ./overlay.nix) ]; });
customPython.interpreter

5. 帮助开发 pypi2nix

克隆 pypi2nix 仓库,并使用 nix-shell 命令进入开发环境。

% git clone https://github.com/nix-community/pypi2nix
% cd pypi2nix
% nix-shell

代码位于 src/pypi2nix

测试

Pypi2nix 包含两种测试:单元测试和集成测试。它们分别位于 /unittests/integrationtests 文件夹中。

单元测试非常直接。它们通过 pytest 运行,并尝试遵循 pytest 最佳实践。理想情况下,pypi2nix 的所有代码都应通过单元测试。如果可能,单元测试不应上网并从互联网获取数据。如果无法避免,请使用 @nix 装饰器(在 unittests.switches 中找到)标记需要网络访问的测试。

集成测试

集成测试稍微复杂一些。我们实现了一个小型框架来编写新测试并维护旧测试。有关编写自定义集成测试的信息,请查看 integrationtests.framework。要从 scripts 目录运行所有集成测试,请运行 run_integration_tests.py。如果您使用 nix-shell 创建开发环境,则 scripts 目录应包含在您的 PATH 变量中。

请注意,所有集成测试用例都是继承自 integrationtests.framework.IntegrationTest 的类。此外,所有这些测试都必须以 TestCase 结尾,例如 MyCustomTestCase

维护脚本

scripts 文件夹包含帮助维护仓库的程序。我们期望用户已安装了 pypi2nix 构建环境中的所有软件包。如果用户选择在项目的顶级目录中进入 nix-shell,我们将 scripts 目录注册到用户的 PATH

版本升级

我们使用 bumpv 来管理此项目的当前版本。此程序应该是开发环境的一部分。

项目详细信息


下载文件

下载适合您平台的应用程序。如果您不确定选择哪一个,请了解更多关于 安装软件包 的信息。

源代码分发

pypi2nix-2.0.4.tar.gz (51.1 kB 查看散列)

上传日期 源代码

构建分发

pypi2nix-2.0.4-py3-none-any.whl (60.4 kB 查看散列)

上传日期 Python 3

由以下支持