Files
nixpkgs/pkgs/development/interpreters/python/passthrufun.nix
adisbladis 3e6a5988c7 pythonInterpreters: Add passthru.pythonABITags
As documented in https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#abi-tag.

This is useful to check whether a wheel is compatible with a certain interpreter. [Pyproject.nix](https://github.com/pyproject-nix/pyproject.nix) has [functions to perform wheel compatibility checking](https://pyproject-nix.github.io/pyproject.nix/lib/pypa.html#function-library-lib.pypa.isWheelFileCompatible) against a Python interpreter, and has computed interpreter ABI tags itself.
The recent addition of free threading (`python313FreeThreading`) complicates this by not being introspectable:
A GIL Python (non free-threaded) has an ABI tag `cp313` while the free-threaded Python has `cp313t`, but the package doesn't communicate whether `enableGIL` is true or false, leaving no way to compute the tag.
The same goes for if debugging support was added to the derivation: A `d` suffix would need to be added.

Additionally ABI tags has no defined format and can really only be accurately computed by having insight into how the ABI tags are used by a specific interpreter, meaning that the only correct place to compute ABI tags is within the context of a particular Python derivation.

While this has no immediate use within nixpkgs it could be used as a basis to provide compatibility assertions regarding wheel compat at eval time.
2025-09-22 16:36:36 +12:00

153 lines
4.3 KiB
Nix

{
lib,
stdenv,
callPackage,
pythonPackagesExtensions,
config,
makeScopeWithSplicing',
...
}:
{
implementation,
libPrefix,
executable,
sourceVersion,
pythonVersion,
packageOverrides,
sitePackages,
hasDistutilsCxxPatch,
pythonOnBuildForBuild,
pythonOnBuildForHost,
pythonOnBuildForTarget,
pythonOnHostForHost,
pythonOnTargetForTarget,
pythonAttr ? null,
pythonABITags ? [ "none" ],
self, # is pythonOnHostForTarget
}:
let
pythonPackages =
let
ensurePythonModules =
items:
let
exceptions = [
stdenv
];
providesSetupHook = lib.attrByPath [ "provides" "setupHook" ] false;
valid =
value: pythonPackages.hasPythonModule value || providesSetupHook value || lib.elem value exceptions;
func =
name: value:
if lib.isDerivation value then
lib.extendDerivation (
valid value
|| throw "${name} should use `buildPythonPackage` or `toPythonModule` if it is to be part of the Python packages set."
) { } value
else
value;
in
lib.mapAttrs func items;
in
ensurePythonModules (
callPackage
# Function that when called
# - imports python-packages.nix
# - adds spliced package sets to the package set
# - applies overrides from `packageOverrides` and `pythonPackagesOverlays`.
(
{
pkgs,
stdenv,
python,
overrides,
}:
let
pythonPackagesFun = import ./python-packages-base.nix {
inherit stdenv pkgs lib;
python = self;
};
otherSplices = {
selfBuildBuild = pythonOnBuildForBuild.pkgs;
selfBuildHost = pythonOnBuildForHost.pkgs;
selfBuildTarget = pythonOnBuildForTarget.pkgs;
selfHostHost = pythonOnHostForHost.pkgs;
selfTargetTarget = pythonOnTargetForTarget.pkgs or { }; # There is no Python TargetTarget.
};
hooks = import ./hooks/default.nix;
keep = self: hooks self { };
optionalExtensions = cond: as: lib.optionals cond as;
pythonExtension = import ../../../top-level/python-packages.nix;
python2Extension = import ../../../top-level/python2-packages.nix;
extensions = lib.composeManyExtensions (
[
hooks
pythonExtension
]
++ (optionalExtensions (!self.isPy3k) [
python2Extension
])
++ pythonPackagesExtensions
++ [
overrides
]
);
aliases =
self: super:
lib.optionalAttrs config.allowAliases (import ../../../top-level/python-aliases.nix lib self super);
in
makeScopeWithSplicing' {
inherit otherSplices keep;
f = lib.extends (lib.composeExtensions aliases extensions) pythonPackagesFun;
}
)
{
overrides = packageOverrides;
python = self;
}
);
in
rec {
isPy27 = pythonVersion == "2.7";
isPy37 = pythonVersion == "3.7";
isPy38 = pythonVersion == "3.8";
isPy39 = pythonVersion == "3.9";
isPy310 = pythonVersion == "3.10";
isPy311 = pythonVersion == "3.11";
isPy312 = pythonVersion == "3.12";
isPy313 = pythonVersion == "3.13";
isPy314 = pythonVersion == "3.14";
isPy2 = lib.strings.substring 0 1 pythonVersion == "2";
isPy3 = lib.strings.substring 0 1 pythonVersion == "3";
isPy3k = isPy3;
isPyPy = lib.hasInfix "pypy" interpreter;
buildEnv = callPackage ./wrapper.nix {
python = self;
inherit (pythonPackages) requiredPythonModules;
};
withPackages = import ./with-packages.nix { inherit buildEnv pythonPackages; };
pkgs = pythonPackages;
interpreter = "${self}/bin/${executable}";
inherit
executable
implementation
libPrefix
pythonVersion
sitePackages
;
inherit sourceVersion;
pythonAtLeast = lib.versionAtLeast pythonVersion;
pythonOlder = lib.versionOlder pythonVersion;
inherit hasDistutilsCxxPatch;
inherit pythonOnBuildForHost;
inherit pythonABITags;
tests = callPackage ./tests.nix {
python = self;
};
inherit pythonAttr;
}