Initial commit

This commit is contained in:
adisbladis 2019-06-17 16:27:16 +01:00
commit 866b5e1c37
No known key found for this signature in database
GPG key ID: 110BFAD44C6249B7
10 changed files with 402 additions and 0 deletions

2
README.org Normal file
View file

@ -0,0 +1,2 @@
* Convert poetry projects to nix automagically
This is under _really_ early development

105
default.nix Normal file
View file

@ -0,0 +1,105 @@
{
pkgs ? import <nixpkgs> { },
python ? pkgs.python3,
pythonPackages ? python.pkgs,
}:
let
importTOML = path: builtins.fromTOML (builtins.readFile path);
# TODO: Because pip (and by extension poetry) supports wheels hashes are a list
# This list has determistic but non-distinguishable origins
# (we dont know what url the hashes corresponds to)
#
# Just grabbing the first possible hash only works ~50% of the time
getSha256 = pname: poetryLock: builtins.elemAt poetryLock.metadata.hashes."${pname}" 0;
# Note: Makes a derivation tree
mkPoetryDerivation = {
pname,
depPkgs,
pyProject,
poetryLock,
overrides,
}: let
pkgMeta = depPkgs."${pname}";
version = pkgMeta.version;
dependencies =
if (builtins.hasAttr "dependencies" pkgMeta)
then (builtins.attrNames pkgMeta.dependencies)
else [ ];
drv = pythonPackages.buildPythonPackage {
inherit pname version;
# TODO: It's probably better to model derivations as an attrset that we pick deps from
# That way we avoid instantiating the same derivation multiple times
propagatedBuildInputs = builtins.map (pname: (mkPoetryDerivation {
inherit pname depPkgs pyProject poetryLock overrides;
})) dependencies;
doCheck = false; # We never get development deps
src = pythonPackages.fetchPypi {
inherit pname version;
sha256 = getSha256 pname poetryLock;
};
};
override =
if (builtins.hasAttr pname overrides)
then overrides."${pname}"
else (drv: drv);
in override drv;
mkPoetryPackage = {
src,
pyproject ? src + "/pyproject.toml",
poetrylock ? src + "/poetry.lock",
overrides ? import ./overrides.nix {
inherit pkgs python pythonPackages;
},
}: let
pyProject = importTOML pyproject;
poetryLock = importTOML poetrylock;
# Turn list of packages from lock-file into attrset for easy lookup
depPkgs = builtins.listToAttrs (builtins.map (pkgMeta: {
name = pkgMeta.name;
value=pkgMeta;
}) poetryLock.package);
# Turn an attrset of name/version pairs into a list of derivations
getDeps = deps: let
depNames = builtins.filter (depName: depName != "python") (builtins.attrNames deps);
in builtins.map (pname: mkPoetryDerivation {
inherit pname depPkgs pyProject poetryLock overrides;
}) depNames;
in pythonPackages.buildPythonApplication {
pname = pyProject.tool.poetry.name;
version = pyProject.tool.poetry.version;
inherit src;
format = "pyproject";
buildInputs = [
pythonPackages.poetry
];
propagatedBuildInputs = getDeps pyProject.tool.poetry.dependencies;
checkInputs = getDeps pyProject.tool.poetry.dev-dependencies;
meta = {
description = pyProject.tool.poetry.description;
licenses = [ pyProject.tool.poetry.license ];
};
};
in {
inherit mkPoetryPackage;
}

1
example_project/.envrc Normal file
View file

@ -0,0 +1 @@
use nix

View file

@ -0,0 +1 @@
Note that this example project contains a hand-patched lock-file to work around https://github.com/sdispater/poetry/issues/1172

View file

@ -0,0 +1,5 @@
with import ../. { };
mkPoetryPackage {
src = ./.;
}

View file

@ -0,0 +1,5 @@
import requests
if __name__ == '__main__':
print(requests.get('https://example.com').text)

200
example_project/poetry.lock generated Normal file
View file

@ -0,0 +1,200 @@
[[package]]
category = "dev"
description = "Atomic file writes."
name = "atomicwrites"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.3.0"
[[package]]
category = "dev"
description = "Classes Without Boilerplate"
name = "attrs"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "19.1.0"
[[package]]
category = "main"
description = "Python package for providing Mozilla's CA Bundle."
name = "certifi"
optional = false
python-versions = "*"
version = "2019.6.16"
[[package]]
category = "main"
description = "Universal encoding detector for Python 2 and 3"
name = "chardet"
optional = false
python-versions = "*"
version = "3.0.4"
[[package]]
category = "dev"
description = "Cross-platform colored terminal text."
marker = "sys_platform == \"win32\""
name = "colorama"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.4.1"
[[package]]
category = "main"
description = "Internationalized Domain Names in Applications (IDNA)"
name = "idna"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "2.8"
[[package]]
category = "dev"
description = "Read metadata from Python packages"
name = "importlib-metadata"
optional = false
python-versions = ">=2.7,!=3.0,!=3.1,!=3.2,!=3.3"
version = "0.18"
[package.dependencies]
zipp = ">=0.5"
[[package]]
category = "dev"
description = "More routines for operating on iterables, beyond itertools"
marker = "python_version > \"2.7\""
name = "more-itertools"
optional = false
python-versions = ">=3.4"
version = "7.0.0"
[[package]]
category = "dev"
description = "Core utilities for Python packages"
name = "packaging"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "19.0"
[package.dependencies]
pyparsing = ">=2.0.2"
six = "*"
[[package]]
category = "dev"
description = "plugin and hook calling mechanisms for python"
name = "pluggy"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "0.12.0"
[package.dependencies]
importlib-metadata = ">=0.12"
[[package]]
category = "dev"
description = "library with cross-python path, ini-parsing, io, code, log facilities"
name = "py"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "1.8.0"
[[package]]
category = "dev"
description = "Python parsing module"
name = "pyparsing"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
version = "2.4.0"
[[package]]
category = "dev"
description = "pytest: simple powerful testing with Python"
name = "pytest"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
version = "4.6.3"
[package.dependencies]
atomicwrites = ">=1.0"
attrs = ">=17.4.0"
colorama = "*"
importlib-metadata = ">=0.12"
packaging = "*"
pluggy = ">=0.12,<1.0"
py = ">=1.5.0"
six = ">=1.10.0"
wcwidth = "*"
[package.dependencies.more-itertools]
python = ">=2.8"
version = ">=4.0.0"
[[package]]
category = "main"
description = "Python HTTP for Humans."
name = "requests"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
version = "2.22.0"
[package.dependencies]
certifi = ">=2017.4.17"
chardet = ">=3.0.2,<3.1.0"
idna = ">=2.5,<2.9"
urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26"
[[package]]
category = "dev"
description = "Python 2 and 3 compatibility utilities"
name = "six"
optional = false
python-versions = ">=2.6, !=3.0.*, !=3.1.*"
version = "1.12.0"
[[package]]
category = "main"
description = "HTTP library with thread-safe connection pooling, file post, and more."
name = "urllib3"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4"
version = "1.25.3"
[[package]]
category = "dev"
description = "Measures number of Terminal column cells of wide-character codes"
name = "wcwidth"
optional = false
python-versions = "*"
version = "0.1.7"
[[package]]
category = "dev"
description = "Backport of pathlib-compatible object wrapper for zip files"
name = "zipp"
optional = false
python-versions = ">=2.7"
version = "0.5.1"
[metadata]
content-hash = "81f5e454b0b11af37c64b11a6288cf92768a311f72008b4ff858b804c22d4f6c"
python-versions = "^3.7"
[metadata.hashes]
atomicwrites = ["75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6", "03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4"]
attrs = ["f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399", "69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79"]
certifi = ["945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695", "046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939"]
chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"]
colorama = ["05eed71e2e327246ad6b38c540c4a3117230b19679b875190486ddd2d721422d", "f8ac84de7840f5b9c4e3347b3c1eaa50f7e49c2b07596221daec5edaabbd7c48"]
idna = ["c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", "ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"]
importlib-metadata = ["cb6ee23b46173539939964df59d3d72c3e0c1b5d54b84f1d8a7e912fe43612db", "6dfd58dfe281e8d240937776065dd3624ad5469c835248219bd16cf2e12dbeb7"]
more-itertools = ["c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a", "2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7"]
packaging = ["0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af", "9e1cbf8c12b1f1ce0bb5344b8d7ecf66a6f8a6e91bcb0c84593ed6d3ab5c4ab3"]
pluggy = ["0825a152ac059776623854c1543d65a4ad408eb3d33ee114dff91e57ec6ae6fc", "b9817417e95936bf75d85d3f8767f7df6cdde751fc40aed3bb3074cbcb77757c"]
py = ["dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53", "64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa"]
pyparsing = ["1873c03321fc118f4e9746baf201ff990ceb915f433f23b395f5580d1840cb2a", "9b6323ef4ab914af344ba97510e966d64ba91055d6b9afa6b30799340e89cc03"]
pytest = ["4a784f1d4f2ef198fe9b7aef793e9fa1a3b2f84e822d9b3a64a181293a572d45", "926855726d8ae8371803f7b2e6ec0a69953d9c6311fa7c3b6c1b929ff92d27da"]
requests = ["11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", "9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"]
six = ["d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73", "3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c"]
urllib3 = ["dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232", "b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1"]
wcwidth = ["3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", "f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"]
zipp = ["ca943a7e809cc12257001ccfb99e3563da9af99d52f261725e96dfe0f9275bc3", "8c1019c6aad13642199fbe458275ad6a84907634cc9f0989877ccc4a2840139d"]

View file

@ -0,0 +1,17 @@
[tool.poetry]
name = "example_project"
version = "0.1.0"
description = "An example project to test poetry2nix"
authors = ["adisbladis <adisbladis@gmail.com>"]
license = "MIT"
[tool.poetry.dependencies]
python = "^3.7"
requests = "^2.22"
[tool.poetry.dev-dependencies]
pytest = "^4.6"
[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"

15
example_project/shell.nix Normal file
View file

@ -0,0 +1,15 @@
with import <nixpkgs> {};
let
# TODO: Stop propagating PYTHONPATH
pythonEnv = (python3.withPackages(ps: [
ps.poetry
])).override (args: {
ignoreCollisions = true;
});
in mkShell {
buildInputs = [
pythonEnv
];
}

51
overrides.nix Normal file
View file

@ -0,0 +1,51 @@
{
pkgs,
python,
pythonPackages,
}:
{
pytest = (drv: drv.overrideAttrs(old: {
buildInputs = old.buildInputs ++ [
pythonPackages.setuptools_scm
];
}));
six = (drv: drv.overrideAttrs(old: {
buildInputs = old.buildInputs ++ [
pythonPackages.setuptools_scm
];
}));
py = (drv: drv.overrideAttrs(old: {
buildInputs = old.buildInputs ++ [
pythonPackages.setuptools_scm
];
}));
zipp = (drv: drv.overrideAttrs(old: {
buildInputs = old.buildInputs ++ [
pythonPackages.setuptools_scm
];
}));
importlib-metadata = (drv: drv.overrideAttrs(old: {
src = pythonPackages.fetchPypi {
pname = "importlib_metadata";
version = old.version;
sha256 = old.src.outputHash;
};
buildInputs = old.buildInputs ++ [
pythonPackages.setuptools_scm
];
}));
pluggy = (drv: drv.overrideAttrs(old: {
buildInputs = old.buildInputs ++ [
pythonPackages.setuptools_scm
];
}));
}