From 184203e05a78f93f3c6329597db4cbec0471984b Mon Sep 17 00:00:00 2001 From: adisbladis Date: Tue, 3 Sep 2024 01:25:44 +1200 Subject: [PATCH] vendor: Upgrade pyproject.nix This includes a full rewrite of the PEP-508 parser which performs ~2x as fast with ~0.4x the RAM. --- dev/treefmt.nix | 3 + vendor/pyproject.nix/lib/default.nix | 37 +- vendor/pyproject.nix/lib/eggs.nix | 73 +- vendor/pyproject.nix/lib/pep440.nix | 495 ++++++----- vendor/pyproject.nix/lib/pep508.nix | 1003 +++++++++++++---------- vendor/pyproject.nix/lib/pep518.nix | 11 +- vendor/pyproject.nix/lib/pep599.nix | 11 +- vendor/pyproject.nix/lib/pep600.nix | 56 +- vendor/pyproject.nix/lib/pep621.nix | 193 ++--- vendor/pyproject.nix/lib/pep656.nix | 43 +- vendor/pyproject.nix/lib/pip.nix | 126 +-- vendor/pyproject.nix/lib/poetry.nix | 529 ++++++------ vendor/pyproject.nix/lib/project.nix | 264 +++--- vendor/pyproject.nix/lib/pypa.nix | 342 ++++---- vendor/pyproject.nix/lib/renderers.nix | 248 +++--- vendor/pyproject.nix/lib/util.nix | 19 +- vendor/pyproject.nix/lib/validators.nix | 65 +- 17 files changed, 1982 insertions(+), 1536 deletions(-) diff --git a/dev/treefmt.nix b/dev/treefmt.nix index 2eec167be..311e4d984 100644 --- a/dev/treefmt.nix +++ b/dev/treefmt.nix @@ -20,6 +20,9 @@ }; black.excludes = [ "vendor/**.py" ]; + + deadnix.excludes = [ "vendor/**.nix" ]; + statix.excludes = [ "vendor/**.nix" ]; }; programs.deadnix.enable = true; diff --git a/vendor/pyproject.nix/lib/default.nix b/vendor/pyproject.nix/lib/default.nix index ac5c2a8f6..84e602d6c 100644 --- a/vendor/pyproject.nix/lib/default.nix +++ b/vendor/pyproject.nix/lib/default.nix @@ -4,19 +4,24 @@ let inherit (lib) fix; in -fix (self: mapAttrs (_: path: import path ({ inherit lib; } // self)) { - pip = ./pip.nix; - pypa = ./pypa.nix; - project = ./project.nix; - renderers = ./renderers.nix; - validators = ./validators.nix; - poetry = ./poetry.nix; - eggs = ./eggs.nix; - pep440 = ./pep440.nix; - pep508 = ./pep508.nix; - pep518 = ./pep518.nix; - pep599 = ./pep599.nix; - pep600 = ./pep600.nix; - pep621 = ./pep621.nix; - pep656 = ./pep656.nix; -}) +fix ( + self: + mapAttrs (_: path: import path ({ inherit lib; } // self)) { + pip = ./pip.nix; + pypa = ./pypa.nix; + project = ./project.nix; + renderers = ./renderers.nix; + validators = ./validators.nix; + scripts = ./scripts.nix; + poetry = ./poetry.nix; + eggs = ./eggs.nix; + pep440 = ./pep440.nix; + pep508 = ./pep508.nix; + pep518 = ./pep518.nix; + pep599 = ./pep599.nix; + pep600 = ./pep600.nix; + pep621 = ./pep621.nix; + pep656 = ./pep656.nix; + pep723 = ./pep723.nix; + } +) diff --git a/vendor/pyproject.nix/lib/eggs.nix b/vendor/pyproject.nix/lib/eggs.nix index 15d47fe41..4709d6ca3 100644 --- a/vendor/pyproject.nix/lib/eggs.nix +++ b/vendor/pyproject.nix/lib/eggs.nix @@ -1,6 +1,12 @@ { lib, ... }: let - inherit (builtins) filter match elemAt compareVersions sort; + inherit (builtins) + filter + match + elemAt + compareVersions + sort + ; inherit (lib) isString; # Tag normalization documented in @@ -16,55 +22,61 @@ let in lib.fix (self: { - /* Regex match an egg file name, returning a list of match groups. Returns null if no match. + /* + Regex match an egg file name, returning a list of match groups. Returns null if no match. - Type: matchEggFileName :: string -> [ string ] + Type: matchEggFileName :: string -> [ string ] */ - matchEggFileName = name: + matchEggFileName = + name: let m = match "([^-]+)-([^-]+)-(.+)\\.egg" name; in if m != null then filter isString m else null; - /* Check whether string is an egg file or not. + /* + Check whether string is an egg file or not. - Type: isEggFileName :: string -> bool + Type: isEggFileName :: string -> bool - Example: - # isEggFileName "cryptography-41.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" - false + Example: + # isEggFileName "cryptography-41.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + false */ isEggFileName = # The filename string name: self.matchEggFileName name != null; - /* Parse an egg file name. + /* + Parse an egg file name. - Type: parsehEggFileName :: string -> AttrSet + Type: parsehEggFileName :: string -> AttrSet - Example: - # parseEggFileName + Example: + # parseEggFileName */ - parseEggFileName = name: + parseEggFileName = + name: let m = self.matchEggFileName name; - mAt = elemAt m; - langM = match "([^0-9]*)(.+)" (mAt 2); - langAt = elemAt langM; + langM = match "([^0-9]*)(.+)" (elemAt m 2); in - assert m != null; { + assert m != null; + assert langM != null; + { filename = name; - distribution = mAt 0; - version = mAt 1; + distribution = elemAt m 0; + version = elemAt m 1; languageTag = { - implementation = normalizeImpl (langAt 0); - version = langAt 1; + implementation = normalizeImpl (elemAt langM 0); + version = elemAt langM 1; }; }; - /* Select compatible eggs from a list and return them in priority order. + /* + Select compatible eggs from a list and return them in priority order. - Type: selectEggs :: derivation -> [ AttrSet ] -> [ AttrSet ] + Type: selectEggs :: derivation -> [ AttrSet ] -> [ AttrSet ] */ selectEggs = # Python interpreter derivation @@ -74,13 +86,14 @@ lib.fix (self: { let inherit (python.passthru) pythonVersion implementation; - langCompatible = filter - (file: file.languageTag.implementation == "python" || file.languageTag.implementation == implementation) - files; + langCompatible = filter ( + file: + file.languageTag.implementation == "python" || file.languageTag.implementation == implementation + ) files; - versionCompatible = filter - (file: compareVersions pythonVersion file.languageTag.version >= 0) - langCompatible; + versionCompatible = filter ( + file: compareVersions pythonVersion file.languageTag.version >= 0 + ) langCompatible; in sort (a: b: compareVersions a.languageTag.version b.languageTag.version > 0) versionCompatible; diff --git a/vendor/pyproject.nix/lib/pep440.nix b/vendor/pyproject.nix/lib/pep440.nix index 158c42f31..555488157 100644 --- a/vendor/pyproject.nix/lib/pep440.nix +++ b/vendor/pyproject.nix/lib/pep440.nix @@ -1,7 +1,23 @@ { lib, ... }: let - inherit (builtins) split filter match length elemAt head fromJSON typeOf compareVersions; - inherit (lib) fix isString toInt sublist findFirst; + inherit (builtins) + split + filter + match + length + elemAt + head + fromJSON + typeOf + compareVersions + ; + inherit (lib) + fix + isString + toInt + sublist + findFirst + ; inherit (import ./util.nix { inherit lib; }) splitComma; # A version of lib.toInt that supports leading zeroes @@ -10,14 +26,23 @@ let matchDigit = match "0?([[:digit:]]+)"; in s: - if s == "*" then s + if s == "*" then + s else let n = fromJSON (head (matchDigit s)); in - assert typeOf n == "int"; n; - - emptyVersion = { dev = null; epoch = 0; local = null; post = null; pre = null; release = [ ]; }; + assert typeOf n == "int"; + n; + + emptyVersion = { + dev = null; + epoch = 0; + local = null; + post = null; + pre = null; + release = [ ]; + }; # We consider some words to be alternate spellings of other words and # in those cases we want to normalize the spellings to our preferred @@ -35,21 +60,28 @@ let normalizedReleaseType = type: normalizedReleaseTypes.${type} or type; # Compare the release fields from the parsed version - compareRelease = offset: ra: rb: - if length ra == offset || length rb == offset then 0 else - ( - let - x = elemAt ra offset; - y = elemAt rb offset; - in - if x == "*" || y == "*" then 0 # Wildcards are always considered equal - else - ( - if x > y then 1 - else if x < y then -1 - else compareRelease (offset + 1) ra rb - ) - ); + compareRelease = + offset: ra: rb: + if length ra == offset || length rb == offset then + 0 + else + ( + let + x = elemAt ra offset; + y = elemAt rb offset; + in + if x == "*" || y == "*" then + 0 # Wildcards are always considered equal + else + ( + if x > y then + 1 + else if x < y then + -1 + else + compareRelease (offset + 1) ra rb + ) + ); # Normalized modifier to it's priority (in case we are comparing an alpha to a beta or similar) modifierPriority = { @@ -61,176 +93,193 @@ let }; # Compare dev/pre/post/local release modifiers - compareVersionModifier = x: y: assert x != null && y != null; let - prioX = modifierPriority.${x.type}; - prioY = modifierPriority.${y.type}; - in - if prioX == prioY then - ( - if x.value == y.value then 0 - else if x.value > y.value then 1 - else -1 - ) - else if prioX > prioY then 1 - else 0; + compareVersionModifier = + x: y: + assert x != null && y != null; + let + prioX = modifierPriority.${x.type}; + prioY = modifierPriority.${y.type}; + in + if prioX == prioY then + ( + if x.value == y.value then + 0 + else if x.value > y.value then + 1 + else + -1 + ) + else if prioX > prioY then + 1 + else + 0; in fix (self: { - /* Parse a version according to PEP-440. - - Type: parseVersion :: string -> AttrSet - - Example: - # parseVersion "3.0.0rc1" - { - dev = null; - epoch = 0; - local = null; - post = null; - pre = { - type = "rc"; - value = 1; - }; - release = [ 3 0 0 ]; - } + /* + Parse a version according to PEP-440. + + Type: parseVersion :: string -> AttrSet + + Example: + # parseVersion "3.0.0rc1" + { + dev = null; + epoch = 0; + local = null; + post = null; + pre = { + type = "rc"; + value = 1; + }; + release = [ 3 0 0 ]; + } */ - parseVersion = version: - let - # Split input into (_, epoch, release, modifiers) - tokens = match "(([0-9]+)!)?([^-\+a-zA-Z]+)(.*)" version; - tokenAt = elemAt tokens; - - # Segments - epochSegment = tokenAt 1; - releaseSegment = tokenAt 2; - modifierLocalSegment = tokenAt 3; - - # Split modifier/local segment - mLocalAt = elemAt (match "([^\\+]*)\\+?(.*)" modifierLocalSegment); - modifiersSegment = mLocalAt 0; - local = mLocalAt 1; - - # Parse each post345/dev1 string into attrset - modifiers = - map - (mod: - let - # Split post345 into ["post" "345"] - m = match "-?([^0-9]+)([0-9]+)" mod; - mAt = elemAt m; - in - assert m != null; { - type = normalizedReleaseType (mAt 0); - value = toIntRelease (mAt 1); - }) - (filter (s: isString s && s != "") (split "\\." modifiersSegment)); + parseVersion = + version: + if version == "" then + emptyVersion + else + let + # Split input into (_, epoch, release, modifiers) + tokens = match "(([0-9]+)!)?([^-\+a-zA-Z]+)(.*)" version; + tokenAt = elemAt tokens; + + # Segments + epochSegment = tokenAt 1; + releaseSegment = tokenAt 2; + modifierLocalSegment = tokenAt 3; + + # Split modifier/local segment + mLocal = match "([^\\+]*)\\+?(.*)" modifierLocalSegment; + mLocalAt = elemAt mLocal; + modifiersSegment = mLocalAt 0; + local = mLocalAt 1; + + # Parse each post345/dev1 string into attrset + modifiers = map ( + mod: + let + # Split post345 into ["post" "345"] + m = match "-?([^0-9]+)([0-9]+)" mod; + in + assert m != null; + { + type = normalizedReleaseType (elemAt m 0); + value = toIntRelease (elemAt m 1); + } + ) (filter (s: isString s && s != "") (split "\\." modifiersSegment)); - in - if version == "" then emptyVersion - else { - # Return epoch defaulting to 0 - epoch = - if epochSegment != null then toInt epochSegment - else 0; - - # Parse release segments delimited by dots into list of ints - release = map toIntRelease (filter (s: isString s && s != "") (split "\\." releaseSegment)); - - # Find modifiers in modifiers list - pre = findFirst (mod: mod.type == "rc" || mod.type == "b" || mod.type == "a") null modifiers; - post = findFirst (mod: mod.type == "post") null modifiers; - dev = findFirst (mod: mod.type == "dev") null modifiers; - - # Local releases needs to be treated specially. - # The value isn't just a straight up number, but an arbitrary string. - local = if local != "" then local else null; - }; - - /* Parse a version conditional. - - Type: parseVersionCond :: string -> AttrSet - - Example: - # parseVersionCond ">=3.0.0rc1" - { - op = ">="; - version = { - dev = null; - epoch = 0; - local = null; - post = null; - pre = { - type = "rc"; - value = 1; - }; - release = [ 3 0 0 ]; - }; - } + in + if tokens == null || mLocal == null then + throw "Invalid PEP-440 version: ${version}" + else + { + # Return epoch defaulting to 0 + epoch = if epochSegment != null then toInt epochSegment else 0; + + # Parse release segments delimited by dots into list of ints + release = map toIntRelease (filter (s: isString s && s != "") (split "\\." releaseSegment)); + + # Find modifiers in modifiers list + pre = findFirst (mod: mod.type == "rc" || mod.type == "b" || mod.type == "a") null modifiers; + post = findFirst (mod: mod.type == "post") null modifiers; + dev = findFirst (mod: mod.type == "dev") null modifiers; + + # Local releases needs to be treated specially. + # The value isn't just a straight up number, but an arbitrary string. + local = if local != "" then local else null; + }; + + /* + Parse a version conditional. + + Type: parseVersionCond :: string -> AttrSet + + Example: + # parseVersionCond ">=3.0.0rc1" + { + op = ">="; + version = { + dev = null; + epoch = 0; + local = null; + post = null; + pre = { + type = "rc"; + value = 1; + }; + release = [ 3 0 0 ]; + }; + } */ - parseVersionCond = cond: ( - let - m = match " *([=> [AttrSet] - - Example: - # parseVersionConds ">=3.0.0rc1,<=4.0" - [ - { - op = ">="; - version = { - dev = null; - epoch = 0; - local = null; - post = null; - pre = { - type = "rc"; - value = 1; - }; - release = [ 3 0 0 ]; - }; - } - { - op = "<="; - version = { - dev = null; - epoch = 0; - local = null; - post = null; - pre = null; - release = [ 4 0 ]; - }; - } - ] + parseVersionCond = + cond: + ( + let + m = match " *([=> [AttrSet] + + Example: + # parseVersionConds ">=3.0.0rc1,<=4.0" + [ + { + op = ">="; + version = { + dev = null; + epoch = 0; + local = null; + post = null; + pre = { + type = "rc"; + value = 1; + }; + release = [ 3 0 0 ]; + }; + } + { + op = "<="; + version = { + dev = null; + epoch = 0; + local = null; + post = null; + pre = null; + release = [ 4 0 ]; + }; + } + ] */ parseVersionConds = conds: map self.parseVersionCond (splitComma conds); - /* Compare two versions as parsed by `parseVersion` according to PEP-440. + /* + Compare two versions as parsed by `parseVersion` according to PEP-440. - Returns: - - -1 for less than - - 0 for equality - - 1 for greater than + Returns: + - -1 for less than + - 0 for equality + - 1 for greater than - Type: compareVersions :: AttrSet -> AttrSet -> int + Type: compareVersions :: AttrSet -> AttrSet -> int - Example: - # compareVersions (parseVersion "3.0.0") (parseVersion "3.0.0") - 0 + Example: + # compareVersions (parseVersion "3.0.0") (parseVersion "3.0.0") + 0 */ compareVersions = - a: - b: + a: b: let releaseComp = compareRelease 0 a.release b.release; preComp = compareVersionModifier a.pre b.pre; @@ -238,53 +287,70 @@ fix (self: { postComp = compareVersionModifier a.post b.post; localComp = compareVersions a.local b.local; in - if a.epoch > b.epoch then 1 - else if a.epoch < b.epoch then -1 + if a.epoch > b.epoch then + 1 + else if a.epoch < b.epoch then + -1 # Compare release field - else if releaseComp != 0 then releaseComp + else if releaseComp != 0 then + releaseComp # Compare pre release - else if a.pre != null && b.pre != null && preComp != 0 then preComp - else if a.pre != null && b.pre == null then -1 - else if b.pre != null && a.pre == null then 1 + else if a.pre != null && b.pre != null && preComp != 0 then + preComp + else if a.pre != null && b.pre == null then + -1 + else if b.pre != null && a.pre == null then + 1 # Compare dev release - else if a.dev != null && b.dev != null && devComp != 0 then devComp - else if a.dev != null && b.dev == null then -1 - else if b.dev != null && a.dev == null then 1 + else if a.dev != null && b.dev != null && devComp != 0 then + devComp + else if a.dev != null && b.dev == null then + -1 + else if b.dev != null && a.dev == null then + 1 # Compare post release - else if a.post != null && b.post != null && postComp != 0 then postComp - else if a.post != null && b.post == null then 1 - else if b.post != null && a.post == null then -1 + else if a.post != null && b.post != null && postComp != 0 then + postComp + else if a.post != null && b.post == null then + 1 + else if b.post != null && a.post == null then + -1 # Compare local # HACK: Local are arbitrary strings. # We do a best estimate by comparing local as versions using builtins.compareVersions. # This is strictly not correct but it's better than no handling.. - else if a.local != null && b.local != null && localComp != 0 then localComp - else if a.local != null && b.local == null then 1 - else if b.local != null && a.local == null then -1 + else if a.local != null && b.local != null && localComp != 0 then + localComp + else if a.local != null && b.local == null then + 1 + else if b.local != null && a.local == null then + -1 # Equal - else 0; + else + 0; - /* Map comparison operators as strings to a comparator function. + /* + Map comparison operators as strings to a comparator function. - Attributes: - - [Compatible release clause](https://peps.python.org/pep-0440/#compatible-release): `~=` - - [Version matching clause](https://peps.python.org/pep-0440/#version-matching): `==` - - [Version exclusion clause](https://peps.python.org/pep-0440/#version-exclusion): `!=` - - [Inclusive ordered comparison clause](https://peps.python.org/pep-0440/#inclusive-ordered-comparison): `<=`, `>=` - - [Exclusive ordered comparison clause](https://peps.python.org/pep-0440/#exclusive-ordered-comparison): `<`, `>` - - [Arbitrary equality clause](https://peps.python.org/pep-0440/#arbitrary-equality): `===` + Attributes: + - [Compatible release clause](https://peps.python.org/pep-0440/#compatible-release): `~=` + - [Version matching clause](https://peps.python.org/pep-0440/#version-matching): `==` + - [Version exclusion clause](https://peps.python.org/pep-0440/#version-exclusion): `!=` + - [Inclusive ordered comparison clause](https://peps.python.org/pep-0440/#inclusive-ordered-comparison): `<=`, `>=` + - [Exclusive ordered comparison clause](https://peps.python.org/pep-0440/#exclusive-ordered-comparison): `<`, `>` + - [Arbitrary equality clause](https://peps.python.org/pep-0440/#arbitrary-equality): `===` - Type: operators.${operator} :: AttrSet -> AttrSet -> bool + Type: operators.${operator} :: AttrSet -> AttrSet -> bool - Example: - # comparators."==" (parseVersion "3.0.0") (parseVersion "3.0.0") - true + Example: + # comparators."==" (parseVersion "3.0.0") (parseVersion "3.0.0") + true */ comparators = { "~=" = @@ -292,16 +358,21 @@ fix (self: { gte = self.comparators.">="; eq = self.comparators."=="; in - a: b: ( + a: b: + ( # Local version identifiers are NOT permitted in this version specifier. assert a.local == null && b.local == null; - gte a b && eq a (b // { - release = sublist 0 ((length b.release) - 1) b.release; - # If a pre-release, post-release or developmental release is named in a compatible release clause as V.N.suffix, then the suffix is ignored when determining the required prefix match. - pre = null; - post = null; - dev = null; - }) + gte a b + && eq a ( + b + // { + release = sublist 0 ((length b.release) - 1) b.release; + # If a pre-release, post-release or developmental release is named in a compatible release clause as V.N.suffix, then the suffix is ignored when determining the required prefix match. + pre = null; + post = null; + dev = null; + } + ) ); "==" = a: b: self.compareVersions a b == 0; "!=" = a: b: self.compareVersions a b != 0; diff --git a/vendor/pyproject.nix/lib/pep508.nix b/vendor/pyproject.nix/lib/pep508.nix index a6780d272..c7322eb53 100644 --- a/vendor/pyproject.nix/lib/pep508.nix +++ b/vendor/pyproject.nix/lib/pep508.nix @@ -1,80 +1,68 @@ -{ lib, pep440, pep599, pypa, ... }: +{ + lib, + pep508, + pep440, + pep599, + pypa, + ... +}: let - inherit (builtins) match elemAt split foldl' substring stringLength typeOf fromJSON isString head mapAttrs elem length; - inherit (lib) stringToCharacters fix; + inherit (builtins) + match + elemAt + foldl' + substring + typeOf + fromJSON + toJSON + isString + head + mapAttrs + elem + length + isList + any + ; + inherit (lib) + stringToCharacters + sublist + hasInfix + ; inherit (import ./util.nix { inherit lib; }) splitComma stripStr; - re = { - operators = "([=>= condPrio.${r}; - - # Parse a value into an attrset of { type = "valueType"; value = ...; } - # Will parse any field name suffixed with "version" as a PEP-440 version, otherwise - # the value is passed through and the type is inferred with builtins.typeOf - parseValueVersionDynamic = name: value: ( - if match "^.+version" name != null && isString value then { - type = "version"; - value = pep440.parseVersion value; - } else { - type = typeOf value; - inherit value; - } - ); - - # Remove groupings ( ) from expression - unparen = - let - matchParen = match "[\t ]*\\((.+)\\)[\t ]*"; - in - expr: - let - m = matchParen expr; - in - if m != null then head m else expr; - - isMarkerVariable = + # Marker fields + their parsers + markerFields = let - markerFields = [ - "implementation_name" - "implementation_version" - "os_name" - "platform_machine" - "platform_python_implementation" - "platform_release" - "platform_system" - "platform_version" - "python_full_version" - "python_version" - "sys_platform" - "extra" - ]; + default = value: { + type = typeOf value; + inherit value; + }; + version = value: { + type = "version"; + value = pep440.parseVersion value; + }; in - s: elem s markerFields; - - unpackValue = value: - if isMarkerVariable value then value - else - ( - let - # If the value is a single ticked string we can't pass it plainly to toJSON. - # Normalise to a double quoted. - singleTicked = match "^'(.+)'$" value; # TODO: Account for escaped ' in input (unescape) - in - fromJSON (if singleTicked != null then "\"" + head singleTicked + "\"" else value) - ); + { + "implementation_name" = default; + "implementation_version" = version; + "os_name" = default; + "platform_machine" = default; + "platform_python_implementation" = default; + "platform_release" = default; + "platform_system" = default; + "platform_version" = version; + "python_full_version" = version; + "python_version" = version; + "sys_platform" = default; + "extra" = + value: + assert isList value || isString value; + { + type = "extra"; + inherit value; + }; + }; # Comparators for simple equality # For versions see pep440.comparators @@ -98,201 +86,325 @@ let boolOps = { "and" = x: y: x && y; "or" = x: y: x || y; - "in" = x: y: lib.strings.hasInfix x y; + "in" = x: y: hasInfix x y; + "not in" = x: y: !(hasInfix x y); }; - isPrimitiveType = - let - primitives = [ - "int" - "float" - "string" - "bool" - ]; - in - type: elem type primitives; + primitives = [ + "int" + "float" + "string" + "bool" + ]; + + # Copied from nixpkgs lib.findFirstIndex internals to save on a little bit of environment allocations. + resultIndex' = + pred: + foldl' ( + index: el: + if index < 0 then + # No match yet before the current index, we need to check the element + if pred el then + # We have a match! Turn it into the actual index to prevent future iterations from modifying it + -index - 1 + else + # Still no match, update the index to the next element (we're counting down, so minus one) + index - 1 + else + # There's already a match, propagate the index without evaluating anything + index + ) (-1); + + inherit (pep508) parseMarkers evalMarkers; in -fix (self: { - /* Parse PEP 508 markers into an AST. - - Type: parseMarkers :: string -> AttrSet - - Example: - # parseMarkers "(os_name=='a' or os_name=='b') and os_name=='c'" - { - lhs = { - lhs = { - lhs = { - type = "variable"; - value = "os_name"; - }; - op = "=="; - rhs = { - type = "string"; - value = "a"; - }; - type = "compare"; - }; - op = "or"; - rhs = { - lhs = { - type = "variable"; - value = "os_name"; - }; - op = "=="; - rhs = { - type = "string"; - value = "b"; - }; - type = "compare"; - }; - type = "boolOp"; - }; - op = "and"; - rhs = { - lhs = { - type = "variable"; - value = "os_name"; - }; - op = "=="; - rhs = { - type = "string"; - value = "c"; - }; - type = "compare"; - }; - type = "boolOp"; - } - */ - parseMarkers = input: - let - # Find the positions of lhs/op/rhs in the input string - pos = foldl' - (acc: c: - let - # # Look ahead to find the operator (either "and", "not" or "or"). - cond = - if self.openP > 0 || acc.inString then "" - else if substring acc.pos 5 input == " and " then "and" - else if substring acc.pos 4 input == " or " then "or" - else if substring acc.pos 4 input == " in " then "in" - else if substring acc.pos 8 input == " not in " then "not in" - else if substring acc.pos 5 input == " not " then "not" - else ""; - - # When we've reached the operator we know the start/end positions of lhs/op/rhs - rhsOffset = - if cond != "" && condGt cond acc.cond then - ( - if (cond == "and" || cond == "not") then 5 - else if (cond == "or" || cond == "in") then 4 - else if cond == "not in" then 8 - else throw "Unknown cond: ${cond}" - ) else -1; - - self = { - # If we are inside a string don't track the opening and closing of parens - openP = if acc.inString then acc.openP else - ( - if c == "(" then acc.openP + 1 - else if c == ")" then acc.openP - 1 - else acc.openP - ); + /* + Parse PEP 508 markers into an AST. - # Check opening and closing of strings - inString = - if acc.inString && c == "'" then true - else if !acc.inString && c == "'" then false - else acc.inString; + Type: parseMarkers :: string -> AttrSet - pos = acc.pos + 1; - - cond = if cond != "" then cond else acc.cond; - - lhs = if (rhsOffset != -1) then acc.pos else acc.lhs; - rhs = if (rhsOffset != -1) then (acc.pos + rhsOffset) else acc.rhs; + Example: + # parseMarkers "(os_name=='a' or os_name=='b') and os_name=='c'" + { + lhs = { + lhs = { + lhs = { + type = "variable"; + value = "os_name"; }; + op = "=="; + rhs = { + type = "string"; + value = "a"; + }; + type = "compare"; + }; + op = "or"; + rhs = { + lhs = { + type = "variable"; + value = "os_name"; + }; + op = "=="; + rhs = { + type = "string"; + value = "b"; + }; + type = "compare"; + }; + type = "boolOp"; + }; + op = "and"; + rhs = { + lhs = { + type = "variable"; + value = "os_name"; + }; + op = "=="; + rhs = { + type = "string"; + value = "c"; + }; + type = "compare"; + }; + type = "boolOp"; + } + */ + parseMarkers = + let + opChars = [ + "=" + ">" + "<" + "!" + "~" + "^" + ]; - in - self) - { - openP = 0; # Number of open parens - inString = false; # If the parser is inside a string - pos = 0; # Parser position - done = false; - - # Keep track of last logical condition to do precedence ordering - cond = ""; - - # Stop positions for each value - lhs = -1; - rhs = -1; - - } - (stringToCharacters input); + # State exit conditions + emptyOrOp = _pos: c: c == " " || c == "(" || c == ")" || elem c opChars; + nonOp = _pos: c: !elem c opChars; + anyCond = _pos: _c: true; + # Use look-behind to assess whether a string was closed + stringCond' = + char: chars: startPos: pos: _c: + pos - 1 != startPos && (elemAt chars (pos - 1)) == char; + singleStringCond' = stringCond' "'"; + doubleStringCond' = stringCond' "\""; in - if pos.lhs == -1 then # No right hand value to extract - ( - let - m = split re.operators (unparen input); - mLength = length m; - mAt = elemAt m; - lhs = stripStr (mAt 0); - in - if (mLength > 1) then assert mLength == 3; { - type = "compare"; - lhs = - if isMarkerVariable lhs then { - type = "variable"; - value = lhs; - } else unpackValue lhs; - op = elemAt (mAt 1) 0; - rhs = parseValueVersionDynamic lhs (unpackValue (stripStr (mAt 2))); - } else if isMarkerVariable input then { - type = "variable"; - value = input; - } else rec { - value = unpackValue input; - type = typeOf value; - } - ) else { - type = "boolOp"; - lhs = self.parseMarkers (unparen (substring 0 pos.lhs input)); - op = substring (pos.lhs + 1) (pos.rhs - pos.lhs - 2) input; - rhs = self.parseMarkers (unparen (substring pos.rhs (stringLength input) input)); - }; + input: + if input == "" then + [ ] + else + let + chars = stringToCharacters input; + cmax = (length chars) - 1; + last = elemAt chars cmax; + singleStringCond = singleStringCond' chars; + doubleStringCond = doubleStringCond' chars; + + # Find tokens in character stream + tokens' = + foldl' + (acc: c: rec { + # Current position + pos = acc.pos + 1; - /* Parse a PEP-508 dependency string. - - Type: parseString :: string -> AttrSet - - Example: - # parseString "cachecontrol[filecache]>=0.13.0" - { - conditions = [ - { - op = ">="; - version = { - dev = null; - epoch = 0; - local = null; - post = null; - pre = null; - release = [ 0 13 0 ]; - }; - } - ]; - markers = null; - name = "cachecontrol"; - extras = [ "filecache" ]; - url = null; - } + # Set start position of token to current position if exit condition matches + start = + if acc.cond pos c then + (if c == " " then -1 else pos) # If character is whitespace keep seeking + else + acc.start; # Else propagate start position + + # Assign new exit condition on state change + cond = + if start == pos || start == -1 then + ( + if c == " " || c == "(" || c == ")" then + anyCond + else if c == "\"" then + (doubleStringCond pos) + else if c == "'" then + (singleStringCond pos) + else if elem c opChars then + nonOp + else + emptyOrOp + ) + else + acc.cond; + + tokens = + # Reached end of token + if acc.start != -1 && start != acc.start then + acc.tokens ++ [ (substring acc.start (pos - acc.start) input) ] + # Reached end of input + else if pos == cmax && acc.start != -1 then + acc.tokens ++ [ (substring acc.start (cmax + 1 - acc.start) input) ] + else + acc.tokens; + + }) + { + pos = -1; # Parser position + start = -1; # Start position for current token (-1 indicates searching through whitespace) + cond = anyCond; # Function condition ending current parser state + tokens = [ ]; # List of discovered tokens as strings + } + chars; + + # Special case: Single character tail token + tokens = + if tokens'.start == cmax && last != " " then tokens'.tokens ++ [ last ] else tokens'.tokens; + + # Group tokens according to paren expression groups + ltokens = length tokens; + groupTokens = + stack: i: + if i == ltokens then + stack + else + let + token = elemAt tokens i; + in + # New group, initialize a new stack + if token == "(" then + ( + let + group' = groupTokens [ ] (i + 1); + in + groupTokens (stack ++ [ (elemAt group' 0) ]) (elemAt group' 1) + ) + # Closing group, return stack + else if token == ")" then + # Return a tuple of stack and next so the "(" branch above can know where the list is closed + [ + stack + (i + 1) + ] + # Append all other token types to stack. + else + groupTokens (stack ++ [ token ]) (i + 1); + + # The grouping routine is a tad slow and uses recursion. + # We can completely avoid it if the input doesn't contain any grouped subexpressions. + groupedTokens = if any (token: token == "(") tokens then groupTokens [ ] 0 else tokens; + + # Reduce values into AST + reduceValue = + lhs': value: + if isList value then + ( + if length value == 1 then + reduceValue lhs' (head value) + else + ( + let + # Find different kinds of infix operators & comparisons + orIdx = resultIndex' (token: token == "or") value; + andIdx = resultIndex' (token: token == "and") value; + compIdx = resultIndex' (token: comparators ? ${token}) value; + inIdx = resultIndex' (token: token == "in") value; + notIdx = # Take possible negation into account + if inIdx > 0 && elemAt value (inIdx - 1) == "not" then inIdx - 1 else -1; + in + # Value has a logical or (takes precedence over and) + if orIdx > 0 then + { + type = "boolOp"; + lhs = reduceValue lhs' (sublist 0 orIdx value); + op = elemAt value orIdx; + rhs = reduceValue lhs' (sublist (orIdx + 1) (length value - 1) value); + } + # Value has a logical and + else if andIdx > 0 then + { + type = "boolOp"; + lhs = reduceValue lhs' (sublist 0 andIdx value); + op = elemAt value andIdx; + rhs = reduceValue lhs' (sublist (andIdx + 1) (length value - 1) value); + } + # Value has a comparison (==, etc) operator + else if compIdx >= 0 then + rec { + type = "compare"; + lhs = reduceValue lhs' (sublist 0 compIdx value); + op = elemAt value compIdx; + rhs = reduceValue lhs (sublist (compIdx + 1) (length value - 1) value); + } + else if notIdx > 0 then + rec { + type = "boolOp"; + lhs = reduceValue lhs' (sublist 0 notIdx value); + op = "not in"; + rhs = reduceValue lhs (sublist (inIdx + 1) (length value - 1) value); + } + else if inIdx > 0 then + rec { + type = "boolOp"; + lhs = reduceValue lhs' (sublist 0 inIdx value); + op = "in"; + rhs = reduceValue lhs (sublist (inIdx + 1) (length value - 1) value); + } + else + throw "Unhandled state for input value: ${toJSON value}" + ) + ) + else if markerFields ? ${value} then + { + type = "variable"; + inherit value; + } + else + ( + let + singleTicked = match "^'(.+)'$" value; + value' = fromJSON (if singleTicked != null then "\"${head singleTicked}\"" else value); + in + if lhs' != { } && lhs'.type == "variable" then + markerFields.${lhs'.value} value' + else + { + type = typeOf value'; + value = value'; + } + ); + in + reduceValue { } groupedTokens; + + /* + Parse a PEP-508 dependency string. + + Type: parseString :: string -> AttrSet + + Example: + # parseString "cachecontrol[filecache]>=0.13.0" + { + conditions = [ + { + op = ">="; + version = { + dev = null; + epoch = 0; + local = null; + post = null; + pre = null; + release = [ 0 13 0 ]; + }; + } + ]; + markers = null; + name = "cachecontrol"; + extras = [ "filecache" ]; + url = null; + } */ - parseString = input: + parseString = + input: let # Split the input into it's distinct parts: The package segment, URL and environment markers tokens = @@ -310,31 +422,35 @@ fix (self: m3 = match "^(.+)@(.+)$" input; in - if m1 != null then { - packageSegment = elemAt m1 0; - url = stripStr (elemAt m1 1); - markerSegment = elemAt m1 2; - } - else if m2 != null then { - packageSegment = elemAt m2 0; - url = null; - markerSegment = elemAt m2 1; - } - else if m3 != null then { - packageSegment = elemAt m3 0; - url = stripStr (elemAt m3 1); - markerSegment = null; - } + if m1 != null then + { + packageSegment = elemAt m1 0; + url = stripStr (elemAt m1 1); + markerSegment = elemAt m1 2; + } + else if m2 != null then + { + packageSegment = elemAt m2 0; + url = null; + markerSegment = elemAt m2 1; + } + else if m3 != null then + { + packageSegment = elemAt m3 0; + url = stripStr (elemAt m3 1); + markerSegment = null; + } else ( if match ".+\/.+" input != null then - # Input is a bare URL + # Input is a bare URL { packageSegment = null; url = input; markerSegment = null; - } else - # Input is a package name + } + else + # Input is a package name { packageSegment = input; url = null; @@ -366,13 +482,16 @@ fix (self: extras = if m1 != null then map pypa.normalizePackageName (splitComma (elemAt m1 1)) else [ ]; in - if tokens.packageSegment == null then { - name = null; - conditions = [ ]; - extras = [ ]; - } else - # Assert that either regex matched - assert m1 != null || m2 != null; { + if tokens.packageSegment == null then + { + name = null; + conditions = [ ]; + extras = [ ]; + } + else + # Assert that either regex matched + assert m1 != null || m2 != null; + { # Based on PEP-508 alone it's not clear whether names should be normalized or not. # From discussion in https://github.com/pypa/packaging-problems/issues/230 # this seems like an oversight and we _should_ actually canonicalize names at parse time. @@ -383,7 +502,8 @@ fix (self: in { name = - if package.name != null then package.name + if package.name != null then + package.name # Infer name from URL if no name was specified explicitly else if tokens.url != null then ( @@ -391,170 +511,195 @@ fix (self: inherit (tokens) url; mEggFragment = match ".+#egg=(.+)" url; in - if mEggFragment != null then elemAt mEggFragment 0 - else null + if mEggFragment != null then elemAt mEggFragment 0 else null ) - else null; + else + null; inherit (package) conditions extras; inherit (tokens) url; - markers = if tokens.markerSegment == null then null else self.parseMarkers tokens.markerSegment; + markers = if tokens.markerSegment == null then null else parseMarkers tokens.markerSegment; }; - /* Create an attrset of platform variables. - As described in https://peps.python.org/pep-0508/#environment-markers. - - Type: mkEnviron :: derivation -> AttrSet - - Example: - # mkEnviron pkgs.python3 - { - implementation_name = { - type = "string"; - value = "cpython"; - }; - implementation_version = { - type = "version"; - value = { - dev = null; - epoch = 0; - local = null; - post = null; - pre = null; - release = [ 3 10 12 ]; - }; - }; - os_name = { - type = "string"; - value = "posix"; - }; - platform_machine = { - type = "string"; - value = "x86_64"; - }; - platform_python_implementation = { - type = "string"; - value = "CPython"; - }; - # platform_release maps to platform.release() which returns - # the running kernel version on Linux. - # Because this field is not reproducible it's left empty. - platform_release = { - type = "string"; - value = ""; - }; - platform_system = { - type = "string"; - value = "Linux"; - }; - # platform_version maps to platform.version() which also returns - # the running kernel version on Linux. - # Because this field is not reproducible it's left empty. - platform_version = { - type = "version"; - value = { - dev = null; - epoch = 0; - local = null; - post = null; - pre = null; - release = [ ]; - }; - }; - python_full_version = { - type = "version"; - value = { - dev = null; - epoch = 0; - local = null; - post = null; - pre = null; - release = [ 3 10 12 ]; - }; - }; - python_version = { - type = "version"; - value = { - dev = null; - epoch = 0; - local = null; - post = null; - pre = null; - release = [ 3 10 ]; - }; - }; - sys_platform = { - type = "string"; - value = "linux"; - }; - } + /* + Create an attrset of platform variables. + As described in https://peps.python.org/pep-0508/#environment-markers. + + Type: mkEnviron :: derivation -> AttrSet + + Example: + # mkEnviron pkgs.python3 + { + implementation_name = { + type = "string"; + value = "cpython"; + }; + implementation_version = { + type = "version"; + value = { + dev = null; + epoch = 0; + local = null; + post = null; + pre = null; + release = [ 3 10 12 ]; + }; + }; + os_name = { + type = "string"; + value = "posix"; + }; + platform_machine = { + type = "string"; + value = "x86_64"; + }; + platform_python_implementation = { + type = "string"; + value = "CPython"; + }; + # platform_release maps to platform.release() which returns + # the running kernel version on Linux. + # Because this field is not reproducible it's left empty. + platform_release = { + type = "string"; + value = ""; + }; + platform_system = { + type = "string"; + value = "Linux"; + }; + # platform_version maps to platform.version() which also returns + # the running kernel version on Linux. + # Because this field is not reproducible it's left empty. + platform_version = { + type = "version"; + value = { + dev = null; + epoch = 0; + local = null; + post = null; + pre = null; + release = [ ]; + }; + }; + python_full_version = { + type = "version"; + value = { + dev = null; + epoch = 0; + local = null; + post = null; + pre = null; + release = [ 3 10 12 ]; + }; + }; + python_version = { + type = "version"; + value = { + dev = null; + epoch = 0; + local = null; + post = null; + pre = null; + release = [ 3 10 ]; + }; + }; + sys_platform = { + type = "string"; + value = "linux"; + }; + } */ - mkEnviron = python: + mkEnviron = + python: let inherit (python) stdenv; inherit (stdenv) targetPlatform; impl = python.passthru.implementation; in - mapAttrs - parseValueVersionDynamic - { - os_name = - if python.pname == "jython" then "java" - else "posix"; - sys_platform = - if stdenv.isLinux then "linux" - else if stdenv.isDarwin then "darwin" - else throw "Unsupported platform"; - platform_machine = - if targetPlatform.isDarwin then targetPlatform.darwinArch - else pep599.manyLinuxTargetMachines.${targetPlatform.parsed.cpu.name} or targetPlatform.parsed.cpu.name; - platform_python_implementation = - if impl == "cpython" then "CPython" - else if impl == "pypy" then "PyPy" - else throw "Unsupported implementation ${impl}"; - platform_release = ""; # Field not reproducible - platform_system = - if stdenv.isLinux then "Linux" - else if stdenv.isDarwin then "Darwin" - else throw "Unsupported platform"; - platform_version = ""; # Field not reproducible - python_version = python.passthru.pythonVersion; - python_full_version = python.version; - implementation_name = python.passthru.implementation; - implementation_version = python.version; - }; + mapAttrs (name: markerFields.${name}) { + os_name = if python.pname == "jython" then "java" else "posix"; + sys_platform = + if stdenv.isLinux then + "linux" + else if stdenv.isDarwin then + "darwin" + else + throw "Unsupported platform"; + platform_machine = + if targetPlatform.isDarwin then + targetPlatform.darwinArch + else + pep599.manyLinuxTargetMachines.${targetPlatform.parsed.cpu.name} or targetPlatform.parsed.cpu.name; + platform_python_implementation = + if impl == "cpython" then + "CPython" + else if impl == "pypy" then + "PyPy" + else + throw "Unsupported implementation ${impl}"; + platform_release = ""; # Field not reproducible + platform_system = + if stdenv.isLinux then + "Linux" + else if stdenv.isDarwin then + "Darwin" + else + throw "Unsupported platform"; + platform_version = ""; # Field not reproducible + python_version = python.passthru.pythonVersion; + python_full_version = python.version; + implementation_name = python.passthru.implementation; + implementation_version = python.version; + }; + + /* + Update one or more keys in an environment created by mkEnviron. - /* Evaluate an environment as returned by `mkEnviron` against markers as returend by `parseMarkers`. + Example: + # setEnviron (mkEnviron pkgs.python3) { platform_release = "5.10.65"; } + */ + setEnviron = environ: updates: environ // mapAttrs (name: markerFields.${name}) updates; - Type: evalMarkers :: AttrSet -> AttrSet -> bool + /* + Evaluate an environment as returned by `mkEnviron` against markers as returend by `parseMarkers`. - Example: - # evalMarkers (mkEnviron pkgs.python3) (parseMarkers "python_version < \"3.11\"") - true + Type: evalMarkers :: AttrSet -> AttrSet -> bool + + Example: + # evalMarkers (mkEnviron pkgs.python3) (parseMarkers "python_version < \"3.11\"") + true */ - evalMarkers = environ: value: ( - let - x = self.evalMarkers environ value.lhs; - y = self.evalMarkers environ value.rhs; - in - if value.type == "compare" then - ( + evalMarkers = + environ: value: + ( + if value.type == "compare" then ( - # Version comparison - if value.lhs.type == "version" || value.rhs.type == "version" then pep440.comparators.${value.op} - # `Extra` environment marker comparison requires special casing because it's equality checks can - # == can be considered a `"key" in set` comparison when multiple extras are activated for a dependency. - # If we didn't treat it this way the check would become quadratic as `evalMarkers` only could check one extra at a time. - else if value.lhs.type == "variable" || value.lhs.value == "extra" then extraComparators.${value.op} - # Simple equality - else comparators.${value.op} - ) x - y - ) - else if value.type == "boolOp" then boolOps.${value.op} x y - else if value.type == "variable" then (self.evalMarkers environ environ.${value.value}) - else if value.type == "version" || value.type == "extra" then value.value - else if isPrimitiveType value.type then value.value - else throw "Unknown type '${value.type}'" - ); - -}) + ( + # Version comparison + if value.lhs.type == "version" || value.rhs.type == "version" then + pep440.comparators.${value.op} + # `Extra` environment marker comparison requires special casing because it's equality checks can + # == can be considered a `"key" in set` comparison when multiple extras are activated for a dependency. + # If we didn't treat it this way the check would become quadratic as `evalMarkers` only could check one extra at a time. + else if value.lhs.type == "variable" || value.lhs.value == "extra" then + extraComparators.${value.op} + # Simple equality + else + comparators.${value.op} + ) + (evalMarkers environ value.lhs) + (evalMarkers environ value.rhs) + ) + else if value.type == "boolOp" then + boolOps.${value.op} (evalMarkers environ value.lhs) (evalMarkers environ value.rhs) + else if value.type == "variable" then + (evalMarkers environ environ.${value.value}) + else if value.type == "version" || value.type == "extra" then + value.value + else if elem value.type primitives then + value.value + else + throw "Unknown type '${value.type}'" + ); + +} diff --git a/vendor/pyproject.nix/lib/pep518.nix b/vendor/pyproject.nix/lib/pep518.nix index 47198b8db..49e846463 100644 --- a/vendor/pyproject.nix/lib/pep518.nix +++ b/vendor/pyproject.nix/lib/pep518.nix @@ -1,12 +1,13 @@ { pep508, ... }: { - /* Parse PEP-518 `build-system.requires` from pyproject.toml. - Type: readPyproject :: AttrSet -> list + /* + Parse PEP-518 `build-system.requires` from pyproject.toml. + Type: readPyproject :: AttrSet -> list - Example: - # parseBuildSystems (lib.importTOML ./pyproject.toml) - [ ] # List of parsed PEP-508 strings as returned by `lib.pep508.parseString`. + Example: + # parseBuildSystems (lib.importTOML ./pyproject.toml) + [ ] # List of parsed PEP-508 strings as returned by `lib.pep508.parseString`. */ parseBuildSystems = pyproject: map pep508.parseString (pyproject.build-system.requires or [ ]); } diff --git a/vendor/pyproject.nix/lib/pep599.nix b/vendor/pyproject.nix/lib/pep599.nix index c7f198915..3e01ddd86 100644 --- a/vendor/pyproject.nix/lib/pep599.nix +++ b/vendor/pyproject.nix/lib/pep599.nix @@ -1,12 +1,13 @@ _: { - /* Map Nixpkgs CPU values to target machines known to be supported for manylinux* wheels (a.k.a. `uname -m`), - in nixpkgs found under the attribute `stdenv.targetPlatform.parsed.cpu.name` + /* + Map Nixpkgs CPU values to target machines known to be supported for manylinux* wheels (a.k.a. `uname -m`), + in nixpkgs found under the attribute `stdenv.targetPlatform.parsed.cpu.name` - Example: - # legacyAliases.powerpc64 - "ppc64" + Example: + # legacyAliases.powerpc64 + "ppc64" */ manyLinuxTargetMachines = { x86_64 = "x86_64"; diff --git a/vendor/pyproject.nix/lib/pep600.nix b/vendor/pyproject.nix/lib/pep600.nix index bfd299c5b..c815d45ba 100644 --- a/vendor/pyproject.nix/lib/pep600.nix +++ b/vendor/pyproject.nix/lib/pep600.nix @@ -1,19 +1,25 @@ { lib, pep599, ... }: let - inherit (builtins) match elemAt compareVersions splitVersion; + inherit (builtins) + match + elemAt + compareVersions + splitVersion + ; inherit (lib) fix; in fix (self: { - /* Map legacy (pre PEP-600) platform tags to PEP-600 compliant ones. + /* + Map legacy (pre PEP-600) platform tags to PEP-600 compliant ones. - https://peps.python.org/pep-0600/#legacy-manylinux-tags + https://peps.python.org/pep-0600/#legacy-manylinux-tags - Type: legacyAliases.${tag} :: AttrSet -> string + Type: legacyAliases.${tag} :: AttrSet -> string - Example: - # legacyAliases."manylinux1_x86_64" or "manylinux1_x86_64" - "manylinux_2_5_x86_64" + Example: + # legacyAliases."manylinux1_x86_64" or "manylinux1_x86_64" + "manylinux_2_5_x86_64" */ legacyAliases = { manylinux1_x86_64 = "manylinux_2_5_x86_64"; @@ -29,13 +35,14 @@ fix (self: { manylinux2014_s390x = "manylinux_2_17_s390x"; }; - /* Check if a manylinux tag is compatible with a given stdenv. + /* + Check if a manylinux tag is compatible with a given stdenv. - Type: manyLinuxTagCompatible :: AttrSet -> derivation -> string -> bool + Type: manyLinuxTagCompatible :: AttrSet -> derivation -> string -> bool - Example: - # manyLinuxTagCompatible pkgs.stdenv.targetPlatform pkgs.stdenv.cc.libc "manylinux_2_5_x86_64" - true + Example: + # manyLinuxTagCompatible pkgs.stdenv.targetPlatform pkgs.stdenv.cc.libc "manylinux_2_5_x86_64" + true */ manyLinuxTagCompatible = # Platform attrset (`lib.systems.elaborate "x86_64-linux"`) @@ -47,19 +54,24 @@ fix (self: { let tag' = self.legacyAliases.${tag} or tag; m = match "manylinux_([0-9]+)_([0-9]+)_(.*)" tag'; - mAt = elemAt m; - tagMajor = mAt 0; - tagMinor = mAt 1; - tagArch = mAt 2; + tagMajor = elemAt m 0; + tagMinor = elemAt m 1; + tagArch = elemAt m 2; sysVersion' = elemAt (splitVersion libc.version); sysMajor = sysVersion' 0; sysMinor = sysVersion' 1; in - if m == null then throw "'${tag'}' is not a valid manylinux tag." - else if platform.libc != "glibc" then false - else if libc.pname != "glibc" then false - else if compareVersions "${sysMajor}.${sysMinor}" "${tagMajor}.${tagMinor}" < 0 then false - else if pep599.manyLinuxTargetMachines.${tagArch} != platform.parsed.cpu.name then false - else true; + if m == null then + throw "'${tag'}' is not a valid manylinux tag." + else if platform.libc != "glibc" then + false + else if libc.pname != "glibc" then + false + else if compareVersions "${sysMajor}.${sysMinor}" "${tagMajor}.${tagMinor}" < 0 then + false + else if pep599.manyLinuxTargetMachines.${tagArch} != platform.parsed.cpu.name then + false + else + true; }) diff --git a/vendor/pyproject.nix/lib/pep621.nix b/vendor/pyproject.nix/lib/pep621.nix index aa76bc617..ed6b1f3bf 100644 --- a/vendor/pyproject.nix/lib/pep621.nix +++ b/vendor/pyproject.nix/lib/pep621.nix @@ -1,36 +1,66 @@ -{ lib, pep440, pep508, pep518, pypa, ... }: +{ + lib, + pep440, + pep508, + pep518, + ... +}: let - inherit (builtins) mapAttrs foldl' split filter elem; - inherit (lib) isString filterAttrs fix; + inherit (builtins) + mapAttrs + foldl' + split + filter + ; + inherit (lib) + isString + filterAttrs + fix + nameValuePair + mapAttrs' + length + ; splitAttrPath = path: filter isString (split "\\." path); - getAttrPath = path: lib.attrByPath (splitAttrPath path) { }; + getAttrPath = path: lib.attrByPath (splitAttrPath path); in -fix (self: { - /* Parse dependencies from pyproject.toml. - - Type: parseDependencies :: AttrSet -> AttrSet - - Example: - # parseDependencies { - # - # pyproject = (lib.importTOML ./pyproject.toml); - # # Don't just look at `project.optional-dependencies` for groups, also look at these: - # extrasAttrPaths = [ "tool.pdm.dev-dependencies" ]; - # } - { - dependencies = [ ]; # List of parsed PEP-508 strings (lib.pep508.parseString) - extras = { - dev = [ ]; # List of parsed PEP-508 strings (lib.pep508.parseString) - }; - build-systems = [ ]; # PEP-518 build-systems (List of parsed PEP-508 strings) - } +fix (_self: { + /* + Parse dependencies from pyproject.toml. + + Type: parseDependencies :: AttrSet -> AttrSet + + Example: + # parseDependencies { + # + # pyproject = (lib.importTOML ./pyproject.toml); + # # Don't just look at `project.optional-dependencies` for groups, also look at these: + # extrasAttrPaths = [ "tool.pdm.dev-dependencies" ]; + # } + { + dependencies = [ ]; # List of parsed PEP-508 strings (lib.pep508.parseString) + extras = { + dev = [ ]; # List of parsed PEP-508 strings (lib.pep508.parseString) + }; + build-systems = [ ]; # PEP-518 build-systems (List of parsed PEP-508 strings) + } */ - parseDependencies = { pyproject, extrasAttrPaths ? [ ] }: + parseDependencies = + { + pyproject, + extrasAttrPaths ? [ ], + extrasListPaths ? { }, + }: let # Fold extras from all considered attributes into one set - extras' = foldl' (acc: attr: acc // getAttrPath attr pyproject) (pyproject.project.optional-dependencies or { }) extrasAttrPaths; + extras' = + foldl' (acc: attr: acc // getAttrPath attr { } pyproject) (pyproject.project.optional-dependencies + or { } + ) extrasAttrPaths + // filterAttrs (_: deps: length deps > 0) ( + mapAttrs' (path: attr: nameValuePair attr (getAttrPath path [ ] pyproject)) extrasListPaths + ); in { dependencies = map pep508.parseString (pyproject.project.dependencies or [ ]); @@ -38,49 +68,25 @@ fix (self: { build-systems = pep518.parseBuildSystems pyproject; }; - /* Parse project.python-requires from pyproject.toml + /* + Parse project.python-requires from pyproject.toml - Type: parseRequiresPython :: AttrSet -> list + Type: parseRequiresPython :: AttrSet -> list - Example: - # parseRequiresPython (lib.importTOML ./pyproject.toml) - [ ] # List of conditions as returned by `lib.pep440.parseVersionCond` + Example: + # parseRequiresPython (lib.importTOML ./pyproject.toml) + [ ] # List of conditions as returned by `lib.pep440.parseVersionCond` */ parseRequiresPython = pyproject: pep440.parseVersionConds (pyproject.project.requires-python or ""); - /* Takes a dependency structure as returned by `lib.pep621.parseDependencies` and transforms it into - a structure with it's package names. + /* + Filter dependencies not relevant for this environment. - Type: getDependenciesNames :: AttrSet -> AttrSet + Type: filterDependenciesByEnviron :: AttrSet -> AttrSet -> AttrSet - Example: - # getDependenciesNames (pep621.parseDependencies { pyproject = (lib.importTOML ./pyproject.toml); }) - { - dependencies = [ "requests" ]; - extras = { - dev = [ "pytest" ]; - }; - build-systems = [ "poetry-core" ]; - } - */ - getDependenciesNames = - let - normalize = pypa.normalizePackageName; - getNames = map (dep: normalize dep.name); - in - dependencies: { - dependencies = getNames dependencies.dependencies; - extras = mapAttrs (_: getNames) dependencies.extras; - build-systems = getNames dependencies.build-systems; - }; - - /* Filter dependencies not relevant for this environment. - - Type: filterDependenciesByEnviron :: AttrSet -> AttrSet -> AttrSet - - Example: - # filterDependenciesByEnviron (lib.pep508.mkEnviron pkgs.python3) (lib.pep621.parseDependencies (lib.importTOML ./pyproject.toml)) - { } # Structure omitted in docs + Example: + # filterDependenciesByEnviron (lib.pep508.mkEnviron pkgs.python3) (lib.pep621.parseDependencies (lib.importTOML ./pyproject.toml)) + { } # Structure omitted in docs */ filterDependenciesByEnviron = # Environ as created by `lib.pep508.mkEnviron`. @@ -91,59 +97,24 @@ fix (self: { dependencies: ( let - filterList = environ: filter (dep: dep.markers == null || pep508.evalMarkers - (environ // { - extra = { - type = "extra"; - value = extras; + environ' = + if extras == [ ] then + environ + else + environ + // { + extra = { + type = "extra"; + value = extras; + }; }; - }) - dep.markers); + + filterList = filter (dep: dep.markers == null || pep508.evalMarkers environ' dep.markers); in { - dependencies = filterList environ dependencies.dependencies; - extras = mapAttrs (_: filterList environ) dependencies.extras; - build-systems = filterList environ dependencies.build-systems; + dependencies = filterList dependencies.dependencies; + extras = mapAttrs (_: filterList) dependencies.extras; + build-systems = filterList dependencies.build-systems; } ); - - /* Filter dependencies by their extras groups. - - Type: filterDependenciesByExtras :: list[string] -> AttrSet -> AttrSet - - Example: - # filterDependenciesByExtras [ "dev" ] (lib.pep621.parseDependencies (lib.importTOML ./pyproject.toml)) - { } # Structure omitted in docs - */ - filterDependenciesByExtras = - # Extras groups as a list of strings. - extras: - # Dependencies as parsed by `lib.pep621.parseDependencies`. - dependencies: - dependencies // { - extras = filterAttrs (group: _: elem group extras) dependencies.extras; - }; - - /* Aggregate of `filterDependencies` & `filterDependenciesByExtras` - - Type: filterDependencies :: AttrSet -> AttrSet - - Example: - # filterDependencies { - # dependencies = lib.pep621.parseDependencies (lib.importTOML ./pyproject.toml); - # environ = lib.pep508.mkEnviron pkgs.python; - # extras = [ "dev" ]; - # } - { } # Structure omitted in docs - */ - filterDependencies = - { - # Dependencies as parsed by `lib.pep621.parseDependencies` - dependencies - , # Environ as created by `lib.pep508.mkEnviron` - environ - , # Extras as a list of strings - extras ? [ ] - , - }: self.filterDependenciesByEnviron environ extras (self.filterDependenciesByExtras extras dependencies); }) diff --git a/vendor/pyproject.nix/lib/pep656.nix b/vendor/pyproject.nix/lib/pep656.nix index 4888dd6c4..770fe29ff 100644 --- a/vendor/pyproject.nix/lib/pep656.nix +++ b/vendor/pyproject.nix/lib/pep656.nix @@ -1,17 +1,23 @@ { pep599, ... }: let - inherit (builtins) match elemAt compareVersions splitVersion; + inherit (builtins) + match + elemAt + compareVersions + splitVersion + ; in { - /* Check if a musllinux tag is compatible with a given stdenv. + /* + Check if a musllinux tag is compatible with a given stdenv. - Type: muslLinuxTagCompatible :: AttrSet -> derivation -> string -> bool + Type: muslLinuxTagCompatible :: AttrSet -> derivation -> string -> bool - Example: - # muslLinuxTagCompatible pkgs.stdenv.targetPlatform pkgs.stdenv.cc.libc "musllinux_1_1_x86_64" - true + Example: + # muslLinuxTagCompatible pkgs.stdenv.targetPlatform pkgs.stdenv.cc.libc "musllinux_1_1_x86_64" + true */ muslLinuxTagCompatible = # Platform attrset (`lib.systems.elaborate "x86_64-linux"`) @@ -22,19 +28,24 @@ in tag: let m = match "musllinux_([0-9]+)_([0-9]+)_(.*)" tag; - mAt = elemAt m; - tagMajor = mAt 0; - tagMinor = mAt 1; - tagArch = mAt 2; + tagMajor = elemAt m 0; + tagMinor = elemAt m 1; + tagArch = elemAt m 2; sysVersion' = elemAt (splitVersion libc.version); sysMajor = sysVersion' 0; sysMinor = sysVersion' 1; in - if m == null then throw "'${tag}' is not a valid musllinux tag." - else if platform.libc != "musl" then false - else if libc.pname != "musl" then false - else if compareVersions "${sysMajor}.${sysMinor}" "${tagMajor}.${tagMinor}" < 0 then false - else if pep599.manyLinuxTargetMachines.${tagArch} != platform.parsed.cpu.name then false - else true; + if m == null then + throw "'${tag}' is not a valid musllinux tag." + else if platform.libc != "musl" then + false + else if libc.pname != "musl" then + false + else if compareVersions "${sysMajor}.${sysMinor}" "${tagMajor}.${tagMinor}" < 0 then + false + else if pep599.manyLinuxTargetMachines.${tagArch} != platform.parsed.cpu.name then + false + else + true; } diff --git a/vendor/pyproject.nix/lib/pip.nix b/vendor/pyproject.nix/lib/pip.nix index bc78dfc06..171efad2f 100644 --- a/vendor/pyproject.nix/lib/pip.nix +++ b/vendor/pyproject.nix/lib/pip.nix @@ -1,9 +1,18 @@ -{ lib -, pep508 -, ... -}: +{ lib, pep508, ... }: let - inherit (builtins) match head tail typeOf split filter foldl' readFile dirOf hasContext unsafeDiscardStringContext; + inherit (builtins) + match + head + tail + typeOf + split + filter + foldl' + readFile + dirOf + hasContext + unsafeDiscardStringContext + ; inherit (import ./util.nix { inherit lib; }) stripStr; uncomment = l: head (match " *([^#]*).*" l); @@ -11,13 +20,14 @@ let in lib.fix (self: { - /* Parse dependencies from requirements.txt + /* + Parse dependencies from requirements.txt - Type: parseRequirementsTxt :: AttrSet -> list + Type: parseRequirementsTxt :: AttrSet -> list - Example: - # parseRequirements ./requirements.txt - [ { flags = []; requirement = {}; # Returned by pep508.parseString } ] + Example: + # parseRequirements ./requirements.txt + [ { flags = []; requirement = {}; # Returned by pep508.parseString } ] */ parseRequirementsTxt = @@ -33,66 +43,78 @@ lib.fix (self: { # We also need to support stringly paths... isPath = typeOf requirements == "path" || hasContext requirements; path' = - if typeOf requirements == "path" then requirements else - /. + unsafeDiscardStringContext requirements; + if typeOf requirements == "path" then + requirements + else + /. + unsafeDiscardStringContext requirements; root = dirOf path'; # Requirements without comments and no empty strings requirements' = if isPath then readFile path' else requirements; - lines' = filter (l: l != "") (map uncomment (filter (l: typeOf l == "string") (split "\n" requirements'))); + lines' = filter (l: l != "") ( + map uncomment (filter (l: typeOf l == "string") (split "\n" requirements')) + ); # Fold line continuations - inherit ((foldl' + inherit ( - acc: l': - let - m = match "(.+) *\\\\" l'; - continue = m != null; - l = stripStr ( - if continue - then (head m) - else l' - ); - in - if continue - then { - line = acc.line ++ [ l ]; - inherit (acc) lines; - } - else { - line = [ ]; - lines = acc.lines ++ [ (acc.line ++ [ l ]) ]; - } + (foldl' + ( + acc: l': + let + m = match "(.+) *\\\\" l'; + continue = m != null; + l = stripStr (if continue then (head m) else l'); + in + if continue then + { + line = acc.line ++ [ l ]; + inherit (acc) lines; + } + else + { + line = [ ]; + lines = acc.lines ++ [ (acc.line ++ [ l ]) ]; + } + ) + { + lines = [ ]; + line = [ ]; + } + lines' + ) ) - { - lines = [ ]; - line = [ ]; - } - lines')) lines; + lines + ; in - foldl' - (acc: l: + foldl' ( + acc: l: let m = match "-(c|r) (.+)" (head l); in - acc ++ ( + acc + ++ ( # Common case, parse string - if m == null - then [{ - requirement = pep508.parseString (head l); - flags = tail l; - }] + if m == null then + [ + { + requirement = pep508.parseString (head l); + flags = tail l; + } + ] # Don't support constraint files - else if (head m) == "c" then throw "Unsupported flag: -c" + else if (head m) == "c" then + throw "Unsupported flag: -c" # Recursive requirements.txt else (self.parseRequirementsTxt ( - if root == null then throw "When importing recursive requirements.txt requirements needs to be passed as a path" - else root + "/${head (tail m)}" + if root == null then + throw "When importing recursive requirements.txt requirements needs to be passed as a path" + else + root + "/${head (tail m)}" )) - )) - [ ] - lines; + ) + ) [ ] lines; }) diff --git a/vendor/pyproject.nix/lib/poetry.nix b/vendor/pyproject.nix/lib/poetry.nix index 5562c8113..b78274ac7 100644 --- a/vendor/pyproject.nix/lib/poetry.nix +++ b/vendor/pyproject.nix/lib/poetry.nix @@ -1,262 +1,317 @@ -{ lib -, pep440 -, pep508 -, pep518 -, ... +{ + lib, + pep440, + pep508, + pep518, + ... }: -lib.fix (self: -let - inherit (builtins) match elemAt foldl' typeOf attrNames head tail mapAttrs length filter split; - inherit (lib) optionalAttrs flatten; - inherit (import ./util.nix { inherit lib; }) splitComma; - - # Translate author from a string like "Name " to a structured set as defined by PEP-621. - translateAuthor = a: - let - mAt = elemAt (match "^(.+) <(.+)>$" a); - in - { name = mAt 0; email = mAt 1; }; - - # Normalize dependecy from poetry dependencies table from (string || set) -> set - normalizeDep = name: dep: ( - let - type = typeOf dep; - in - if type == "string" then { - inherit name; - version = dep; - } - else if type == "set" then dep // { inherit name; } - else throw "Unexpected type: ${type}" - ); - - # Rewrite the right hand side version for caret comparisons according to the rules laid out in - # https://python-poetry.org/docs/dependency-specification/#caret-requirements - rewriteCaretRhs = release: - let - state = foldl' - (state: v: - let - nonzero = state.nonzero || v != 0; - in - state // { - release = state.release ++ [ - ( - if nonzero && !state.nonzero then (v + 1) - else if nonzero then 0 - else v - ) - ]; - inherit nonzero; - }) - { - release = [ ]; - nonzero = false; - } - release; - in - if !state.nonzero - then ([ (head state.release + 1) ] ++ tail state.release) - else state.release; +lib.fix ( + self: + let + inherit (builtins) + match + elemAt + foldl' + typeOf + attrNames + head + tail + mapAttrs + length + filter + split + ; + inherit (lib) optionalAttrs concatLists; + inherit (import ./util.nix { inherit lib; }) splitComma; - # Poetry dependency tables are of mixed types: - # [tool.poetry.dependencies] - # python = "^3.8" - # cachecontrol = { version = "^0.13.0", extras = ["filecache"] } - # foo = [ - # {version = "<=1.9", python = ">=3.6,<3.8"}, - # {version = "^2.0", python = ">=3.8"} - # ] - # - # These are all valid. Normalize the input to a list of: - # [ - # { name = "python"; version = "^3.8"; } - # { name = "cachecontrol"; version = "^0.13.0"; extras = ["filecache"]; } - # { name = "foo"; version = "<=1.9"; python = ">=3.6,<3.8"; } - # { name = "foo"; version = "^2.0"; python = ">=3.8"; } - # ] - normalizeDependendenciesToList = deps: foldl' - (acc: name: acc ++ ( + # Translate author from a string like "Name " to a structured set as defined by PEP-621. + translateAuthor = + a: let - dep = deps.${name}; + m = match "^(.+) <(.+)>$" a; in - if typeOf dep == "list" then map (normalizeDep name) dep - else [ (normalizeDep name dep) ] - )) [ ] - (attrNames deps); + assert m != null; + { + name = elemAt m 0; + email = elemAt m 1; + }; - # Normalized version of parseVersionCond' - parseVersionConds = s: flatten (map self.parseVersionCond (splitComma s)); + # Normalize dependecy from poetry dependencies table from (string || set) -> set + normalizeDep = + name: dep: + ( + let + type = typeOf dep; + in + if type == "string" then + { + inherit name; + version = dep; + } + else if type == "set" then + dep // { inherit name; } + else + throw "Unexpected type: ${type}" + ); - dummyMarker = { - type = "bool"; - value = true; - }; + # Rewrite the right hand side version for caret comparisons according to the rules laid out in + # https://python-poetry.org/docs/dependency-specification/#caret-requirements + rewriteCaretRhs = + release: + let + state = + foldl' + ( + state: v: + let + nonzero = state.nonzero || v != 0; + in + state + // { + release = state.release ++ [ + ( + if nonzero && !state.nonzero then + (v + 1) + else if nonzero then + 0 + else + v + ) + ]; + inherit nonzero; + } + ) + { + release = [ ]; + nonzero = false; + } + release; + in + if !state.nonzero then ([ (head state.release + 1) ] ++ tail state.release) else state.release; - # Analogous to pep508.parseString - parseDependency = dep: - let - # Poetry has Python as a separate field in the structured dependency object. - # This is non-standard. Rewrite these expressions as a nested set of logical ANDs that - # looks like regular parsed markers as if they were standard PEP-508, just written in a bit of a funky - # nested way that no human would do. - markers = foldl' - (rhs: pyCond: { - type = "boolOp"; - op = "and"; - lhs = { - type = "compare"; - inherit (pyCond) op; - lhs = { - type = "variable"; - value = "python_version"; - }; - rhs = { - type = "version"; - value = pyCond.version; - }; - }; - inherit rhs; - }) - ( - # Encode no markers as a marker that always evaluates to true to simplify fold logi above. - if dep ? markers then pep508.parseMarkers dep.markers else dummyMarker + # Poetry dependency tables are of mixed types: + # [tool.poetry.dependencies] + # python = "^3.8" + # cachecontrol = { version = "^0.13.0", extras = ["filecache"] } + # foo = [ + # {version = "<=1.9", python = ">=3.6,<3.8"}, + # {version = "^2.0", python = ">=3.8"} + # ] + # + # These are all valid. Normalize the input to a list of: + # [ + # { name = "python"; version = "^3.8"; } + # { name = "cachecontrol"; version = "^0.13.0"; extras = ["filecache"]; } + # { name = "foo"; version = "<=1.9"; python = ">=3.6,<3.8"; } + # { name = "foo"; version = "^2.0"; python = ">=3.8"; } + # ] + normalizeDependendenciesToList = + deps: + foldl' ( + acc: name: + acc + ++ ( + let + dep = deps.${name}; + in + if typeOf dep == "list" then map (normalizeDep name) dep else [ (normalizeDep name dep) ] ) - (if dep ? python then parseVersionConds dep.python else [ ]); + ) [ ] (attrNames deps); - in - { - inherit (dep) name; - conditions = parseVersionConds dep.version; - extras = dep.extras or [ ]; - url = dep.url or null; - markers = if markers == dummyMarker then null else markers; + dummyMarker = { + type = "bool"; + value = true; }; -in -{ - /* - Translate a Pyproject.toml from Poetry to PEP-621 project metadata. - This function transposes a PEP-621 project table on top of an existing Pyproject.toml populated with data from `tool.poetry`. - Notably does not translate dependencies/optional-dependencies. + # Analogous to pep508.parseString + parseDependency = + dep: + let + # Poetry has Python as a separate field in the structured dependency object. + # This is non-standard. Rewrite these expressions as a nested set of logical ANDs that + # looks like regular parsed markers as if they were standard PEP-508, just written in a bit of a funky + # nested way that no human would do. + markers = + foldl' + (rhs: pyCond: { + type = "boolOp"; + op = "and"; + lhs = { + type = "compare"; + inherit (pyCond) op; + lhs = { + type = "variable"; + value = "python_version"; + }; + rhs = { + type = "version"; + value = pyCond.version; + }; + }; + inherit rhs; + }) + ( + # Encode no markers as a marker that always evaluates to true to simplify fold logi above. + if dep ? markers then pep508.parseMarkers dep.markers else dummyMarker + ) + (if dep ? python then self.parseVersionConds dep.python else [ ]); + + in + { + inherit (dep) name; + conditions = if dep ? version then self.parseVersionConds dep.version else [ ]; + extras = dep.extras or [ ]; + url = dep.url or null; + markers = if markers == dummyMarker then null else markers; + }; + + in + { + /* + Translate a Pyproject.toml from Poetry to PEP-621 project metadata. + This function transposes a PEP-621 project table on top of an existing Pyproject.toml populated with data from `tool.poetry`. + Notably does not translate dependencies/optional-dependencies. - For parsing dependencies from Poetry see `lib.poetry.parseDependencies`. + For parsing dependencies from Poetry see `lib.poetry.parseDependencies`. - Type: translatePoetryProject :: AttrSet -> lambda + Type: translatePoetryProject :: AttrSet -> lambda - Example: - # translatePoetryProject (lib.importTOML ./pyproject.toml) - { } # TOML contents, structure omitted. See PEP-621 for more information on data members. + Example: + # translatePoetryProject (lib.importTOML ./pyproject.toml) + { } # TOML contents, structure omitted. See PEP-621 for more information on data members. */ - translatePoetryProject = pyproject: assert !(pyproject ? project); let - inherit (pyproject.tool) poetry; - in - pyproject // { - project = { - inherit (poetry) name version description; - authors = map translateAuthor poetry.authors; - urls = optionalAttrs (poetry ? homepage) - { - Homepage = poetry.homepage; - } // optionalAttrs (poetry ? repository) { - Repository = poetry.repository; - } // optionalAttrs (poetry ? documentation) { - Documentation = poetry.documentation; + translatePoetryProject = + pyproject: + assert !(pyproject ? project); + let + inherit (pyproject.tool) poetry; + in + pyproject + // { + project = + { + inherit (poetry) name version description; + authors = map translateAuthor poetry.authors; + urls = + optionalAttrs (poetry ? homepage) { Homepage = poetry.homepage; } + // optionalAttrs (poetry ? repository) { Repository = poetry.repository; } + // optionalAttrs (poetry ? documentation) { Documentation = poetry.documentation; }; + } + // optionalAttrs (poetry ? license) { license.text = poetry.license; } + // optionalAttrs (poetry ? maintainers) { maintainers = map translateAuthor poetry.maintainers; } + // optionalAttrs (poetry ? readme) { inherit (poetry) readme; } + // optionalAttrs (poetry ? keywords) { inherit (poetry) keywords; } + // optionalAttrs (poetry ? classifiers) { inherit (poetry) classifiers; } + // optionalAttrs (poetry ? scripts) { inherit (poetry) scripts; } + // optionalAttrs (poetry ? plugins) { entry-points = poetry.plugins; }; }; - } // optionalAttrs (poetry ? license) { - license.text = poetry.license; - } // optionalAttrs (poetry ? maintainers) { - maintainers = map translateAuthor poetry.maintainers; - } // optionalAttrs (poetry ? readme) { - inherit (poetry) readme; - } // optionalAttrs (poetry ? keywords) { - inherit (poetry) keywords; - } // optionalAttrs (poetry ? classifiers) { - inherit (poetry) classifiers; - } // optionalAttrs (poetry ? scripts) { - inherit (poetry) scripts; - } // optionalAttrs (poetry ? plugins) { - entry-points = poetry.plugins; - }; - }; - /* Parse dependencies from pyproject.toml (Poetry edition). - This function is analogous to `lib.pep621.parseDependencies`. + /* + Parse dependencies from pyproject.toml (Poetry edition). + This function is analogous to `lib.pep621.parseDependencies`. - Type: parseDependencies :: AttrSet -> AttrSet + Type: parseDependencies :: AttrSet -> AttrSet + + Example: + # parseDependencies { + # + # pyproject = (lib.importTOML ./pyproject.toml); + # } + { + dependencies = [ ]; # List of parsed PEP-508 strings (lib.pep508.parseString) + extras = { + dev = [ ]; # List of parsed PEP-508 strings (lib.pep508.parseString) + }; + build-systems = [ ]; # PEP-518 build-systems (List of parsed PEP-508 strings) + } + */ + parseDependencies = pyproject: { + dependencies = map parseDependency ( + normalizeDependendenciesToList (pyproject.tool.poetry.dependencies or { }) + ); + extras = mapAttrs ( + _: group: map parseDependency (normalizeDependendenciesToList group.dependencies) + ) pyproject.tool.poetry.group or { }; + build-systems = pep518.parseBuildSystems pyproject; + }; - Example: - # parseDependencies { - # - # pyproject = (lib.importTOML ./pyproject.toml); - # } - { - dependencies = [ ]; # List of parsed PEP-508 strings (lib.pep508.parseString) - extras = { - dev = [ ]; # List of parsed PEP-508 strings (lib.pep508.parseString) - }; - build-systems = [ ]; # PEP-518 build-systems (List of parsed PEP-508 strings) - } - */ - parseDependencies = pyproject: { - dependencies = map parseDependency (normalizeDependendenciesToList (pyproject.tool.poetry.dependencies or { })); - extras = mapAttrs (_: group: map parseDependency (normalizeDependendenciesToList group.dependencies)) pyproject.tool.poetry.group or { }; - build-systems = pep518.parseBuildSystems pyproject; - }; + /* + Parse a version conditional. + Supports additional non-standard operators `^` and `~` used by Poetry. - /* Parse a version conditional. - Supports additional non-standard operators `^` and `~` used by Poetry. + Because some expressions desugar to multiple expressions parseVersionCond returns a list. - Because some expressions desugar to multiple expressions parseVersionCond returns a list. + Type: parseVersionCond :: string -> [ AttrSet ] + */ + parseVersionCond = + cond: + ( + let + m = match "^([~^])?([a-zA-Z0-9].+)$" cond; + c = elemAt m 0; + rest = elemAt m 1; + # Pad version before parsing as it's _much_ easier to reason about + # once they're the same length + version = pep440.parseVersion (lib.versions.pad 3 rest); - Type: parseVersionCond :: string -> [ AttrSet ] - */ - parseVersionCond = cond: ( - let - m = match "^([~^])?([a-zA-Z0-9].+)$" cond; - mAt = elemAt m; - c = mAt 0; - rest = mAt 1; - # Pad version before parsing as it's _much_ easier to reason about - # once they're the same length - version = pep440.parseVersion (lib.versions.pad 3 rest); + # Count the number of segments in the input to use an an index in ~ rewriting + segments = length (filter (tok: typeOf tok == "string") (split "\\." rest)); + in + if m == null then + [ (pep440.parseVersionCond cond) ] + # Desugar ~ into >= && < + else if c == "~" then + [ + { + op = ">="; + inherit version; + } + { + op = "<"; + version = version // { + release = lib.imap0 ( + i: tok: + if i >= segments - 1 then + 0 + else if i == segments - 2 then + (tok + 1) + else + tok + ) version.release; + }; + } + ] + # Desugar ^ into >= && < + else if c == "^" then + [ + { + op = ">="; + inherit version; + } + { + op = "<"; + version = version // { + release = rewriteCaretRhs version.release; + }; + } + ] + # Versions without operators are exact matches, add operator according to PEP-440 + else + [ + { + op = "=="; + inherit version; + } + ] + ); - # Count the number of segments in the input to use an an index in ~ rewriting - segments = length (filter (tok: typeOf tok == "string") (split "\\." rest)); - in - if m == null then [ (pep440.parseVersionCond cond) ] - # Desugar ~ into >= && < - else if c == "~" then [ - { - op = ">="; - inherit version; - } - { - op = "<"; - version = version // { - release = lib.imap0 (i: tok: if i >= segments - 1 then 0 else if i == segments - 2 then (tok + 1) else tok) version.release; - }; - } - ] - # Desugar ^ into >= && < - else if c == "^" then [ - { - op = ">="; - inherit version; - } - { - op = "<"; - version = version // { - release = rewriteCaretRhs version.release; - }; - } - ] - # Versions without operators are exact matches, add operator according to PEP-440 - else [{ - op = "=="; - inherit version; - }] - ); + /* + Parse a comma separated list version conditionals. + Supports additional non-standard operators `^` and `~` used by Poetry. -}) + Type: parseVersionConds :: string -> [ AttrSet ] + */ + parseVersionConds = s: concatLists (map self.parseVersionCond (splitComma s)); + } +) diff --git a/vendor/pyproject.nix/lib/project.nix b/vendor/pyproject.nix/lib/project.nix index a463fa286..9d6859fb9 100644 --- a/vendor/pyproject.nix/lib/project.nix +++ b/vendor/pyproject.nix/lib/project.nix @@ -1,4 +1,12 @@ -{ pep621, poetry, pip, lib, renderers, validators, ... }: +{ + pep621, + poetry, + pip, + lib, + renderers, + validators, + ... +}: let inherit (builtins) mapAttrs; @@ -6,95 +14,138 @@ let # Map over renderers and inject project argument. # This allows for a user interface like: # project.renderers.buildPythonPackage { } where project is already curried. - curryProject = attrs: project: lib.mapAttrs (_: func: args: func (args // { inherit project; })) attrs; + curryProject = + attrs: project: + lib.mapAttrs ( + _: func: args: + func (args // { inherit project; }) + ) attrs; + + # Package manager specific extensions. + # Remap extension fields to optional-dependencies + uvListPaths = { + "tool.uv.dev-dependencies" = "dev-dependencies"; + }; + pdmAttrPaths = [ "tool.pdm.dev-dependencies" ]; in lib.fix (self: { - /* Load dependencies from a PEP-621 pyproject.toml. - - Type: loadPyproject :: AttrSet -> AttrSet - - Example: - # loadPyproject { pyproject = lib.importTOML } - { - dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` - build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` - pyproject = { }; # The unmarshaled contents of pyproject.toml - projectRoot = null; # Path to project root - requires-python = null; # requires-python as parsed by pep621.parseRequiresPython - } + /* + Load dependencies from a PEP-621 pyproject.toml. + + Type: loadPyproject :: AttrSet -> AttrSet + + Example: + # loadPyproject { pyproject = lib.importTOML } + { + dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` + build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` + pyproject = { }; # The unmarshaled contents of pyproject.toml + projectRoot = null; # Path to project root + requires-python = null; # requires-python as parsed by pep621.parseRequiresPython + } */ loadPyproject = { # The unmarshaled contents of pyproject.toml - pyproject ? lib.importTOML (projectRoot + "/pyproject.toml") - , # Example: extrasAttrPaths = [ "tool.pdm.dev-dependencies" ]; - extrasAttrPaths ? [ ] - , # Path to project root - projectRoot ? null - , - }: lib.fix (project: { - dependencies = pep621.parseDependencies { inherit pyproject extrasAttrPaths; }; + pyproject ? lib.importTOML (projectRoot + "/pyproject.toml"), + # Example: extrasAttrPaths = [ "tool.pdm.dev-dependencies" ]; + extrasAttrPaths ? [ ], + # Example: extrasListPaths = { "tool.uv.dependencies.dev-dependencies" = "dev-dependencies"; } + extrasListPaths ? { }, + # Path to project root + projectRoot ? null, + }: + lib.fix (project: { + dependencies = pep621.parseDependencies { inherit pyproject extrasAttrPaths extrasListPaths; }; inherit pyproject projectRoot; renderers = curryProject renderers project; validators = curryProject validators project; requires-python = pep621.parseRequiresPython pyproject; }); - /* Load dependencies from a PDM pyproject.toml. + /* + Load dependencies from a uv pyproject.toml. - Type: loadPDMPyproject :: AttrSet -> AttrSet + Type: loadUVPyproject :: AttrSet -> AttrSet - Example: - # loadPyproject { pyproject = lib.importTOML } - { - dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` - build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` - pyproject = { }; # The unmarshaled contents of pyproject.toml - projectRoot = null; # Path to project root - requires-python = null; # requires-python as parsed by pep621.parseRequiresPython - } + Example: + # loadUVPyproject { projectRoot = ./.; } + { + dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` + build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` + pyproject = { }; # The unmarshaled contents of pyproject.toml + projectRoot = null; # Path to project root + requires-python = null; # requires-python as parsed by pep621.parseRequiresPython + } */ - loadPDMPyproject = + loadUVPyproject = { # The unmarshaled contents of pyproject.toml - pyproject ? lib.importTOML (projectRoot + "/pyproject.toml") - , # Path to project root - projectRoot ? null - , # The unmarshaled contents of pdm.lock - pdmLock ? lib.importTOML (projectRoot + "/pdm.lock") - , - }: self.loadPyproject + pyproject ? lib.importTOML (projectRoot + "/pyproject.toml"), + # Path to project root + projectRoot ? null, + }: + self.loadPyproject { + inherit pyproject projectRoot; + extrasListPaths = uvListPaths; + }; + + /* + Load dependencies from a PDM pyproject.toml. + + Type: loadPDMPyproject :: AttrSet -> AttrSet + + Example: + # loadPyproject { projectRoot = ./.; } { - inherit pyproject projectRoot; - extrasAttrPaths = [ "tool.pdm.dev-dependencies" ]; - } // { + dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` + build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` + pyproject = { }; # The unmarshaled contents of pyproject.toml + projectRoot = null; # Path to project root + requires-python = null; # requires-python as parsed by pep621.parseRequiresPython + } + */ + loadPDMPyproject = + { + # The unmarshaled contents of pyproject.toml + pyproject ? lib.importTOML (projectRoot + "/pyproject.toml"), + # Path to project root + projectRoot ? null, + # The unmarshaled contents of pdm.lock + pdmLock ? lib.importTOML (projectRoot + "/pdm.lock"), + }: + self.loadPyproject { + inherit pyproject projectRoot; + extrasAttrPaths = pdmAttrPaths; + } + // { inherit pdmLock; }; - /* Load dependencies from a Poetry pyproject.toml. + /* + Load dependencies from a Poetry pyproject.toml. - Type: loadPoetryPyproject :: AttrSet -> AttrSet + Type: loadPoetryPyproject :: AttrSet -> AttrSet - Example: - # loadPoetryPyproject { pyproject = lib.importTOML } - { - dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` - build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` - pyproject = { }; # The unmarshaled contents of pyproject.toml - projectRoot = null; # Path to project root - requires-python = null; # requires-python as parsed by pep621.parseRequiresPython - } + Example: + # loadPoetryPyproject { projectRoot = ./.; } + { + dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` + build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` + pyproject = { }; # The unmarshaled contents of pyproject.toml + projectRoot = null; # Path to project root + requires-python = null; # requires-python as parsed by pep621.parseRequiresPython + } */ loadPoetryPyproject = { # The unmarshaled contents of pyproject.toml - pyproject ? lib.importTOML (projectRoot + "/pyproject.toml") - , # Path to project root - projectRoot ? null - , # The unmarshaled contents of pyproject.toml - poetryLock ? lib.importTOML (projectRoot + "/poetry.lock") - , + pyproject ? lib.importTOML (projectRoot + "/pyproject.toml"), + # Path to project root + projectRoot ? null, + # The unmarshaled contents of poetry.lock + poetryLock ? lib.importTOML (projectRoot + "/poetry.lock"), }: let pyproject-pep621 = poetry.translatePoetryProject pyproject; @@ -109,30 +160,31 @@ lib.fix (self: { requires-python = null; }); - /* Load dependencies from a requirements.txt. + /* + Load dependencies from a requirements.txt. - Note that as requirements.txt is lacking important project metadata this is incompatible with some renderers. + Note that as requirements.txt is lacking important project metadata this is incompatible with some renderers. - Type: loadRequirementsTxt :: AttrSet -> AttrSet + Type: loadRequirementsTxt :: AttrSet -> AttrSet - Example: - # loadRequirementstxt { requirements = builtins.readFile ./requirements.txt; root = ./.; } - { - dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` - build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` - pyproject = null; # The unmarshaled contents of pyproject.toml - projectRoot = null; # Path to project root - requires-python = null; # requires-python as parsed by pep621.parseRequiresPython - } + Example: + # loadRequirementstxt { requirements = builtins.readFile ./requirements.txt; projectRoot = ./.; } + { + dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` + build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` + pyproject = null; # The unmarshaled contents of pyproject.toml + projectRoot = null; # Path to project root + requires-python = null; # requires-python as parsed by pep621.parseRequiresPython + } */ loadRequirementsTxt = { # The contents of requirements.txt - requirements ? builtins.readFile (projectRoot + "/requirements.txt") - , # Path to project root - projectRoot ? null - , - }: lib.fix (project: { + requirements ? builtins.readFile (projectRoot + "/requirements.txt"), + # Path to project root + projectRoot ? null, + }: + lib.fix (project: { dependencies = { dependencies = map (x: x.requirement) (pip.parseRequirementsTxt requirements); extras = { }; @@ -145,34 +197,42 @@ lib.fix (self: { requires-python = null; }); - /* Load dependencies from a either a PEP-621 or Poetry pyproject.toml file. - This function is intended for 2nix authors that wants to include local pyproject.toml files - but don't know up front whether they're from Poetry or PEP-621. - - Type: loadPyprojectDynamic :: AttrSet -> AttrSet - - Example: - # loadPyprojectDynamic { pyproject = lib.importTOML } - { - dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` - build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` - pyproject = { }; # The unmarshaled contents of pyproject.toml - projectRoot = null; # Path to project root - requires-python = null; # requires-python as parsed by pep621.parseRequiresPython - } + /* + Load dependencies from a either a PEP-621 or Poetry pyproject.toml file. + This function is intended for 2nix authors that wants to include local pyproject.toml files + but don't know up front whether they're from Poetry or PEP-621. + + Type: loadPyprojectDynamic :: AttrSet -> AttrSet + + Example: + # loadPyprojectDynamic { projectRoot = ./.; } + { + dependencies = { }; # Parsed dependency structure in the schema of `lib.pep621.parseDependencies` + build-systems = [ ]; # Returned by `lib.pep518.parseBuildSystems` + pyproject = { }; # The unmarshaled contents of pyproject.toml + projectRoot = null; # Path to project root + requires-python = null; # requires-python as parsed by pep621.parseRequiresPython + } */ loadPyprojectDynamic = { # The unmarshaled contents of pyproject.toml - pyproject ? lib.importTOML (projectRoot + "/pyproject.toml") - , # Path to project root - projectRoot ? null + pyproject ? lib.importTOML (projectRoot + "/pyproject.toml"), + # Path to project root + projectRoot ? null, }: let - isPoetry = lib.hasAttrByPath [ "tool" "poetry" ] pyproject; - isPep621 = lib.hasAttrByPath [ "project" ] pyproject; + isPoetry = pyproject ? tool.poetry; + isPep621 = pyproject ? project; in - (if isPoetry then self.loadPoetryPyproject else if isPep621 then self.loadPyproject else throw "Project is neither Poetry nor PEP-621") { - inherit pyproject projectRoot; - }; + if isPoetry then + self.loadPoetryPyproject { inherit pyproject projectRoot; } + else if isPep621 then + self.loadPyproject { + inherit pyproject projectRoot; + extrasListPaths = uvListPaths; + extrasAttrPaths = pdmAttrPaths; + } + else + throw "Project is neither Poetry nor PEP-621"; }) diff --git a/vendor/pyproject.nix/lib/pypa.nix b/vendor/pyproject.nix/lib/pypa.nix index 99f308817..825fffeda 100644 --- a/vendor/pyproject.nix/lib/pypa.nix +++ b/vendor/pyproject.nix/lib/pypa.nix @@ -1,6 +1,21 @@ -{ lib, pep600, pep656, ... }: +{ + lib, + pep600, + pep656, + ... +}: let - inherit (builtins) concatStringsSep filter split match elemAt compareVersions length sort head; + inherit (builtins) + concatStringsSep + filter + split + match + elemAt + compareVersions + length + sort + head + ; inherit (lib) isString toLower; inherit (lib.strings) hasPrefix toInt; @@ -22,21 +37,25 @@ let optionalString = s: if s != "" then s else null; - checkTagVersion = sourceVersion: tagVersion: tagVersion == null || tagVersion == sourceVersion.major || ( - hasPrefix sourceVersion.major tagVersion && ( - (toInt (sourceVersion.major + sourceVersion.minor)) >= toInt tagVersion - ) - ); + checkTagVersion = + sourceVersion: tagVersion: + tagVersion == null + || tagVersion == sourceVersion.major + || ( + hasPrefix sourceVersion.major tagVersion + && ((toInt (sourceVersion.major + sourceVersion.minor)) >= toInt tagVersion) + ); in lib.fix (self: { - /* Normalize package name as documented in https://packaging.python.org/en/latest/specifications/name-normalization/#normalization + /* + Normalize package name as documented in https://packaging.python.org/en/latest/specifications/name-normalization/#normalization - Type: normalizePackageName :: string -> string + Type: normalizePackageName :: string -> string - Example: - # readPyproject "Friendly-Bard" - "friendly-bard" + Example: + # readPyproject "Friendly-Bard" + "friendly-bard" */ normalizePackageName = let @@ -45,92 +64,98 @@ lib.fix (self: { in name: toLower (concatDash (filter isString (splitSep name))); - /* Parse Python tags. + /* + Parse Python tags. - As described in https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#python-tag. + As described in https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#python-tag. - Type: parsePythonTag :: string -> AttrSet + Type: parsePythonTag :: string -> AttrSet - Example: - # parsePythonTag "cp37" - { - implementation = "cpython"; - version = "37"; - } - */ + Example: + # parsePythonTag "cp37" + { + implementation = "cpython"; + version = "37"; + } + */ parsePythonTag = tag: let m = match "([a-z]+)([0-9]*)" tag; - mAt = elemAt m; in - assert m != null; { - implementation = normalizeImpl (mAt 0); - version = optionalString (mAt 1); + assert m != null; + { + implementation = normalizeImpl (elemAt m 0); + version = optionalString (elemAt m 1); }; - /* Parse ABI tags. + /* + Parse ABI tags. - As described in https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#python-tag. + As described in https://packaging.python.org/en/latest/specifications/platform-compatibility-tags/#python-tag. - Type: parseABITag :: string -> AttrSet + Type: parseABITag :: string -> AttrSet - Example: - # parseABITag "cp37dmu" - { - rest = "dmu"; - implementation = "cp"; - version = "37"; - } + Example: + # parseABITag "cp37dmu" + { + rest = "dmu"; + implementation = "cp"; + version = "37"; + } */ parseABITag = tag: let m = match "([a-z]+)([0-9]*)_?([a-z0-9]*)" tag; - mAt = elemAt m; in - assert m != null; { - implementation = normalizeImpl (mAt 0); - version = optionalString (mAt 1); - rest = mAt 2; + assert m != null; + { + implementation = normalizeImpl (elemAt m 0); + version = optionalString (elemAt m 1); + rest = elemAt m 2; }; - /* Check whether string is a sdist file or not. + /* + Check whether string is a sdist file or not. - Type: isSdistFileName :: string -> bool + Type: isSdistFileName :: string -> bool - Example: - # isSdistFileName "cryptography-41.0.1.tar.gz" - true + Example: + # isSdistFileName "cryptography-41.0.1.tar.gz" + true */ isSdistFileName = # The filename string name: matchSdistFileName name != null; + /* + Regex match a wheel file name, returning a list of match groups. Returns null if no match. - /* Regex match a wheel file name, returning a list of match groups. Returns null if no match. - - Type: matchWheelFileName :: string -> [ string ] + Type: matchWheelFileName :: string -> [ string ] */ - matchWheelFileName = name: + matchWheelFileName = + name: let m = match "([^-]+)-([^-]+)(-([[:digit:]][^-]*))?-([^-]+)-([^-]+)-(.+).whl" name; in if m != null then filter isString m else null; - /* Check whether string is a wheel file or not. + /* + Check whether string is a wheel file or not. - Type: isWheelFileName :: string -> bool + Type: isWheelFileName :: string -> bool - Example: - # isWheelFileName "cryptography-41.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" - true + Example: + # isWheelFileName "cryptography-41.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl" + true */ isWheelFileName = # The filename string name: matchWheelFileName name != null; - /* Parse PEP-427 wheel file names. + /* + Parse PEP-427 wheel file names. Type: parseFileName :: string -> AttrSet @@ -159,27 +184,28 @@ lib.fix (self: { name: let m = matchWheelFileName name; - mAt = elemAt m; in - assert m != null; { - distribution = mAt 0; - version = mAt 1; - buildTag = mAt 3; - languageTags = map self.parsePythonTag (filter isString (split "\\." (mAt 4))); - abiTag = self.parseABITag (mAt 5); - platformTags = filter isString (split "\\." (mAt 6)); + assert m != null; + { + distribution = elemAt m 0; + version = elemAt m 1; + buildTag = elemAt m 3; + languageTags = map self.parsePythonTag (filter isString (split "\\." (elemAt m 4))); + abiTag = self.parseABITag (elemAt m 5); + platformTags = filter isString (split "\\." (elemAt m 6)); # Keep filename around so selectWheel & such that returns structured filtered # data becomes more ergonomic to use filename = name; }; - /* Check whether an ABI tag is compatible with this python interpreter. + /* + Check whether an ABI tag is compatible with this python interpreter. - Type: isABITagCompatible :: derivation -> string -> bool + Type: isABITagCompatible :: derivation -> string -> bool - Example: - # isABITagCompatible pkgs.python3 (pypa.parseABITag "cp37") - true + Example: + # isABITagCompatible pkgs.python3 (pypa.parseABITag "cp37") + true */ isABITagCompatible = # Python interpreter derivation @@ -193,25 +219,24 @@ lib.fix (self: { # None is a wildcard compatible with any implementation (abiTag.implementation == "none" || abiTag.implementation == "any") || - # implementation == sys.implementation.name - abiTag.implementation == implementation + # implementation == sys.implementation.name + abiTag.implementation == implementation || - # The CPython stable ABI is abi3 as in the shared library suffix. - (abiTag.implementation == "abi" && implementation == "cpython") + # The CPython stable ABI is abi3 as in the shared library suffix. + (abiTag.implementation == "abi" && implementation == "cpython") ) && - # Check version - ( - checkTagVersion sourceVersion abiTag.version - ); + # Check version + (checkTagVersion sourceVersion abiTag.version); - /* Check whether a platform tag is compatible with this python interpreter. + /* + Check whether a platform tag is compatible with this python interpreter. - Type: isPlatformTagCompatible :: AttrSet -> derivation -> string -> bool + Type: isPlatformTagCompatible :: AttrSet -> derivation -> string -> bool - Example: - # isPlatformTagCompatible pkgs.python3 "manylinux2014_x86_64" - true + Example: + # isPlatformTagCompatible pkgs.python3 "manylinux2014_x86_64" + true */ isPlatformTagCompatible = # Platform attrset (`lib.systems.elaborate "x86_64-linux"`) @@ -220,27 +245,32 @@ lib.fix (self: { libc: # Python tag platformTag: - if platformTag == "any" then true - else if hasPrefix "manylinux" platformTag then pep600.manyLinuxTagCompatible platform libc platformTag - else if hasPrefix "musllinux" platformTag then pep656.muslLinuxTagCompatible platform libc platformTag + if platformTag == "any" then + true + else if hasPrefix "manylinux" platformTag then + pep600.manyLinuxTagCompatible platform libc platformTag + else if hasPrefix "musllinux" platformTag then + pep656.muslLinuxTagCompatible platform libc platformTag else if hasPrefix "macosx" platformTag then ( let m = match "macosx_([0-9]+)_([0-9]+)_(.+)" platformTag; - mAt = elemAt m; - major = mAt 0; - minor = mAt 1; - arch = mAt 2; + major = elemAt m 0; + minor = elemAt m 1; + arch = elemAt m 2; in - assert m != null; ( + assert m != null; + ( platform.isDarwin - && - ((arch == "universal2" && (platform.darwinArch == "arm64" || platform.darwinArch == "x86_64")) || arch == platform.darwinArch) - && - compareVersions platform.darwinSdkVersion "${major}.${minor}" >= 0 + && ( + (arch == "universal2" && (platform.darwinArch == "arm64" || platform.darwinArch == "x86_64")) + || arch == platform.darwinArch + ) + && compareVersions platform.darwinSdkVersion "${major}.${minor}" >= 0 ) ) - else if platformTag == "win32" then (platform.isWindows && platform.is32Bit && platform.isx86) + else if platformTag == "win32" then + (platform.isWindows && platform.is32Bit && platform.isx86) else if hasPrefix "win_" platformTag then ( let @@ -248,12 +278,16 @@ lib.fix (self: { arch = elemAt m 0; in assert m != null; - platform.isWindows && ( + platform.isWindows + && ( # Note that these platform mappings are incomplete. # Nixpkgs should gain windows platform tags so we don't have to map them manually here. - if arch == "amd64" then platform.isx86_64 - else if arch == "arm64" then platform.isAarch64 - else false + if arch == "amd64" then + platform.isx86_64 + else if arch == "arm64" then + platform.isAarch64 + else + false ) ) else if hasPrefix "linux" platformTag then @@ -265,15 +299,17 @@ lib.fix (self: { assert m != null; platform.isLinux && arch == platform.linuxArch ) - else throw "Unknown platform tag: '${platformTag}'"; + else + throw "Unknown platform tag: '${platformTag}'"; - /* Check whether a Python language tag is compatible with this Python interpreter. + /* + Check whether a Python language tag is compatible with this Python interpreter. - Type: isPythonTagCompatible :: derivation -> AttrSet -> bool + Type: isPythonTagCompatible :: derivation -> AttrSet -> bool - Example: - # isPythonTagCompatible pkgs.python3 (pypa.parsePythonTag "py3") - true + Example: + # isPythonTagCompatible pkgs.python3 (pypa.parsePythonTag "py3") + true */ isPythonTagCompatible = # Python interpreter derivation @@ -287,21 +323,21 @@ lib.fix (self: { # Python is a wildcard compatible with any implementation pythonTag.implementation == "python" || - # implementation == sys.implementation.name - pythonTag.implementation == implementation + # implementation == sys.implementation.name + pythonTag.implementation == implementation ) && - # Check version - checkTagVersion sourceVersion pythonTag.version - ; + # Check version + checkTagVersion sourceVersion pythonTag.version; - /* Check whether wheel file name is compatible with this python interpreter. + /* + Check whether wheel file name is compatible with this python interpreter. - Type: isWheelFileCompatible :: derivation -> AttrSet -> bool + Type: isWheelFileCompatible :: derivation -> AttrSet -> bool - Example: - # isWheelFileCompatible pkgs.python3 (pypa.parseWheelFileName "Pillow-9.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl") - true + Example: + # isWheelFileCompatible pkgs.python3 (pypa.parseWheelFileName "Pillow-9.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl") + true */ isWheelFileCompatible = # Platform attrset (`lib.systems.elaborate "x86_64-linux"`) @@ -314,19 +350,18 @@ lib.fix (self: { file: ( self.isABITagCompatible python file.abiTag - && - lib.any (self.isPythonTagCompatible python) file.languageTags - && - lib.any (self.isPlatformTagCompatible platform libc) file.platformTags + && lib.any (self.isPythonTagCompatible python) file.languageTags + && lib.any (self.isPlatformTagCompatible platform libc) file.platformTags ); - /* Select compatible wheels from a list and return them in priority order. + /* + Select compatible wheels from a list and return them in priority order. - Type: selectWheels :: AttrSet -> derivation -> [ AttrSet ] -> [ AttrSet ] + Type: selectWheels :: AttrSet -> derivation -> [ AttrSet ] -> [ AttrSet ] - Example: - # selectWheels (lib.systems.elaborate "x86_64-linux") [ (pypa.parseWheelFileName "Pillow-9.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl") ] - [ (pypa.parseWheelFileName "Pillow-9.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl") ] + Example: + # selectWheels (lib.systems.elaborate "x86_64-linux") [ (pypa.parseWheelFileName "Pillow-9.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl") ] + [ (pypa.parseWheelFileName "Pillow-9.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl") ] */ selectWheels = # Platform attrset (`lib.systems.elaborate "x86_64-linux"`) @@ -335,8 +370,7 @@ lib.fix (self: { python: # List of files as parsed by parseWheelFileName files: - if ! lib.isAttrs platform - then + if !lib.isAttrs platform then throw '' SelectWheel was called with wrong type for its first argument 'platform'. Pass only elaborated platforms. @@ -346,41 +380,41 @@ lib.fix (self: { else let # Get sorting/filter criteria fields - withSortedTags = map - (file: - let - abiCompatible = self.isABITagCompatible python file.abiTag; - - # Filter only compatible tags - languageTags = filter (self.isPythonTagCompatible python) file.languageTags; - # Extract the tag as a number. E.g. "37" is `toInt "37"` and "none"/"any" is 0 - languageTags' = map (tag: if tag == "none" then 0 else toInt tag.version) languageTags; - - in - { - bestLanguageTag = head (sort (x: y: x > y) languageTags'); - compatible = abiCompatible && length languageTags > 0 && lib.any (self.isPlatformTagCompatible platform python.stdenv.cc.libc) file.platformTags; - inherit file; - }) - files; + withSortedTags = map ( + file: + let + abiCompatible = self.isABITagCompatible python file.abiTag; + + # Filter only compatible tags + languageTags = filter (self.isPythonTagCompatible python) file.languageTags; + # Extract the tag as a number. E.g. "37" is `toInt "37"` and "none"/"any" is 0 + languageTags' = map (tag: if tag == "none" then 0 else toInt tag.version) languageTags; + + in + { + bestLanguageTag = head (sort (x: y: x > y) languageTags'); + compatible = + abiCompatible + && length languageTags > 0 + && lib.any (self.isPlatformTagCompatible platform python.stdenv.cc.libc) file.platformTags; + inherit file; + } + ) files; # Only consider files compatible with this interpreter compatibleFiles = filter (file: file.compatible) withSortedTags; # Sort files based on their tags - sorted = sort - ( - x: y: - x.file.distribution > y.file.distribution - || x.file.version > y.file.version - || (x.file.buildTag != null && (y.file.buildTag == null || x.file.buildTag > y.file.buildTag)) - || x.bestLanguageTag > y.bestLanguageTag - ) - compatibleFiles; + sorted = sort ( + x: y: + x.file.distribution > y.file.distribution + || x.file.version > y.file.version + || (x.file.buildTag != null && (y.file.buildTag == null || x.file.buildTag > y.file.buildTag)) + || x.bestLanguageTag > y.bestLanguageTag + ) compatibleFiles; in # Strip away temporary sorting metadata - map (file': file'.file) sorted - ; + map (file': file'.file) sorted; }) diff --git a/vendor/pyproject.nix/lib/renderers.nix b/vendor/pyproject.nix/lib/renderers.nix index 1199ceaed..4ac77583a 100644 --- a/vendor/pyproject.nix/lib/renderers.nix +++ b/vendor/pyproject.nix/lib/renderers.nix @@ -1,20 +1,41 @@ -{ lib -, pep508 -, pep440 -, pep621 -, ... +{ + lib, + pep508, + pep440, + pep621, + ... }: let - inherit (builtins) attrValues length attrNames head foldl'; - inherit (lib) optionalAttrs flatten mapAttrs' filterAttrs; + inherit (builtins) + attrValues + length + attrNames + head + foldl' + ; + inherit (lib) + optionalAttrs + mapAttrs' + mapAttrs + filterAttrs + concatMap + ; # Group licenses by their SPDX IDs for easy lookup - licensesBySpdxId = mapAttrs' - (_: license: { - name = license.spdxId; - value = license; - }) - (filterAttrs (_: license: license ? spdxId) lib.licenses); + licensesBySpdxId = mapAttrs' (_: license: { + name = license.spdxId; + value = license; + }) (filterAttrs (_: license: license ? spdxId) lib.licenses); + + getDependencies' = + pythonPackages: + concatMap ( + dep: + let + pkg = pythonPackages.${dep.name}; + in + [ pkg ] ++ concatMap (extra: pkg.optional-dependencies.${extra} or [ ]) dep.extras + ); in { @@ -29,34 +50,40 @@ in Example: # withPackages (lib.project.loadPyproject { ... }) «lambda @ «string»:1:1» - */ + */ withPackages = { # Project metadata as returned by `lib.project.loadPyproject` - project - , # Python derivation - python - , # Python extras (optionals) to enable - extras ? [ ] - , # Extra withPackages function - extraPackages ? _ps: [ ] + project, + # Python derivation + python, + # Python extras (optionals) to enable + extras ? [ ], + # Extra withPackages function + extraPackages ? _ps: [ ], + # PEP-508 environment + environ ? pep508.mkEnviron python, }: let - filteredDeps = pep621.filterDependencies { - inherit (project) dependencies; - environ = pep508.mkEnviron python; - inherit extras; - }; - namedDeps = pep621.getDependenciesNames filteredDeps; - flatDeps = namedDeps.dependencies ++ flatten (attrValues namedDeps.extras) ++ namedDeps.build-systems; + filteredDeps = pep621.filterDependenciesByEnviron environ extras project.dependencies; + getDependencies = getDependencies' python.pkgs; in ps: let buildSystems' = - if namedDeps.build-systems != [ ] then [ ] - else [ ps.setuptools ps.wheel ]; + if filteredDeps.build-systems != [ ] then + [ ] + else + [ + ps.setuptools + ps.wheel + ]; in - map (dep: ps.${dep}) flatDeps ++ extraPackages ps ++ buildSystems'; + getDependencies filteredDeps.dependencies + ++ attrValues (mapAttrs (_group: getDependencies) project.dependencies.extras) + ++ getDependencies filteredDeps.build-systems + ++ extraPackages ps + ++ buildSystems'; /* Renders a project as an argument that can be passed to buildPythonPackage/buildPythonApplication. @@ -68,37 +95,35 @@ in Example: # buildPythonPackage { project = lib.project.loadPyproject ...; python = pkgs.python3; } - { pname = "blinker"; version = "1.3.3.7"; propagatedBuildInputs = [ ]; } - */ + { pname = "blinker"; version = "1.3.3.7"; dependencies = [ ]; } + */ buildPythonPackage = { # Project metadata as returned by `lib.project.loadPyproject` - project - , # Python derivation - python - , # Python extras (optionals) to enable - extras ? [ ] - , # Map a Python extras group name to a Nix attribute set like: + project, + # Python derivation + python, + # Python extras (markers) to enable. + extras ? [ ], + # Map a Python extras group name to a Nix attribute set like: # { dev = "checkInputs"; } # This is intended to be used with optionals such as test dependencies that you might - # want to add to checkInputs instead of propagatedBuildInputs - extrasAttrMappings ? { } - , # Which package format to pass to buildPythonPackage + # want to remap to checkInputs. + extrasAttrMappings ? { }, + # Which package format to pass to buildPythonPackage # If the format is "wheel" PEP-518 build-systems are excluded from the build. - format ? "pyproject" + format ? "pyproject", + # PEP-508 environment + environ ? pep508.mkEnviron python, + # }: let - filteredDeps = pep621.filterDependencies { - inherit (project) dependencies; - environ = pep508.mkEnviron python; - inherit extras; - }; + filteredDeps = pep621.filterDependenciesByEnviron environ extras project.dependencies; - pythonVersion = pep440.parseVersion python.version; + pythonVersion = environ.python_full_version.value; pythonPackages = python.pkgs; - - namedDeps = pep621.getDependenciesNames filteredDeps; + getDependencies = getDependencies' pythonPackages; inherit (project) pyproject; @@ -108,63 +133,72 @@ in urls = project'.urls or { }; in # Optional changelog - optionalAttrs (urls ? changelog) - { - inherit (urls) changelog; - } // - # Optional description - optionalAttrs (project' ? description) { - inherit (project') description; - } // - # Optional license - optionalAttrs (lib.hasAttrByPath [ "license" "text" ] project') ( - assert !(project'.license ? file); { - # From PEP-621: - # "The text key has a string value which is the license of the project whose meaning is that of the License field from the core metadata. - # These keys are mutually exclusive, so a tool MUST raise an error if the metadata specifies both keys." - # Hence the assert above. - license = licensesBySpdxId.${project'.license.text} or project'.license.text; - } - ) // - # Only set mainProgram if we only have one script, otherwise it's ambigious which one is main - ( - let - scriptNames = attrNames project'.scripts; - in - optionalAttrs (project' ? scripts && length scriptNames == 1) { - mainProgram = head scriptNames; - } - ); + optionalAttrs (urls ? changelog) { inherit (urls) changelog; } + // + # Optional description + optionalAttrs (project' ? description) { inherit (project') description; } + // + # Optional license + optionalAttrs (project' ? license.text) ( + assert !(project'.license ? file); + { + # From PEP-621: + # "The text key has a string value which is the license of the project whose meaning is that of the License field from the core metadata. + # These keys are mutually exclusive, so a tool MUST raise an error if the metadata specifies both keys." + # Hence the assert above. + license = licensesBySpdxId.${project'.license.text} or project'.license.text; + } + ) + // + # Only set mainProgram if we only have one script, otherwise it's ambigious which one is main + ( + let + scriptNames = attrNames project'.scripts; + in + optionalAttrs (project' ? scripts && length scriptNames == 1) { mainProgram = head scriptNames; } + ); + + optional-dependencies = lib.mapAttrs (_group: getDependencies) project.dependencies.extras; in foldl' - (attrs: group: - let - attr = extrasAttrMappings.${group} or "propagatedBuildInputs"; - in - attrs // { - ${attr} = attrs.${attr} or [ ] ++ map (dep: python.pkgs.${dep}) namedDeps.extras.${group}; - }) - ({ - propagatedBuildInputs = map (dep: python.pkgs.${dep}) namedDeps.dependencies; - inherit format meta; - passthru = { - optional-dependencies = lib.mapAttrs (_group: deps: map (dep: python.pkgs.${dep.name}) deps) project.dependencies.extras; - }; - } // optionalAttrs (format != "wheel") { - nativeBuildInputs = - if namedDeps.build-systems != [ ] then map (dep: pythonPackages.${dep}) namedDeps.build-systems - else [ pythonPackages.setuptools pythonPackages.wheel ]; - } // optionalAttrs (pyproject.project ? name) { - pname = pyproject.project.name; - } // optionalAttrs (project.projectRoot != null) { - src = project.projectRoot; - } - // optionalAttrs (pyproject.project ? version) { - inherit (pyproject.project) version; - } - // optionalAttrs (project.requires-python != null) { - disabled = ! lib.all (spec: pep440.comparators.${spec.op} pythonVersion spec.version) project.requires-python; - }) - (attrNames namedDeps.extras); + ( + attrs: group: + let + attr = extrasAttrMappings.${group} or "dependencies"; + in + if !extrasAttrMappings ? ${group} then + attrs + else + attrs // { ${attr} = attrs.${attr} or [ ] ++ getDependencies filteredDeps.extras.${group}; } + ) + ( + { + pyproject = format == "pyproject"; + dependencies = + getDependencies filteredDeps.dependencies + # map (dep: python.pkgs.${dep}) namedDeps.dependencies + ++ concatMap (group: optional-dependencies.${group} or [ ]) extras; + inherit optional-dependencies meta; + } + // optionalAttrs (format != "pyproject") { inherit format; } + // optionalAttrs (format != "wheel") { + build-system = + if filteredDeps.build-systems != [ ] then + getDependencies filteredDeps.build-systems + else + [ + pythonPackages.setuptools + pythonPackages.wheel + ]; + } + // optionalAttrs (pyproject.project ? name) { pname = pyproject.project.name; } + // optionalAttrs (project.projectRoot != null) { src = project.projectRoot; } + // optionalAttrs (pyproject.project ? version) { inherit (pyproject.project) version; } + // optionalAttrs (project.requires-python != null) { + disabled = + !lib.all (spec: pep440.comparators.${spec.op} pythonVersion spec.version) project.requires-python; + } + ) + (attrNames filteredDeps.extras); } diff --git a/vendor/pyproject.nix/lib/util.nix b/vendor/pyproject.nix/lib/util.nix index 2160d2f0a..eb5d6a92b 100644 --- a/vendor/pyproject.nix/lib/util.nix +++ b/vendor/pyproject.nix/lib/util.nix @@ -1,19 +1,24 @@ # Small utilities for internal reuse, not exposed externally { lib }: let - inherit (builtins) filter match split head; + inherit (builtins) + filter + match + split + head + ; inherit (lib) isString; - isEmptyStr = s: isString s && match " *" s == null; in -{ +rec { + isEmptyStr = s: isString s && match " *" s == null; + splitComma = s: if s == "" then [ ] else filter isEmptyStr (split " *, *" s); - stripStr = s: + stripStr = + s: let t = match "[\t ]*(.*[^\t ])[\t ]*" s; in - if t == null - then "" - else head t; + if t == null then "" else head t; } diff --git a/vendor/pyproject.nix/lib/validators.nix b/vendor/pyproject.nix/lib/validators.nix index 6e2e11782..895f1d6eb 100644 --- a/vendor/pyproject.nix/lib/validators.nix +++ b/vendor/pyproject.nix/lib/validators.nix @@ -1,13 +1,14 @@ -{ lib -, pep440 -, pep508 -, pep621 -, pypa -, ... +{ + lib, + pep440, + pep508, + pep621, + pypa, + ... }: let inherit (builtins) attrValues foldl' filter; - inherit (lib) flatten; + inherit (lib) concatLists; in { @@ -32,40 +33,42 @@ in version = "0.9.1"; }; } - */ + */ validateVersionConstraints = { # Project metadata as returned by `lib.project.loadPyproject` - project - , # Python derivation - python - , # Python extras (optionals) to enable - extras ? [ ] - , + project, + # Python derivation + python, + # Python extras (optionals) to enable + extras ? [ ], }: let - filteredDeps = pep621.filterDependencies { - inherit (project) dependencies; - environ = pep508.mkEnviron python; - inherit extras; - }; - flatDeps = filteredDeps.dependencies ++ flatten (attrValues filteredDeps.extras) ++ filteredDeps.build-systems; + environ = pep508.mkEnviron python; + filteredDeps = pep621.filterDependenciesByEnviron environ [ ] project.dependencies; + flatDeps = + filteredDeps.dependencies + ++ concatLists (attrValues filteredDeps.extras) + ++ filteredDeps.build-systems; in - foldl' - (acc: dep: + foldl' ( + acc: dep: let pname = pypa.normalizePackageName dep.name; pversion = python.pkgs.${pname}.version; version = pep440.parseVersion python.pkgs.${pname}.version; - incompatible = filter (cond: ! pep440.comparators.${cond.op} version cond.version) dep.conditions; + incompatible = filter (cond: !pep440.comparators.${cond.op} version cond.version) dep.conditions; in - if incompatible == [ ] then acc else acc // { - ${pname} = { - version = pversion; - conditions = incompatible; - }; - }) - { } - flatDeps; + if incompatible == [ ] then + acc + else + acc + // { + ${pname} = { + version = pversion; + conditions = incompatible; + }; + } + ) { } flatDeps; }