我有一个python应用程序(基于Django),我有几个与应用程序一起使用的独立维护脚本,我不得不每隔一段时间调用一次。他们必须导入我的应用程序的一部分(子包)。目前,我只是将它们放在我的顶级目录中:
application/
djangoproject/
djangoapp/
otherpackage/
tool1.py
tool2.py
tool1.py
会做什么
from djangoproject import wsgi
from djangoapp.models import Poll
我已经积累了相当多的这些工具,并希望将它们移到scripts
子目录中。然后,我希望能够通过python scripts/tool1.py
或cd scripts; python tool1.py
来呼叫他们。
我理解(有时感叹)Python的导入是如何工作的,我知道我可以在每个脚本中添加一些行来将父目录添加到PYTHONPATH
。我想知道是否有一个广泛的模式来处理这样的各种脚本集合。也许可以将路径操作放到另一个文件中,并让每个脚本都以import mainproject
开头?
我正在使用virtualenv,并使用pip安装依赖项。但是应用程序本身目前还没有使用setup.py
,我认为将脚本移动到通过pip安装的单独软件包是没有用的,因为我在开发过程中经常更改它们,有很多一次性的。
答案 0 :(得分:1)
组织源代码的方式因项目而异。从我多年的经验来看,最好和最抒情的方式是始终拥有setup.py
。
在这种情况下,您可以将pip install -e .
和来自.
目录的editable version伪装入virtualenv。实际上,并没有真正安装(即复制),而是“链接”:源代码目录将添加到sys.path
.pth
个文件中,因此您可以编辑&之后尝试没有任何特殊的复制/安装步骤。
有关此问题的更多信息,您可以使用extra dependencies扩展setup.py
,例如开发目的,并按pip install -e .[dev]
安装它们。更像是一种奇特的后果。
其余的取决于脚本的性质。
如果脚本是应用程序的一部分,则应通过entry-points in setup.py
安装它们。
# setup.py:
setup(
entry_points={
'console_scripts': [
'tool1 = mytools.tool1:main',
'tool2 = mytools.tool2:main',
],
},
)
在这种情况下,在pip install -e .
之后,如果使用系统python,它们将位于virtualenv的bin
文件夹中,或者位于/usr/local/bin
或类似文件夹中。您可以像这样执行它们:
source .venv/bin/activate
tool1 ...
# OR:
~/path/to/venv/bin/tool2
以这种方式安装的脚本完全了解它们所安装的virtualenv,因此不需要激活,也不需要显式的python二进制文件。
如果脚本用于代码维护,而不是语义上是应用程序的一部分,那么它们通常被放入./scripts/
目录(或任何其他目录,例如./ci/
),其中shebang位于顶部(#!/usr/bin/env python
)。例如,tool1.py
:
#!/usr/bin/env python
def main():
pass
if __name__ == '__main__':
main()
由于这个shebang在当前的virtualenv中执行如下:
source .venv/bin/activate
./scripts/tool1.py ...
# OR:
~/path/to/venv/bin/python ./scripts/tool1.py
与通过入口点安装的脚本不同,这些脚本不以任何方式了解自己的virtualenv,因此virtualenv应该是激活或显式使用的python。
当脚本是非python时,也使用这种方式,例如对于bash脚本。
在这两种情况下,requirements.txt
文件有时用于 pin 应用程序的&依赖项的版本(带pip freeze
),这样部署就可以持久化了。可预测的。但这是另一个故事 - 关于应用程序的部署,而不是关于包装和安装;维护。
requirements.txt
文件会不时重新生成,以满足setup.py
中新的未固定(即灵活)要求以及可用的新软件包版本。但通常它是生成的内容(尽管在回购中提交),而不是手工维护的内容。
如果您因任何原因严格不希望setup.py
,那么请使用修改后的env var执行这些脚本:
PYTHONPATH=. python scripts/tool1.py
或者从内部攻击sys.path
:
# tools1.py
import sys
import os
sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
这正是pip install -e .
所做的,只是在每次调用时手动完成,而不是使用virtualenv中的.pth
文件。而且这看起来很糟糕。
然而,正如我们所知,hacky解决方案和重复解决方案,特别是那些重复标准工具包的解决方案,都被认为是“pythonic”。