【问题标题】:Proper initialization of sys.path when main script is in a submodule当主脚本位于子模块中时正确初始化 sys.path
【发布时间】:2013-03-25 17:54:35
【问题描述】:

sys.path的第一个条目是当前脚本的目录,根据docs。在以下设置中,我想更改此默认设置。想象一下下面的目录结构:

src/
    core/
        stuff/
        tools/
            tool1.py
            tool2.py
    gui/
        morestuff/
        gui.py

脚本tool*.pygui.py 旨在作为脚本运行,如下所示:

python src/core/tools/tool2.py
python src/gui/gui.py

现在所有工具都从src.core.stuff 导入,GUI 需要gui.morestuff。这意味着sys.path[0] 应该指向src/,但它默认指向src/core/tools/src/gui/

我可以在每个脚本中调整sys.path[0](使用如下结构,例如,在gui.py 的开头):

if __name__ == '__main__':
    if sys.path[0]: sys.path[0] = os.path.dirname(os.path.abspath(sys.path[0]))

但是,这有点多余,对于包含数千个脚本的成熟代码库来说,这变得乏味。我也知道-m 开关:

python -m gui.gui

但这要求当前目录为src/

有没有更好的方法来达到预期的结果,例如通过修改__init__.py 文件?

编辑:这是针对 Python 2.7 的:

~$ python -V
Python 2.7.3

【问题讨论】:

  • 对于初学者来说,python -m core.tools.tool1
  • 你用的是哪个python版本?
  • @Kos:感谢您阅读整篇文章。
  • @krlmlr (擦眼镜) :)
  • 你可以使用setup.py来定义entry_points,例如entry_points={'console_scripts': ['my-tool=my.core.tools.tool2:main', 'my-gui=my.gui.gui:main']}(假设顶层包被称为my并且功能在main()函数中)它定义了@987654347 @ 和 my-gui 脚本。

标签: python module path python-import


【解决方案1】:

唯一官方批准的运行包中脚本的方法是使用-m 标志。虽然您可以直接运行脚本并尝试在每个脚本中自己进行sys.path 操作,但这可能会很痛苦。如果您在文件夹之间移动脚本,则可能还需要更改重写 sys.path 的逻辑以反映新位置。即使sys.path 正确,显式相对导入也无法正常工作。

现在,使python -m mypackage.mymodule 工作需要您位于项目的顶级文件夹(在您的情况下为src),或者该顶级文件夹位于Python 搜索路径中。要求您位于特定文件夹中很尴尬,并且您已经说过您不希望这样做。让src 进入搜索路径是我们的目标。

我认为最好的方法是使用PYTHONPATH 环境变量将解释器指向您项目的src 文件夹,以便它可以从任何地方找到您的包。

此解决方案设置简单(环境变量可以在您的.profile.bashrc 或其他等效位置自动设置),并且适用于任意数量的脚本。如果您移动您的项目,只需更新您的环境设置即可完成所有设置,无需为每个脚本做更多工作。

【讨论】:

  • 感谢您的回答。我想这样做是为了避免首先设置PYTHONPATH...
【解决方案2】:

这里有三个基本选项。我在生产环境和个人项目中都经历了这三个方面。在许多方面,它们相互建立。不过,我的建议是直接跳到最后一个。

根本问题是您需要将./src 目录放在python 搜索路径中。这就是 python 打包的真正意义所在。

PYTHONPATH

调整 Python 路径的最直接、用户定义的方法是通过环境变量 PYTHONPATH。您可以在运行时设置它,执行以下操作:

PYTHONPATH=/src python src/gui/gui.py

您当然也可以在您的全局环境中设置它,因此希望所有需要它的进程都能找到正确的PYTHONPATH。但是,请记住,您将永远忘记一个。通常在凌晨 3 点,当您的 cron 任务最终运行时。

网站包

为避免需要环境变量,您的选择几乎是将您的软件包含在源路径的现有条目中,或者找到一些其他方法来添加新的搜索路径。因此,这可能意味着将您的 src 目录的内容放入 /usr/lib/python2.7/site-packages 或您的系统 site-packages 所在的任何位置。

由于您可能不想将代码实际包含在站点包中,因此您可以为您的两个子包创建符号链接。

由于多种原因,这当然不太理想。如果您对命名不小心,那么机器上的每个 python 程序都会突然面临潜在的名称冲突。您正在向机器上的每个用户公开您的软件。如果更新了 python get,您可能会遇到问题。如果你添加一个新的子包,现在你必须创建一个新的符号链接。

更好的方法是在站点包中的某处包含一个.pth 文件。当python遇到这些文件时,它会将内容(应该是目录的名称)添加到搜索路径中。这避免了必须记住为每个新子包添加新符号链接的问题。

virtualenv 和打包

最好的解决办法就是硬着头皮做真正的python打包。这与virtualenv and pip 等出色工具相结合,让您拥有一个隔离(或半隔离)的 Python 环境。

在 virtualenv 下,您可以为您的项目自定义 site-packages,您可以轻松地将软件安装到其中,避免早期解决方案的所有问题。 virtualenv 还可以轻松维护可执行脚本,使其运行的 python 环境完全符合您的预期。

一个缺点是您必须编写和维护setup.py,它将指示pip(python 安装程序)将您的软件包含在 virtualenv 中。内容类似于:

!/usr/bin/env 蟒蛇 # -*- 编码:utf-8 -*- 从 distutils.core 导入设置 设置( 名称='我的项目', package_dir={'myproject': 'src'}, 脚本=['src/gui/gui.py', 'src/core/tools/tool1.py', 'src/core/tools/tool2.py'] )

所以,要设置这个环境,它看起来像这样:

virtualenv env
env/bin/pip install -e setup.py

要运行您的脚本,您只需执行以下操作:

env/bin/tool1.py

【讨论】:

  • 感谢您非常详细的回答。不幸的是,打包不是一种选择,该软件正在 SVN 工作副本中开发。但是,我想知道是否也可以将第二种方法与我的 SVN 工作副本一起使用与 virtualenv 相关:将适当格式的 .pth 文件放入虚拟环境的 lib/python2.X/site-packages 子目录中。有什么想法吗?
  • 第三个选项不能满足您的需要吗?使用pip install -e 安装包会安装包及其脚本,链接到您的开发代码(而不是安装副本),因此对 repo 中代码的任何编辑都将自动在 virtualenv 中可用。
  • 将适当的 .pth 文件放入 virtualenv 基本上是 pip install -e 为您所做的(根据 Josh 的回复)它还将占位符写入您的 bin 目录,将其链接到适当的 python 实例。
  • 将尝试第三个选项。非常感谢!
【解决方案3】:

我想这样做是为了避免一开始就设置 PYTHONPATH

还有其他地方可以挂接到 Python 的 sys.path 初始化,使用 site 模块,该模块(默认情况下)在 Python 初始化时自动导入。

基于site.py中的这段代码...

# Prefixes for site-packages; add additional prefixes like /usr/local here
PREFIXES = [sys.prefix, sys.exec_prefix]

...看起来好像这个文件被设计为在安装后修改,这是一种选择,尽管它也提供了其他可以影响sys.path的方式,例如通过将.pth 文件放在site-packages 目录中的某个位置。

假设所需的结果是让代码“开箱即用”,这将有效,但仅适用于单个系统上的所有用户。

如果您需要它在多个系统上工作,那么您必须对所有系统应用相同的更改。

对于部署而言,这没什么大不了的。事实上,许多 Python 包已经做了类似的事情。例如在 Ubuntu 上...

~$ dpkg -L python-imaging | grep pth
/usr/share/pyshared/PIL.pth
/usr/lib/python2.7/dist-packages/PIL.pth

...但是,如果您的目的是让多个并发开发人员更容易,每个开发人员都使用自己的系统,那么您最好坚持当前的选项,即为每个 Python 模块添加一些“样板”代码作为脚本运行。

可能还有其他选择,但这取决于您要达到的目标。

【讨论】:

    猜你喜欢
    • 2016-02-05
    • 2021-04-06
    • 2017-06-06
    • 1970-01-01
    • 2020-02-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多