diff --git a/README.md b/README.md index 5a2bac9..4cb26c7 100644 --- a/README.md +++ b/README.md @@ -161,7 +161,7 @@ command is not available. Because Anaconda and Miniconda may install standard commands (e.g. `curl`, `openssl`, `sqlite3`, etc.) into their prefix, we'd recommend you to install [pyenv-which-ext](https://github.com/yyuu/pyenv-which-ext). -You can manage `conda` environments by `conda env` as same manner as standard Anaconda/Miniconda installations. +You can manage `conda` environments by `conda create` as same manner as standard Anaconda/Miniconda installations. To use those environments, you can use `pyenv activate` and `pyenv deactivate`. ``` @@ -172,7 +172,7 @@ $ conda env list # myenv /home/yyuu/.pyenv/versions/miniconda3-3.9.1/envs/myenv root * /home/yyuu/.pyenv/versions/miniconda3-3.9.1 -$ pyenv activate myenv +$ pyenv activate miniconda3-3.9.1/envs/myenv discarding /home/yyuu/.pyenv/versions/miniconda3-3.9.1/bin from PATH prepending /home/yyuu/.pyenv/versions/miniconda3-3.9.1/envs/myenv/bin to PATH $ python --version @@ -181,6 +181,20 @@ $ pyenv deactivate discarding /home/yyuu/.pyenv/versions/miniconda3-3.9.1/envs/myenv/bin from PATH ``` +If `conda` is available, `pyenv virtualenv` will use it to create environment by `conda create`. + +``` +$ pyenv version +miniconda3-3.9.1 (set by /home/yyuu/.pyenv/version) +$ pyenv virtualenv myenv2 +$ conda env list +# conda environments: +# +myenv /home/yyuu/.pyenv/versions/miniconda3-3.9.1/envs/myenv +myenv /home/yyuu/.pyenv/versions/miniconda3-3.9.1/envs/myenv2 +root * /home/yyuu/.pyenv/versions/miniconda3-3.9.1 +``` + You can use version like `miniconda3-3.9.1/envs/myenv` to specify `conda` environment as a version in pyenv. ``` diff --git a/bin/pyenv-virtualenv b/bin/pyenv-virtualenv index 6aeab10..414f526 100755 --- a/bin/pyenv-virtualenv +++ b/bin/pyenv-virtualenv @@ -103,45 +103,58 @@ http_get_wget() { version() { detect_venv local version - if [ -n "$USE_PYVENV" ]; then - version="$(pyenv-which pyvenv 2>/dev/null || true)" - version="${version#${PYENV_ROOT}/versions/}" - version="${version%/bin/pyvenv}" - echo "pyenv-virtualenv ${PYENV_VIRTUALENV_VERSION} (pyvenv ${version:-unknown})" + if [ -n "${USE_CONDA}" ]; then + version="$(pyenv-exec conda --version 2>/dev/null || true)" + echo "pyenv-virtualenv ${PYENV_VIRTUALENV_VERSION} (conda ${version:-unknown})" else - version="$(venv --version 2>/dev/null || true)" - echo "pyenv-virtualenv ${PYENV_VIRTUALENV_VERSION} (virtualenv ${version:-unknown})" + if [ -n "$USE_PYVENV" ]; then + version="$(pyenv-which pyvenv 2>/dev/null || true)" + version="${version#${PYENV_ROOT}/versions/}" + version="${version%/bin/pyvenv}" + echo "pyenv-virtualenv ${PYENV_VIRTUALENV_VERSION} (pyvenv ${version:-unknown})" + else + version="$(pyenv-exec virtualenv --version 2>/dev/null || true)" + echo "pyenv-virtualenv ${PYENV_VIRTUALENV_VERSION} (virtualenv ${version:-unknown})" + fi fi } usage() { # We can remove the sed fallback once pyenv 0.2.0 is widely available. pyenv-help virtualenv 2>/dev/null || sed -ne '/^#/!q;s/.//;s/.//;1,4d;p' < "$0" - venv --help 2>/dev/null || true + if [ -n "${USE_CONDA}" ]; then + pyenv-exec conda create --help 2>/dev/null || true + else + if [ -n "${USE_PYVENV}" ]; then + pyenv-exec pyvenv --help 2>/dev/null || true + else + pyenv-exec virtualenv --help 2>/dev/null || true + fi + fi [ -z "$1" ] || exit "$1" } detect_venv() { # Check the existence of executables as a workaround for the issue with pyenv-which-ext # https://github.com/yyuu/pyenv-virtualenv/issues/26 - if [ -x "$(pyenv-prefix)/bin/virtualenv" ]; then - HAS_VIRTUALENV=1 - fi - if [ -x "$(pyenv-prefix)/bin/pyvenv" ]; then - HAS_PYVENV=1 + local prefix="$(pyenv-prefix)" + if [ -x "${prefix}/bin/conda" ]; then + HAS_CONDA=1 + else + if [ -x "${prefix}/bin/virtualenv" ]; then + HAS_VIRTUALENV=1 + fi + if [ -x "${prefix}/bin/pyvenv" ]; then + HAS_PYVENV=1 + fi fi # Use pyvenv only if there is pyvenv, virtualenv is not installed, and `-p` not given - if [ -n "${HAS_PYVENV}" ] && [ -z "${HAS_VIRTUALENV}" ] && [ -z "${VIRTUALENV_PYTHON}" ]; then - USE_PYVENV=1 - fi -} - -venv() { - local args=("$@") - if [ -n "${USE_PYVENV}" ]; then - pyenv-exec pyvenv "${args[@]}" + if [ -n "${HAS_CONDA}" ]; then + USE_CONDA=1 else - pyenv-exec virtualenv "${args[@]}" + if [ -n "${HAS_PYVENV}" ] && [ -z "${HAS_VIRTUALENV}" ] && [ -z "${VIRTUALENV_PYTHON}" ]; then + USE_PYVENV=1 + fi fi } @@ -296,12 +309,13 @@ fi export PYENV_VERSION="${VERSION_NAME}" # Source version must exist before creating virtualenv. -if ! pyenv-prefix 1>/dev/null 2>&1; then +PREFIX="$(pyenv-prefix 2>/dev/null || true)" +if [ ! -d "${PREFIX}" ]; then echo "pyenv-virtualenv: \`${PYENV_VERSION}' is not installed in pyenv." 1>&2 exit 1 fi -if pyenv-virtualenv-prefix "${VERSION_NAME}" 1>/dev/null 2>&1; then +if [[ "${PREFIX}" != "${PREFIX%/envs/*}" ]]; then echo "pyenv-virtualenv: nested virtualenv is not supported." 1>&2 exit 1 fi @@ -333,6 +347,7 @@ fi unset HAS_VIRTUALENV unset HAS_PYVENV +unset USE_CONDA unset USE_PYVENV detect_venv @@ -350,37 +365,48 @@ if [ -n "$UPGRADE" ]; then fi fi -if [ -n "${USE_PYVENV}" ]; then - # Unset some arguments not supported by pyvenv - unset QUIET - unset VERBOSE +if [ -n "${USE_CONDA}" ]; then + # e.g. `conda create -n py35 python=3.5 anaconda` if [ -n "${VIRTUALENV_PYTHON}" ]; then - echo "pyenv-virtualenv: \`--python=${VIRTUALENV_PYTHON}' is not supported by pyvenv." 1>&2 - exit 1 + VIRTUALENV_PYTHON="${VIRTUALENV_PYTHON##*/}" + VIRTUALENV_PYTHON="${VIRTUALENV_PYTHON#python}" + if [ -n "${VIRTUALENV_PYTHON}" ]; then + VIRTUALENV_OPTIONS[${#VIRTUALENV_OPTIONS[*]}]="python=${VIRTUALENV_PYTHON}" + fi fi else - if [ -n "${VIRTUALENV_PYTHON}" ]; then - if [[ "${VIRTUALENV_PYTHON}" == "${VIRTUALENV_PYTHON##*/}" ]] || [[ "${VIRTUALENV_PYTHON}" == "${PYENV_ROOT}/shims/"* ]]; then - python="$(pyenv-which "${VIRTUALENV_PYTHON##*/}" 2>/dev/null || true)" - if [ -x "${python}" ]; then - VIRTUALENV_OPTIONS[${#VIRTUALENV_OPTIONS[*]}]="--python=${python}" - else - python="$(PYENV_VERSION="$(pyenv-whence "${VIRTUALENV_PYTHON##*/}" 2>/dev/null | tail -n 1 || true)" pyenv-which "${VIRTUALENV_PYTHON##*/}" 2>/dev/null || true)" + if [ -n "${USE_PYVENV}" ]; then + # Unset some arguments not supported by pyvenv + unset QUIET + unset VERBOSE + if [ -n "${VIRTUALENV_PYTHON}" ]; then + echo "pyenv-virtualenv: \`--python=${VIRTUALENV_PYTHON}' is not supported by pyvenv." 1>&2 + exit 1 + fi + else + if [ -n "${VIRTUALENV_PYTHON}" ]; then + if [[ "${VIRTUALENV_PYTHON}" == "${VIRTUALENV_PYTHON##*/}" ]] || [[ "${VIRTUALENV_PYTHON}" == "${PYENV_ROOT}/shims/"* ]]; then + python="$(pyenv-which "${VIRTUALENV_PYTHON##*/}" 2>/dev/null || true)" if [ -x "${python}" ]; then VIRTUALENV_OPTIONS[${#VIRTUALENV_OPTIONS[*]}]="--python=${python}" else - echo "pyenv-virtualenv: \`${VIRTUALENV_PYTHON##*/}' is not installed in pyenv." 1>&2 - exit 1 + python="$(PYENV_VERSION="$(pyenv-whence "${VIRTUALENV_PYTHON##*/}" 2>/dev/null | tail -n 1 || true)" pyenv-which "${VIRTUALENV_PYTHON##*/}" 2>/dev/null || true)" + if [ -x "${python}" ]; then + VIRTUALENV_OPTIONS[${#VIRTUALENV_OPTIONS[*]}]="--python=${python}" + else + echo "pyenv-virtualenv: \`${VIRTUALENV_PYTHON##*/}' is not installed in pyenv." 1>&2 + exit 1 + fi fi + else + VIRTUALENV_OPTIONS[${#VIRTUALENV_OPTIONS[*]}]="--python=${VIRTUALENV_PYTHON}" fi - else - VIRTUALENV_OPTIONS[${#VIRTUALENV_OPTIONS[*]}]="--python=${VIRTUALENV_PYTHON}" fi - fi - if [ -z "${HAS_VIRTUALENV}" ]; then - VIRTUALENV_VERSION="==${VIRTUALENV_VERSION}" - pyenv-exec pip install $QUIET $VERBOSE "virtualenv${VIRTUALENV_VERSION%==}" - HAS_VIRTUALENV=1 + if [ -z "${HAS_VIRTUALENV}" ]; then + VIRTUALENV_VERSION="==${VIRTUALENV_VERSION}" + pyenv-exec pip install $QUIET $VERBOSE "virtualenv${VIRTUALENV_VERSION%==}" + HAS_VIRTUALENV=1 + fi fi fi @@ -456,7 +482,15 @@ STATUS=0 # Change to cache directory to reuse them between invocations. mkdir -p "${PYENV_VIRTUALENV_CACHE_PATH}" cd "${PYENV_VIRTUALENV_CACHE_PATH}" -venv $QUIET $VERBOSE "${VIRTUALENV_OPTIONS[@]}" "${VIRTUALENV_PATH}" || STATUS="$?" +if [ -n "${USE_CONDA}" ]; then + pyenv-exec conda create $QUIET $VERBOSE --name "${VIRTUALENV_PATH##*/}" --yes "${VIRTUALENV_OPTIONS[@]}" python || STATUS="$?" +else + if [ -n "${USE_PYVENV}" ]; then + pyenv-exec pyvenv $QUIET $VERBOSE "${VIRTUALENV_OPTIONS[@]}" "${VIRTUALENV_PATH}" || STATUS="$?" + else + pyenv-exec virtualenv $QUIET $VERBOSE "${VIRTUALENV_OPTIONS[@]}" "${VIRTUALENV_PATH}" || STATUS="$?" + fi +fi ## Create symlink in the `versions` directory for backward compatibility if [ -d "${VIRTUALENV_PATH}" ] && [ -n "${COMPAT_VIRTUALENV_PATH}" ]; then diff --git a/test/conda-activate.bats b/test/conda-activate.bats index ce6bec9..1cdf071 100644 --- a/test/conda-activate.bats +++ b/test/conda-activate.bats @@ -20,7 +20,7 @@ setup() { @test "activate conda root from current version" { export PYENV_VIRTUALENV_INIT=1 - create_conda "anaconda-2.3.0" + setup_conda "anaconda-2.3.0" stub pyenv-version-name "echo anaconda-2.3.0" stub pyenv-virtualenv-prefix "anaconda-2.3.0 : echo \"${PYENV_ROOT}/versions/anaconda-2.3.0\"" stub pyenv-prefix "anaconda-2.3.0 : echo \"${PYENV_ROOT}/versions/anaconda-2.3.0\"" @@ -43,12 +43,13 @@ EOS unstub pyenv-virtualenv-prefix unstub pyenv-prefix unstub pyenv-sh-deactivate + teardown_conda "anaconda-2.3.0" } @test "activate conda root from current version (fish)" { export PYENV_VIRTUALENV_INIT=1 - create_conda "anaconda-2.3.0" + setup_conda "anaconda-2.3.0" stub pyenv-version-name "echo anaconda-2.3.0" stub pyenv-virtualenv-prefix "anaconda-2.3.0 : echo \"${PYENV_ROOT}/versions/anaconda-2.3.0\"" stub pyenv-prefix "anaconda-2.3.0 : echo \"${PYENV_ROOT}/versions/anaconda-2.3.0\"" @@ -69,13 +70,14 @@ EOS unstub pyenv-virtualenv-prefix unstub pyenv-prefix unstub pyenv-sh-deactivate + teardown_conda "anaconda-2.3.0" } @test "activate conda root from command-line argument" { export PYENV_VIRTUALENV_INIT=1 - create_conda "anaconda-2.3.0" - create_conda "miniconda-3.9.1" + setup_conda "anaconda-2.3.0" + setup_conda "miniconda-3.9.1" stub pyenv-virtualenv-prefix "miniconda-3.9.1 : echo \"${PYENV_ROOT}/versions/miniconda-3.9.1\"" stub pyenv-prefix "miniconda-3.9.1 : echo \"${PYENV_ROOT}/versions/miniconda-3.9.1\"" stub pyenv-sh-deactivate "--force --quiet : echo deactivated" @@ -98,12 +100,14 @@ EOS unstub pyenv-virtualenv-prefix unstub pyenv-prefix unstub pyenv-sh-deactivate + teardown_conda "anaconda-2.3.0" + teardown_conda "miniconda-3.9.1" } @test "activate conda env from current version" { export PYENV_VIRTUALENV_INIT=1 - create_conda "anaconda-2.3.0" "foo" + setup_conda "anaconda-2.3.0" "foo" stub pyenv-version-name "echo anaconda-2.3.0/envs/foo" stub pyenv-virtualenv-prefix "anaconda-2.3.0/envs/foo : echo \"${PYENV_ROOT}/versions/anaconda-2.3.0/envs/foo\"" stub pyenv-prefix "anaconda-2.3.0/envs/foo : echo \"${PYENV_ROOT}/versions/anaconda-2.3.0/envs/foo\"" @@ -126,13 +130,14 @@ EOS unstub pyenv-virtualenv-prefix unstub pyenv-prefix unstub pyenv-sh-deactivate + teardown_conda "anaconda-2.3.0" "foo" } @test "activate conda env from command-line argument" { export PYENV_VIRTUALENV_INIT=1 - create_conda "anaconda-2.3.0" "foo" - create_conda "miniconda-3.9.1" "bar" + setup_conda "anaconda-2.3.0" "foo" + setup_conda "miniconda-3.9.1" "bar" stub pyenv-virtualenv-prefix "miniconda-3.9.1/envs/bar : echo \"${PYENV_ROOT}/versions/miniconda-3.9.1\"" stub pyenv-prefix "miniconda-3.9.1/envs/bar : echo \"${PYENV_ROOT}/versions/miniconda-3.9.1/envs/bar\"" stub pyenv-sh-deactivate "--force --quiet : echo deactivated" @@ -155,4 +160,6 @@ EOS unstub pyenv-virtualenv-prefix unstub pyenv-prefix unstub pyenv-sh-deactivate + teardown_conda "anaconda-2.3.0" "foo" + teardown_conda "miniconda-3.9.1" "bar" } diff --git a/test/conda-deactivate.bats b/test/conda-deactivate.bats index fde3caa..3076360 100644 --- a/test/conda-deactivate.bats +++ b/test/conda-deactivate.bats @@ -21,7 +21,7 @@ setup() { export PYENV_ACTIVATE_SHELL= export CONDA_DEFAULT_ENV="root" - create_conda "anaconda-2.3.0" + setup_conda "anaconda-2.3.0" PYENV_SHELL="bash" run pyenv-sh-deactivate @@ -46,6 +46,8 @@ if declare -f deactivate 1>/dev/null 2>&1; then unset -f deactivate; fi; EOS + + teardown_conda "anaconda-2.3.0" } @test "deactivate conda root (fish)" { @@ -53,8 +55,7 @@ EOS export PYENV_ACTIVATE_SHELL= export CONDA_DEFAULT_ENV="root" - - create_conda "anaconda-2.3.0" + setup_conda "anaconda-2.3.0" PYENV_SHELL="fish" run pyenv-sh-deactivate @@ -75,6 +76,8 @@ if functions -g deactivate; functions -e deactivate; end; EOS + + teardown_conda "anaconda-2.3.0" } @test "deactivate conda env" { @@ -82,8 +85,7 @@ EOS export PYENV_ACTIVATE_SHELL= export CONDA_DEFAULT_ENV="foo" - - create_conda "anaconda-2.3.0" "foo" + setup_conda "anaconda-2.3.0" "foo" PYENV_SHELL="bash" run pyenv-sh-deactivate @@ -108,4 +110,6 @@ if declare -f deactivate 1>/dev/null 2>&1; then unset -f deactivate; fi; EOS + + teardown_conda "anaconda-2.3.0" "foo" } diff --git a/test/conda-prefix.bats b/test/conda-prefix.bats index 489087c..6965d52 100644 --- a/test/conda-prefix.bats +++ b/test/conda-prefix.bats @@ -7,8 +7,7 @@ setup() { } @test "display conda root" { - create_conda "anaconda-2.3.0" - + setup_conda "anaconda-2.3.0" stub pyenv-version-name "echo anaconda-2.3.0" stub pyenv-prefix "anaconda-2.3.0 : echo \"${PYENV_ROOT}/versions/anaconda-2.3.0\"" @@ -21,11 +20,11 @@ OUT unstub pyenv-version-name unstub pyenv-prefix + teardown_conda "anaconda-2.3.0" } @test "display conda env" { - create_conda "anaconda-2.3.0" "foo" - + setup_conda "anaconda-2.3.0" "foo" stub pyenv-version-name "echo anaconda-2.3.0/envs/foo" stub pyenv-prefix "anaconda-2.3.0/envs/foo : echo \"${PYENV_ROOT}/versions/anaconda-2.3.0/envs/foo\"" @@ -38,4 +37,5 @@ OUT unstub pyenv-version-name unstub pyenv-prefix + teardown_conda "anaconda-2.3.0" "foo" } diff --git a/test/conda.bats b/test/conda.bats new file mode 100644 index 0000000..0eb79d6 --- /dev/null +++ b/test/conda.bats @@ -0,0 +1,84 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + export PYENV_ROOT="${TMP}/pyenv" +} + +stub_pyenv() { + stub pyenv-version-name "echo \${PYENV_VERSION}" + stub pyenv-prefix " : echo '${PYENV_ROOT}/versions/${PYENV_VERSION}'" + stub pyenv-hooks "virtualenv : echo" + stub pyenv-rehash " : echo rehashed" +} + +unstub_pyenv() { + unstub pyenv-version-name + unstub pyenv-prefix + unstub pyenv-hooks + unstub pyenv-rehash +} + +@test "create virtualenv by conda create" { + export PYENV_VERSION="miniconda3-3.16.0" + setup_conda "${PYENV_VERSION}" + stub_pyenv "${PYENV_VERSION}" + stub pyenv-prefix " : echo '${PYENV_ROOT}/versions/${PYENV_VERSION}'" + stub pyenv-exec "conda * : echo PYENV_VERSION=\${PYENV_VERSION} \"\$@\"" + stub pyenv-exec "python -s -m ensurepip : true" + + run pyenv-virtualenv venv + + assert_success + assert_output <