有没有可能污染环境?

2022-03-12 00:00:00 python conda

问题描述

我在PyCharm中设置了一个项目,并且有一个现有的CONDA环境。我的脚本在控制台内运行时有效。

我希望能够从任何位置运行python -m path_to_my_script/script.py,但我需要激活Conda。Conda建议我这样做conda init,但我担心它可能会在某个地方更改设置并损坏某些东西。

conda init做什么?


解决方案

应答策略

conda init命令的确切功能及其结果是特定于shell的。与其试图涵盖所有案例,不如让我们浏览一个案例,并注意到人们可以通过替换感兴趣的外壳来复制这种分析。


案例研究:conda init zsh

让我们看一下zsh作为shell。这是一个通用的shell(MacOS10.15+的默认shell),非常接近bash。另外,我还没有配置它。

探测命令:演练

许多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功能的冗余尝试序列。它们从功能最强到功能最差排序。

孔达希望做什么

代码

__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:硬编码外壳函数

如果这个奇怪的内部命令失败,DEVS会包含一些基本shell函数的硬编码版本(特别是conda activate)。这位于:

miniconda3/etc/profile.d/conda.sh

他们只需检查文件是否存在并将其作为源文件即可。让我们点击最后一个选项,然后返回查看功能。

后备2:最后的避难所

绝对最后的办法是从字面上违反Conda v4.4以来的标准建议,即简单地将基本环境的bin目录放在PATH上。在这种情况下,没有conda activate功能;这只会确保Conda在您的路径上。


详细信息: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
        ehash
    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 "$@")" || eturn
    eval "$ask_conda"
    __conda_hashr
}

__conda_reactivate() {
    local ask_conda
    ask_conda="$(PS1="${PS1:-}" __conda_exe shell.posix reactivate)" || eturn
    eval "$ask_conda"
    __conda_hashr
}

conda() {
    local cmd="${1-__missing__}"
    case "$cmd" in
        activate|deactivate)
            __conda_activate "$@"
            ;;
        install|update|upgrade|remove|uninstall)
            __conda_exe "$@" || eturn
            __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()。在最后一步,它然后激活基础环境。

为什么要这样做?

这是这样设计的,以便响应配置设置。配置选项(如auto_activate_basechange_ps1)会影响Conda处理shell的方式,因此会更改Conda在其shell函数中包含的功能。


Conda";是否污染环境?

不完全是。可以通过配置设置禁用自动激活和提示修改等主要行为功能,因此conda init最终只需将conda activate功能添加到shell中,无需手动操作路径即可在环境之间进行干净切换。

相关文章