python/pdm: several improvements

- infer build-systems from pyproject.toml
- add more generic getClosure lib function to get the closure of any given dependency + extra
- resolve extras correctly for each computed closure
- overhaul some of the existing tests
This commit is contained in:
DavHau 2023-12-10 12:20:04 +07:00 committed by mergify[bot]
parent 916bca75a1
commit 3f6c7cbd80
8 changed files with 557 additions and 109 deletions

View File

@ -32,6 +32,10 @@
parsed_lock_data = libpdm.parseLockData { parsed_lock_data = libpdm.parseLockData {
inherit environ lock_data; inherit environ lock_data;
}; };
buildSystemNames =
map
(name: (libpyproject.pep508.parseString name).name)
(pyproject.pyproject.build-system.requires or []);
in { in {
imports = [ imports = [
dream2nix.modules.dream2nix.WIP-groups dream2nix.modules.dream2nix.WIP-groups
@ -66,6 +70,7 @@ in {
format = lib.mkDefault "pyproject"; format = lib.mkDefault "pyproject";
}; };
mkDerivation = { mkDerivation = {
buildInputs = map (name: config.deps.python3.pkgs.${name}) buildSystemNames;
propagatedBuildInputs = propagatedBuildInputs =
map map
(x: (lib.head (lib.attrValues x)).public) (x: (lib.head (lib.attrValues x)).public)
@ -73,13 +78,16 @@ in {
(lib.attrValues config.groups.default.packages); (lib.attrValues config.groups.default.packages);
}; };
groups = let groups = let
populateGroup = groupname: deps: let groupNames = lib.attrNames groups_with_deps;
deps' = libpdm.selectForGroups { populateGroup = groupname: let
# Get transitive closure for specific group.
# The 'default' group is always included no matter the selection.
transitiveGroupDeps = libpdm.closureForGroups {
inherit parsed_lock_data groups_with_deps; inherit parsed_lock_data groups_with_deps;
groupNames = lib.unique ["default" groupname]; groupNames = lib.unique ["default" groupname];
}; };
packages = lib.flip lib.mapAttrs deps' (name: pkg: { packages = lib.flip lib.mapAttrs transitiveGroupDeps (name: pkg: {
${pkg.version}.module = {...} @ depConfig: let ${pkg.version}.module = {...} @ depConfig: let
selector = selector =
if lib.isFunction depConfig.config.sourceSelector if lib.isFunction depConfig.config.sourceSelector
@ -108,20 +116,20 @@ in {
}; };
mkDerivation = { mkDerivation = {
# required: { pname, file, version, hash, kind, curlOpts ? "" }: # required: { pname, file, version, hash, kind, curlOpts ? "" }:
src = libpyproject-fetchers.fetchFromPypi { src = lib.mkDefault (libpyproject-fetchers.fetchFromPypi {
pname = name; pname = name;
file = source.file; file = source.file;
version = pkg.version; version = pkg.version;
hash = source.hash; hash = source.hash;
}; });
propagatedBuildInputs = propagatedBuildInputs =
lib.forEach lib.mapAttrsToList
parsed_lock_data.${name}.dependencies (name: dep: (lib.head (lib.attrValues (config.groups.${groupname}.packages.${name}))).public)
(depName: (lib.head (lib.attrValues (config.groups.${groupname}.packages.${depName}))).public); (libpdm.getClosure parsed_lock_data name pkg.extras);
}; };
}; };
}); });
in {inherit packages;}; in {inherit packages;};
in in
lib.mapAttrs populateGroup groups_with_deps; lib.genAttrs groupNames populateGroup;
} }

View File

@ -156,12 +156,52 @@
then wheel then wheel
else null; else null;
# Get the dependency names out from a list of parsed deps # Get the dependency names out from a list of parsed deps which are
# required due to the current environment.
# requiredDeps :: Attrset -> [Attrset] -> [String] # requiredDeps :: Attrset -> [Attrset] -> [String]
requiredDeps = environ: parsed_deps: let requiredDeps = environ: parsed_deps: let
requiredDeps' = lib.filter (isDependencyRequired environ) parsed_deps; requiredDeps' = lib.filter (isDependencyRequired environ) parsed_deps;
in in
map (dep: dep.name) requiredDeps'; requiredDeps';
# TODO: validate against lock file version.
parsePackage = environ: item: let
sources = sourcesToAttrs item.files;
compatibleSources =
lib.filterAttrs
(
filename: source:
isUsableFilename {inherit environ filename;}
)
sources;
parsedDeps = map libpyproject.pep508.parseString item.dependencies or [];
in {
inherit (item) name version;
extras = item.extras or [];
dependencies = requiredDeps environ parsedDeps;
sources = compatibleSources;
# In the future we could add additional meta data fields
# such as summary
};
# Create a string identified for a set of extras.
mkExtrasKey = dep @ {extras ? [], ...}:
if extras == []
then "default"
else lib.concatStringsSep "," extras;
# Constructs dependency entry for internal use.
# We could use the pyproject.nix representation directly instead, but it seems
# easier to test this code if we only keep the data we need.
mkDepEntry = parsed_lock_data: dep @ {
name,
extras,
...
}: {
extras = extras;
sources = parsed_lock_data.${name}.${mkExtrasKey {inherit extras;}}.sources;
version = parsed_lock_data.${name}.${mkExtrasKey {inherit extras;}}.version;
};
# Parse lockfile data. # Parse lockfile data.
# Returns a set with package name as key # Returns a set with package name as key
@ -170,36 +210,20 @@
parseLockData = { parseLockData = {
lock_data, lock_data,
environ, # Output from `libpyproject.pep508.mkEnviron` environ, # Output from `libpyproject.pep508.mkEnviron`
}: let }:
# TODO: validate against lock file version. lib.foldl
parsePackage = item: let (acc: dep:
sources = sourcesToAttrs item.files; acc
compatibleSources = // {
lib.filterAttrs "${dep.name}" =
( acc.${dep.name}
filename: source: or {}
isUsableFilename {inherit environ filename;} // {
) "${mkExtrasKey dep}" = dep;
sources; };
parsedDeps = with lib.trivial; ( })
map {}
((flip pipe) [ (map (parsePackage environ) lock_data.package);
libpyproject.pep508.parseString
])
item.dependencies or []
);
value = {
dependencies = requiredDeps environ parsedDeps;
inherit (item) version;
sources = compatibleSources;
# In the future we could add additional meta data fields
# such as summary
};
in
lib.nameValuePair item.name value;
in
# TODO: packages need to be filtered on environment.
lib.listToAttrs (map parsePackage lock_data.package);
# Create an overview of all groups and dependencies # Create an overview of all groups and dependencies
# Keys are group names, and values lists with strings. # Keys are group names, and values lists with strings.
@ -224,31 +248,76 @@
all_groups; all_groups;
# Get a set with all transitive dependencies flattened. # Get a set with all transitive dependencies flattened.
# For every dependency we have the version, source # For every dependency we have the version, sources and extras.
# and dependencies as names. # getClosure :: Attrset -> String -> [String] -> Attrset
# getDepsRecursively :: Attrset -> String -> Attrset getClosure = parsed_lock_data: name: extras: let
getDepsRecursively = parsedLockData: name: let closure = builtins.genericClosure {
getDeps = name: let startSet = [
dep = parsedLockData.${name}; {
in key = "${name}#${mkExtrasKey {inherit extras;}}";
[{"${name}" = dep;}] ++ lib.flatten (map getDeps dep.dependencies); value = parsed_lock_data.${name}.${mkExtrasKey {inherit extras;}};
in }
lib.attrsets.mergeAttrsList (lib.unique (getDeps name)); ];
operator = item:
lib.forEach item.value.dependencies or [] (dep: {
key = "${dep.name}#${mkExtrasKey dep}";
value = parsed_lock_data.${dep.name}.${mkExtrasKey dep};
});
};
# Select the dependencies we need in our group. # mapping of all dependencies by name with merged extras.
# Here we recurse so we get a set with all dependencies. depsByNames =
# selectForGroups :: {Attrset, Attrset, String} lib.foldl'
selectForGroups = { (
acc: x: let
dep = x.value;
in
acc
// {
"${dep.name}" =
acc.${dep.name}
or {}
// {
extras =
lib.sort (x: y: x > y)
(lib.unique (acc.${dep.name}.extras or [] ++ dep.extras));
version = dep.version;
sources = dep.sources;
};
}
)
{}
closure;
in
# remove self references from the closure to prevent cycles
builtins.removeAttrs depsByNames [name];
# Compute the dependency closure for the given groups.
# closureForGroups :: {Attrset, Attrset, String}
closureForGroups = {
parsed_lock_data, parsed_lock_data,
groups_with_deps, groups_with_deps,
groupNames, groupNames,
}: let }: let
# List of top-level package names we need. # List of all top-level dependencies for the given groups.
deps_top_level = deps_top_level =
lib.concatMap lib.concatMap
(groupName: groups_with_deps.${groupName}) (groupName: groups_with_deps.${groupName})
groupNames; groupNames;
getDeps = getDepsRecursively parsed_lock_data; # Top-level dependencies in expected format.
# Children are already returned in correct format by 'getClosure'.
topLevelEntries =
map
(dep: {
name = dep.name;
value = mkDepEntry parsed_lock_data dep;
})
deps_top_level;
# helper to get the closure for a single dependency.
getClosureForDep = dep: getClosure parsed_lock_data dep.name dep.extras;
in in
lib.attrsets.mergeAttrsList (map getDeps deps_top_level); # top-level dependencies
(lib.listToAttrs topLevelEntries)
# transitive dependencies
// (lib.attrsets.mergeAttrsList (map getClosureForDep deps_top_level));
} }

View File

@ -82,11 +82,11 @@ in {
"certifi-2023.7.22.zip" "certifi-2023.7.22.zip"
]; ];
in { in {
test_selectSdist__tar_gz = { test_tar_gz = {
expr = libpdm.selectSdist names; expr = libpdm.selectSdist names;
expected = "certifi-2023.7.22.tar.gz"; expected = "certifi-2023.7.22.tar.gz";
}; };
test_selectSdist__no_sdist = let test_no_sdist = let
names = [ names = [
"certifi-2023.7.22-py3-abi3-any.whl" "certifi-2023.7.22-py3-abi3-any.whl"
]; ];
@ -94,7 +94,7 @@ in {
expr = libpdm.selectSdist names; expr = libpdm.selectSdist names;
expected = null; expected = null;
}; };
test_selectSdist__order = let test_order = let
names = [ names = [
"certifi-2023.7.22.zip" "certifi-2023.7.22.zip"
"certifi-2023.7.22.tar.gz" "certifi-2023.7.22.tar.gz"
@ -117,7 +117,7 @@ in {
}; };
preferWheelSelector = { preferWheelSelector = {
test_preferWheelSelector__has_wheel = let test_has_wheel = 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"
@ -127,7 +127,7 @@ in {
expr = libpdm.preferWheelSelector names; expr = libpdm.preferWheelSelector names;
expected = "certifi-2023.7.22-py3-abi3-any.whl"; expected = "certifi-2023.7.22-py3-abi3-any.whl";
}; };
test_preferWheelSelector__only_sdist = let test_only_sdist = let
names = [ names = [
"certifi-2023.7.22.zip" "certifi-2023.7.22.zip"
"certifi-2023.7.22.tar.gz" "certifi-2023.7.22.tar.gz"
@ -139,7 +139,7 @@ in {
}; };
preferSdistSelector = { preferSdistSelector = {
test_preferSdistSelector__has_sdist = let test_has_sdist = 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"
@ -149,7 +149,7 @@ in {
expr = libpdm.preferSdistSelector names; expr = libpdm.preferSdistSelector names;
expected = "certifi-2023.7.22.tar.gz"; expected = "certifi-2023.7.22.tar.gz";
}; };
test_preferSdistSelectorr__only_sdist = let test_only_sdist = let
names = [ names = [
"certifi-2023.7.22.zip" "certifi-2023.7.22.zip"
"certifi-2023.7.22.tar.gz" "certifi-2023.7.22.tar.gz"
@ -159,29 +159,90 @@ in {
expected = "certifi-2023.7.22.tar.gz"; expected = "certifi-2023.7.22.tar.gz";
}; };
}; };
parsePackage = {
test_simple = {
expr = libpdm.parsePackage linux_environ {
name = "foo";
version = "1.0.0";
extras = ["extra1" "extra2"];
requires_python = ">=3.9";
files = [
{
file = "foo-1.0.0-py3-none-any.whl";
hash = "sha256:foo";
}
{
file = "foo-1.0.0.tar.gz";
hash = "sha256:bar";
}
];
dependencies = [
"bar[security,performance]==1.0.0"
];
};
expected = {
name = "foo";
version = "1.0.0";
extras = ["extra1" "extra2"];
dependencies = [
{
conditions = [
{
op = "==";
version = {
dev = null;
epoch = 0;
local = null;
post = null;
pre = null;
release = [1 0 0];
};
}
];
extras = ["security" "performance"];
markers = null;
name = "bar";
url = null;
}
];
sources = {
"foo-1.0.0-py3-none-any.whl" = {
file = "foo-1.0.0-py3-none-any.whl";
hash = "sha256:foo";
};
"foo-1.0.0.tar.gz" = {
file = "foo-1.0.0.tar.gz";
hash = "sha256:bar";
};
};
};
};
};
parseLockData = let parseLockData = let
lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock; lock_data = lib.importTOML ./fixtures/pdm-example1.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;
}; };
in { in {
test_parseLockData = { test_simple = {
expr = expr =
(parsed ? "requests") (parsed ? "requests")
&& (parsed.requests.version == version) && (parsed.requests.default.version == version)
&& (parsed.requests ? sources); && (parsed.requests.default ? sources);
expected = true; expected = true;
}; };
test_parseLockData_file = { test_file = {
expr = libpdm.preferWheelSelector (lib.attrNames parsed.requests.sources); expr = libpdm.preferWheelSelector (lib.attrNames parsed.requests.default.sources);
expected = "requests-2.31.0-py3-none-any.whl"; expected = "requests-2.31.0-py3-none-any.whl";
}; };
test_parseLockData_dependencies = { test_dependencies = {
expr = parsed.requests.dependencies; expr = map (dep: dep.name) parsed.requests.default.dependencies;
expected = [ expected = [
"certifi" "certifi"
"charset-normalizer" "charset-normalizer"
@ -189,68 +250,150 @@ in {
"urllib3" "urllib3"
]; ];
}; };
test_candidates_with_different_extras = rec {
expr = libpdm.parseLockData {
environ = linux_environ;
lock_data = {
package = [
{
name = "foo";
version = "1.0.0";
extras = [];
dependencies = [
"dep1==1.0.0"
];
files = [
{
file = "foo-1.0.0.tar.gz";
hash = "sha256:bar";
}
];
}
{
name = "foo";
version = "1.0.0";
extras = ["extra1" "extra2"];
dependencies = [
"extradep1==1.0.0"
];
files = [
{
file = "foo-1.0.0.tar.gz";
hash = "sha256:bar";
}
];
}
];
};
};
expected = {
foo = {
default = {
name = "foo";
version = "1.0.0";
extras = [];
dependencies = expr.foo.default.dependencies;
sources = expr.foo.default.sources;
};
"extra1,extra2" = {
name = "foo";
version = "1.0.0";
extras = ["extra1" "extra2"];
dependencies = expr.foo."extra1,extra2".dependencies;
sources = expr.foo."extra1,extra2".sources;
};
};
};
};
}; };
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 ./fixtures/pyproject.toml);
groups_with_deps = libpdm.groupsWithDeps { groups_with_deps = libpdm.groupsWithDeps {
inherit environ pyproject; inherit environ pyproject;
}; };
in { in {
test_groupsWithDeps__has_main_group = { test_has_main_group = {
expr = groups_with_deps ? "default"; expr = groups_with_deps ? "default";
expected = true; expected = true;
}; };
test_groupsWithDeps__main_group_has_deps = { test_main_group_has_deps = {
expr = groups_with_deps.default; expr = map (dep: dep.name) groups_with_deps.default;
expected = ["requests"]; expected = ["requests"];
}; };
test_groupsWithDeps__optionals_dev_has_deps = { test_optionals_dev_has_deps = {
expr = groups_with_deps.dev; expr = map (dep: dep.name) groups_with_deps.dev;
expected = ["pi"]; expected = ["pi"];
}; };
}; };
getDepsRecursively = let getClosure = let
environ = linux_environ; environ = linux_environ;
lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock; lock_data_2 = lib.importTOML ./fixtures/pdm-example1.lock;
parsed_lock_data = libpdm.parseLockData { parsed_lock_data_2 = libpdm.parseLockData {
inherit environ lock_data; inherit environ;
lock_data = lock_data_2;
}; };
deps = libpdm.getDepsRecursively parsed_lock_data "requests"; in rec {
in { deps = libpdm.getClosure parsed_lock_data_2 "requests" [];
test_getDepsRecursively_names = {
expr = lib.attrNames deps; test_versions = {
expected = ["certifi" "charset-normalizer" "idna" "requests" "urllib3"]; expr = lib.mapAttrs (name: dep: dep.version) deps;
};
test_getDepsRecursively_versions = {
expr = lib.mapAttrs (key: value: value.version) deps;
expected = { expected = {
certifi = "2023.7.22"; certifi = "2023.7.22";
charset-normalizer = "3.2.0"; charset-normalizer = "3.2.0";
idna = "3.4"; idna = "3.4";
requests = "2.31.0";
urllib3 = "2.0.5"; urllib3 = "2.0.5";
}; };
}; };
test_getDepsRecursively_sources = let
test_sources = let
selectFilename = sources: libpdm.preferWheelSelector (lib.attrNames sources); selectFilename = sources: libpdm.preferWheelSelector (lib.attrNames sources);
in { in {
expr = lib.mapAttrs (key: value: selectFilename value.sources) deps; expr = lib.mapAttrs (name: dep: selectFilename dep.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-manylinux_2_17_x86_64.manylinux2014_x86_64.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";
urllib3 = "urllib3-2.0.5-py3-none-any.whl"; urllib3 = "urllib3-2.0.5-py3-none-any.whl";
}; };
}; };
test_no_cycles = let
lock_data = lib.importTOML ./fixtures/pdm-with-cycles.lock;
parsed_lock_data = libpdm.parseLockData {
inherit environ lock_data;
};
deps = libpdm.getClosure parsed_lock_data "pyjwt" ["crypto"];
in {
expr = lib.mapAttrs (name: dep: dep.version) deps;
expected = {
cffi = "1.16.0";
cryptography = "41.0.7";
pycparser = "2.21";
};
};
test_closure_collects_all_extras = let
lock_data = lib.importTOML ./fixtures/pdm-extras.lock;
parsed_lock_data = libpdm.parseLockData {
inherit environ lock_data;
};
deps = libpdm.getClosure parsed_lock_data "root" [];
in {
expr.foo = deps.foo.extras;
expr.bar = deps.bar.extras;
expected = {
foo = ["extra1"];
bar = ["extra2"];
};
};
}; };
selectForGroups = let closureForGroups = 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 ./fixtures/pyproject.toml);
lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock; lock_data = lib.importTOML ./fixtures/pdm-example1.lock;
groups_with_deps = libpdm.groupsWithDeps { groups_with_deps = libpdm.groupsWithDeps {
inherit environ pyproject; inherit environ pyproject;
}; };
@ -258,20 +401,20 @@ in {
inherit lock_data; inherit lock_data;
environ = linux_environ; environ = linux_environ;
}; };
deps_default = libpdm.selectForGroups { deps_default = libpdm.closureForGroups {
inherit parsed_lock_data groups_with_deps; inherit parsed_lock_data groups_with_deps;
groupNames = ["default"]; groupNames = ["default"];
}; };
deps_dev = libpdm.selectForGroups { deps_dev = libpdm.closureForGroups {
inherit parsed_lock_data groups_with_deps; inherit parsed_lock_data groups_with_deps;
groupNames = ["default" "dev"]; groupNames = ["default" "dev"];
}; };
in { in {
test_selectForGroups_names = { test_names = {
expr = lib.attrNames deps_default; expr = lib.attrNames deps_default;
expected = ["certifi" "charset-normalizer" "idna" "requests" "urllib3"]; expected = ["certifi" "charset-normalizer" "idna" "requests" "urllib3"];
}; };
test_selectForGroups_versions = { test_versions = {
expr = lib.mapAttrs (key: value: value.version) deps_default; expr = lib.mapAttrs (key: value: value.version) deps_default;
expected = { expected = {
certifi = "2023.7.22"; certifi = "2023.7.22";
@ -281,7 +424,7 @@ in {
urllib3 = "2.0.5"; urllib3 = "2.0.5";
}; };
}; };
test_selectForGroups_sources = let test_closureForGroups_sources = let
selectFilename = sources: libpdm.preferWheelSelector (lib.attrNames sources); selectFilename = sources: libpdm.preferWheelSelector (lib.attrNames sources);
in { in {
expr = lib.mapAttrs (key: value: selectFilename value.sources) deps_default; expr = lib.mapAttrs (key: value: selectFilename value.sources) deps_default;
@ -293,7 +436,7 @@ in {
urllib3 = "urllib3-2.0.5-py3-none-any.whl"; urllib3 = "urllib3-2.0.5-py3-none-any.whl";
}; };
}; };
test_selectForGroups_dev_versions = { test_dev_versions = {
expr = lib.mapAttrs (key: value: value.version) deps_dev; expr = lib.mapAttrs (key: value: value.version) deps_dev;
expected = { expected = {
certifi = "2023.7.22"; certifi = "2023.7.22";

View File

@ -0,0 +1,103 @@
# This file is @generated by PDM.
# It is not intended for manual editing.
[metadata]
groups = ["default", "dev"]
cross_platform = true
static_urls = false
lock_version = "4.3"
content_hash = "sha256:6b124fd8499d7ddefb13cb60a64760b0601a8db060263247076bc4f4309cd6cd"
[[package]]
name = "certifi"
version = "2023.7.22"
requires_python = ">=3.6"
summary = "Python package for providing Mozilla's CA Bundle."
files = [
{file = "certifi-2023.7.22-py3-none-any.whl", hash = "sha256:92d6037539857d8206b8f6ae472e8b77db8058fec5937a1ef3f54304089edbb9"},
{file = "certifi-2023.7.22.tar.gz", hash = "sha256:539cc1d13202e33ca466e88b2807e29f4c13049d6d87031a3c110744495cb082"},
]
[[package]]
name = "charset-normalizer"
version = "3.2.0"
requires_python = ">=3.7.0"
summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
files = [
{file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"},
{file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"},
{file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"},
{file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"},
{file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"},
{file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"},
{file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"},
{file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"},
{file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"},
{file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"},
{file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"},
{file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"},
]
[[package]]
name = "idna"
version = "3.4"
requires_python = ">=3.5"
summary = "Internationalized Domain Names in Applications (IDNA)"
files = [
{file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"},
{file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"},
]
[[package]]
name = "pi"
version = "0.1.2"
summary = "Simpler python package installation"
files = [
{file = "pi-0.1.2.tar.gz", hash = "sha256:8d503a790317f7fbce7469c4160e44b1c76492571b2b0c0596636a1794800f75"},
]
[[package]]
name = "requests"
version = "2.31.0"
requires_python = ">=3.7"
summary = "Python HTTP for Humans."
dependencies = [
"certifi>=2017.4.17",
"charset-normalizer<4,>=2",
"idna<4,>=2.5",
"urllib3<3,>=1.21.1",
]
files = [
{file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"},
{file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"},
]
[[package]]
name = "urllib3"
version = "2.0.5"
requires_python = ">=3.7"
summary = "HTTP library with thread-safe connection pooling, file post, and more."
files = [
{file = "urllib3-2.0.5-py3-none-any.whl", hash = "sha256:ef16afa8ba34a1f989db38e1dbbe0c302e4289a47856990d0682e374563ce35e"},
{file = "urllib3-2.0.5.tar.gz", hash = "sha256:13abf37382ea2ce6fb744d4dad67838eec857c9f4f57009891805e0b5e123594"},
]

View File

@ -0,0 +1,39 @@
# Tests: When 'root' is resolved,
[metadata]
groups = ["default"]
strategy = ["cross_platform"]
lock_version = "4.4"
[[package]]
name = "foo"
version = "1.0.0"
files = []
[[package]]
name = "foo"
extras = ["extra1"]
version = "1.0.0"
files = []
[[package]]
name = "bar"
version = "1.0.0"
files = []
[[package]]
name = "bar"
extras = ["extra2"]
version = "1.0.0"
files = []
[[package]]
name = "root"
version = "1.0.0"
dependencies = [
"foo[extra1]==1.0.0",
"foo==1.0.0",
"bar==1.0.0",
"bar[extra2]==1.0.0"
]
files = []

View File

@ -0,0 +1,64 @@
# This file is @generated by PDM.
# It is not intended for manual editing.
[metadata]
groups = ["default", "default", "dev"]
strategy = ["cross_platform"]
lock_version = "4.4"
content_hash = "sha256:ac27f396f238ef001a11c5abacfd9412eafa638da66cfe9d079176e5c2b30c31"
[[package]]
name = "pyjwt"
version = "2.8.0"
requires_python = ">=3.7"
summary = "JSON Web Token implementation in Python"
files = [
{file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"},
]
[[package]]
name = "pyjwt"
version = "2.8.0"
extras = ["crypto"]
requires_python = ">=3.7"
summary = "JSON Web Token implementation in Python"
dependencies = [
"cryptography>=3.4.0",
"pyjwt==2.8.0",
]
files = [
{file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"},
]
[[package]]
name = "cffi"
version = "1.16.0"
requires_python = ">=3.8"
summary = "Foreign Function Interface for Python calling C code."
dependencies = [
"pycparser",
]
files = [
{file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"},
]
[[package]]
name = "cryptography"
version = "41.0.7"
requires_python = ">=3.7"
summary = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
dependencies = [
"cffi>=1.12",
]
files = [
{file = "cryptography-41.0.7.tar.gz", hash = "sha256:13f93ce9bea8016c253b34afc6bd6a75993e5c40672ed5405a9c832f0d4a00bc"},
]
[[package]]
name = "pycparser"
version = "2.21"
requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
summary = "C parser in Python"
files = [
{file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"},
]

View File

@ -0,0 +1,23 @@
[project]
name = "my-project"
version = "0.1.0"
description = ""
dependencies = [
"requests>=2.31.0",
]
requires-python = ">=3.10"
readme = "README.md"
license = {text = "MIT"}
[project.scripts]
my-script = "my_project:main"
[project.optional-dependencies]
dev = [
"pi>=0.1.2",
]
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"

View File

@ -1,8 +1,8 @@
{ {
pkgs ? import <nixpkgs> {}, pkgs ? import <nixpkgs> {},
lib ? import <nixpkgs/lib>, lib ? import <nixpkgs/lib>,
inputs ? {}, dream2nix ? import ../../../. inputs,
dream2nix ? import ../../.. inputs, inputs ? (import ../../../.).inputs,
}: let }: let
eval = module: eval = module:
(lib.evalModules { (lib.evalModules {
@ -19,9 +19,8 @@
in { in {
test_pdm = let test_pdm = let
config = eval { config = eval {
# TODO: create fixtures pdm.lockfile = ./../test_python-pdm-lib/fixtures/pdm-example1.lock;
pdm.lockfile = ./../../../examples/packages/single-language/python-pdm/pdm.lock; pdm.pyproject = ./../test_python-pdm-lib/fixtures/pyproject.toml;
pdm.pyproject = ./../../../examples/packages/single-language/python-pdm/pyproject.toml;
}; };
in { in {
expr = (lib.head (lib.attrValues config.groups.default.packages.certifi)).public ? drvPath; expr = (lib.head (lib.attrValues config.groups.default.packages.certifi)).public ? drvPath;