搞乱 venv 还是 python 的新口味?

Mess with venv or new flavor of python?

问题

问题是是否有另一种我没有想到的方法来解决下面的这些问题,或者它真的是用我们的工具构建 python 风格的答案吗?我有一个建议的解决方案 解决了 问题,但这并不意味着它是正确的答案。

问题

我在一个支持组织工作,我们正在为我们的主要产品开发支持工具。该产品具有 运行 自身的 OS 风味。我们在包装中要解决三个不同的问题 o

  1. OS 的风格具有我们可以安装的原始 python 二进制文件,但这限制了我们只能使用 python 的 OS 版本它将改变为一个单独的团队来管理 OS。预计这将在未来 2 年内在 python 的 3.4、3.5 和 3.6 版本之间发生变化 - 这些变化会影响我们始终如一地使用的库,无论好坏。

  2. 我们构建的工具也可在没有外部连接的站点上用作独立工具进行分析。我们目前仅限于 "hope the python that's on wherever they're using it plays nice".

  3. python 的更高版本中有显着的性能改进,我们想利用这些改进,但不升级到 3.6 就无法利用,但是这会导致我们无法使用较旧的版本,因为 python 库中的一些显着差异破坏了一个与另一个的东西。

我最初的方法是尝试制作一个可重定位的独立 virtualenv,但我越看那段代码,越发现它只是编辑 PYTHONHOME 和 PATH,如果你希望它是可重定位的,无论如何你都必须复制所有的二进制文件,或者只有当主机有你为之构建的 python 版本时它才可用。这也有需要对 virtualenv 脚本进行大量修改的缺点,以便修改 shebang,更新路径等 - 并且需要在每次移动时更新,或者具有动态 shebang。

感觉不对的建议解决方案

现在我正在考虑创建我们自己的 "flavor" of python - 但这感觉就像带着斧头去参加晚宴切胡萝卜。它解决了可以使用的多个位置的所有问题,所有位置都具有与我们安装的工具一致的最新版本 python,因此用户需要做的就是 运行使用安装这些东西的 /bin 更新 PATH 的脚本。

所以回到问题:

是否有另一种我没有想到的方法来解决这些问题,或者这真的是使用我们的工具构建 python 风格的答案吗?我是因为缺乏经验而让我感觉不对,还是我应该考虑这是一个有效的答案?

您实际上可以在本地安装 Python 到一个没有 root 权限的目录,并且 运行 它完全独立于任何系统 Python。您的工具可以使用此独立 Python 安装,而不是任何类型的系统级依赖项。

  • 使用 tar -xzf Python-X.X.X.tgz
  • 解压 Python 版本
  • 使用 mkdir ~/python36
  • 为安装的 Python 创建一个目录
  • 进入解压安装文件目录使用cd Python-X.X.X
  • 使用 ./configure -prefix=/home/user/python36
  • 为您的系统生成 Python 生成文件
  • 使用make altinstall prefix=/home/user/python36 exec-prefix=/home/user/python36安装Python到本地目录(注意altinstall的使用很关键,因为这确保Linux依赖Python 2.7或其他已安装版本未更换)

现在,您可以 运行 使用 /home/user/python36/bin/python3 script.py 在此本地安装的项目 使用此方法,您可以在头脑中使用 Python 的一个版本进行开发,而不必预期您无法控制的变化。如果您想为您的工具更新您的Python,这是一个非常有意和原子的操作。

类似于 J. Blackadar 的回答,我知道至少有一家公司这样做。

云提供商 Heroku 有一个用 NodeJS 编写的 CLI 工具。 Heroku 使用自制软件来分发包。他们还使用相同的包管理器来分发他们自己的 nodejs 版本,名为 heroku/brew/node。您可以看到,有时程序本身会更新,当 NodeJS 中有更新时,它们会同时更新两者。问题是,有时 NodeJS 本身会更新,但 heroku 不会更新它自己的 NodeJS,因此他们可以继续使用自己的版本,直到 运行 使用新版本进行适当的测试。

您也可以安装您自己的 python 版本并单独更新,或者使用 virtualenv 安装特定版本的 python,但我相信您已经尝试过了。

另一种选择是使用 docker 当然,您在任何地方都将拥有相同的环境,但由于您有一个特殊的 OS。

我实际上最终混合了两者 - 我创建了我自己的 "activate" 动态分配路径的文件(无论激活文件从哪里调用都有一些额外的功能,并将其放入 python 二进制文件。获取后,它将更新 shebangs。

  1. activate 现在将使用它自己的目录动态设置路径 它作为新的 PATH 垫片驻留在其中。与大多数情况一样,这假设 venvs,激活脚本驻留在 bin 中。

  2. activate 现在有一个帮助程序脚本和一个为所有人调用它的包装器 bin 中有 shebang 的文件。它将 shebang 更新到的那些文件 当前 python 可执行文件的路径(应该是 此时由 virtualenv 激活脚本更新)。它会 仅当 shebang 不同时才这样做。

脚本改动供参考:

function update_shebang() {
    # Call like: update_shebang check_sas_cabling
    full_path=
    # Back up our scripts, making them hidden by default.
    file_dir=$(dirname $full_path)
    file_name=$(basename $full_path)
    cp ${full_path} "${file_dir}/.${file_name}.bak"
    # Find old shebang - will look something like this:
    # /workdir/pure_python/bin/python3
    oldbang=$(grep '#!' ${full_path})
    newbang_path=$(readlink -f `which python`)
    # We have to escape the !, but then it keeps the backslash, so we have to
    # get rid of it.
    newbang=$(echo "#\!$newbang_path" | tr -d '\')
    # Just using sed in place
    # We dont' want to spam people every time they activate - so only modify
    # them if they aren't the same.
    if [ "$(echo $oldbang | tr -d "/")" != "$(echo $newbang | tr -d "/")" ]; then
        # Don't modify binary matches.
        if [ ! "$(echo $oldbang | grep 'Binary')" ]; then
            echo "Updated shebang for ${file_name} from ${oldbang} to ${newbang}"
            sed -i "s|${oldbang}|${newbang}|" ${full_path}
        fi
    fi
}

function update_all_shebangs() {
    # Wrapper around update_shebang to update all the
    # shebangs in ${NEW_PATH}/bin.
    given_path=
    myfiles=$(ls $given_path | grep -ve "^activate$")
    for file in $myfiles; do
        fullpath=$given_path/$file
        if grep -q "#!" $fullpath; then
            update_shebang $fullpath || exit 1
        fi
    done
}

以及新的动态路径部分:

NEW_PATH="$(dirname $(dirname $(readlink -f -n $BASH_SOURCE)))"

我还在 docker 图像中创建了这个,运行 所有 pip 安装我需要的东西,然后将它与所有这些捆绑在一起 - 所以它需要一点时间,但是开箱即用,真正意义上的可重定位。

一旦我编写了所需的 docker 安装脚本,就会发布包含此内容的框架回购协议。 :D