python3Packages.mkPythonEditablePackage: init
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
{
|
||||
buildPythonPackage,
|
||||
lib,
|
||||
hatchling,
|
||||
tomli-w,
|
||||
}:
|
||||
{
|
||||
pname,
|
||||
version,
|
||||
|
||||
# Editable root as string.
|
||||
# Environment variables will be expanded at runtime using os.path.expandvars.
|
||||
root,
|
||||
|
||||
# Arguments passed on verbatim to buildPythonPackage
|
||||
derivationArgs ? { },
|
||||
|
||||
# Python dependencies
|
||||
dependencies ? [ ],
|
||||
optional-dependencies ? { },
|
||||
|
||||
# PEP-518 build-system https://peps.python.org/pep-518
|
||||
build-system ? [ ],
|
||||
|
||||
# PEP-621 entry points https://peps.python.org/pep-0621/#entry-points
|
||||
scripts ? { },
|
||||
gui-scripts ? { },
|
||||
entry-points ? { },
|
||||
|
||||
passthru ? { },
|
||||
meta ? { },
|
||||
}:
|
||||
|
||||
# Create a PEP-660 (https://peps.python.org/pep-0660/) editable package pointing to an impure location outside the Nix store.
|
||||
# The primary use case of this function is to enable local development workflows where the local package is installed into a virtualenv-like environment using withPackages.
|
||||
|
||||
assert lib.isString root;
|
||||
let
|
||||
# In editable mode build-system's are considered to be runtime dependencies.
|
||||
dependencies' = dependencies ++ build-system;
|
||||
|
||||
pyproject = {
|
||||
# PEP-621 project table
|
||||
project = {
|
||||
name = pname;
|
||||
inherit
|
||||
version
|
||||
scripts
|
||||
gui-scripts
|
||||
entry-points
|
||||
;
|
||||
dependencies = map lib.getName dependencies';
|
||||
optional-dependencies = lib.mapAttrs (_: lib.getName) optional-dependencies;
|
||||
};
|
||||
|
||||
# Allow empty package
|
||||
tool.hatch.build.targets.wheel.bypass-selection = true;
|
||||
|
||||
# Include our editable pointer file in build
|
||||
tool.hatch.build.targets.wheel.force-include."_${pname}.pth" = "_${pname}.pth";
|
||||
|
||||
# Build editable package using hatchling
|
||||
build-system = {
|
||||
requires = [ "hatchling" ];
|
||||
build-backend = "hatchling.build";
|
||||
};
|
||||
};
|
||||
|
||||
in
|
||||
buildPythonPackage (
|
||||
{
|
||||
inherit
|
||||
pname
|
||||
version
|
||||
optional-dependencies
|
||||
passthru
|
||||
meta
|
||||
;
|
||||
dependencies = dependencies';
|
||||
|
||||
pyproject = true;
|
||||
|
||||
unpackPhase = ''
|
||||
python -c "import json, tomli_w; print(tomli_w.dumps(json.load(open('$pyprojectContentsPath'))))" > pyproject.toml
|
||||
echo 'import os.path, sys; sys.path.insert(0, os.path.expandvars("${root}"))' > _${pname}.pth
|
||||
'';
|
||||
|
||||
build-system = [ hatchling ];
|
||||
}
|
||||
// derivationArgs
|
||||
// {
|
||||
# Note: Using formats.toml generates another intermediary derivation that needs to be built.
|
||||
# We inline the same functionality for better UX.
|
||||
nativeBuildInputs = (derivationArgs.nativeBuildInputs or [ ]) ++ [ tomli-w ];
|
||||
pyprojectContents = builtins.toJSON pyproject;
|
||||
passAsFile = [ "pyprojectContents" ];
|
||||
preferLocalBuild = true;
|
||||
}
|
||||
)
|
||||
@@ -61,6 +61,8 @@ let
|
||||
|
||||
removePythonPrefix = lib.removePrefix namePrefix;
|
||||
|
||||
mkPythonEditablePackage = callPackage ./editable.nix { };
|
||||
|
||||
mkPythonMetaPackage = callPackage ./meta-package.nix { };
|
||||
|
||||
# Convert derivation to a Python module.
|
||||
@@ -99,7 +101,7 @@ in {
|
||||
inherit buildPythonPackage buildPythonApplication;
|
||||
inherit hasPythonModule requiredPythonModules makePythonPath disabled disabledIf;
|
||||
inherit toPythonModule toPythonApplication;
|
||||
inherit mkPythonMetaPackage;
|
||||
inherit mkPythonMetaPackage mkPythonEditablePackage;
|
||||
|
||||
python = toPythonModule python;
|
||||
|
||||
|
||||
@@ -122,6 +122,43 @@ let
|
||||
}
|
||||
);
|
||||
|
||||
# Test editable package support
|
||||
editableTests = let
|
||||
testPython = python.override {
|
||||
self = testPython;
|
||||
packageOverrides = pyfinal: pyprev: {
|
||||
# An editable package with a script that loads our mutable location
|
||||
my-editable = pyfinal.mkPythonEditablePackage {
|
||||
pname = "my-editable";
|
||||
version = "0.1.0";
|
||||
root = "$NIX_BUILD_TOP/src"; # Use environment variable expansion at runtime
|
||||
# Inject a script
|
||||
scripts = {
|
||||
my-script = "my_editable.main:main";
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
in {
|
||||
editable-script = runCommand "editable-test" {
|
||||
nativeBuildInputs = [ (testPython.withPackages (ps: [ ps.my-editable ])) ];
|
||||
} ''
|
||||
mkdir -p src/my_editable
|
||||
|
||||
cat > src/my_editable/main.py << EOF
|
||||
def main():
|
||||
print("hello mutable")
|
||||
EOF
|
||||
|
||||
test "$(my-script)" == "hello mutable"
|
||||
test "$(python -c 'import sys; print(sys.path[1])')" == "$NIX_BUILD_TOP/src"
|
||||
|
||||
touch $out
|
||||
'';
|
||||
};
|
||||
|
||||
# Tests to ensure overriding works as expected.
|
||||
overrideTests = let
|
||||
extension = self: super: {
|
||||
@@ -192,4 +229,4 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
in lib.optionalAttrs (stdenv.hostPlatform == stdenv.buildPlatform ) (environmentTests // integrationTests // overrideTests // condaTests)
|
||||
in lib.optionalAttrs (stdenv.hostPlatform == stdenv.buildPlatform ) (environmentTests // integrationTests // overrideTests // condaTests // editableTests)
|
||||
|
||||
Reference in New Issue
Block a user