Conda 初始化污染环境?
Conda init polluting environment?
我在 Pycharm 中设置了一个项目,其中包含现有的 conda 环境。我的脚本在 运行 来自控制台时有效。
我希望能够从任何位置 运行 python -m path_to_my_script/script.py
,但我需要激活 conda。 Conda 建议我这样做 conda init
但我担心它可能会更改某处的设置并破坏某些东西。
conda init
是做什么的?
您的谨慎可能是有道理的。 conda init
命令将 Anaconda 添加到 Linux 或 Mac 上的路径(不推荐在 Windows 上)。 Anaconda FAQ
您还需要先 运行 宁 source <path to conda>/bin/activate
为 conda init
做准备。
通过在代码第一行的 shebang 语句中指定 python 环境,您可以 运行 在所需的 conda 环境中使用脚本。
您可以通过激活所需的环境并执行 which python
来获得该值
(base) -> conda activate py39
(py39) -> which python
/home/user/anaconda3/envs/py39/bin/python
shebang 将是:
#!/home/user/anaconda3/env/py39/bin/python
在 Mac 和 Linux 上,一旦您添加了 shebang,您就可以将文件修改为可执行文件(例如 chmod 700 myscript.py
)和直接从命令行 运行 . (我不是 Windows 用户,所以 ymmv。)
(base) -> <path-to-script>/myscript.py
(现在 运行 在 shebang 虚拟环境中而不是在基础环境中。)
回答策略
conda init
命令的确切作用及其结果是 shell 特定的。我们不是试图涵盖所有案例,而是通过一个案例,并注意可以通过替换他们感兴趣的 shell 来复制此分析。
案例研究:conda init zsh
让我们把zsh
看成shell。这是一个常见的 shell(macOS 10.15+ 的默认值)并且非常接近 bash。另外,我还没有配置它。
探测命令:Dry 运行
许多 Conda 命令通过 --dry-run, -d
标志包含某种形式的枯燥 运行 功能,结合冗长标志,可以看到不执行它们会做什么。对于 init
命令,仅干 运行 只会告诉我们它 会 修改哪些文件:
$ conda init -d zsh
no change /Users/mfansler/miniconda3/condabin/conda
no change /Users/mfansler/miniconda3/bin/conda
no change /Users/mfansler/miniconda3/bin/conda-env
no change /Users/mfansler/miniconda3/bin/activate
no change /Users/mfansler/miniconda3/bin/deactivate
no change /Users/mfansler/miniconda3/etc/profile.d/conda.sh
no change /Users/mfansler/miniconda3/etc/fish/conf.d/conda.fish
no change /Users/mfansler/miniconda3/shell/condabin/Conda.psm1
no change /Users/mfansler/miniconda3/shell/condabin/conda-hook.ps1
no change /Users/mfansler/miniconda3/lib/python3.7/site-packages/xontrib/conda.xsh
no change /Users/mfansler/miniconda3/etc/profile.d/conda.csh
modified /Users/mfansler/.zshrc
==> For changes to take effect, close and re-open your current shell. <==
这里我们可以看到它计划针对zsh的用户级资源文件,/Users/mfansler/.zshrc
,但是它没有告诉我们它将如何修改它。还有,天哪!这里的 UX 很糟糕,因为它丝毫没有反映出我使用了 -d
标志这一事实。但别担心:只要 -d
标志在那里,它就不会真正改变事情。
补丁预览
要查看它究竟会做什么,请向命令添加一个详细信息标志 (-v
)。这将提供之前输出的所有内容,但现在将向我们展示它将用于修补(更新).zshrc
文件的差异。
$ conda init -dv zsh
/Users/mfansler/.zshrc
---
+++
@@ -0,0 +1,16 @@
+
+# >>> conda initialize >>>
+# !! Contents within this block are managed by 'conda init' !!
+__conda_setup="$('/Users/mfansler/miniconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
+if [ $? -eq 0 ]; then
+ eval "$__conda_setup"
+else
+ if [ -f "/Users/mfansler/miniconda3/etc/profile.d/conda.sh" ]; then
+ . "/Users/mfansler/miniconda3/etc/profile.d/conda.sh"
+ else
+ export PATH="/Users/mfansler/miniconda3/bin:$PATH"
+ fi
+fi
+unset __conda_setup
+# <<< conda initialize <<<
+
# ...the rest is exactly as above
即行动计划是将这16行添加到.zshrc
文件中。在这种情况下,我没有现有的 .zshrc
文件,因此它计划将其添加到第 1 行。如果该文件已经存在,它将附加这些行。
解释 Shell 代码
在关注细节之前,让我们先概述一下这段代码。从本质上讲,这是设置某些 shell 功能的一系列冗余尝试。它们按功能从多到少排序。
康达希望做什么
密码
__conda_setup="$('/Users/mfansler/miniconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
eval "$__conda_setup"
从 conda
本身获取一些东西,将结果存储到一个字符串中,然后如果命令有一个干净的退出 ($? -eq 0
),则评估该字符串。这里巧妙的工程是子进程(技术上 python -m conda
)在当前进程(zsh
)中传回一个可以是 运行 的结果,允许它定义 shell 函数.
稍后我将深入了解这里发生的事情。
回退 1:硬编码 Shell 函数
如果那个奇怪的内部命令失败了,开发者包括了一些基本 shell 功能的硬编码版本(特别是 conda activate
)。这是在:
miniconda3/etc/profile.d/conda.sh
他们只是检查文件是否存在并获取它。让我们点击最后一个选项,然后我们将回头看看功能。
后备 2:不得已
绝对不得已的办法是从字面上违反自 Conda v4.4 以来的长期建议,即简单地将 base 环境的 bin
目录放在 PATH
.在这种情况下,没有 conda activate
功能;这只能确保 Conda 在您的 PATH 上。
详细信息:Shell 功能
回到预期的情况,我们可以通过简单地获取该字符串结果来准确检查它将评估的内容:
$ conda shell.zsh hook
__add_sys_prefix_to_path() {
# In dev-mode CONDA_EXE is python.exe and on Windows
# it is in a different relative location to condabin.
if [ -n "${_CE_CONDA}" ] && [ -n "${WINDIR+x}" ]; then
SYSP=$(\dirname "${CONDA_EXE}")
else
SYSP=$(\dirname "${CONDA_EXE}")
SYSP=$(\dirname "${SYSP}")
fi
if [ -n "${WINDIR+x}" ]; then
PATH="${SYSP}/bin:${PATH}"
PATH="${SYSP}/Scripts:${PATH}"
PATH="${SYSP}/Library/bin:${PATH}"
PATH="${SYSP}/Library/usr/bin:${PATH}"
PATH="${SYSP}/Library/mingw-w64/bin:${PATH}"
PATH="${SYSP}:${PATH}"
else
PATH="${SYSP}/bin:${PATH}"
fi
\export PATH
}
__conda_exe() (
__add_sys_prefix_to_path
"$CONDA_EXE" $_CE_M $_CE_CONDA "$@"
)
__conda_hashr() {
if [ -n "${ZSH_VERSION:+x}" ]; then
\rehash
elif [ -n "${POSH_VERSION:+x}" ]; then
: # pass
else
\hash -r
fi
}
__conda_activate() {
if [ -n "${CONDA_PS1_BACKUP:+x}" ]; then
# Handle transition from shell activated with conda <= 4.3 to a subsequent activation
# after conda updated to >= 4.4. See issue #6173.
PS1="$CONDA_PS1_BACKUP"
\unset CONDA_PS1_BACKUP
fi
\local ask_conda
ask_conda="$(PS1="${PS1:-}" __conda_exe shell.posix "$@")" || \return
\eval "$ask_conda"
__conda_hashr
}
__conda_reactivate() {
\local ask_conda
ask_conda="$(PS1="${PS1:-}" __conda_exe shell.posix reactivate)" || \return
\eval "$ask_conda"
__conda_hashr
}
conda() {
\local cmd="${1-__missing__}"
case "$cmd" in
activate|deactivate)
__conda_activate "$@"
;;
install|update|upgrade|remove|uninstall)
__conda_exe "$@" || \return
__conda_reactivate
;;
*)
__conda_exe "$@"
;;
esac
}
if [ -z "${CONDA_SHLVL+x}" ]; then
\export CONDA_SHLVL=0
# In dev-mode CONDA_EXE is python.exe and on Windows
# it is in a different relative location to condabin.
if [ -n "${_CE_CONDA:+x}" ] && [ -n "${WINDIR+x}" ]; then
PATH="$(\dirname "$CONDA_EXE")/condabin${PATH:+":${PATH}"}"
else
PATH="$(\dirname "$(\dirname "$CONDA_EXE")")/condabin${PATH:+":${PATH}"}"
fi
\export PATH
# We're not allowing PS1 to be unbound. It must at least be set.
# However, we're not exporting it, which can cause problems when starting a second shell
# via a first shell (i.e. starting zsh from bash).
if [ -z "${PS1+x}" ]; then
PS1=
fi
fi
conda activate base
我不打算遍历所有这些,但主要部分是它没有直接将 bin
放在 PATH 上,而是定义了一个名为 conda
的 shell 函数,并且这用作 condabin/conda
入口点的包装器。这也定义了一个新功能 conda activate
,它在幕后使用 shell 函数 __conda_activate()
。在最后一步,它会激活 base 环境。
为什么要这样做?
这是为了响应配置设置而设计的。 auto_activate_base
和 change_ps1
等配置选项会影响 Conda 操作 shell 的方式,从而改变 Conda 在其 shell 函数中包含的功能。
康达“污染环境”吗?
不是真的。可以通过配置设置禁用自动激活和提示修改等主要行为,因此 conda init
最终只是将 conda activate
功能添加到 shell,从而无需在环境之间进行干净的切换必须手动操作 PATH。
我在 Pycharm 中设置了一个项目,其中包含现有的 conda 环境。我的脚本在 运行 来自控制台时有效。
我希望能够从任何位置 运行 python -m path_to_my_script/script.py
,但我需要激活 conda。 Conda 建议我这样做 conda init
但我担心它可能会更改某处的设置并破坏某些东西。
conda init
是做什么的?
您的谨慎可能是有道理的。 conda init
命令将 Anaconda 添加到 Linux 或 Mac 上的路径(不推荐在 Windows 上)。 Anaconda FAQ
您还需要先 运行 宁 source <path to conda>/bin/activate
为 conda init
做准备。
通过在代码第一行的 shebang 语句中指定 python 环境,您可以 运行 在所需的 conda 环境中使用脚本。
您可以通过激活所需的环境并执行 which python
(base) -> conda activate py39
(py39) -> which python
/home/user/anaconda3/envs/py39/bin/python
shebang 将是:
#!/home/user/anaconda3/env/py39/bin/python
在 Mac 和 Linux 上,一旦您添加了 shebang,您就可以将文件修改为可执行文件(例如 chmod 700 myscript.py
)和直接从命令行 运行 . (我不是 Windows 用户,所以 ymmv。)
(base) -> <path-to-script>/myscript.py
(现在 运行 在 shebang 虚拟环境中而不是在基础环境中。)
回答策略
conda init
命令的确切作用及其结果是 shell 特定的。我们不是试图涵盖所有案例,而是通过一个案例,并注意可以通过替换他们感兴趣的 shell 来复制此分析。
案例研究:conda init zsh
让我们把zsh
看成shell。这是一个常见的 shell(macOS 10.15+ 的默认值)并且非常接近 bash。另外,我还没有配置它。
探测命令:Dry 运行
许多 Conda 命令通过 --dry-run, -d
标志包含某种形式的枯燥 运行 功能,结合冗长标志,可以看到不执行它们会做什么。对于 init
命令,仅干 运行 只会告诉我们它 会 修改哪些文件:
$ conda init -d zsh
no change /Users/mfansler/miniconda3/condabin/conda
no change /Users/mfansler/miniconda3/bin/conda
no change /Users/mfansler/miniconda3/bin/conda-env
no change /Users/mfansler/miniconda3/bin/activate
no change /Users/mfansler/miniconda3/bin/deactivate
no change /Users/mfansler/miniconda3/etc/profile.d/conda.sh
no change /Users/mfansler/miniconda3/etc/fish/conf.d/conda.fish
no change /Users/mfansler/miniconda3/shell/condabin/Conda.psm1
no change /Users/mfansler/miniconda3/shell/condabin/conda-hook.ps1
no change /Users/mfansler/miniconda3/lib/python3.7/site-packages/xontrib/conda.xsh
no change /Users/mfansler/miniconda3/etc/profile.d/conda.csh
modified /Users/mfansler/.zshrc
==> For changes to take effect, close and re-open your current shell. <==
这里我们可以看到它计划针对zsh的用户级资源文件,/Users/mfansler/.zshrc
,但是它没有告诉我们它将如何修改它。还有,天哪!这里的 UX 很糟糕,因为它丝毫没有反映出我使用了 -d
标志这一事实。但别担心:只要 -d
标志在那里,它就不会真正改变事情。
补丁预览
要查看它究竟会做什么,请向命令添加一个详细信息标志 (-v
)。这将提供之前输出的所有内容,但现在将向我们展示它将用于修补(更新).zshrc
文件的差异。
$ conda init -dv zsh
/Users/mfansler/.zshrc
---
+++
@@ -0,0 +1,16 @@
+
+# >>> conda initialize >>>
+# !! Contents within this block are managed by 'conda init' !!
+__conda_setup="$('/Users/mfansler/miniconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
+if [ $? -eq 0 ]; then
+ eval "$__conda_setup"
+else
+ if [ -f "/Users/mfansler/miniconda3/etc/profile.d/conda.sh" ]; then
+ . "/Users/mfansler/miniconda3/etc/profile.d/conda.sh"
+ else
+ export PATH="/Users/mfansler/miniconda3/bin:$PATH"
+ fi
+fi
+unset __conda_setup
+# <<< conda initialize <<<
+
# ...the rest is exactly as above
即行动计划是将这16行添加到.zshrc
文件中。在这种情况下,我没有现有的 .zshrc
文件,因此它计划将其添加到第 1 行。如果该文件已经存在,它将附加这些行。
解释 Shell 代码
在关注细节之前,让我们先概述一下这段代码。从本质上讲,这是设置某些 shell 功能的一系列冗余尝试。它们按功能从多到少排序。
康达希望做什么
密码
__conda_setup="$('/Users/mfansler/miniconda3/bin/conda' 'shell.zsh' 'hook' 2> /dev/null)"
if [ $? -eq 0 ]; then
eval "$__conda_setup"
从 conda
本身获取一些东西,将结果存储到一个字符串中,然后如果命令有一个干净的退出 ($? -eq 0
),则评估该字符串。这里巧妙的工程是子进程(技术上 python -m conda
)在当前进程(zsh
)中传回一个可以是 运行 的结果,允许它定义 shell 函数.
稍后我将深入了解这里发生的事情。
回退 1:硬编码 Shell 函数
如果那个奇怪的内部命令失败了,开发者包括了一些基本 shell 功能的硬编码版本(特别是 conda activate
)。这是在:
miniconda3/etc/profile.d/conda.sh
他们只是检查文件是否存在并获取它。让我们点击最后一个选项,然后我们将回头看看功能。
后备 2:不得已
绝对不得已的办法是从字面上违反自 Conda v4.4 以来的长期建议,即简单地将 base 环境的 bin
目录放在 PATH
.在这种情况下,没有 conda activate
功能;这只能确保 Conda 在您的 PATH 上。
详细信息:Shell 功能
回到预期的情况,我们可以通过简单地获取该字符串结果来准确检查它将评估的内容:
$ conda shell.zsh hook
__add_sys_prefix_to_path() {
# In dev-mode CONDA_EXE is python.exe and on Windows
# it is in a different relative location to condabin.
if [ -n "${_CE_CONDA}" ] && [ -n "${WINDIR+x}" ]; then
SYSP=$(\dirname "${CONDA_EXE}")
else
SYSP=$(\dirname "${CONDA_EXE}")
SYSP=$(\dirname "${SYSP}")
fi
if [ -n "${WINDIR+x}" ]; then
PATH="${SYSP}/bin:${PATH}"
PATH="${SYSP}/Scripts:${PATH}"
PATH="${SYSP}/Library/bin:${PATH}"
PATH="${SYSP}/Library/usr/bin:${PATH}"
PATH="${SYSP}/Library/mingw-w64/bin:${PATH}"
PATH="${SYSP}:${PATH}"
else
PATH="${SYSP}/bin:${PATH}"
fi
\export PATH
}
__conda_exe() (
__add_sys_prefix_to_path
"$CONDA_EXE" $_CE_M $_CE_CONDA "$@"
)
__conda_hashr() {
if [ -n "${ZSH_VERSION:+x}" ]; then
\rehash
elif [ -n "${POSH_VERSION:+x}" ]; then
: # pass
else
\hash -r
fi
}
__conda_activate() {
if [ -n "${CONDA_PS1_BACKUP:+x}" ]; then
# Handle transition from shell activated with conda <= 4.3 to a subsequent activation
# after conda updated to >= 4.4. See issue #6173.
PS1="$CONDA_PS1_BACKUP"
\unset CONDA_PS1_BACKUP
fi
\local ask_conda
ask_conda="$(PS1="${PS1:-}" __conda_exe shell.posix "$@")" || \return
\eval "$ask_conda"
__conda_hashr
}
__conda_reactivate() {
\local ask_conda
ask_conda="$(PS1="${PS1:-}" __conda_exe shell.posix reactivate)" || \return
\eval "$ask_conda"
__conda_hashr
}
conda() {
\local cmd="${1-__missing__}"
case "$cmd" in
activate|deactivate)
__conda_activate "$@"
;;
install|update|upgrade|remove|uninstall)
__conda_exe "$@" || \return
__conda_reactivate
;;
*)
__conda_exe "$@"
;;
esac
}
if [ -z "${CONDA_SHLVL+x}" ]; then
\export CONDA_SHLVL=0
# In dev-mode CONDA_EXE is python.exe and on Windows
# it is in a different relative location to condabin.
if [ -n "${_CE_CONDA:+x}" ] && [ -n "${WINDIR+x}" ]; then
PATH="$(\dirname "$CONDA_EXE")/condabin${PATH:+":${PATH}"}"
else
PATH="$(\dirname "$(\dirname "$CONDA_EXE")")/condabin${PATH:+":${PATH}"}"
fi
\export PATH
# We're not allowing PS1 to be unbound. It must at least be set.
# However, we're not exporting it, which can cause problems when starting a second shell
# via a first shell (i.e. starting zsh from bash).
if [ -z "${PS1+x}" ]; then
PS1=
fi
fi
conda activate base
我不打算遍历所有这些,但主要部分是它没有直接将 bin
放在 PATH 上,而是定义了一个名为 conda
的 shell 函数,并且这用作 condabin/conda
入口点的包装器。这也定义了一个新功能 conda activate
,它在幕后使用 shell 函数 __conda_activate()
。在最后一步,它会激活 base 环境。
为什么要这样做?
这是为了响应配置设置而设计的。 auto_activate_base
和 change_ps1
等配置选项会影响 Conda 操作 shell 的方式,从而改变 Conda 在其 shell 函数中包含的功能。
康达“污染环境”吗?
不是真的。可以通过配置设置禁用自动激活和提示修改等主要行为,因此 conda init
最终只是将 conda activate
功能添加到 shell,从而无需在环境之间进行干净的切换必须手动操作 PATH。