diff --git a/plugins/pyenv-link/LICENSE b/plugins/pyenv-link/LICENSE new file mode 100644 index 00000000..b32e7f6d --- /dev/null +++ b/plugins/pyenv-link/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 yfprojects + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/plugins/pyenv-link/README.md b/plugins/pyenv-link/README.md new file mode 100644 index 00000000..67fa6379 --- /dev/null +++ b/plugins/pyenv-link/README.md @@ -0,0 +1,62 @@ +# pyenv-link +A [pyenv](https://github.com/pyenv/pyenv) plugin for linking python versions and virtual envs into your pyenv root. + +This plugin recommends the [pyenv-virtualenv](https://github.com/pyenv/pyenv-virtualenv/) plugin. + +## Installation + +This will install the latest development version of *pyenv-link* into +the `$(pyenv root)/plugins/pyenv-link` directory. + +**Important note:** If you installed pyenv into a non-standard directory, make +sure that you clone this repo into the 'plugins' directory of wherever you +installed into. + +From inside that directory you can: + - Check out a specific release tag. + - Get the latest development release by running `git pull` to download the + latest changes. + + +```bash +git clone https://github.com/real-yfprojects/pyenv-link.git $(pyenv root)/plugins/pyenv-link +``` + +For the Fish shell: + +```fish +git clone https://github.com/real-yfprojects/pyenv-link.git (pyenv root)/plugins/pyenv-link +``` + +## Usage + +Make an arbitrary virtualenv available through pyenv. This automatically guesses a fitting name from the prompt, the directory name or the location of the venv. + +```console +$ pyenv link version .venv +Linked new version named myproject-py3.10 +``` + +You can also specify a name to use for the venv. + +```console +$ pyenv link version .venv myname +Linked new version named myname +``` + +Now you can make pyenv activate/use the venv automatically: + +```console +$ pyenv local myproject-py3.10 +``` + +## Contributing +This project is developed in an open-source, community-driven way, as a voluntary effort in the authors’ free time. + +All kinds of contributions are greatly appreciated. This not only includes implementing new features but also refactoring work or improvements to the documentation. +Bug reports, feature requests and other suggestions are welcome too. + +### TODO + +- [ ] Tests +- [ ] CI \ No newline at end of file diff --git a/plugins/pyenv-link/bin/pyenv-link b/plugins/pyenv-link/bin/pyenv-link new file mode 100755 index 00000000..1992e229 --- /dev/null +++ b/plugins/pyenv-link/bin/pyenv-link @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# +# Summary: Activate virtual environment +# +# Usage: pyenv link version +# +# Link a virtual python environment to pyenvs version directory +# so that the venv can be used like one created with *pyenv-virtualenv*. +# +# should be a path to a virtual python environments location. + +set -e +[ -n "$PYENV_DEBUG" ] && set -x + +# Provide pyenv completions +case "$1" in +--complete) + if [ -z "$2" ]; then + echo version + else + shift 2 + pyenv-link-version --complete "$@" + fi + ;; +version) + shift + pyenv-link-version "$@" + exit $? + ;; +*) + pyenv-help --usage link >&2 + exit 1 + ;; +esac diff --git a/plugins/pyenv-link/bin/pyenv-link-version b/plugins/pyenv-link/bin/pyenv-link-version new file mode 100755 index 00000000..45fd4f67 --- /dev/null +++ b/plugins/pyenv-link/bin/pyenv-link-version @@ -0,0 +1,145 @@ +#!/usr/bin/env bash +# +# Summary: Activate virtual environment +# +# Usage: pyenv link-venv [--dry] [--quiet] [] +# +# Link a virtual python environment to pyenvs version directory +# so that the venv can be used like one created with *pyenv-virtualenv*. +# +# should be a path to a virtual python environments location. +# should be string used as a version name it may not be present +# in the pyenv version directory yet. +# If not specified a matching name is guessed from pyvenv.cfg +# or directory name of the venv. +set -e +[ -n "$PYENV_DEBUG" ] && set -x + +# functions + +if ! { + enable -f "${BASH_SOURCE%/*}"/../../../libexec/pyenv-realpath.dylib realpath || + type realpath # realpath available? +} >/dev/null 2>&1; then + # realpath requires GNU coreutils to be installed. That's why its bundled with pyenv + echo realpath not available >&2 + + # work-around when realpath is unavailable + realpath() { + echo "$(cd "$1" && pwd)" + } +fi + +array_doesnt_contain() { + local array="$1[@]" + local seeking=$2 + local in=0 + for element in "${!array}"; do + if [[ $element == "$seeking" ]]; then + in=1 + break + fi + done + return $in +} + +COMMON_VENV_NAMES=('venv' 'env' '.venv' '.env' 'ENV' 'VENV') +guess_name() { + local venv_dir=$1 + local name + + # guess venv name from pyvenv.cfg + local pyvenv_cfg_path=$venv_dir/pyvenv.cfg + if [ -f "$pyvenv_cfg_path" ]; then + # if `prompt` key wasn't found the var remains empty + name=$(cut -b 1-1024 "$pyvenv_cfg_path" | sed -n '/^ *prompt *= */s///p') + fi + + # guess venv name from name of venv directory + local venv_dir_name=${venv_dir##*/} + if [ -z "$name" ] && array_doesnt_contain COMMON_VENV_NAMES "$venv_dir_name"; then + name=$venv_dir_name + fi + + # guess venv name from name of the parent directory of the venv directory + local venv_dir_parent=${venv_dir%/*} + local venv_dir_parent_name=${venv_dir_parent##*/} + if [ -z "$name" ]; then + name=$venv_dir_parent_name + fi + + echo "$name" +} + +# process args +POSARGS=() +# Provide pyenv completions +if [ "$1" = "--complete" ]; then + echo --dry + echo --quiet + exit +fi + +for arg in "$@"; do + case $arg in + --dry) + dry=true # any value + ;; + --quiet) + quiet=true # any value + ;; + -*) + pyenv-help --usage link-version >&2 + exit 1 + ;; + *) + POSARGS+=("$arg") + ;; + esac +done + +if [ ! ${#POSARGS[@]} -le 2 ] || [ ! ${#POSARGS[@]} -ge 1 ]; then + pyenv-help --usage link-version >&2 + exit 1 +fi + +venv_dir=${POSARGS[0]%/} +venv_name=${POSARGS[1]} + +# Check venv exists +if [ ! -d "$venv_dir" ]; then + echo "The virtual env you specified doesn't exist" + exit 2 +fi + +# make venv dir absolute +venv_dir=$(realpath "$venv_dir") + +# determine venv name +if [ -z "$venv_name" ]; then + venv_name=$(guess_name "$venv_dir") +fi + +# check whether version exists already +if pyenv-prefix "$venv_name" >/dev/null 2>&1; then + echo Version "$venv_name" already exists >&2 + exit 3 +fi + +# construct location to link to +pyenv_prefix_path="$PYENV_ROOT/versions/$venv_name" + +# link to pyenv version directory +if [ -z "$dry" ]; then + # Handle OS types + case "$(uname -s)" in + Darwin*) + # long form flags aren't supported + ln -s "$venv_dir" "$pyenv_prefix_path";; + *) + ln --symbolic "$venv_dir" "$pyenv_prefix_path";; + esac +fi + +# output +[ -z "$quiet" ] && echo Linked new version named "$venv_name" || true