PYPI包装中的SRC/文件夹是否具有特殊的含义,还是仅是约定?

发布于 2025-02-09 17:26:47 字数 667 浏览 1 评论 0 原文

我正在学习如何根据教程为PYTI打包Python项目()。对于示例项目,他们使用文件夹结构:

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/

任何缺点或引起并发症吗?

I'm learning how to package Python projects for PyPI according to the tutorial (https://packaging.python.org/en/latest/tutorials/packaging-projects/). For the example project, they use the folder structure:

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

I am just wondering why the src/ folder is needed? Does it serve a particular purpose? Could one instead include the package directly in the top folder? E.g. would

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

have any disadvantages or cause complications?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

阳光下慵懒的猫 2025-02-16 17:26:47

有一个有趣的博客文章关于此主题;基本上,使用 src 可以防止在项目目录中运行测试时,软件包源文件夹将导入而不是已安装的软件包(并且测试应始终针对安装的软件包进行,以便情况相同至于用户)。

考虑以下示例项目,其中正在开发的软件包的名称为 mypkg 。它包含 __ INIT __。py 文件和另一个 data.txt non-code资源:

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

此处, mypkg/__ init __ 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'

现在,运行 coverage> coverage> coverage> mm pytest 从基本目录中提供这个项目的印象是一切都很好:

$ coverage run -m pytest
[...]
test/test_data.py .                                             [100%]

========================== 1 passed in 0.01s ==========================

但是,有一个微妙的问题。运行 coverage Run -M Pytest Invokes pytest 通过 python -m pytest ,即使用 -m 开关。如文档中所述,这具有“副作用”:

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

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

现在,让我们进一步假设我们忘了在我们的项目规范中包含 data.txt 资源(毕竟,没有 subtest.in )。因此,该文件实际上不包含在安装 mypkg 的版本中(例如,通过 python -m pip install. 安装。)。通过直接运行 pytest 来揭示这一点:

$ pytest
[...]
======================= short test summary info =======================
ERROR test/test_data.py - FileNotFoundError: [Errno 2] No such file ...
!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!
========================== 1 error in 0.13s ===========================

因此,尽管安装了 mypkg 被损坏,但使用 coverage 测试通过。该测试没有捕获它,因为它是针对源树而不是安装版本的。如果我们已经使用 src 目录来包含 mypkg 软件包,则通过 -m 添加当前的工作目录不会引起任何问题,例如当前工作目录中没有包装 mypkg 。

但是最后,使用 src 不是必需品,而是更多的惯例/最佳实践。例如,请求不使用 src ,它们仍然设法成为一种流行和成功的项目。

There is an interesting blog post about this topic; basically, using src prevents that when running tests from within the project directory, the package source folder gets imported instead of the installed package (and tests should always run against installed packages, so that the situation is the same as for a user).

Consider the following example project where the name of the package under development is mypkg. It contains an __init__.py file and another DATA.txt non-code resource:

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

Here, mypkg/__init__.py accesses the DATA.txt resource and loads its content:

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

The script test/test_data.py checks that mypkg.data actually contains 'foo':

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

Now, running coverage run -m pytest from within the base directory gives the impression that everything is alright with the project:

$ coverage run -m pytest
[...]
test/test_data.py .                                             [100%]

========================== 1 passed in 0.01s ==========================

However, there's a subtle issue. Running coverage run -m pytest invokes pytest via python -m pytest, i.e. using the -m switch. This has a "side effect", as mentioned in the docs:

[...] As with the -c option, the current directory will be added to the start of sys.path. [...]

This means that when importing mypkg in test/test_data.py, it didn't import the installed version but it imported the package from the source tree in mypkg instead.

Now, let's further assume that we forgot to include the DATA.txt resource in our project specification (after all, there is no MANIFEST.in). So this file is actually not included in the installed version of mypkg (installation e.g. via python -m pip install .). This is revealed by running pytest directly:

$ pytest
[...]
======================= short test summary info =======================
ERROR test/test_data.py - FileNotFoundError: [Errno 2] No such file ...
!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!
========================== 1 error in 0.13s ===========================

Hence, when using coverage the test passed despite the installation of mypkg being broken. The test didn't capture this as it was run against the source tree rather than the installed version. If we had used a src directory to contain the mypkg package, then adding the current working directory via -m would have caused no problems, as there is no package mypkg in the current working directory anymore.

But in the end, using src is not a requirement but more of a convention/best practice. For example requests doesn't use src and they still manage to be a popular and successful project.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文