PyPI包装中的src/文件夹是否有特殊含义,还是只是一种惯例?

9 人关注

我正在学习如何根据教程为PyPI打包Python项目( https://packaging.python.org/en/latest/tutorials/packaging-projects/ ).对于这个例子项目,他们使用的是文件夹结构。

packaging_tutorial/
├── LICENSE
├── pyproject.toml
├── README.md
├── src/
│   └── example_package_YOUR_USERNAME_HERE/
│       ├── __init__.py
│       └── example.py
└── tests/

我只是想知道为什么需要src/文件夹?它有什么特殊用途吗?是否可以直接把软件包放在最上面的文件夹里?例如,将

packaging_tutorial/
├── LICENSE
├── pyproject.toml
├── README.md
├── example_package_YOUR_USERNAME_HERE/
│   ├── __init__.py
│   └── example.py
└── tests/

是否有任何缺点或引起并发症?

3 个评论
你可以使用src,也可以不使用,两者都有优点和缺点。Python打包可以用很多不同的方法,这已经成为初学者的噩梦了。你可以阅读这篇文章 blog.ionelmc.ro/2014/05/25/python-packaging . I hope it helps
有一个有趣的 blog post 关于这个话题;基本上,使用 src 可以防止在项目目录内运行测试时,软件包文件夹被导入而不是已安装的软件包(而测试应该总是针对已安装的软件包运行)。但最终这不是一个要求,而更像是一个惯例/最佳做法。比如说 requests 不使用 src ,他们仍然能够成为一个受欢迎和成功的项目。
@a_guest 我觉得你的评论如果扩展一下,可能有资格作为一个答案。
python
packaging
pypi
Wolpertinger
Wolpertinger
发布于 2022-06-22
1 个回答
a_guest
a_guest
发布于 2022-06-29
已采纳
0 人赞同

有一个有趣的 blog post 关于这个话题;基本上,使用 src 可以防止在项目目录内运行测试时,包的源文件夹被导入,而不是已安装的包(而测试应该总是针对已安装的包运行,所以情况与用户相同)。

考虑下面的例子,开发中的包的名字是 mypkg 。它包含一个 __init__.py 文件和另一个 DATA.txt 非代码资源。

├── mypkg │   ├── DATA.txt │   └── __init__.py ├── pyproject.toml ├── setup.cfg └── test └── test_data.py

在这里, mypkg/__init__.py 访问 DATA.txt 资源并加载其内容。

from importlib.resources import read_text
data = read_text('mypkg', 'DATA.txt').strip()  # The content is 'foo'.

脚本test/test_data.py检查mypkg.data是否真的包含'foo'

import mypkg
def test():
    assert mypkg.data == 'foo'

Now, running coverage run -m pytest在基本目录中,给人的印象是该项目一切正常。

$ coverage run -m pytest
[...]
test/test_data.py .                                             [100%]
========================== 1 passed in 0.01s ==========================

然而,有一个微妙的问题。运行coverage run -m pytest会调用pytest通过python -m pytest,即使用-m开关。这有一个 "副作用",正如文档中提到的。

[...] 与-c选项一样,当前目录将被添加到sys.path的开头。[...]

这意味着在test/test_data.py中导入mypkg时,它没有导入已安装的版本,而是从mypkg的源树中导入了包。

现在,让我们进一步假设,我们忘记在我们的项目规范中包括DATA.txt资源(毕竟,没有MANIFEST.in).所以这个文件实际上不包括在已安装的版本的mypkg(例如通过python -m pip install .安装)。这一点通过直接运行pytest可以看出。

$ pytest
[...]
======================= short test summary info =======================
ERROR test/test_data.py - FileNotFoundError: [Errno 2] No such file ...