当主脚本在子模块中时,正确初始化sys.path

时间:2013-04-03 20:57:40

标签: python module path python-import

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

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

3 个答案:

答案 0 :(得分:5)

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

现在,使python -m mypackage.mymodule工作需要您在项目的顶级文件夹(在您的情况下为src),或者要在Python搜索路径上的顶级文件夹。要求你在一个特定的文件夹是很尴尬的,你已经说过你不想这样。然后将src纳入搜索路径是我们的目标。

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

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

答案 1 :(得分:4)

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

根本问题是您需要./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得到更新,你可能会遇到问题。如果添加新的子包,现在必须创建一个新的符号链接。

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

virtualenv and packaging

最好的解决方案就是咬紧牙关,做真正的蟒蛇包装。这与像virtualenv and pip这样的强大工具相结合,让你有一个孤立的(或半隔离的)python环境。

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

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

!/usr/bin/env python
# -*- coding: utf-8 -*-

from distutils.core import setup


setup(
    name='myproject',
    package_dir={'myproject': 'src'},
    scripts=['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

答案 2 :(得分:1)

  

我想这样做是为了避免首先设置PYTHONPATH

还有其他地方可以使用site模块挂钩Python的sys.path初始化,这是(默认情况下)在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模块添加一些'样板'代码作为脚本运行。

可能还有另一种选择,但这取决于你想要实现的目标。