nixpkgs覆盖Python包未在systemPackages生效的解决方法
当在nixpkgs.config.packageOverrides或nixpkgs.overlays中覆盖python3包时,environment.systemPackages内的Python应用并未使用这些覆盖配置,如何才能让被覆盖的python3包被对应Python3应用正确引用?
以下为使用nixpkgs.overlays的darwin-configuration.nix示例配置,目标为移除pyopenssl依赖,规避darwin-aarch64平台下pyopenssl标记为损坏、无法评估的问题,同时自定义myawscli2包传入覆盖后的python3:
{ config, pkgs, lib, ... }: { environment.systemPackages = [ pkgs.myawscli2 ]; nixpkgs.overlays = let overlayRemovePyopenssl = self: super: let removePyopenssl = pythonpkgs: lib.filter (pythonpkg: !(pythonpkg != null && pythonpkg ? pname && pythonpkg.pname == "pyopenssl")) pythonpkgs; in { python3 = super.python3.override { packageOverrides = python-self: python-super: rec { # Delete pyopenssl; workaround for broken package on darwin-aarch64 # “Package ‘python3.10-pyopenssl-22.0.0’ in /nix/store/<hash>-nixpkgs/nixpkgs/pkgs/development/python-modules/pyopenssl/default.nix:73 is marked as broken, refusing to evaluate” urllib3 = python-super.urllib3.overridePythonAttrs (origattrs: rec { propagatedBuildInputs = removePyopenssl origattrs.propagatedBuildInputs; }); twisted = python-super.twisted.overridePythonAttrs (origattrs: { checkInputs = removePyopenssl origattrs.checkInputs; }); }; }; myawscli2 = (self.awscli2.override { # override the python3 arg of awscli2 python3 = self.python3; }); }; in [ overlayRemovePyopenssl ]; system.stateVersion = 4; }
经测试,独立Python包urllib3已正确应用overlay配置:
nix-repl> lib = import <nixpkgs>.lib nix-repl> :l <darwin> nix-repl> lib.forEach pkgs.python3.pkgs.urllib3.propagatedBuildInputs (x: x.pname) [ "brotli" "certifi" "cryptography" "idna" "python3" ]
但使用urllib3的Python应用并未应用该覆盖,awscli2依赖的urllib3传递依赖中仍存在pyopenssl:
nix-repl> lib.forEach (lib.findFirst (x: x.pname == "urllib3") null pkgs.awscli2.propagatedBuildInputs).propagatedBuildInputs (x: x.pname) [ "brotli" "certifi" "cryptography" "idna" "pyopenssl" "python3" ]
尝试使用nixpkgs.config.packageOverrides实现相同配置,得到了完全一致的结果:
{ config, pkgs, lib, ... }: { environment.systemPackages = [ pkgs.myawscli2 ]; nixpkgs.config.packageOverrides = super: let removePyopenssl = pythonpkgs: lib.filter (pythonpkg: !(pythonpkg != null && lib.hasAttr "pname" pythonpkg && pythonpkg.pname == "pyopenssl")) pythonpkgs; in { python3 = super.python3.override { packageOverrides = python-self: python-super: rec { # workaround for # “Package ‘python3.10-pyopenssl-22.0.0’ in /nix/store/<hash>-nixpkgs/nixpkgs/pkgs/development/python-modules/pyopenssl/default.nix:73 is marked as broken, refusing to evaluate” urllib3 = python-super.urllib3.overridePythonAttrs (origattrs: rec { propagatedBuildInputs = removePyopenssl origattrs.propagatedBuildInputs; }); twisted = python-super.twisted.overridePythonAttrs (origattrs: { checkInputs = removePyopenssl origattrs.checkInputs; }); }; }; myawscli2 = (pkgs.awscli2.override { python3 = pkgs.python3; }); }; system.stateVersion = 4; }
该现象与nixpkgs官方手册描述存在矛盾,手册说明:pythonPackages.twisted现在已被全局覆盖,所有引用twisted的软件包以及NixOS服务(例如services.buildbot-worker)都将使用新的定义。
问题核心是Python包集合的引用闭包没有被完全替换:
- 通过
override创建新的python3实例、修改该实例下的包集合后,nixpkgs中预置的awscli2这类Python应用,在打包时已经把依赖从原始(super层)的Python包集合中固定捕获,单纯给应用传python3 = self.python3参数,只会替换运行时调用的解释器路径,不会自动重写整个Python依赖闭包。 - 仅覆盖顶层的
python3属性,没有同步覆盖顶层的python3Packages属性,很多Python应用实际是直接从顶层python3Packages取依赖,不会主动从传入的python3实例下的pkgs集合拉取修改后的包。
正确的overlay写法需要保证所有Python相关属性都引用修改后的包集合,参考如下配置:
{ config, pkgs, lib, ... }: { environment.systemPackages = [ pkgs.myawscli2 ]; nixpkgs.overlays = let overlayRemovePyopenssl = self: super: let removePyopenssl = pythonpkgs: lib.filter (pythonpkg: !(pythonpkg != null && pythonpkg ? pname && pythonpkg.pname == "pyopenssl")) pythonpkgs; # 先定义修改后的Python解释器及对应包集合 customPython3 = super.python3.override { packageOverrides = python-self: python-super: rec { urllib3 = python-super.urllib3.overridePythonAttrs (origattrs: rec { propagatedBuildInputs = removePyopenssl origattrs.propagatedBuildInputs; }); twisted = python-super.twisted.overridePythonAttrs (origattrs: { checkInputs = removePyopenssl origattrs.checkInputs; }); }; }; in { # 同步覆盖顶层python3和python3Packages,保证全局引用一致 python3 = customPython3; python3Packages = customPython3.pkgs; # 基于修改后的Python包集合重新构建awscli2,不要只传python3参数 myawscli2 = customPython3.pkgs.awscli2; }; in [ overlayRemovePyopenssl ]; system.stateVersion = 4; }
配置生效后可以重新验证:pkgs.myawscli2依赖的urllib3将不再带有pyopenssl传递依赖,和全局修改的python3.pkgs.urllib3保持一致。
如果是用nixpkgs.config.packageOverrides的场景,逻辑完全相同:同步覆盖python3和python3Packages两个顶层属性,再基于自定义Python的包集合构建目标应用即可。
内容的提问来源于stack exchange,提问作者yonran

