My personal project and infrastructure archive
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
nomicon/pkgs/development/tools/poetry2nix/poetry2nix/lib.nix

240 lines
7.8 KiB

{ lib, pkgs, stdenv }:
let
inherit (import ./semver.nix { inherit lib ireplace; }) satisfiesSemver;
inherit (builtins) genList length;
# Replace a list entry at defined index with set value
ireplace = idx: value: list: (
genList (i: if i == idx then value else (builtins.elemAt list i)) (length list)
);
# Do some canonicalisation of module names
moduleName = name: lib.toLower (lib.replaceStrings [ "_" "." ] [ "-" "-" ] name);
# Get a full semver pythonVersion from a python derivation
getPythonVersion = python:
let
pyVer = lib.splitVersion python.pythonVersion ++ [ "0" ];
ver = lib.splitVersion python.version;
major = l: lib.elemAt l 0;
minor = l: lib.elemAt l 1;
joinVersion = v: lib.concatStringsSep "." v;
in
joinVersion (if major pyVer == major ver && minor pyVer == minor ver then ver else pyVer);
# Compare a semver expression with a version
isCompatible = version:
let
operators = {
"||" = cond1: cond2: cond1 || cond2;
"," = cond1: cond2: cond1 && cond2; # , means &&
"&&" = cond1: cond2: cond1 && cond2;
};
splitRe = "(" + (builtins.concatStringsSep "|" (builtins.map (x: lib.replaceStrings [ "|" ] [ "\\|" ] x) (lib.attrNames operators))) + ")";
in
expr:
let
tokens = builtins.filter (x: x != "") (builtins.split splitRe expr);
combine = acc: v:
let
isOperator = builtins.typeOf v == "list";
operator = if isOperator then (builtins.elemAt v 0) else acc.operator;
in
if isOperator then (acc // { inherit operator; }) else {
inherit operator;
state = operators."${operator}" acc.state (satisfiesSemver version v);
};
initial = { operator = "&&"; state = true; };
in
if expr == "" then true else (builtins.foldl' combine initial tokens).state;
fromTOML = builtins.fromTOML or
(
toml: builtins.fromJSON (
builtins.readFile (
pkgs.runCommand "from-toml"
{
inherit toml;
allowSubstitutes = false;
preferLocalBuild = true;
}
''
${pkgs.remarshal}/bin/remarshal \
-if toml \
-i <(echo "$toml") \
-of json \
-o $out
''
)
)
);
readTOML = path: fromTOML (builtins.readFile path);
#
# Returns the appropriate manylinux dependencies and string representation for the file specified
#
getManyLinuxDeps = f:
let
ml = pkgs.pythonManylinuxPackages;
in
if lib.strings.hasInfix "manylinux1" f then { pkg = [ ml.manylinux1 ]; str = "1"; }
else if lib.strings.hasInfix "manylinux2010" f then { pkg = [ ml.manylinux2010 ]; str = "2010"; }
else if lib.strings.hasInfix "manylinux2014" f then { pkg = [ ml.manylinux2014 ]; str = "2014"; }
else if lib.strings.hasInfix "manylinux_" f then { pkg = [ ml.manylinux2014 ]; str = "pep600"; }
else { pkg = [ ]; str = null; };
# Predict URL from the PyPI index.
# Args:
# pname: package name
# file: filename including extension
# hash: SRI hash
# kind: Language implementation and version tag
predictURLFromPypi = lib.makeOverridable (
{ pname, file, hash, kind }:
"https://files.pythonhosted.org/packages/${kind}/${lib.toLower (builtins.substring 0 1 file)}/${pname}/${file}"
);
# Fetch from the PyPI index.
# At first we try to fetch the predicated URL but if that fails we
# will use the Pypi API to determine the correct URL.
# Args:
# pname: package name
# file: filename including extension
# version: the version string of the dependency
# hash: SRI hash
# kind: Language implementation and version tag
fetchFromPypi = lib.makeOverridable (
{ pname, file, version, hash, kind, curlOpts ? "" }:
let
predictedURL = predictURLFromPypi { inherit pname file hash kind; };
in
(pkgs.stdenvNoCC.mkDerivation {
name = file;
nativeBuildInputs = [
pkgs.buildPackages.curl
pkgs.buildPackages.jq
];
isWheel = lib.strings.hasSuffix "whl" file;
system = "builtin";
preferLocalBuild = true;
impureEnvVars = lib.fetchers.proxyImpureEnvVars ++ [
"NIX_CURL_FLAGS"
];
inherit pname file version curlOpts predictedURL;
builder = ./fetch-from-pypi.sh;
outputHashMode = "flat";
outputHashAlgo = "sha256";
outputHash = hash;
passthru = {
urls = [ predictedURL ]; # retain compatibility with nixpkgs' fetchurl
};
})
);
fetchFromLegacy = lib.makeOverridable (
{ python, pname, url, file, hash }:
let
pathParts =
(builtins.filter
({ prefix, path }: "NETRC" == prefix)
builtins.nixPath);
netrc_file = if (pathParts != [ ]) then (builtins.head pathParts).path else "";
in
pkgs.runCommand file
{
nativeBuildInputs = [ python ];
impureEnvVars = lib.fetchers.proxyImpureEnvVars;
outputHashMode = "flat";
outputHashAlgo = "sha256";
outputHash = hash;
NETRC = netrc_file;
} ''
python ${./fetch_from_legacy.py} ${url} ${pname} ${file}
mv ${file} $out
''
);
getBuildSystemPkgs =
{ pythonPackages
, pyProject
}:
let
missingBuildBackendError = "No build-system.build-backend section in pyproject.toml. "
+ "Add such a section as described in https://python-poetry.org/docs/pyproject/#poetry-and-pep-517";
requires = lib.attrByPath [ "build-system" "requires" ] (throw missingBuildBackendError) pyProject;
requiredPkgs = builtins.map (n: lib.elemAt (builtins.match "([^!=<>~[]+).*" n) 0) requires;
in
builtins.map (drvAttr: pythonPackages.${drvAttr} or (throw "unsupported build system requirement ${drvAttr}")) requiredPkgs;
# Find gitignore files recursively in parent directory stopping with .git
findGitIgnores = path:
let
parent = path + "/..";
gitIgnore = path + "/.gitignore";
isGitRoot = builtins.pathExists (path + "/.git");
hasGitIgnore = builtins.pathExists gitIgnore;
gitIgnores = if hasGitIgnore then [ gitIgnore ] else [ ];
in
lib.optionals (builtins.pathExists path && builtins.toString path != "/" && ! isGitRoot) (findGitIgnores parent) ++ gitIgnores;
/*
Provides a source filtering mechanism that:
- Filters gitignore's
- Filters pycache/pyc files
- Uses cleanSourceFilter to filter out .git/.hg, .o/.so, editor backup files & nix result symlinks
*/
cleanPythonSources = { src }:
let
gitIgnores = findGitIgnores src;
pycacheFilter = name: type:
(type == "directory" && ! lib.strings.hasInfix "__pycache__" name)
|| (type == "regular" && ! lib.strings.hasSuffix ".pyc" name)
;
in
lib.cleanSourceWith {
filter = lib.cleanSourceFilter;
src = lib.cleanSourceWith {
filter = pkgs.nix-gitignore.gitignoreFilterPure pycacheFilter gitIgnores src;
inherit src;
};
};
# Maps Nixpkgs CPU values to target machines known to be supported for manylinux* wheels.
# (a.k.a. `uname -m` output from CentOS 7)
#
# This is current as of manylinux2014 (PEP-0599), and is a superset of manylinux2010 / manylinux1.
# s390x is not supported in Nixpkgs, so we don't map it.
manyLinuxTargetMachines = {
x86_64 = "x86_64";
i686 = "i686";
aarch64 = "aarch64";
armv7l = "armv7l";
powerpc64 = "ppc64";
powerpc64le = "ppc64le";
};
# Machine tag for our target platform (if available)
getTargetMachine = stdenv: manyLinuxTargetMachines.${stdenv.targetPlatform.parsed.cpu.name} or null;
in
{
inherit
fetchFromPypi
fetchFromLegacy
getManyLinuxDeps
isCompatible
readTOML
getBuildSystemPkgs
satisfiesSemver
cleanPythonSources
moduleName
getPythonVersion
getTargetMachine
;
}