ChatGPT解决这个技术问题 Extra ChatGPT

Python 应用程序的最佳项目结构是什么? [关闭]

关闭。这个问题需要更加集中。它目前不接受答案。想改进这个问题?更新问题,使其仅通过编辑此帖子专注于一个问题。 5年前关闭。改进这个问题

想象一下,您想用 Python 开发一个重要的最终用户桌面(不是 Web)应用程序。构建项目文件夹层次结构的最佳方法是什么?

理想的功能是易于维护、IDE 友好、适合源代码控制分支/合并以及易于生成安装包。

尤其是:

你把源放在哪里?您将应用程序启动脚本放在哪里?你把 IDE 项目放在哪里?你把单元/验收测试放在哪里?您将非 Python 数据(例如配置文件)放在哪里?对于 pyd/so 二进制扩展模块,您将非 Python 源代码(例如 C++)放在哪里?


S
S.Lott

没有太大关系。任何让你开心的事情都会奏效。没有很多愚蠢的规则,因为 Python 项目可以很简单。

/scripts 或 /bin 用于那种命令行界面的东西

/tests 为您的测试

/lib 用于您的 C 语言库

/doc 用于大多数文档

/apidoc 用于 Epydoc 生成的 API 文档。

顶级目录可以包含 README、Config 等。

艰难的选择是是否使用 /src 树。 Python 不像 Java 或 C 那样区分 /src/lib/bin

由于某些人认为顶级 /src 目录毫无意义,因此您的顶级目录可以是应用程序的顶级架构。

/foo

/酒吧

/baz

我建议将所有这些放在“我的产品名称”目录下。因此,如果您正在编写一个名为 quux 的应用程序,则包含所有这些内容的目录名为 /quux

然后,另一个项目的 PYTHONPATH 可以包含 /path/to/quux/foo 以重用 QUUX.foo 模块。

就我而言,因为我使用 Komodo Edit,所以我的 IDE cuft 是单个 .KPF 文件。我实际上将它放在顶级 /quux 目录中,并省略了将其添加到 SVN。


您会推荐任何开源 python 项目来模拟它们的目录结构吗?
看看 Django 就是一个很好的例子。
我不倾向于认为 Django 是一个很好的例子——在我的书中玩 sys.path 是一个即时 DQ。
关于“技巧”:Django 将根项目文件夹的父级添加到 sys.path,以便可以将模块导入为“from project.app.module import klass”或“from app.module import klass”。
哦,我喜欢这个技巧,现在正在使用它。我想将共享模块放在另一个目录中,我不想在系统范围内安装模块,也不想要求人们手动修改 PYTHONPATH。除非人们提出更好的建议,否则我认为这实际上是最干净的方法。
S
Søren Løvborg

根据 Jean-Paul Calderone 的Filesystem structure of a Python project

Project/
|-- bin/
|   |-- project
|
|-- project/
|   |-- test/
|   |   |-- __init__.py
|   |   |-- test_main.py
|   |   
|   |-- __init__.py
|   |-- main.py
|
|-- setup.py
|-- README

bin文件夹中的可执行文件如何引用项目模块? (我认为 python 语法不允许在包含语句中使用 ../
@ThorSummoner 仅在保留在单个包中时才有效。要在此处进行相对导入,您需要在 bin 文件夹和 Project 顶级文件夹中都有一个 __init__.py 文件。
@ThorSummoner 简单。你安装包! (pip install -e /path/to/Project)
如果有人能用 hello.py 和 hello-test.py 压缩这个布局的示例并让我们的新手可以使用它,那就太棒了。
@Bloke 核心是 -e 标志,它将包安装为可编辑包,即将其安装为指向实际项目文件夹的链接。然后可执行文件只需 import project 即可访问该模块。
C
Community

这个 blog post by Jean-Paul Calderone 通常在 Freenode 上的#python 中作为答案给出。

Python 项目的文件系统结构 做:将目录命名为与您的项目相关的名称。例如,如果您的项目名为“Twisted”,请将其源文件的顶级目录命名为 Twisted。当你发布版本时,你应该包含一个版本号后缀:Twisted-2.5。创建一个目录 Twisted/bin 并把你的可执行文件放在那里,如果你有的话。不要给它们一个 .py 扩展名,即使它们是 Python 源文件。除了导入和调用项目中其他地方定义的主函数外,不要在其中放置任何代码。 (轻微的皱纹:因为在 Windows 上,解释器是由文件扩展名选择的,所以你的 Windows 用户确实想要 .py 扩展名。所以,当你为 Windows 打包时,你可能想要添加它。不幸的是,没有简单的 distutils 技巧我知道要自动化这个过程。考虑到在 POSIX 上,.py 扩展名只是一个缺点,而在 Windows 上,缺少的是一个实际的错误,如果您的用户群包括 Windows 用户,您可能希望选择只拥有 .py到处都是扩展名。)如果您的项目可以表示为单个 Python 源文件,则将其放入目录中,并将其命名为与您的项目相关的名称。例如,Twisted/twisted.py。如果您需要多个源文件,请改为创建一个包(Twisted/twisted/,带有一个空的 Twisted/twisted/__init__.py)并将您的源文件放入其中。例如,Twisted/twisted/internet.py。将单元测试放在包的子包中(注意——这意味着上面的单个 Python 源文件选项是一个技巧——你总是需要至少一个其他文件来进行单元测试)。例如,Twisted/twisted/test/。当然,用 Twisted/twisted/test/__init__.py 把它做成一个包。将测试放在 Twisted/twisted/test/test_internet.py 之类的文件中。添加 Twisted/README 和 Twisted/setup.py 来分别解释和安装你的软件,如果你感觉不错的话。不要:将源代码放在名为 src 或 lib 的目录中。这使得不安装就很难运行。把你的测试放在你的 Python 包之外。这使得很难针对已安装的版本运行测试。创建一个只有 __init__.py 的包,然后将所有代码放入 __init__.py。只需制作一个模块而不是一个包,它更简单。尝试想出一些神奇的技巧,让 Python 能够导入您的模块或包,而无需用户将包含它的目录添加到他们的导入路径中(通过 PYTHONPATH 或其他一些机制)。您不会正确处理所有情况,当您的软件在他们的环境中无法运行时,用户会生您的气。


这正是我所需要的。 “不要试图想出神奇的技巧来让 Python 能够导入你的模块或包,而无需用户将包含它的目录添加到他们的导入路径中。”很高兴知道!
对“将源代码放在名为 src 或 lib 的目录中。这使得不安装很难运行。”感到困惑。会安装什么?是导致问题的目录名称,还是它根本是子目录的事实?
“这使得不安装就很难运行。” - 这才是重点
我觉得这个示例使用 Twisted 作为项目名称具有讽刺意味,因为官方 Twisted library 现在使用 src 布局,这与第一个“不要”建议相矛盾:“将您的源代码放在一个名为 src 的目录中或lib。这使得不安装就很难运行。”这就是重点(见Ionel Cristian Mărieș's article)。
做:“将你的源代码放在一个名为 src 或 lib 的目录中。”
a
alfonx

查看Open Sourcing a Python Project the Right Way

让我摘录那篇优秀文章的项目布局部分:

在设置项目时,布局(或目录结构)对于正确处理很重要。合理的布局意味着潜在的贡献者不必永远花时间寻找一段代码;文件位置很直观。由于我们正在处理现有项目,这意味着您可能需要移动一些东西。让我们从顶部开始。大多数项目都有许多顶级文件(如 setup.py、README.md、requirements.txt 等)。然后每个项目都应该有三个目录: 包含项目文档的 docs 目录 以项目名称命名的目录,用于存储实际的 Python 包 两个位置之一的测试目录 包含测试代码和资源的包目录下 作为一个支架-alone 顶级目录 为了更好地了解文件的组织方式,这是我的一个项目 sandman 的布局的简化快照: $ pwd ~/code/sandman $ tree 。 |- 许可证 |- README.md |- TODO.md |- 文档 | |-- conf.py | |-- 生成 | |-- 索引.rst | |-- 安装.rst | |-- 模块.rst | |-- 快速入门.rst | |-- sandman.rst |- requirements.txt |- 沙子 | |-- __init__.py | |-- 异常.py | |-- 模型.py | |-- 沙子.py | |-- 测试 | |-- 模型.py | |-- test_sandman.py |- setup.py 如你所见,有一些顶级文件,一个docs目录(生成的是一个空目录,sphinx将放置生成的文档),一个sandman目录,下面还有一个test目录沙人。


我这样做了,但更重要的是:我有一个顶层 Makefile,它带有一个 'env' 目标,可以自动执行 'virtualenv env ; ./env/bin/pip install -r requirements.txt ; ./env/bin/python setup.py develop',通常也是一个依赖于 env 的“测试”目标,还安装测试依赖项,然后运行 py.test。
@pjz 你能扩展你的想法吗?您是说将 Makefile 置于与 setup.py 相同的级别吗?因此,如果我对您的理解正确,make env 会自动创建一个新的 venv 并将软件包安装到其中...?
@St.Antario 完全正确。如前所述,我通常还有一个“测试”目标来运行测试,有时还有一个“发布”目标,它查看当前标签并构建一个轮子并将其发送给 pypi。
在这种结构中,/code/sandman/sandman/ 中的任何文件如何在 /code/sandman/docs/ 中导入某些内容?比如说,我想从 sandman.py 导入 config.py。我该怎么做?
g
guettli

“Python Packaging Authority”有一个示例项目:

https://github.com/pypa/sampleproject

它是一个示例项目,作为 Python 打包用户指南的打包和分发项目教程的辅助工具。


+ root/src/* 结构的趋势:github.com/pypa/sampleproject/commit/…
有关项目结构的建议,另请参阅 setuptools.readthedocs.io/_/downloads/en/latest/pdf
M
Maggyero

尝试使用 python_boilerplate 模板启动项目。它在很大程度上遵循最佳实践(例如 those here),但更适合您发现自己愿意在某个时候将您的项目拆分为多个鸡蛋的情况(相信我,除了最简单的项目之外,您会的。一种常见的情况是您必须使用其他人的库的本地修改版本)。

你把源放在哪里?对于相当大的项目,将源分成几个鸡蛋是有意义的。每个鸡蛋都将作为一个单独的 setuptools-layout 在 PROJECT_ROOT/src/ 下。

对于相当大的项目,将源分成几个鸡蛋是有意义的。每个鸡蛋都将作为一个单独的 setuptools-layout 在 PROJECT_ROOT/src/ 下。

您将应用程序启动脚本放在哪里?理想的选择是将应用程序启动脚本注册为其中一个鸡蛋中的 entry_point。

理想的选择是将应用程序启动脚本注册为其中一个鸡蛋中的 entry_point。

你把 IDE 项目放在哪里?取决于IDE。他们中的许多人将他们的东西保存在项目根目录的 PROJECT_ROOT/. 中,这很好。

取决于IDE。他们中的许多人将他们的东西保存在项目根目录的 PROJECT_ROOT/. 中,这很好。

你把单元/验收测试放在哪里?每个 egg 都有一组单独的测试,保存在其 PROJECT_ROOT/src//tests 目录中。我个人更喜欢使用 py.test 来运行它们。

每个 egg 都有一组单独的测试,保存在其 PROJECT_ROOT/src//tests 目录中。我个人更喜欢使用 py.test 来运行它们。

您将非 Python 数据(例如配置文件)放在哪里?这取决于。可以有不同类型的非 Python 数据。 “资源”,即必须打包在鸡蛋中的数据。该数据进入相应的 egg 目录,位于包命名空间中的某处。它可以通过 setuptools 中的 pkg_resources 包使用,或者从 Python 3.7 开始通过标准库中的 importlib.resources 模块使用。 “配置文件”,即非 Python 文件,它们被视为项目源文件的外部文件,但在应用程序开始运行时必须使用一些值进行初始化。在开发过程中,我更喜欢将这些文件保存在 PROJECT_ROOT/config 中。对于部署,可以有多种选择。在 Windows 上可以使用 %APP_DATA%//config,在 Linux 上可以使用 /etc/ 或 /opt//config。生成的文件,即可以由应用程序在执行期间创建或修改的文件。我希望在开发期间将它们保留在 PROJECT_ROOT/var 中,并在 Linux 部署期间将它们保留在 /var 下。

这取决于。可以有不同类型的非 Python 数据。 “资源”,即必须打包在鸡蛋中的数据。该数据进入相应的 egg 目录,位于包命名空间中的某处。它可以通过 setuptools 中的 pkg_resources 包使用,或者从 Python 3.7 开始通过标准库中的 importlib.resources 模块使用。 “配置文件”,即非 Python 文件,它们被视为项目源文件的外部文件,但在应用程序开始运行时必须使用一些值进行初始化。在开发过程中,我更喜欢将这些文件保存在 PROJECT_ROOT/config 中。对于部署,可以有多种选择。在 Windows 上可以使用 %APP_DATA%//config,在 Linux 上可以使用 /etc/ 或 /opt//config。生成的文件,即可以由应用程序在执行期间创建或修改的文件。我希望在开发期间将它们保留在 PROJECT_ROOT/var 中,并在 Linux 部署期间将它们保留在 /var 下。

“资源”,即必须打包在鸡蛋中的数据。该数据进入相应的 egg 目录,位于包命名空间中的某处。它可以通过 setuptools 中的 pkg_resources 包使用,或者从 Python 3.7 开始通过标准库中的 importlib.resources 模块使用。

“配置文件”,即非 Python 文件,它们被视为项目源文件的外部文件,但在应用程序开始运行时必须使用一些值进行初始化。在开发过程中,我更喜欢将这些文件保存在 PROJECT_ROOT/config 中。对于部署,可以有多种选择。在 Windows 上可以使用 %APP_DATA%//config,在 Linux 上可以使用 /etc/ 或 /opt//config。

生成的文件,即可以由应用程序在执行期间创建或修改的文件。我希望在开发期间将它们保留在 PROJECT_ROOT/var 中,并在 Linux 部署期间将它们保留在 /var 下。

对于 pyd/so 二进制扩展模块,您将非 Python 源代码(例如 C++)放在哪里?进入 PROJECT_ROOT/src//native

进入 PROJECT_ROOT/src//native

文档通常会进入 PROJECT_ROOT/docPROJECT_ROOT/src/<egg_name>/doc(这取决于您是否将某些鸡蛋视为单独的大型项目)。一些附加配置将位于 PROJECT_ROOT/buildout.cfgPROJECT_ROOT/setup.cfg 等文件中。


感谢您的精彩回答!你为我澄清了很多事情!我只有一个问题:鸡蛋可以嵌套吗?
不,你不能在将 .egg 文件存储在其他 .egg 文件中的意义上“嵌套”鸡蛋,并希望这会有很大用处[除非你做了一些非常奇怪的事情]。但是,您可以做的是创建“虚拟”鸡蛋 - 不提供任何有用代码的空包,但在其依赖项列表中列出其他包。这样,当用户尝试安装这样的包时,他将递归地安装许多依赖的鸡蛋。
@KT 你能详细说明一下你如何处理生成的数据吗?特别是,您(在代码中)如何区分开发和部署?我想你有一些 base_data_location 变量,但你如何适当地设置它?
我想你说的是“运行时数据”——人们经常会放在 /var/packagename 或 ~/.packagename/var 或诸如此类的东西下。大多数情况下,这些选择足以作为您的用户不想更改的默认设置。如果您想调整此行为,则选项相当丰富,我认为没有一个适合所有人的最佳实践。典型选择:a) ~/.packagename/configfile, b) export MY_PACKAGE_CONFIG=/path/to/configfile c) 命令行选项或函数参数 d) 这些组合。
请注意,在某个地方有一个单例 Config 类是很常见的,它为您处理您最喜欢的配置加载逻辑,甚至可能让用户在运行时修改设置。不过,总的来说,我认为这是一个值得单独提出问题的问题(可能在此之前的某个地方已经问过)。
J
Jason Baker

根据我的经验,这只是一个迭代的问题。将您的数据和代码放在您认为的任何地方。有可能,无论如何你都会错的。但是,一旦您对事情将如何形成有了更好的了解,您就可以更好地进行这些猜测。

至于扩展源,我们在 trunk 下有一个 Code 目录,其中包含一个用于 python 的目录和一个用于各种其他语言的目录。就个人而言,我更倾向于下次尝试将任何扩展代码放入自己的存储库中。

话虽如此,我回到我最初的观点:不要把它搞得太大。把它放在似乎对你有用的地方。如果您发现某些东西不起作用,则可以(并且应该)对其进行更改。


是的。我试图成为“Pythonic”:显式优于隐式。目录层次结构的读取/检查比写入更多。 ETC..
C
Charles Duffy

使用 setuptools 中的 package_data 支持最好将非 Python 数据捆绑在 Python 模块中。我强烈推荐的一件事是使用名称空间包来创建多个项目可以使用的共享名称空间——很像将包放在 com.yourcompany.yourproject 中的 Java 约定(并且能够拥有一个共享的 com.yourcompany.utils 名称空间)。

重新分支和合并,如果你使用足够好的源代码控制系统,它甚至可以通过重命名来处理合并; Bazaar 在这方面特别擅长。

与此处的其他一些答案相反,我对拥有 src 顶级目录(旁边有 doctest 目录)表示 +1。文档目录树的特定约定将根据您使用的内容而有所不同;例如,Sphinx 有其快速入门工具支持的自己的约定。

请利用 setuptools 和 pkg_resources;这使得其他项目更容易依赖于您的代码的特定版本(如果您使用 package_data,则可以使用不同的非代码文件同时安装多个版本)。