mirror of
https://github.com/vale981/poetry2nix
synced 2025-03-05 09:11:39 -05:00
Add PEP508 marker support
This is still experimental and may contain bugs
This commit is contained in:
parent
be7dab861a
commit
ff8d58af7d
2 changed files with 204 additions and 15 deletions
34
default.nix
34
default.nix
|
@ -1,7 +1,6 @@
|
||||||
{
|
{
|
||||||
pkgs ? import <nixpkgs> { },
|
pkgs ? import <nixpkgs> { },
|
||||||
lib ? pkgs.lib,
|
lib ? pkgs.lib,
|
||||||
python ? pkgs.python3,
|
|
||||||
}:
|
}:
|
||||||
|
|
||||||
let
|
let
|
||||||
|
@ -16,7 +15,7 @@ let
|
||||||
satisfiesSemver = (import ./semver.nix {inherit lib;}).satisfies;
|
satisfiesSemver = (import ./semver.nix {inherit lib;}).satisfies;
|
||||||
|
|
||||||
# Check Python version is compatible with package
|
# Check Python version is compatible with package
|
||||||
isCompatible = pythonVersions: let
|
isCompatible = pythonVersion: pythonVersions: let
|
||||||
operators = {
|
operators = {
|
||||||
"||" = cond1: cond2: cond1 || cond2;
|
"||" = cond1: cond2: cond1 || cond2;
|
||||||
"," = cond1: cond2: cond1 && cond2; # , means &&
|
"," = cond1: cond2: cond1 && cond2; # , means &&
|
||||||
|
@ -27,7 +26,7 @@ let
|
||||||
operator = if isOperator then (builtins.elemAt v 0) else acc.operator;
|
operator = if isOperator then (builtins.elemAt v 0) else acc.operator;
|
||||||
in if isOperator then (acc // {inherit operator;}) else {
|
in if isOperator then (acc // {inherit operator;}) else {
|
||||||
inherit operator;
|
inherit operator;
|
||||||
state = operators."${operator}" acc.state (satisfiesSemver python.version v);
|
state = operators."${operator}" acc.state (satisfiesSemver pythonVersion v);
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
operator = ",";
|
operator = ",";
|
||||||
|
@ -47,12 +46,18 @@ let
|
||||||
isBdist = f: builtins.match "^.*?whl$" f.file != null;
|
isBdist = f: builtins.match "^.*?whl$" f.file != null;
|
||||||
isSdist = f: ! isBdist f;
|
isSdist = f: ! isBdist f;
|
||||||
|
|
||||||
|
mkEvalPep508 = import ./pep508.nix {
|
||||||
|
inherit lib;
|
||||||
|
stdenv = pkgs.stdenv;
|
||||||
|
};
|
||||||
|
|
||||||
mkPoetryPackage = {
|
mkPoetryPackage = {
|
||||||
src,
|
src,
|
||||||
pyproject ? src + "/pyproject.toml",
|
pyproject ? src + "/pyproject.toml",
|
||||||
poetrylock ? src + "/poetry.lock",
|
poetrylock ? src + "/poetry.lock",
|
||||||
overrides ? defaultPoetryOverrides,
|
overrides ? defaultPoetryOverrides,
|
||||||
meta ? {},
|
meta ? {},
|
||||||
|
python ? pkgs.python3,
|
||||||
...
|
...
|
||||||
}@attrs: let
|
}@attrs: let
|
||||||
pyProject = importTOML pyproject;
|
pyProject = importTOML pyproject;
|
||||||
|
@ -63,6 +68,8 @@ let
|
||||||
specialAttrs = [ "pyproject" "poetrylock" "overrides" ];
|
specialAttrs = [ "pyproject" "poetrylock" "overrides" ];
|
||||||
passedAttrs = builtins.removeAttrs attrs specialAttrs;
|
passedAttrs = builtins.removeAttrs attrs specialAttrs;
|
||||||
|
|
||||||
|
evalPep508 = mkEvalPep508 python;
|
||||||
|
|
||||||
# Create an overriden version of pythonPackages
|
# Create an overriden version of pythonPackages
|
||||||
#
|
#
|
||||||
# We need to avoid mixing multiple versions of pythonPackages in the same
|
# We need to avoid mixing multiple versions of pythonPackages in the same
|
||||||
|
@ -85,7 +92,10 @@ let
|
||||||
then "wheel"
|
then "wheel"
|
||||||
else "setuptools";
|
else "setuptools";
|
||||||
|
|
||||||
in self.buildPythonPackage {
|
# Filter derivations by their PEP508 markers
|
||||||
|
markerFiltered = if builtins.hasAttr "marker" pkgMeta then (! evalPep508 pkgMeta.marker) else false;
|
||||||
|
|
||||||
|
in if markerFiltered then null else self.buildPythonPackage {
|
||||||
pname = pkgMeta.name;
|
pname = pkgMeta.name;
|
||||||
version = pkgMeta.version;
|
version = pkgMeta.version;
|
||||||
|
|
||||||
|
@ -101,7 +111,7 @@ let
|
||||||
in builtins.map (dep: self."${dep}") dependencies;
|
in builtins.map (dep: self."${dep}") dependencies;
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
broken = ! isCompatible pkgMeta.python-versions;
|
broken = ! isCompatible python.version pkgMeta.python-versions;
|
||||||
};
|
};
|
||||||
|
|
||||||
src =
|
src =
|
||||||
|
@ -126,7 +136,7 @@ let
|
||||||
value = let
|
value = let
|
||||||
drv = mkPoetryDep pkgMeta;
|
drv = mkPoetryDep pkgMeta;
|
||||||
override = getAttrDefault pkgMeta.name overrides (_: _: drv: drv);
|
override = getAttrDefault pkgMeta.name overrides (_: _: drv: drv);
|
||||||
in override self super drv;
|
in if drv != null then (override self super drv) else null;
|
||||||
}) poetryLock.package;
|
}) poetryLock.package;
|
||||||
|
|
||||||
in {
|
in {
|
||||||
|
@ -134,13 +144,7 @@ let
|
||||||
pytest_xdist = super.pytest_xdist.overrideAttrs(old: {
|
pytest_xdist = super.pytest_xdist.overrideAttrs(old: {
|
||||||
doInstallCheck = false;
|
doInstallCheck = false;
|
||||||
});
|
});
|
||||||
} // builtins.listToAttrs lockPkgs // {
|
} // builtins.listToAttrs lockPkgs;
|
||||||
# Temporary hacks (Missing support for markers)
|
|
||||||
enum34 = null;
|
|
||||||
functools32 = null;
|
|
||||||
typing = null;
|
|
||||||
futures = null;
|
|
||||||
};
|
|
||||||
in python.override { inherit packageOverrides; self = py; };
|
in python.override { inherit packageOverrides; self = py; };
|
||||||
pythonPackages = py.pkgs;
|
pythonPackages = py.pkgs;
|
||||||
|
|
||||||
|
@ -160,9 +164,9 @@ let
|
||||||
|
|
||||||
# TODO: Only conditionally include poetry based on build-system
|
# TODO: Only conditionally include poetry based on build-system
|
||||||
# buildInputs = mkInput "buildInputs" ([ pythonPackages.poetry ]);
|
# buildInputs = mkInput "buildInputs" ([ pythonPackages.poetry ]);
|
||||||
buildInputs = mkInput "buildInputs" ([ pythonPackages.setuptools ]);
|
buildInputs = mkInput "buildInputs" ([ pythonPackages.poetry ]);
|
||||||
|
|
||||||
propagatedBuildInputs = mkInput "propagatedBuildInputs" (getDeps "dependencies");
|
propagatedBuildInputs = mkInput "propagatedBuildInputs" (getDeps "dependencies") ++ ([ pythonPackages.setuptools ]);
|
||||||
checkInputs = mkInput "checkInputs" (getDeps "dev-dependencies");
|
checkInputs = mkInput "checkInputs" (getDeps "dev-dependencies");
|
||||||
|
|
||||||
passthru = {
|
passthru = {
|
||||||
|
|
185
pep508.nix
Normal file
185
pep508.nix
Normal file
|
@ -0,0 +1,185 @@
|
||||||
|
{ lib, stdenv }: python:
|
||||||
|
|
||||||
|
let
|
||||||
|
|
||||||
|
# Like builtins.substring but with stop being offset instead of length
|
||||||
|
substr = start: stop: s: builtins.substring start (stop - start) s;
|
||||||
|
|
||||||
|
# Strip leading/trailing whitespace from string
|
||||||
|
stripStr = s: lib.elemAt (builtins.split "^ *| *$" s) 2;
|
||||||
|
|
||||||
|
findSubExpressionsFun = acc: c: (
|
||||||
|
if c == "(" then (
|
||||||
|
let
|
||||||
|
posNew = acc.pos + 1;
|
||||||
|
isOpen = acc.openP == 0;
|
||||||
|
startPos = if isOpen then posNew else acc.startPos;
|
||||||
|
exprs = if isOpen then acc.exprs else acc.exprs ++ [ (substr acc.exprPos (acc.pos - 1) acc.expr) ];
|
||||||
|
in acc // {
|
||||||
|
inherit exprs startPos;
|
||||||
|
pos = posNew;
|
||||||
|
openP = acc.openP + 1;
|
||||||
|
}
|
||||||
|
) else if c == ")" then (
|
||||||
|
let
|
||||||
|
openP = acc.openP - 1;
|
||||||
|
exprs = findSubExpressions (substr acc.startPos acc.pos acc.expr);
|
||||||
|
in acc // {
|
||||||
|
inherit openP;
|
||||||
|
pos = acc.pos + 1;
|
||||||
|
exprs = if openP == 0 then acc.exprs ++ [ exprs ] else acc.exprs;
|
||||||
|
exprPos = if openP == 0 then acc.pos + 1 else acc.exprPos;
|
||||||
|
}
|
||||||
|
) else acc // {pos = acc.pos + 1;}
|
||||||
|
);
|
||||||
|
|
||||||
|
# Make a tree out of expression groups (parens)
|
||||||
|
findSubExpressions = expr: let
|
||||||
|
acc = builtins.foldl' findSubExpressionsFun {
|
||||||
|
exprs = [];
|
||||||
|
expr = expr;
|
||||||
|
pos = 0;
|
||||||
|
openP = 0;
|
||||||
|
exprPos = 0;
|
||||||
|
startPos = 0;
|
||||||
|
} (lib.stringToCharacters expr);
|
||||||
|
in acc.exprs ++ [ (substr acc.exprPos acc.pos expr) ];
|
||||||
|
|
||||||
|
parseExpressions = exprs: let
|
||||||
|
splitCond = (s: builtins.map
|
||||||
|
(x: if builtins.typeOf x == "list" then (builtins.elemAt x 0) else x)
|
||||||
|
(builtins.split " (and|or) " s));
|
||||||
|
|
||||||
|
mapfn = expr: (
|
||||||
|
if (builtins.match "^ ?$" expr != null) then null # Filter empty
|
||||||
|
else if (builtins.elem expr [ "and" "or" ]) then {
|
||||||
|
type = "bool";
|
||||||
|
value = expr;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
type = "expr";
|
||||||
|
value = expr;
|
||||||
|
});
|
||||||
|
|
||||||
|
parsed = builtins.filter (x: x != null) (builtins.map mapfn (splitCond exprs));
|
||||||
|
|
||||||
|
in if builtins.typeOf exprs == "string" then parsed else builtins.map parseExpressions exprs;
|
||||||
|
|
||||||
|
# Transform individual expressions to structured expressions
|
||||||
|
# This function also performs variable substitution, replacing environment markers with their explicit values
|
||||||
|
transformExpressions = exprs: let
|
||||||
|
variables = {
|
||||||
|
os_name = "posix"; # TODO: Check other platforms
|
||||||
|
sys_platform = (
|
||||||
|
if stdenv.isLinux then "linux"
|
||||||
|
else if stdenv.isDarwin then "darwin"
|
||||||
|
else throw "Unsupported platform"
|
||||||
|
);
|
||||||
|
platform_machine = stdenv.platform.kernelArch;
|
||||||
|
platform_python_implementation = "CPython"; # Only CPython supported for now
|
||||||
|
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 = "cpython"; # Only cpython supported for now
|
||||||
|
implementation_version = python.version;
|
||||||
|
extra = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
substituteVar = value: if builtins.hasAttr value variables then (builtins.toJSON variables."${value}") else value;
|
||||||
|
|
||||||
|
processVar = value: builtins.foldl' (acc: v: v acc) value [
|
||||||
|
stripStr
|
||||||
|
substituteVar
|
||||||
|
];
|
||||||
|
|
||||||
|
in if builtins.typeOf exprs == "set" then (
|
||||||
|
if exprs.type == "expr" then (let
|
||||||
|
mVal = ''[a-zA-Z0-9\'"_\. ]+'';
|
||||||
|
mOp = "in|[!=<>]+";
|
||||||
|
e = stripStr exprs.value;
|
||||||
|
m = builtins.map stripStr (builtins.match ''^(${mVal}) *(${mOp}) *(${mVal})$'' e);
|
||||||
|
in {
|
||||||
|
type = "expr";
|
||||||
|
value = {
|
||||||
|
op = builtins.elemAt m 1;
|
||||||
|
values = [
|
||||||
|
(processVar (builtins.elemAt m 0))
|
||||||
|
(processVar (builtins.elemAt m 2))
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}) else exprs
|
||||||
|
) else builtins.map transformExpressions exprs;
|
||||||
|
|
||||||
|
# Recursively eval all expressions
|
||||||
|
evalExpressions = exprs: let
|
||||||
|
unmarshal = v: (
|
||||||
|
# TODO: Handle single quoted values
|
||||||
|
if v == "True" then true
|
||||||
|
else if v == "False" then false
|
||||||
|
else builtins.fromJSON v
|
||||||
|
);
|
||||||
|
hasElem = needle: haystack: builtins.elem needle (builtins.filter (x: builtins.typeOf x == "string") (builtins.split " " haystack));
|
||||||
|
# TODO: Implement all operators
|
||||||
|
op = {
|
||||||
|
"<=" = x: y: (unmarshal x) <= (unmarshal y);
|
||||||
|
"<" = x: y: (unmarshal x) < (unmarshal y);
|
||||||
|
"!=" = x: y: x != y;
|
||||||
|
"==" = x: y: x == y;
|
||||||
|
">=" = x: y: (unmarshal x) >= (unmarshal y);
|
||||||
|
">" = x: y: (unmarshal x) > (unmarshal y);
|
||||||
|
"~=" = null;
|
||||||
|
"===" = null;
|
||||||
|
"in" = x: y: let
|
||||||
|
values = builtins.filter (x: builtins.typeOf x == "string") (builtins.split " " (unmarshal y));
|
||||||
|
in builtins.elem (unmarshal x) values;
|
||||||
|
};
|
||||||
|
in if builtins.typeOf exprs == "set" then (
|
||||||
|
if exprs.type == "expr" then (let
|
||||||
|
expr = exprs;
|
||||||
|
result = (op."${expr.value.op}") (builtins.elemAt expr.value.values 0) (builtins.elemAt expr.value.values 1);
|
||||||
|
in {
|
||||||
|
type = "value";
|
||||||
|
value = result;
|
||||||
|
}) else exprs
|
||||||
|
) else builtins.map evalExpressions exprs;
|
||||||
|
|
||||||
|
# Now that we have performed an eval all that's left to do is to concat the graph into a single bool
|
||||||
|
reduceExpressions = exprs: let
|
||||||
|
cond = {
|
||||||
|
"and" = x: y: x && y;
|
||||||
|
"or" = x: y: x || y;
|
||||||
|
};
|
||||||
|
|
||||||
|
reduceExpressionsFun = acc: v: (
|
||||||
|
if builtins.typeOf v == "set" then (
|
||||||
|
if v.type == "value" then (
|
||||||
|
acc // {
|
||||||
|
value = cond."${acc.cond}" acc.value v.value;
|
||||||
|
}
|
||||||
|
) else if v.type == "bool" then (
|
||||||
|
acc // {
|
||||||
|
cond = v.value;
|
||||||
|
}
|
||||||
|
) else throw "Unsupported type"
|
||||||
|
) else if builtins.typeOf v == "list" then (
|
||||||
|
builtins.foldl' reduceExpressionsFun acc v
|
||||||
|
) else throw "Unsupported type"
|
||||||
|
);
|
||||||
|
in (builtins.foldl' reduceExpressionsFun {
|
||||||
|
value = true;
|
||||||
|
cond = "and";
|
||||||
|
} exprs).value;
|
||||||
|
|
||||||
|
in e: builtins.foldl' (acc: v: v acc) e [
|
||||||
|
findSubExpressions
|
||||||
|
parseExpressions
|
||||||
|
transformExpressions
|
||||||
|
evalExpressions
|
||||||
|
reduceExpressions
|
||||||
|
]
|
Loading…
Add table
Reference in a new issue