Support missing versions being present and set in a local .python-version (#3134)

Requested in https://github.com/pyenv/pyenv/issues/2680
for deployments with a stock `.pyenv-version` that can use any
of a number of Python versions
and for compatibility with `uv`.

* Support `pyenv local --force`
* Support `pyenv-version-file-write --force`
* Support `pyenv version-name --force`
* Ignore missing versions when searching for executables
* Display "commmand not found" even when there are nonexistent versions

* exec.bats: replace `python` and `rspec` with something that doesn't exist globally, either
in Ubuntu Github CI, `python` exists globally
This commit is contained in:
native-api 2024-12-16 02:32:45 +03:00 committed by GitHub
parent aef6a2a6d0
commit dc873cf568
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 120 additions and 19 deletions

View File

@ -91,6 +91,10 @@ or, if you prefer 3.3.3 over 2.7.6,
Python 3.3.3
You can use the `-f/--force` flag to force setting versions even if some aren't installed.
This is mainly useful in special cases like provisioning scripts.
## `pyenv global`
Sets the global version of Python to be used in all shells by writing

View File

@ -21,7 +21,7 @@ if [ "$1" = "--complete" ]; then
exec pyenv-shims --short
fi
PYENV_VERSION="$(pyenv-version-name)"
PYENV_VERSION="$(pyenv-version-name -f)"
PYENV_COMMAND="$1"
if [ -z "$PYENV_COMMAND" ]; then
@ -29,9 +29,9 @@ if [ -z "$PYENV_COMMAND" ]; then
exit 1
fi
export PYENV_VERSION
PYENV_COMMAND_PATH="$(pyenv-which "$PYENV_COMMAND")"
PYENV_BIN_PATH="${PYENV_COMMAND_PATH%/*}"
export PYENV_VERSION
OLDIFS="$IFS"
IFS=$'\n' scripts=(`pyenv-hooks exec`)

View File

@ -2,9 +2,11 @@
#
# Summary: Set or show the local application-specific Python version(s)
#
# Usage: pyenv local <version> <version2> <..>
# Usage: pyenv local [-f|--force] [<version> [...]]
# pyenv local --unset
#
# -f/--force Do not verify that the versions being set exist
#
# Sets the local application-specific Python version(s) by writing the
# version name to a file named `.python-version'.
#
@ -36,12 +38,25 @@ if [ "$1" = "--complete" ]; then
exec pyenv-versions --bare
fi
while [[ $# -gt 0 ]]
do
case "$1" in
-f|--force)
FORCE=1
shift
;;
*)
break
;;
esac
done
versions=("$@")
if [ "$versions" = "--unset" ]; then
rm -f .python-version
elif [ -n "$versions" ]; then
pyenv-version-file-write .python-version "${versions[@]}"
pyenv-version-file-write ${FORCE:+-f }.python-version "${versions[@]}"
else
if version_file="$(pyenv-version-file "$PWD")"; then
IFS=: versions=($(pyenv-version-file-read "$version_file"))

View File

@ -1,9 +1,25 @@
#!/usr/bin/env bash
# Usage: pyenv version-file-write <file> <version>
# Usage: pyenv version-file-write [-f|--force] <file> <version> [...]
#
# -f/--force Don't verify that the versions exist
set -e
[ -n "$PYENV_DEBUG" ] && set -x
while [[ $# -gt 0 ]]
do
case "$1" in
-f|--force)
FORCE=1
shift
;;
*)
break
;;
esac
done
PYENV_VERSION_FILE="$1"
shift || true
versions=("$@")
@ -14,7 +30,7 @@ if [ -z "$versions" ] || [ -z "$PYENV_VERSION_FILE" ]; then
fi
# Make sure the specified version is installed.
pyenv-prefix "${versions[@]}" >/dev/null
[[ -z $FORCE ]] && pyenv-prefix "${versions[@]}" >/dev/null
# Write the version out to disk.
# Create an empty file. Using "rm" might cause a permission error.

View File

@ -1,8 +1,25 @@
#!/usr/bin/env bash
# Summary: Show the current Python version
#
# -f/--force (Internal) If a version doesn't exist, print it as is rather than produce an error
set -e
[ -n "$PYENV_DEBUG" ] && set -x
while [[ $# -gt 0 ]]
do
case "$1" in
-f|--force)
FORCE=1
shift
;;
*)
break
;;
esac
done
if [ -z "$PYENV_VERSION" ]; then
PYENV_VERSION_FILE="$(pyenv-version-file)"
PYENV_VERSION="$(pyenv-version-file-read "$PYENV_VERSION_FILE" || true)"
@ -33,17 +50,21 @@ OLDIFS="$IFS"
# Remove the explicit 'python-' prefix from versions like 'python-3.12'.
normalised_version="${version#python-}"
if version_exists "${version}" || [ "$version" = "system" ]; then
versions=("${versions[@]}" "${version}")
versions+=("${version}")
elif version_exists "${normalised_version}"; then
versions=("${versions[@]}" "${normalised_version}")
versions+=("${normalised_version}")
elif resolved_version="$(pyenv-latest -b "${version}")"; then
versions=("${versions[@]}" "${resolved_version}")
versions+=("${resolved_version}")
elif resolved_version="$(pyenv-latest -b "${normalised_version}")"; then
versions=("${versions[@]}" "${resolved_version}")
versions+=("${resolved_version}")
else
if [[ -n $FORCE ]]; then
versions+=("${normalised_version}")
else
echo "pyenv: version \`$version' is not installed (set by $(pyenv-version-origin))" >&2
any_not_installed=1
fi
fi
done
}
IFS="$OLDIFS"

View File

@ -96,7 +96,6 @@ else
for version in "${nonexistent_versions[@]}"; do
echo "pyenv: version \`$version' is not installed (set by $(pyenv-version-origin))" >&2
done
exit 1
fi
echo "pyenv: $PYENV_COMMAND: command not found" >&2

View File

@ -16,16 +16,22 @@ create_executable() {
@test "fails with invalid version" {
export PYENV_VERSION="3.4"
run pyenv-exec python -V
assert_failure "pyenv: version \`3.4' is not installed (set by PYENV_VERSION environment variable)"
run pyenv-exec nonexistent
assert_failure <<EOF
pyenv: version \`3.4' is not installed (set by PYENV_VERSION environment variable)
pyenv: nonexistent: command not found
EOF
}
@test "fails with invalid version set from file" {
mkdir -p "$PYENV_TEST_DIR"
cd "$PYENV_TEST_DIR"
echo 2.7 > .python-version
run pyenv-exec rspec
assert_failure "pyenv: version \`2.7' is not installed (set by $PWD/.python-version)"
run pyenv-exec nonexistent
assert_failure <<EOF
pyenv: version \`2.7' is not installed (set by $PWD/.python-version)
pyenv: nonexistent: command not found
EOF
}
@test "completes with names of executables" {

View File

@ -41,6 +41,18 @@ setup() {
assert [ "$(cat .python-version)" = "1.2.3" ]
}
@test "fails to set a nonexistent local version" {
run pyenv-local 1.2.3
assert_failure "pyenv: version \`1.2.3' not installed"
assert [ ! -e .python-version ]
}
@test "sets a nonexistent local version with --force" {
run pyenv-local -f 1.2.3
assert_success ""
assert [ "$(cat .python-version)" = "1.2.3" ]
}
@test "changes local version" {
echo "1.0-pre" > .python-version
mkdir -p "${PYENV_ROOT}/versions/1.2.3"

View File

@ -9,7 +9,7 @@ setup() {
@test "invocation without 2 arguments prints usage" {
run pyenv-version-file-write
assert_failure "Usage: pyenv version-file-write <file> <version>"
assert_failure "Usage: pyenv version-file-write [-f|--force] <file> <version> [...]"
run pyenv-version-file-write "one" ""
assert_failure
}
@ -21,6 +21,13 @@ setup() {
assert [ ! -e ".python-version" ]
}
@test "setting nonexistent version succeeds with force" {
assert [ ! -e ".python-version" ]
run pyenv-version-file-write --force ".python-version" "2.7.6"
assert_success
assert [ -e ".python-version" ]
}
@test "writes value to arbitrary file" {
mkdir -p "${PYENV_ROOT}/versions/2.7.6"
assert [ ! -e "my-version" ]

View File

@ -73,6 +73,11 @@ SH
assert_failure "pyenv: version \`1.2' is not installed (set by PYENV_VERSION environment variable)"
}
@test "missing version with --force" {
PYENV_VERSION=1.2 run pyenv-version-name -f
assert_success "1.2"
}
@test "one missing version (second missing)" {
create_version "3.5.1"
PYENV_VERSION="3.5.1:1.2" run pyenv-version-name

View File

@ -71,7 +71,16 @@ create_executable() {
@test "version not installed" {
create_executable "3.4" "py.test"
PYENV_VERSION=3.3 run pyenv-which py.test
assert_failure "pyenv: version \`3.3' is not installed (set by PYENV_VERSION environment variable)"
assert_failure <<OUT
pyenv: version \`3.3' is not installed (set by PYENV_VERSION environment variable)
pyenv: py.test: command not found
The \`py.test' command exists in these Python versions:
3.4
Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.
OUT
}
@test "versions not installed" {
@ -80,6 +89,13 @@ create_executable() {
assert_failure <<OUT
pyenv: version \`2.7' is not installed (set by PYENV_VERSION environment variable)
pyenv: version \`3.3' is not installed (set by PYENV_VERSION environment variable)
pyenv: py.test: command not found
The \`py.test' command exists in these Python versions:
3.4
Note: See 'pyenv help global' for tips on allowing both
python2 and python3 to be found.
OUT
}