From 3447433988c28b630659eaf6a4b06ad41ceeeae2 Mon Sep 17 00:00:00 2001 From: Rene Leonhardt <65483435+reneleonhardt@users.noreply.github.com> Date: Sat, 1 Jun 2024 15:39:21 +0200 Subject: [PATCH] feat: Allow major or minor version lookup --- libexec/pyenv | 22 +++--- libexec/pyenv-installed | 120 +++++++++++++++++++++++++++++++ libexec/pyenv-version-file-write | 53 +++++++++++--- 3 files changed, 179 insertions(+), 16 deletions(-) create mode 100755 libexec/pyenv-installed diff --git a/libexec/pyenv b/libexec/pyenv index fdea4911..ea7779c8 100755 --- a/libexec/pyenv +++ b/libexec/pyenv @@ -1,5 +1,6 @@ #!/usr/bin/env bash set -e +export LC_ALL=C # boost grep performance by disabling unicode if [ "$1" = "--debug" ]; then export PYENV_DEBUG=1 @@ -118,16 +119,21 @@ case "$command" in exec pyenv-help ;; * ) - command_path="$(command -v "pyenv-$command" || true)" - if [ -z "$command_path" ]; then - if [ "$command" == "shell" ]; then - abort "shell integration not enabled. Run \`pyenv init' for instructions." - else - abort "no such command \`$command'" + # Just a version number given (or `latest` or `system`) -> assume `pyenv local $@` + if grep -q -E "^[0-9]+(\.[0-9]+){0,2}$" <<<"${command}" || [ "$command" == "latest" ] || [ "$command" == "system" ]; then + command_path="pyenv-local" + else + command_path="$(command -v "pyenv-$command" || true)" + if [ -z "$command_path" ]; then + if [ "$command" == "shell" ]; then + abort "shell integration not enabled. Run \`pyenv init' for instructions." + else + abort "no such command \`$command'" + fi fi - fi - shift 1 + shift 1 + fi if [ "$1" = --help ]; then if [[ "$command" == "sh-"* ]]; then echo "pyenv help \"$command\"" diff --git a/libexec/pyenv-installed b/libexec/pyenv-installed new file mode 100755 index 00000000..f6c03a22 --- /dev/null +++ b/libexec/pyenv-installed @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +# Summary: Display an installed Python version +# Usage: pyenv installed [] +# +# Displays the installed Python version, searching for shortcuts if necessary. +# If no version is given, `pyenv installed' displays the latest installed version. +# If version is `system`, `system` will be returned if an installed system Python can be found. +# If version is `latest`, the latest installed Python version will be returned (3.12.3). +# If version is `3`, the latest installed Python version of this major version will be returned (3.12.3). +# If version is `12` or `3.12`, the latest installed patch of this minor version will be returned (3.12.3). +# If version is `3.12.3`, the same version will be returned if that patch version is installed (3.12.3). +# If version is not found or no versions are installed, an error message will be returned with an error code. + +set -e +[ -n "$PYENV_DEBUG" ] && set -x + +# Provide pyenv completions +if [ "$1" = "--complete" ]; then + echo system + exec pyenv-versions --bare +fi + +majors=({3,2}) # Supported Python versions: 3,2 (latest first) + +versions() { + # Sort correctly (3.10.9 comes before 3.10.10) + pyenv versions --bare | sort -V | $(type -p ggrep grep | head -1) -F "$query" || true +} + +latest_version() { + versions | tail -1 +} + +latest_major() { + # 3 -> latest major 3.12.3 + versions | grep -oE "^$(regex "$1")\\.[0-9]+\\.[0-9]+$" | tail -1 +} + +latest_minor() { + # 3.12 -> latest minor 3.12.3 + versions | grep -oE "^$(regex "$1")\\.([0-9]+)$" | tail -1 +} + +installed() { + # 3.12.3 -> 3.12.3 if installed (else "") + versions | grep -oE "^$(regex "$1")$" || true +} + +regex() { + echo "${1//\./\\.}" +} + +if [ -n "$1" ]; then + version="$1" +else + version="latest" +fi + +if [ "$version" = "system" ]; then + if PYTHON_PATH="$(PYENV_VERSION="${version}" pyenv-which python 2>/dev/null)" || \ + PYTHON_PATH="$(PYENV_VERSION="${version}" pyenv-which python3 2>/dev/null)" || \ + PYTHON_PATH="$(PYENV_VERSION="${version}" pyenv-which python2 2>/dev/null)"; then + echo "system" + exit 0 + else + echo "pyenv: system version not found in PATH" >&2 + exit 1 + fi +fi +if [ "$version" = "latest" ]; then + LATEST_PATCH=$(latest_version) + if [ -n "$LATEST_PATCH" ]; then + echo "$LATEST_PATCH" + exit 0 + else + echo "pyenv: no versions installed" >&2 + exit 1 + fi +fi + +# Check version=3 (major without minor and patch) => 3.12.3 (latest major version) +# Or version=12 (minor without major and patch) => 3.12.3 (latest patch version) +if grep -q -E "^[0-9]+(\s*)$" <<<"${version}"; then + for major in "${majors[@]}"; do + if [ "$version" = "$major" ]; then + LATEST_MAJOR=$(latest_major "$version") + if [ -n "$LATEST_MAJOR" ]; then + echo "$LATEST_MAJOR" + exit 0 + fi + else + LATEST_MINOR=$(latest_minor "$major.$version") + if [ -n "$LATEST_MINOR" ]; then + echo "$LATEST_MINOR" + exit 0 + fi + fi + done +fi + +# Check version=3.12 (minor without patch) => 3.12.3 (latest patch version) +if grep -q -E "^[0-9]+\.[0-9]+(\s*)$" <<<"${version}"; then + LATEST_MINOR=$(latest_minor "$version") + if [ -n "$LATEST_MINOR" ]; then + echo "$LATEST_MINOR" + exit 0 + fi +fi + +# Check version=3.12.3 (full version number) => 3.12.3 (installed version) +if grep -q -E "^[0-9]+\.[0-9]+\.[0-9]+(\s*)$" <<<"${version}"; then + INSTALLED=$(installed "$version") + if [ -n "$INSTALLED" ]; then + echo "$INSTALLED" + exit 0 + fi +fi + +echo "pyenv: version \`${version}' not installed" >&2 +exit 1 diff --git a/libexec/pyenv-version-file-write b/libexec/pyenv-version-file-write index 7095140f..92081792 100755 --- a/libexec/pyenv-version-file-write +++ b/libexec/pyenv-version-file-write @@ -1,5 +1,8 @@ #!/usr/bin/env bash +# Summary: Writes specified version(s) to the specified file if the version(s) exist # Usage: pyenv version-file-write +# If the specified version is not installed, only display error message and return error code. +# If only a single version="system" is specified and installed, display previous version (if any) and remove file. set -e [ -n "$PYENV_DEBUG" ] && set -x @@ -13,12 +16,46 @@ if [ -z "$versions" ] || [ -z "$PYENV_VERSION_FILE" ]; then exit 1 fi -# Make sure the specified version is installed. -pyenv-prefix "${versions[@]}" >/dev/null +# Make sure the specified version(s) are installed. +# Use pyenv-prefix loop logic. +if [ -n "$1" ]; then + OLDIFS="$IFS" + { + IFS=: + export PYENV_VERSION="$*" + } + IFS="$OLDIFS" +elif [ -z "$PYENV_VERSION" ]; then + PYENV_VERSION="$(pyenv-version-name)" +fi -# Write the version out to disk. -# Create an empty file. Using "rm" might cause a permission error. -> "$PYENV_VERSION_FILE" -for version in "${versions[@]}"; do - echo "$version" >> "$PYENV_VERSION_FILE" -done +PYENV_VERSIONS=() +OLDIFS="$IFS" +{ + IFS=: + for version in ${PYENV_VERSION}; do + if ! INSTALLED="$(pyenv-installed "$version" 2>&1)"; then + echo "$INSTALLED" >&2 + exit 1 + fi + PYENV_VERSIONS=("${PYENV_VERSIONS[@]}" "$INSTALLED") + done +} +IFS="$OLDIFS" + +# Special case: only system was specified and found +if [ "$1" = "system" ]; then + if [ -f "$PYENV_VERSION_FILE" ]; then + if previous="$(head -1 "$PYENV_VERSION_FILE" | grep -E "^[0-9]+\.[0-9]+\.[0-9]+$")"; then + echo "pyenv: using system version instead of $previous now" + fi + rm "$PYENV_VERSION_FILE" + fi +else + # Write the version out to disk. + # Create an empty file. Using "rm" might cause a permission error. + > "$PYENV_VERSION_FILE" + for version in "${PYENV_VERSIONS[@]}"; do + echo "$version" >> "$PYENV_VERSION_FILE" + done +fi