From 3f6c7cbd80ed508731c91c33a2a9b592a9132ab6 Mon Sep 17 00:00:00 2001 From: DavHau Date: Sun, 10 Dec 2023 12:20:04 +0700 Subject: [PATCH] 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 --- modules/dream2nix/WIP-python-pdm/default.nix | 26 +- modules/dream2nix/WIP-python-pdm/lib.nix | 167 +++++++++---- .../nix-unit/test_python-pdm-lib/default.nix | 235 ++++++++++++++---- .../fixtures/pdm-example1.lock | 103 ++++++++ .../fixtures/pdm-extras.lock | 39 +++ .../fixtures/pdm-with-cycles.lock | 64 +++++ .../fixtures/pyproject.toml | 23 ++ tests/nix-unit/test_python-pdm/default.nix | 9 +- 8 files changed, 557 insertions(+), 109 deletions(-) create mode 100644 tests/nix-unit/test_python-pdm-lib/fixtures/pdm-example1.lock create mode 100644 tests/nix-unit/test_python-pdm-lib/fixtures/pdm-extras.lock create mode 100644 tests/nix-unit/test_python-pdm-lib/fixtures/pdm-with-cycles.lock create mode 100644 tests/nix-unit/test_python-pdm-lib/fixtures/pyproject.toml diff --git a/modules/dream2nix/WIP-python-pdm/default.nix b/modules/dream2nix/WIP-python-pdm/default.nix index 51f76621..413de4be 100644 --- a/modules/dream2nix/WIP-python-pdm/default.nix +++ b/modules/dream2nix/WIP-python-pdm/default.nix @@ -32,6 +32,10 @@ parsed_lock_data = libpdm.parseLockData { inherit environ lock_data; }; + buildSystemNames = + map + (name: (libpyproject.pep508.parseString name).name) + (pyproject.pyproject.build-system.requires or []); in { imports = [ dream2nix.modules.dream2nix.WIP-groups @@ -66,6 +70,7 @@ in { format = lib.mkDefault "pyproject"; }; mkDerivation = { + buildInputs = map (name: config.deps.python3.pkgs.${name}) buildSystemNames; propagatedBuildInputs = map (x: (lib.head (lib.attrValues x)).public) @@ -73,13 +78,16 @@ in { (lib.attrValues config.groups.default.packages); }; groups = let - populateGroup = groupname: deps: let - deps' = libpdm.selectForGroups { + groupNames = lib.attrNames groups_with_deps; + 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; 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 selector = if lib.isFunction depConfig.config.sourceSelector @@ -108,20 +116,20 @@ in { }; mkDerivation = { # required: { pname, file, version, hash, kind, curlOpts ? "" }: - src = libpyproject-fetchers.fetchFromPypi { + src = lib.mkDefault (libpyproject-fetchers.fetchFromPypi { pname = name; file = source.file; version = pkg.version; hash = source.hash; - }; + }); propagatedBuildInputs = - lib.forEach - parsed_lock_data.${name}.dependencies - (depName: (lib.head (lib.attrValues (config.groups.${groupname}.packages.${depName}))).public); + lib.mapAttrsToList + (name: dep: (lib.head (lib.attrValues (config.groups.${groupname}.packages.${name}))).public) + (libpdm.getClosure parsed_lock_data name pkg.extras); }; }; }); in {inherit packages;}; in - lib.mapAttrs populateGroup groups_with_deps; + lib.genAttrs groupNames populateGroup; } diff --git a/modules/dream2nix/WIP-python-pdm/lib.nix b/modules/dream2nix/WIP-python-pdm/lib.nix index 2e9caf85..8ef52754 100644 --- a/modules/dream2nix/WIP-python-pdm/lib.nix +++ b/modules/dream2nix/WIP-python-pdm/lib.nix @@ -156,12 +156,52 @@ then wheel 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 = environ: parsed_deps: let requiredDeps' = lib.filter (isDependencyRequired environ) parsed_deps; 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. # Returns a set with package name as key @@ -170,36 +210,20 @@ parseLockData = { lock_data, environ, # Output from `libpyproject.pep508.mkEnviron` - }: let - # TODO: validate against lock file version. - parsePackage = item: let - sources = sourcesToAttrs item.files; - compatibleSources = - lib.filterAttrs - ( - filename: source: - isUsableFilename {inherit environ filename;} - ) - sources; - parsedDeps = with lib.trivial; ( - map - ((flip pipe) [ - 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); + }: + lib.foldl + (acc: dep: + acc + // { + "${dep.name}" = + acc.${dep.name} + or {} + // { + "${mkExtrasKey dep}" = dep; + }; + }) + {} + (map (parsePackage environ) lock_data.package); # Create an overview of all groups and dependencies # Keys are group names, and values lists with strings. @@ -224,31 +248,76 @@ all_groups; # Get a set with all transitive dependencies flattened. - # For every dependency we have the version, source - # and dependencies as names. - # getDepsRecursively :: Attrset -> String -> Attrset - getDepsRecursively = parsedLockData: name: let - getDeps = name: let - dep = parsedLockData.${name}; - in - [{"${name}" = dep;}] ++ lib.flatten (map getDeps dep.dependencies); - in - lib.attrsets.mergeAttrsList (lib.unique (getDeps name)); + # For every dependency we have the version, sources and extras. + # getClosure :: Attrset -> String -> [String] -> Attrset + getClosure = parsed_lock_data: name: extras: let + closure = builtins.genericClosure { + startSet = [ + { + key = "${name}#${mkExtrasKey {inherit extras;}}"; + value = parsed_lock_data.${name}.${mkExtrasKey {inherit extras;}}; + } + ]; + 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. - # Here we recurse so we get a set with all dependencies. - # selectForGroups :: {Attrset, Attrset, String} - selectForGroups = { + # mapping of all dependencies by name with merged extras. + depsByNames = + lib.foldl' + ( + 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, groups_with_deps, groupNames, }: let - # List of top-level package names we need. + # List of all top-level dependencies for the given groups. deps_top_level = lib.concatMap (groupName: groups_with_deps.${groupName}) 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 - lib.attrsets.mergeAttrsList (map getDeps deps_top_level); + # top-level dependencies + (lib.listToAttrs topLevelEntries) + # transitive dependencies + // (lib.attrsets.mergeAttrsList (map getClosureForDep deps_top_level)); } diff --git a/tests/nix-unit/test_python-pdm-lib/default.nix b/tests/nix-unit/test_python-pdm-lib/default.nix index a8430b41..43088c0b 100644 --- a/tests/nix-unit/test_python-pdm-lib/default.nix +++ b/tests/nix-unit/test_python-pdm-lib/default.nix @@ -82,11 +82,11 @@ in { "certifi-2023.7.22.zip" ]; in { - test_selectSdist__tar_gz = { + test_tar_gz = { expr = libpdm.selectSdist names; expected = "certifi-2023.7.22.tar.gz"; }; - test_selectSdist__no_sdist = let + test_no_sdist = let names = [ "certifi-2023.7.22-py3-abi3-any.whl" ]; @@ -94,7 +94,7 @@ in { expr = libpdm.selectSdist names; expected = null; }; - test_selectSdist__order = let + test_order = let names = [ "certifi-2023.7.22.zip" "certifi-2023.7.22.tar.gz" @@ -117,7 +117,7 @@ in { }; preferWheelSelector = { - test_preferWheelSelector__has_wheel = let + test_has_wheel = let names = [ "certifi-2023.7.22-py3-abi3-any.whl" "certifi-2023.7.22.tar.gz" @@ -127,7 +127,7 @@ in { expr = libpdm.preferWheelSelector names; expected = "certifi-2023.7.22-py3-abi3-any.whl"; }; - test_preferWheelSelector__only_sdist = let + test_only_sdist = let names = [ "certifi-2023.7.22.zip" "certifi-2023.7.22.tar.gz" @@ -139,7 +139,7 @@ in { }; preferSdistSelector = { - test_preferSdistSelector__has_sdist = let + test_has_sdist = let names = [ "certifi-2023.7.22-py3-abi3-any.whl" "certifi-2023.7.22.tar.gz" @@ -149,7 +149,7 @@ in { expr = libpdm.preferSdistSelector names; expected = "certifi-2023.7.22.tar.gz"; }; - test_preferSdistSelectorr__only_sdist = let + test_only_sdist = let names = [ "certifi-2023.7.22.zip" "certifi-2023.7.22.tar.gz" @@ -159,29 +159,90 @@ in { 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 - lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock; + lock_data = lib.importTOML ./fixtures/pdm-example1.lock; version = "2.31.0"; parsed = libpdm.parseLockData { inherit lock_data; environ = linux_environ; }; in { - test_parseLockData = { + test_simple = { expr = (parsed ? "requests") - && (parsed.requests.version == version) - && (parsed.requests ? sources); + && (parsed.requests.default.version == version) + && (parsed.requests.default ? sources); expected = true; }; - test_parseLockData_file = { - expr = libpdm.preferWheelSelector (lib.attrNames parsed.requests.sources); + test_file = { + expr = libpdm.preferWheelSelector (lib.attrNames parsed.requests.default.sources); expected = "requests-2.31.0-py3-none-any.whl"; }; - test_parseLockData_dependencies = { - expr = parsed.requests.dependencies; + test_dependencies = { + expr = map (dep: dep.name) parsed.requests.default.dependencies; expected = [ "certifi" "charset-normalizer" @@ -189,68 +250,150 @@ in { "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 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 { inherit environ pyproject; }; in { - test_groupsWithDeps__has_main_group = { + test_has_main_group = { expr = groups_with_deps ? "default"; expected = true; }; - test_groupsWithDeps__main_group_has_deps = { - expr = groups_with_deps.default; + test_main_group_has_deps = { + expr = map (dep: dep.name) groups_with_deps.default; expected = ["requests"]; }; - test_groupsWithDeps__optionals_dev_has_deps = { - expr = groups_with_deps.dev; + test_optionals_dev_has_deps = { + expr = map (dep: dep.name) groups_with_deps.dev; expected = ["pi"]; }; }; - getDepsRecursively = let + getClosure = let environ = linux_environ; - lock_data = lib.importTOML ./../../../examples/packages/single-language/python-pdm/pdm.lock; - parsed_lock_data = libpdm.parseLockData { - inherit environ lock_data; + lock_data_2 = lib.importTOML ./fixtures/pdm-example1.lock; + parsed_lock_data_2 = libpdm.parseLockData { + inherit environ; + lock_data = lock_data_2; }; - deps = libpdm.getDepsRecursively parsed_lock_data "requests"; - in { - test_getDepsRecursively_names = { - expr = lib.attrNames deps; - expected = ["certifi" "charset-normalizer" "idna" "requests" "urllib3"]; - }; - test_getDepsRecursively_versions = { - expr = lib.mapAttrs (key: value: value.version) deps; + in rec { + deps = libpdm.getClosure parsed_lock_data_2 "requests" []; + + test_versions = { + expr = lib.mapAttrs (name: dep: dep.version) deps; expected = { certifi = "2023.7.22"; charset-normalizer = "3.2.0"; idna = "3.4"; - requests = "2.31.0"; urllib3 = "2.0.5"; }; }; - test_getDepsRecursively_sources = let + + test_sources = let selectFilename = sources: libpdm.preferWheelSelector (lib.attrNames sources); in { - expr = lib.mapAttrs (key: value: selectFilename value.sources) deps; + expr = lib.mapAttrs (name: dep: selectFilename dep.sources) deps; expected = { 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"; 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"; }; }; + + 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; - 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; + pyproject = libpdm.loadPdmPyProject (lib.importTOML ./fixtures/pyproject.toml); + lock_data = lib.importTOML ./fixtures/pdm-example1.lock; groups_with_deps = libpdm.groupsWithDeps { inherit environ pyproject; }; @@ -258,20 +401,20 @@ in { inherit lock_data; environ = linux_environ; }; - deps_default = libpdm.selectForGroups { + deps_default = libpdm.closureForGroups { inherit parsed_lock_data groups_with_deps; groupNames = ["default"]; }; - deps_dev = libpdm.selectForGroups { + deps_dev = libpdm.closureForGroups { inherit parsed_lock_data groups_with_deps; groupNames = ["default" "dev"]; }; in { - test_selectForGroups_names = { + test_names = { expr = lib.attrNames deps_default; expected = ["certifi" "charset-normalizer" "idna" "requests" "urllib3"]; }; - test_selectForGroups_versions = { + test_versions = { expr = lib.mapAttrs (key: value: value.version) deps_default; expected = { certifi = "2023.7.22"; @@ -281,7 +424,7 @@ in { urllib3 = "2.0.5"; }; }; - test_selectForGroups_sources = let + test_closureForGroups_sources = let selectFilename = sources: libpdm.preferWheelSelector (lib.attrNames sources); in { expr = lib.mapAttrs (key: value: selectFilename value.sources) deps_default; @@ -293,7 +436,7 @@ in { 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; expected = { certifi = "2023.7.22"; diff --git a/tests/nix-unit/test_python-pdm-lib/fixtures/pdm-example1.lock b/tests/nix-unit/test_python-pdm-lib/fixtures/pdm-example1.lock new file mode 100644 index 00000000..9e28b0df --- /dev/null +++ b/tests/nix-unit/test_python-pdm-lib/fixtures/pdm-example1.lock @@ -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"}, +] diff --git a/tests/nix-unit/test_python-pdm-lib/fixtures/pdm-extras.lock b/tests/nix-unit/test_python-pdm-lib/fixtures/pdm-extras.lock new file mode 100644 index 00000000..84b5e092 --- /dev/null +++ b/tests/nix-unit/test_python-pdm-lib/fixtures/pdm-extras.lock @@ -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 = [] diff --git a/tests/nix-unit/test_python-pdm-lib/fixtures/pdm-with-cycles.lock b/tests/nix-unit/test_python-pdm-lib/fixtures/pdm-with-cycles.lock new file mode 100644 index 00000000..30f09a4e --- /dev/null +++ b/tests/nix-unit/test_python-pdm-lib/fixtures/pdm-with-cycles.lock @@ -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"}, +] diff --git a/tests/nix-unit/test_python-pdm-lib/fixtures/pyproject.toml b/tests/nix-unit/test_python-pdm-lib/fixtures/pyproject.toml new file mode 100644 index 00000000..59851d25 --- /dev/null +++ b/tests/nix-unit/test_python-pdm-lib/fixtures/pyproject.toml @@ -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" diff --git a/tests/nix-unit/test_python-pdm/default.nix b/tests/nix-unit/test_python-pdm/default.nix index ba433352..faba6e4f 100644 --- a/tests/nix-unit/test_python-pdm/default.nix +++ b/tests/nix-unit/test_python-pdm/default.nix @@ -1,8 +1,8 @@ { pkgs ? import {}, lib ? import , - inputs ? {}, - dream2nix ? import ../../.. inputs, + dream2nix ? import ../../../. inputs, + inputs ? (import ../../../.).inputs, }: let eval = module: (lib.evalModules { @@ -19,9 +19,8 @@ in { test_pdm = let config = eval { - # TODO: create fixtures - pdm.lockfile = ./../../../examples/packages/single-language/python-pdm/pdm.lock; - pdm.pyproject = ./../../../examples/packages/single-language/python-pdm/pyproject.toml; + pdm.lockfile = ./../test_python-pdm-lib/fixtures/pdm-example1.lock; + pdm.pyproject = ./../test_python-pdm-lib/fixtures/pyproject.toml; }; in { expr = (lib.head (lib.attrValues config.groups.default.packages.certifi)).public ? drvPath;