Prior to this commit, pythonRelaxDeps would only support removing
version constraints from "Requires-Dist" lines formatted in a particular
way ("foo (>= 1.2.3)"). This way is deprecated as per PyPA Core Metadata
Specs v2.1 [1]:
> Tools parsing the format should accept optional parentheses around
> this, but tools generating it should not use parentheses.
Additionally, a "Requires-Dist" dependency specification can contain
other metadata than just package name and version (extra names,
environment marker). These were being silently dropped by the prior
version of pythonRelaxDeps, or the version could not be relaxed.
The actual grammar is defined in PEP 508 [2]. Our tool of choice here is
sed extended regexps, so there's only so much we can do to be correct
with this parser. The regexp implemented in this commit makes an attempt
at supporting [extra] names, ; env_markers, as well as version specs
without parentheses. There are still unsupported features (URL specs) as
well as unhandled edge cases, but at some point trying to make the
regexp better is bound to awake ZALGO [3].
[1] https://packaging.python.org/en/latest/specifications/core-metadata/#requires-dist-multiple-use
[2] https://peps.python.org/pep-0508/#grammar
[3] https://stackoverflow.com/a/1732454/179806
108 lines
3.1 KiB
Bash
108 lines
3.1 KiB
Bash
# shellcheck shell=bash
|
|
|
|
# Setup hook that modifies Python dependencies versions.
|
|
#
|
|
# Example usage in a derivation:
|
|
#
|
|
# { …, pythonPackages, … }:
|
|
#
|
|
# pythonPackages.buildPythonPackage {
|
|
# …
|
|
# nativeBuildInputs = [ pythonPackages.pythonRelaxDepsHook ];
|
|
#
|
|
# # This will relax the dependency restrictions
|
|
# # e.g.: abc>1,<=2 -> abc
|
|
# pythonRelaxDeps = [ "abc" ];
|
|
# # This will relax all dependencies restrictions instead
|
|
# # pythonRelaxDeps = true;
|
|
# # This will remove the dependency
|
|
# # e.g.: cde>1,<=2 -> <nothing>
|
|
# pythonRemoveDeps = [ "cde" ];
|
|
# # This will remove all dependencies from the project
|
|
# # pythonRemoveDeps = true;
|
|
# …
|
|
# }
|
|
#
|
|
# IMPLEMENTATION NOTES:
|
|
#
|
|
# The "Requires-Dist" dependency specification format is described in PEP 508.
|
|
# Examples that the regular expressions in this hook needs to support:
|
|
#
|
|
# Requires-Dist: foo
|
|
# -> foo
|
|
# Requires-Dist: foo[optional]
|
|
# -> foo[optional]
|
|
# Requires-Dist: foo[optional]~=1.2.3
|
|
# -> foo[optional]
|
|
# Requires-Dist: foo[optional, xyz] (~=1.2.3)
|
|
# -> foo[optional, xyz]
|
|
# Requires-Dist: foo[optional]~=1.2.3 ; os_name = "posix"
|
|
# -> foo[optional] ; os_name = "posix"
|
|
#
|
|
# Currently unsupported: URL specs (foo @ https://example.com/a.zip).
|
|
|
|
_pythonRelaxDeps() {
|
|
local -r metadata_file="$1"
|
|
|
|
if [[ -z "${pythonRelaxDeps:-}" ]] || [[ "$pythonRelaxDeps" == 0 ]]; then
|
|
return
|
|
elif [[ "$pythonRelaxDeps" == 1 ]]; then
|
|
sed -i "$metadata_file" -r \
|
|
-e 's/(Requires-Dist: [a-zA-Z0-9_.-]+\s*(\[[^]]+\])?)[^;]*(;.*)?/\1\3/'
|
|
else
|
|
for dep in $pythonRelaxDeps; do
|
|
sed -i "$metadata_file" -r \
|
|
-e "s/(Requires-Dist: $dep\s*(\[[^]]+\])?)[^;]*(;.*)?/\1\3/"
|
|
done
|
|
fi
|
|
}
|
|
|
|
_pythonRemoveDeps() {
|
|
local -r metadata_file="$1"
|
|
|
|
if [[ -z "${pythonRemoveDeps:-}" ]] || [[ "$pythonRemoveDeps" == 0 ]]; then
|
|
return
|
|
elif [[ "$pythonRemoveDeps" == 1 ]]; then
|
|
sed -i "$metadata_file" \
|
|
-e '/Requires-Dist:.*/d'
|
|
else
|
|
for dep in $pythonRemoveDeps; do
|
|
sed -i "$metadata_file" \
|
|
-e "/Requires-Dist: $dep/d"
|
|
done
|
|
fi
|
|
|
|
}
|
|
|
|
pythonRelaxDepsHook() {
|
|
pushd dist
|
|
|
|
# See https://peps.python.org/pep-0491/#escaping-and-unicode
|
|
local -r pkg_name="${pname//[^[:alnum:].]/_}-$version"
|
|
local -r unpack_dir="unpacked"
|
|
local -r metadata_file="$unpack_dir/$pkg_name/$pkg_name.dist-info/METADATA"
|
|
|
|
# We generally shouldn't have multiple wheel files, but let's be safer here
|
|
for wheel in "$pkg_name"*".whl"; do
|
|
@pythonInterpreter@ -m wheel unpack --dest "$unpack_dir" "$wheel"
|
|
rm -rf "$wheel"
|
|
|
|
_pythonRelaxDeps "$metadata_file"
|
|
_pythonRemoveDeps "$metadata_file"
|
|
|
|
if (( "${NIX_DEBUG:-0}" >= 1 )); then
|
|
echo "pythonRelaxDepsHook: resulting METADATA for '$wheel':"
|
|
cat "$unpack_dir/$pkg_name/$pkg_name.dist-info/METADATA"
|
|
fi
|
|
|
|
@pythonInterpreter@ -m wheel pack "$unpack_dir/$pkg_name"
|
|
done
|
|
|
|
# Remove the folder since it will otherwise be in the dist output.
|
|
rm -rf "$unpack_dir"
|
|
|
|
popd
|
|
}
|
|
|
|
postBuild+=" pythonRelaxDepsHook"
|