python/pdm: improve implementation and UI

- implement per dependency sourceSelector option
- update pyproject.nix
- update nix-unit
This commit is contained in:
DavHau 2023-12-04 14:15:38 +07:00 committed by mergify[bot]
parent 9ce00f86e9
commit 7292ee5455
7 changed files with 133 additions and 77 deletions

View File

@ -79,11 +79,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1688870561, "lastModified": 1698974481,
"narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", "narHash": "sha256-yPncV9Ohdz1zPZxYHQf47S8S0VrnhV7nNhCawY46hDA=",
"owner": "nix-community", "owner": "nix-community",
"repo": "nix-github-actions", "repo": "nix-github-actions",
"rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", "rev": "4bb5e752616262457bc7ca5882192a564c0472d2",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -98,26 +98,40 @@
"flake-parts" "flake-parts"
], ],
"nix-github-actions": "nix-github-actions", "nix-github-actions": "nix-github-actions",
"nixpkgs": [ "nixpkgs": "nixpkgs",
"nixpkgs"
],
"treefmt-nix": "treefmt-nix" "treefmt-nix": "treefmt-nix"
}, },
"locked": { "locked": {
"lastModified": 1694670962, "lastModified": 1701194455,
"narHash": "sha256-HvMq0TJGYSx37zHm4j2d+JUZx4/6X7xKEt/0DeCiwjQ=", "narHash": "sha256-TlCq5DnIlfMFS+b/kqi0vR/8gRgVpqisOr+gjpo1VPc=",
"owner": "adisbladis", "owner": "nix-community",
"repo": "nix-unit", "repo": "nix-unit",
"rev": "3ed2378bddad85257fc508a291408f9ed9673d01", "rev": "2a061b6981c12b1a995740799a4465c3124f60ac",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "adisbladis", "owner": "nix-community",
"repo": "nix-unit", "repo": "nix-unit",
"type": "github" "type": "github"
} }
}, },
"nixpkgs": { "nixpkgs": {
"locked": {
"lastModified": 1701045352,
"narHash": "sha256-iWsDbWzBP4gotkRfg/lH2A3O9wFoJc+yVO8CDuHLRe8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "5171694860f185961daff3b1b413dabcab421300",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "master",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": { "locked": {
"lastModified": 1696604326, "lastModified": 1696604326,
"narHash": "sha256-YXUNI0kLEcI5g8lqGMb0nh67fY9f2YoJsILafh6zlMo=", "narHash": "sha256-YXUNI0kLEcI5g8lqGMb0nh67fY9f2YoJsILafh6zlMo=",
@ -159,7 +173,7 @@
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"flake-parts": "flake-parts", "flake-parts": "flake-parts",
"nix-unit": "nix-unit", "nix-unit": "nix-unit",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs_2",
"pre-commit-hooks": "pre-commit-hooks" "pre-commit-hooks": "pre-commit-hooks"
} }
}, },
@ -186,11 +200,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1689620039, "lastModified": 1699786194,
"narHash": "sha256-BtNwghr05z7k5YMdq+6nbue+nEalvDepuA7qdQMAKoQ=", "narHash": "sha256-3h3EH1FXQkIeAuzaWB+nK0XK54uSD46pp+dMD3gAcB4=",
"owner": "numtide", "owner": "numtide",
"repo": "treefmt-nix", "repo": "treefmt-nix",
"rev": "719c2977f958c41fa60a928e2fbc50af14844114", "rev": "e82f32aa7f06bbbd56d7b12186d555223dc399d1",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -18,8 +18,8 @@
pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix"; pre-commit-hooks.url = "github:cachix/pre-commit-hooks.nix";
pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs"; pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs";
nix-unit.url = "github:adisbladis/nix-unit"; nix-unit.url = "github:nix-community/nix-unit";
nix-unit.inputs.nixpkgs.follows = "nixpkgs"; # nix-unit.inputs.nixpkgs.follows = "nixpkgs";
nix-unit.inputs.flake-parts.follows = "flake-parts"; nix-unit.inputs.flake-parts.follows = "flake-parts";
devshell = { devshell = {

View File

@ -6,6 +6,9 @@
}: let }: let
libpdm = import ./lib.nix { libpdm = import ./lib.nix {
inherit lib libpyproject; inherit lib libpyproject;
python3 = config.deps.python3;
targetPlatform =
lib.systems.elaborate config.deps.python3.stdenv.targetPlatform;
}; };
libpyproject = import (dream2nix.inputs.pyproject-nix + "/lib") {inherit lib;}; libpyproject = import (dream2nix.inputs.pyproject-nix + "/lib") {inherit lib;};
@ -18,18 +21,8 @@
stdenvNoCC = config.deps.stdenvNoCC; stdenvNoCC = config.deps.stdenvNoCC;
}; };
selectWheel = files: let
wheelFiles = lib.filter libpyproject.pypa.isWheelFileName files;
wheels = map libpyproject.pypa.parseWheelFileName wheelFiles;
selected = libpyproject.pypa.selectWheels config.deps.python3.stdenv.targetPlatform config.deps.python3 wheels;
in
if lib.length selected == 0
then null
else (lib.head selected).filename;
lock_data = lib.importTOML config.pdm.lockfile; lock_data = lib.importTOML config.pdm.lockfile;
environ = libpyproject.pep508.mkEnviron config.deps.python3; environ = libpyproject.pep508.mkEnviron config.deps.python3;
selector = config.pdm.sourceSelector;
pyproject = libpdm.loadPdmPyProject (lib.importTOML config.pdm.pyproject); pyproject = libpdm.loadPdmPyProject (lib.importTOML config.pdm.pyproject);
@ -37,7 +30,7 @@
inherit environ pyproject; inherit environ pyproject;
}; };
parsed_lock_data = libpdm.parseLockData { parsed_lock_data = libpdm.parseLockData {
inherit environ lock_data selector; inherit environ lock_data;
}; };
in { in {
imports = [ imports = [
@ -47,7 +40,13 @@ in {
./interface.nix ./interface.nix
]; ];
name = pyproject.pyproject.project.name; name = pyproject.pyproject.project.name;
version = pyproject.pyproject.project.version; version = lib.mkDefault (
if pyproject.pyproject.project ? version
then pyproject.pyproject.project.version
else if lib.elem "version" pyproject.pyproject.project.dynamic or []
then "dynamic"
else "unknown"
);
deps = {nixpkgs, ...}: { deps = {nixpkgs, ...}: {
inherit inherit
(nixpkgs) (nixpkgs)
@ -59,14 +58,12 @@ in {
stdenv stdenv
; ;
}; };
pdm.sourceSelector = lib.mkDefault selectWheel; pdm.sourceSelector = lib.mkDefault libpdm.preferWheelSelector;
overrideAll = { overrideAll = {
options.sourceSelector = import ./sourceSelectorOption.nix {inherit lib;};
# TODO: per dependency selector isn't yet respected
config.sourceSelector = lib.mkOptionDefault config.pdm.sourceSelector; config.sourceSelector = lib.mkOptionDefault config.pdm.sourceSelector;
}; };
buildPythonPackage = { buildPythonPackage = {
format = "pyproject"; format = lib.mkDefault "pyproject";
}; };
mkDerivation = { mkDerivation = {
propagatedBuildInputs = propagatedBuildInputs =
@ -83,27 +80,39 @@ in {
}; };
packages = lib.flip lib.mapAttrs deps' (name: pkg: { packages = lib.flip lib.mapAttrs deps' (name: pkg: {
${pkg.version}.module = { ${pkg.version}.module = {...} @ depConfig: let
inherit name; selector =
version = pkg.version; if lib.isFunction depConfig.config.sourceSelector
then depConfig.config.sourceSelector
else if depConfig.config.sourceSelector == "wheel"
then libpdm.preferWheelSelector
else if depConfig.config.sourceSelector == "sdist"
then libpdm.preferSdistSelector
else throw "Invalid sourceSelector: ${depConfig.config.sourceSelector}";
source = pkg.sources.${selector (lib.attrNames pkg.sources)};
in {
imports = [ imports = [
./interface-dependency.nix
dream2nix.modules.dream2nix.buildPythonPackage dream2nix.modules.dream2nix.buildPythonPackage
dream2nix.modules.dream2nix.mkDerivation dream2nix.modules.dream2nix.mkDerivation
dream2nix.modules.dream2nix.package-func dream2nix.modules.dream2nix.package-func
]; ];
inherit name;
version = pkg.version;
buildPythonPackage = { buildPythonPackage = {
format = format = lib.mkDefault (
if lib.hasSuffix ".whl" pkg.source.file if lib.hasSuffix ".whl" source.file
then "wheel" then "wheel"
else "pyproject"; else "pyproject"
);
}; };
mkDerivation = { mkDerivation = {
# required: { pname, file, version, hash, kind, curlOpts ? "" }: # required: { pname, file, version, hash, kind, curlOpts ? "" }:
src = libpyproject-fetchers.fetchFromPypi { src = libpyproject-fetchers.fetchFromPypi {
pname = name; pname = name;
file = pkg.source.file; file = source.file;
version = pkg.version; version = pkg.version;
hash = pkg.source.hash; hash = source.hash;
}; };
propagatedBuildInputs = propagatedBuildInputs =
lib.forEach lib.forEach

View File

@ -0,0 +1,5 @@
{lib, ...}: {
options = {
sourceSelector = import ./sourceSelectorOption.nix {inherit lib;};
};
}

View File

@ -1,6 +1,8 @@
{ {
lib, lib,
libpyproject, libpyproject,
python3,
targetPlatform,
}: rec { }: rec {
# Get the filename from an URL. # Get the filename from an URL.
# getFilename :: String -> String # getFilename :: String -> String
@ -115,8 +117,19 @@
# Select a single wheel from a list of filenames # Select a single wheel from a list of filenames
# This assumes filtering on usable wheels has already been performed. # This assumes filtering on usable wheels has already been performed.
# selectWheel :: [String] -> String # selectWheel :: [String] -> String
selectWheel = filenames: selectWheel = files: let
lib.findFirst (x: lib.hasSuffix ".whl" x) null filenames; wheelFiles = lib.filter libpyproject.pypa.isWheelFileName files;
wheels = map libpyproject.pypa.parseWheelFileName wheelFiles;
selected =
libpyproject.pypa.selectWheels
targetPlatform
python3
wheels;
in (
if lib.length selected == 0
then null
else (lib.head selected).filename
);
# Source selectors. # Source selectors.
# Prefer to select a wheel from a list of filenames. # Prefer to select a wheel from a list of filenames.
@ -157,7 +170,6 @@
parseLockData = { parseLockData = {
lock_data, lock_data,
environ, # Output from `libpyproject.pep508.mkEnviron` environ, # Output from `libpyproject.pep508.mkEnviron`
selector,
}: let }: let
# TODO: validate against lock file version. # TODO: validate against lock file version.
parsePackage = item: let parsePackage = item: let
@ -172,7 +184,6 @@
parsedDeps = with lib.trivial; ( parsedDeps = with lib.trivial; (
map map
((flip pipe) [ ((flip pipe) [
lib.strings.toLower
libpyproject.pep508.parseString libpyproject.pep508.parseString
]) ])
item.dependencies or [] item.dependencies or []
@ -180,7 +191,7 @@
value = { value = {
dependencies = requiredDeps environ parsedDeps; dependencies = requiredDeps environ parsedDeps;
inherit (item) version; inherit (item) version;
source = sources.${selector (lib.attrNames compatibleSources)}; sources = compatibleSources;
# In the future we could add additional meta data fields # In the future we could add additional meta data fields
# such as summary # such as summary
}; };

View File

@ -1,6 +1,9 @@
{lib}: {lib}:
lib.mkOption { lib.mkOption {
type = lib.types.functionTo lib.types.str; type =
lib.types.either
(lib.types.enum ["wheel" "sdist"])
(lib.types.functionTo lib.types.str);
description = '' description = ''
A selector function which picks a source for a specific dependency A selector function which picks a source for a specific dependency
Python dependencies can have multiple possible sources, like for example: Python dependencies can have multiple possible sources, like for example:

View File

@ -1,18 +1,25 @@
{ {
pkgs ? import <nixpkgs> {}, pkgs ? import <nixpkgs> {},
lib ? import <nixpkgs/lib>, lib ? import <nixpkgs/lib>,
dream2nix ? (import (../../../modules + "/flake.nix")).outputs inputs, dream2nix ? (import ../../../.),
inputs ? (import (../../../modules + "/default.nix")).inputs, inputs ? (import ../../../.).inputs,
}: let }: let
libpdm = (import ../../../modules/dream2nix/WIP-python-pdm/lib.nix) { libpdm = (import ../../../modules/dream2nix/WIP-python-pdm/lib.nix) {
inherit lib libpyproject; inherit lib libpyproject;
python3 = pkgs.python3;
targetPlatform = lib.systems.elaborate "x86_64-linux";
}; };
pyproject-nix = inputs.pyproject-nix; pyproject-nix = inputs.pyproject-nix;
libpyproject = import (pyproject-nix + "/lib") {inherit lib;}; libpyproject = import (pyproject-nix + "/lib") {inherit lib;};
linux_environ = lib.importJSON ./environ.json; linux_environ = lib.importJSON ./environ.json;
test_isDependencyRequired = { testIsUsableSdistFilename = filename: let
environ = linux_environ;
in
libpdm.isUsableSdistFilename {inherit environ filename;};
in {
isDependencyRequired = {
test_isDependencyRequired__not_required = { test_isDependencyRequired__not_required = {
expr = expr =
libpdm.isDependencyRequired libpdm.isDependencyRequired
@ -29,12 +36,7 @@
}; };
}; };
testIsUsableSdistFilename = filename: let isUsableFilename = let
environ = linux_environ;
in
libpdm.isUsableSdistFilename {inherit environ filename;};
tests_isUsableFilename = let
testIsUsableWheelFilename = filename: let testIsUsableWheelFilename = filename: let
environ = linux_environ; environ = linux_environ;
in in
@ -51,7 +53,7 @@
}; };
}; };
tests_selectExtension = let selectExtension = let
names = [ names = [
"certifi-2023.7.22-py3-abi3-any.whl" "certifi-2023.7.22-py3-abi3-any.whl"
"certifi-2023.7.22.tar.gz" "certifi-2023.7.22.tar.gz"
@ -72,7 +74,7 @@
}; };
}; };
tests_selectSdist = let selectSdist = let
names = [ names = [
"certifi-2023.7.22-py3-abi3-any.whl" "certifi-2023.7.22-py3-abi3-any.whl"
"certifi-2023.7.22.tar.gz" "certifi-2023.7.22.tar.gz"
@ -103,7 +105,18 @@
}; };
}; };
tests_preferWheelSelector = { selectWheel = {
test_empty = {
expr = libpdm.selectWheel [];
expected = null;
};
test_simple_call = {
expr = libpdm.selectWheel ["charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl"];
expected = null;
};
};
preferWheelSelector = {
test_preferWheelSelector__has_wheel = let test_preferWheelSelector__has_wheel = let
names = [ names = [
"certifi-2023.7.22-py3-abi3-any.whl" "certifi-2023.7.22-py3-abi3-any.whl"
@ -124,7 +137,8 @@
expected = "certifi-2023.7.22.tar.gz"; expected = "certifi-2023.7.22.tar.gz";
}; };
}; };
tests_preferSdistSelector = {
preferSdistSelector = {
test_preferSdistSelector__has_sdist = let test_preferSdistSelector__has_sdist = let
names = [ names = [
"certifi-2023.7.22-py3-abi3-any.whl" "certifi-2023.7.22-py3-abi3-any.whl"
@ -145,25 +159,24 @@
expected = "certifi-2023.7.22.tar.gz"; expected = "certifi-2023.7.22.tar.gz";
}; };
}; };
tests_parseLockData = let parseLockData = let
lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock; lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock;
version = "2.31.0"; version = "2.31.0";
parsed = libpdm.parseLockData { parsed = libpdm.parseLockData {
inherit lock_data; inherit lock_data;
environ = linux_environ; environ = linux_environ;
selector = libpdm.preferWheelSelector;
}; };
in { in {
test_parseLockData = { test_parseLockData = {
expr = expr =
(parsed ? "requests") (parsed ? "requests")
&& (parsed.requests.version == version) && (parsed.requests.version == version)
&& (parsed.requests ? source); && (parsed.requests ? sources);
expected = true; expected = true;
}; };
test_parseLockData_file = { test_parseLockData_file = {
expr = parsed.requests.source.file; expr = libpdm.preferWheelSelector (lib.attrNames parsed.requests.sources);
expected = "requests-2.31.0-py3-none-any.whl"; expected = "requests-2.31.0-py3-none-any.whl";
}; };
@ -177,7 +190,7 @@
]; ];
}; };
}; };
tests_groupsWithDeps = let groupsWithDeps = let
environ = linux_environ; environ = linux_environ;
pyproject = libpdm.loadPdmPyProject (lib.importTOML ./../../../examples/packages/single-language/python-pdm/pyproject.toml); pyproject = libpdm.loadPdmPyProject (lib.importTOML ./../../../examples/packages/single-language/python-pdm/pyproject.toml);
groups_with_deps = libpdm.groupsWithDeps { groups_with_deps = libpdm.groupsWithDeps {
@ -198,12 +211,11 @@
}; };
}; };
tests_getDepsRecursively = let getDepsRecursively = let
environ = linux_environ; environ = linux_environ;
lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock; lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock;
parsed_lock_data = libpdm.parseLockData { parsed_lock_data = libpdm.parseLockData {
inherit environ lock_data; inherit environ lock_data;
selector = libpdm.preferWheelSelector;
}; };
deps = libpdm.getDepsRecursively parsed_lock_data "requests"; deps = libpdm.getDepsRecursively parsed_lock_data "requests";
in { in {
@ -221,11 +233,13 @@
urllib3 = "2.0.5"; urllib3 = "2.0.5";
}; };
}; };
test_getDepsRecursively_sources = { test_getDepsRecursively_sources = let
expr = lib.mapAttrs (key: value: value.source.file) deps; selectFilename = sources: libpdm.preferWheelSelector (lib.attrNames sources);
in {
expr = lib.mapAttrs (key: value: selectFilename value.sources) deps;
expected = { expected = {
certifi = "certifi-2023.7.22-py3-none-any.whl"; certifi = "certifi-2023.7.22-py3-none-any.whl";
charset-normalizer = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl"; charset-normalizer = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl";
idna = "idna-3.4-py3-none-any.whl"; idna = "idna-3.4-py3-none-any.whl";
requests = "requests-2.31.0-py3-none-any.whl"; requests = "requests-2.31.0-py3-none-any.whl";
urllib3 = "urllib3-2.0.5-py3-none-any.whl"; urllib3 = "urllib3-2.0.5-py3-none-any.whl";
@ -233,7 +247,7 @@
}; };
}; };
tests_selectForGroups = let selectForGroups = let
environ = linux_environ; environ = linux_environ;
pyproject = libpdm.loadPdmPyProject (lib.importTOML ./../../../examples/packages/single-language/python-pdm/pyproject.toml); pyproject = libpdm.loadPdmPyProject (lib.importTOML ./../../../examples/packages/single-language/python-pdm/pyproject.toml);
lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock; lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock;
@ -243,7 +257,6 @@
parsed_lock_data = libpdm.parseLockData { parsed_lock_data = libpdm.parseLockData {
inherit lock_data; inherit lock_data;
environ = linux_environ; environ = linux_environ;
selector = libpdm.preferWheelSelector;
}; };
deps_default = libpdm.selectForGroups { deps_default = libpdm.selectForGroups {
inherit parsed_lock_data groups_with_deps; inherit parsed_lock_data groups_with_deps;
@ -268,11 +281,13 @@
urllib3 = "2.0.5"; urllib3 = "2.0.5";
}; };
}; };
test_selectForGroups_sources = { test_selectForGroups_sources = let
expr = lib.mapAttrs (key: value: value.source.file) deps_default; selectFilename = sources: libpdm.preferWheelSelector (lib.attrNames sources);
in {
expr = lib.mapAttrs (key: value: selectFilename value.sources) deps_default;
expected = { expected = {
certifi = "certifi-2023.7.22-py3-none-any.whl"; certifi = "certifi-2023.7.22-py3-none-any.whl";
charset-normalizer = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl"; charset-normalizer = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl";
idna = "idna-3.4-py3-none-any.whl"; idna = "idna-3.4-py3-none-any.whl";
requests = "requests-2.31.0-py3-none-any.whl"; requests = "requests-2.31.0-py3-none-any.whl";
urllib3 = "urllib3-2.0.5-py3-none-any.whl"; urllib3 = "urllib3-2.0.5-py3-none-any.whl";
@ -290,5 +305,4 @@
}; };
}; };
}; };
in }
test_isDependencyRequired // tests_isUsableFilename // tests_selectExtension // tests_selectSdist // tests_preferWheelSelector // tests_preferSdistSelector // tests_parseLockData // tests_groupsWithDeps // tests_getDepsRecursively // tests_selectForGroups