3

我正在使用 Python 开发一些特定于公司的应用程序。有一个自定义共享模块(“库”)描述了一些数据和算法,并且有几十个 Python 脚本可以使用这个库。这些文件非常多,因此它们被组织在子文件夹中

myproject
    apps
        main_apps
            app1.py
            app2.py
            ...
        utils
            util1.py
            util2.py
            ...
    library
        __init__.py
        submodule1
            __init__.py
            file1.py
            ...
        submodule2
            ...

用户想要运行这些脚本,只需转到 myproject\utils 并启动“py util2.py some_params”即可。这些用户中有许多是开发人员,因此他们经常想要编辑库并立即使用更新的代码重新运行脚本。该项目还使用了一些 3rd 方库,我想确保每个人都使用这些库的相同版本。

现在,我遇到了两个关键问题:

  1. 如何从(应用程序)引用(库)?
  2. 如何管理第三方依赖?

第一个问题对于很多 Python 开发者来说都很熟悉,并且在 SO 上被问过很多次:指示 Python 从 "....\library" 导入包是相当困难的。我测试了几种不同的方法,但似乎 python 不愿意在任何地方搜索包,而是在标准库位置或脚本本身的文件夹中搜索。

  • 相对导入不起作用,因为脚本不是库的一部分(即使它是,当脚本直接执行时这仍然不起作用,除非它放在我想避免的“根”项目文件夹中)
  • 将 .pth 文件(从阅读本文档中可能会想到)到脚本文件夹显然没有任何效果

当然直接干预 sys.path 工作,但是每个脚本文件中的样板代码看起来都非常糟糕

import sys, os.path
here = os.path.dirname(os.path.realpath(__file__))
module_root = os.path.abspath(os.path.join(here, '../..'))
sys.path.append(python_root)
import my_library

我意识到发生这种情况是因为 Python 希望我的库被正确“安装”,如果这个库与使用它的脚本分开开发,那确实是唯一正确的方法。但不幸的是,情况并非如此,我认为每次更改库时重新“安装”库将非常不方便并且容易出错。

第二个问题很简单。有人在我们的 app/lib 中添加了一个新的 3rd 方模块,其他人在更新他们的应用程序后开始看到导入问题。几个开发分支,用户执行 pip 安装的不同时刻,很少回滚 - 每个人最终都会使用不同版本的 3rd 方模块。在我的情况下,由于许多开发人员大量使用较旧的 Python 2.x 代码,而我想继续使用 Python 3.x,因此事情变得更加复杂

在为我的问题寻找可能的解决方案时,我发现 Python 中有一个非常出色的虚拟环境功能。事情看起来很光明:

  1. 为 myproject 创建一个 venv
  2. 分发 Requirements.txt 文件作为应用程序的一部分,并提供一个相应地填充 venv 的脚本
  3. 将我自己的库符号链接到 venv site_packages 文件夹,以便 Python 始终检测到它

这个解决方案看起来非常自然和强大。我明确地为我的项目设置了我自己的环境,并将我需要的任何东西放入这个 venv,包括我自己的库,我仍然可以在运行中进行编辑。它确实有效。但是调用activate.bat来激活这个 python 环境和另一个批处理文件来停用它是一团糟,尤其是在 Windows 平台上。正在编辑 sys.path 的样板代码看起来很糟糕,但至少它不会像这个潜在的修复那样干扰用户体验。

所以有一个问题我想问。

  1. 有没有办法将特定的 python venv 绑定到特定的文件夹,所以 python 启动器会自动将此 venv 用于这些文件夹中的脚本?
  2. 有没有更好的替代方法来处理我错过的这种情况?

我的项目环境是在 Windows 10 上运行的 Python 3.6。

4

1 回答 1

1

我想我终于找到了一个合理的答案。只需在 venv 中添加指向 python 解释器的 shebang 行就足够了,例如

#!../../venv/Scripts/python

完整的项目结构将如下所示

myproject
    apps
        main_apps
            app1.py (with shebang)
            app2.py (with shebang)
            ...
        utils
            util1.py (with shebang)
            util2.py (with shebang)
            ...
    library
        __init__.py
        submodule1
            __init__.py
            file1.py
            ...
        submodule2
            ...
    venv
        (python interpreter, 3rd party modules)
        (symlink to library)
    requirements.txt
    init_environment.bat

事情是这样的:

  1. venv 是一个虚拟 python 环境,包含项目所需的一切
  2. init_environment.bat 是根据 requirements.txt 填充 venv 并将我的库的符号链接放入 venv 站点模块的脚本
  3. 所有脚本都以指向 venv 解释器的 shebang 行开头(带有相对路径)

有一个完整的自定义环境,其中包含所有库,包括我自己的库和使用它的脚本都将具有非常自然的导入。每当我的项目中的任何面向用户的脚本从控制台或 Windows 资源管理器启动时,Python 启动器还将自动选择 Python 3.6 作为解释器并加载相关模块。

缺点:

  1. 如果从其他文件夹调用脚本,则相对 shebang 将不起作用
  2. 用户仍需手动运行init_environment.bat以根据 requirements.txt 更新虚拟环境
  3. Windows 上的 init_environment 脚本需要提升权限才能进行符号链接(但希望 MS 奇怪的决定将在 17 年 4 月即将推出的 Win10 更新中得到修复)

但是我可以忍受这些限制。希望这将有助于其他寻找类似问题的人。

仍然很高兴听到其他选项(作为答案)和批评者(作为评论)。

于 2017-03-06T18:36:18.350 回答