如何恢复旧的Python环境?

-2 投票
1 回答
73 浏览
提问于 2025-04-13 17:40

我遇到了一点麻烦,因为服务器升级了,之前的一些Python环境不再工作了。我不能直接激活旧环境,但我想创建一个新的环境,尽量让它和旧环境一样。

这意味着我需要在新环境中安装和旧环境相同版本的包。

有没有办法从旧环境中提取这些信息,比如导出到一个requirements.txt文件里?在旧环境中似乎找不到可以用的requirements.txt文件。

补充:谢谢大家的讨论。是我没在升级前做pip freeze,这是我的错,但我没想到事情会这么脆弱。

有些代码比较旧(我知道会有人提到在某些情况下使用了Python 2.7),但是比如说,当我从一个激活的包中运行pip freeze时,我得到了:

# pip freeze

/.../venv/bin/python: error while loading shared libraries: libpython2.7.so.1.0: cannot open shared object file: No such file or directory

我知道机器上安装了Python 2.7,但我在整个服务器上查找了所有文件,还是找不到这个对象文件。

1 个回答

1

这里有一个 shell 脚本,它大致上能实现 pip freeze 的功能。我在一个我知道结果的环境中测试过它(就是 Charcoal SmokeDetector 项目),结果大致上是对的;不过,有很多 Python 包的名称和文件名并不完全一致。

#!/bin/sh

: ${1?"Syntax: $0 <sdenv>">}

site_packages=""
for d in "$1"/lib/python3.?/site-packages "$1"/lib/python3.??/site-packages; do
    test -d "$d" || continue
    [ "$site_packages" ] && echo "$0: ignoring $site_packages" >&2
    site_packages="$d"
    break
done

: ${site_packages?"No lib/python3.*/site_packages found in $1"}

for package_dir in "$site_packages"/*/; do
    case $package_dir in
        *.dist-info/ | *.egg-info/ | */__pycache__/ ) continue;;
    esac
    found=""
    for metadata in "${package_dir%/}"-*.dist-info/METADATA \
                    "${package_dir%/}"-*.egg-info/PKG-INFO; do
        test -f "$metadata" || continue
        awk '/^Name:[ \t]*/ { name=$0; sub(/^Name:[ \t]*/, "", name) }
            /^Version:[ \t]*/ {ver=$0; sub(/^Version:[ \t]*/, "", ver) }
            name && ver { print name "==" ver; found=1; exit }
            END { exit(1-found) }' "$metadata" && found=yes && break
    done
    test "$found" && continue
    package_name=$(basename "$package_dir")
    if [ -f "${package_dir}__init__.py" ]; then
        if [ "${package_name}" != "${package_name#_}" ] &&
               [ -d "$site_packages/${package_name#_}" ]; then
            continue
        fi
        PYTHONPATH="${site_packages%/site-packages}${PYTHONPATH+:}${PYTHONPATH}" \
            python3 <<________HERE 2>/dev/null && found="yes"
import $package_name
ver = $package_name.__version__
print(f'$package_name=={ver}')
________HERE
    fi
    test "$found" && continue
    echo "$0: could not extract metadata for $package_name" >&2
    echo "$package_name"
done

另外,这个脚本也有 pip freeze 常见的问题;它会列出所有已安装的包,而不仅仅是你明确要求的那些包。所以比如说,如果你想安装 se7en 这个包,而它依赖于 six,那么 six 也会出现在结果中,即使将来 se7en 的新版本不再需要 six 的情况下,这个包依然会被列出来。

对于 Smokey 的依赖,上面的脚本会对 dns(实际包名是 dnspython)、attr(我 觉得 实际包名是 attrs,但它奇怪地创建了一个单数形式的目录)、msgpack(实际包名是 msgpack-python)和 pkg_resources(没有提供版本号)发出警告。可以说这个脚本可以改进一下,修复其中的一些问题,但可能还有其他一些特殊情况没有考虑到。总之,可以把这当作一个粗略的草图。

撰写回答