Python 应用程序的最佳项目结构是什么?[关闭]
-
08-07-2019 - |
题
想象一下,您想要用 Python 开发一个重要的最终用户桌面(不是 Web)应用程序。构建项目文件夹层次结构的最佳方法是什么?
理想的特性是易于维护、IDE 友好、适合源代码控制分支/合并以及易于生成安装包。
尤其:
- 你把源码放在哪里?
- 应用程序启动脚本放在哪里?
- 你把 IDE 项目放在哪里?
- 您将单元/验收测试放在哪里?
- 将非 Python 数据(例如配置文件)放在哪里?
- 将非 Python 源(例如 pyd/so 二进制扩展模块的 C++)放在哪里?
解决方案
没什么太大关系。任何让你快乐的事情都会起作用。没有太多愚蠢的规则,因为 Python 项目很简单。
/scripts
或者/bin
对于那种命令行界面的东西/tests
为了你的测试/lib
用于您的 C 语言库/doc
对于大多数文档/apidoc
用于 Epydoc 生成的 API 文档。
顶级目录可以包含 README、Config 等。
艰难的选择是是否使用 /src
树。Python 之间没有区别 /src
, /lib
, , 和 /bin
就像 Java 或 C 那样。
既然是顶级 /src
目录被某些人视为毫无意义,您的顶级目录可以是应用程序的顶级架构。
/foo
/bar
/baz
我建议将所有这些都放在“name-of-my-product”目录下。因此,如果您正在编写一个名为 quux
, ,包含所有这些内容的目录被命名为 /quux
.
另一个项目的 PYTHONPATH
, ,那么,可以包括 /path/to/quux/foo
重用 QUUX.foo
模块。
就我而言,由于我使用 Komodo Edit,因此我的 IDE cuft 是单个 .KPF 文件。我实际上把它放在顶层 /quux
目录,并省略将其添加到 SVN。
其他提示
根据Jean-Paul Calderone的文件系统结构Python项目:
Project/
|-- bin/
| |-- project
|
|-- project/
| |-- test/
| | |-- __init__.py
| | |-- test_main.py
| |
| |-- __init__.py
| |-- main.py
|
|-- setup.py
|-- README
这 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 或其他机制)。你会 不是 正确处理所有情况,当您的软件无法在他们的环境中运行时,用户会对您生气。
让我摘录一下 项目布局 那篇优秀文章的一部分:
设置项目时,正确的布局(或目录结构)非常重要。合理的布局意味着潜在的贡献者不必永远花时间寻找一段代码;文件位置很直观。由于我们正在处理现有项目,这意味着您可能需要移动一些东西。
让我们从顶部开始。大多数项目都有许多顶级文件(如 setup.py、README.md、requirements.txt 等)。每个项目都应该有三个目录:
- 包含项目文档的 docs 目录
- 以项目名称命名的目录,用于存储实际的 Python 包
- 位于两个位置之一的测试目录
- 在包含测试代码和资源的包目录下
- 作为一个独立的顶级目录,可以更好地了解您的文件应该如何组织,这是我一个项目的简化快照Sandman:Sandman:
$ pwd
~/code/sandman
$ tree
.
|- LICENSE
|- README.md
|- TODO.md
|- docs
| |-- conf.py
| |-- generated
| |-- index.rst
| |-- installation.rst
| |-- modules.rst
| |-- quickstart.rst
| |-- sandman.rst
|- requirements.txt
|- sandman
| |-- __init__.py
| |-- exception.py
| |-- model.py
| |-- sandman.py
| |-- test
| |-- models.py
| |-- test_sandman.py
|- setup.py
正如你所看到的,有一些顶级文件、一个 docs 目录(生成的是一个空目录,sphinx 将在其中放置生成的文档)、一个 sandman 目录和一个 sandman 下的 test 目录。
<!>“Python Packaging Authority <!>”;有一个示例项目:
https://github.com/pypa/sampleproject
这是一个示例项目,作为Python Packaging用户指南的包装和分发项目教程的辅助。
尝试使用以下命令启动项目 python_样板 模板。它很大程度上遵循最佳实践(例如 这里的人),但更适合万一你发现自己愿意在某个时候将你的项目分成多个鸡蛋(相信我,除了最简单的项目之外,你会的。一种常见的情况是您必须使用其他人的库的本地修改版本)。
你把源码放在哪里?
- 对于相当大的项目,将源代码分成几个鸡蛋是有意义的。每个鸡蛋都将作为单独的 setuptools-layout 在
PROJECT_ROOT/src/<egg_name>
.
- 对于相当大的项目,将源代码分成几个鸡蛋是有意义的。每个鸡蛋都将作为单独的 setuptools-layout 在
应用程序启动脚本放在哪里?
- 理想的选择是将应用程序启动脚本注册为
entry_point
在其中一个鸡蛋中。
- 理想的选择是将应用程序启动脚本注册为
你把 IDE 项目放在哪里?
- 取决于 IDE。他们中的许多人都把自己的东西放在里面
PROJECT_ROOT/.<something>
在项目的根目录中,这很好。
- 取决于 IDE。他们中的许多人都把自己的东西放在里面
您将单元/验收测试放在哪里?
- 每个鸡蛋都有一套单独的测试,保存在其内部
PROJECT_ROOT/src/<egg_name>/tests
目录。我个人更喜欢使用py.test
运行它们。
- 每个鸡蛋都有一套单独的测试,保存在其内部
将非 Python 数据(例如配置文件)放在哪里?
- 这取决于。可以有不同类型的非 Python 数据。
- “资源”, , IE。必须封装在鸡蛋内的数据。该数据进入相应的 Egg 目录,位于包命名空间内的某个位置。它可以通过以下方式使用
pkg_resources
包裹。 - “配置文件”, , IE。非Python文件被视为项目源文件的外部,但在应用程序开始运行时必须使用一些值进行初始化。在开发过程中我更喜欢将这些文件保存在
PROJECT_ROOT/config
. 。对于部署,可以有多种选择。在 Windows 上可以使用%APP_DATA%/<app-name>/config
, ,在 Linux 上,/etc/<app-name>
或者/opt/<app-name>/config
. - 生成的文件, , IE。应用程序在执行期间可能创建或修改的文件。我宁愿把它们留在
PROJECT_ROOT/var
在开发期间,以及在/var
在Linux部署期间。
- “资源”, , IE。必须封装在鸡蛋内的数据。该数据进入相应的 Egg 目录,位于包命名空间内的某个位置。它可以通过以下方式使用
- 这取决于。可以有不同类型的非 Python 数据。
- 将非 Python 源(例如 pyd/so 二进制扩展模块的 C++)放在哪里?
- 进入
PROJECT_ROOT/src/<egg_name>/native
- 进入
文档通常会进入 PROJECT_ROOT/doc
或者 PROJECT_ROOT/src/<egg_name>/doc
(这取决于你是否将某些鸡蛋视为一个单独的大项目)。一些额外的配置将位于以下文件中 PROJECT_ROOT/buildout.cfg
和 PROJECT_ROOT/setup.cfg
.
根据我的经验,这只是一个迭代问题。将您的数据和代码放在您认为的任何地方。机会是,你无论如何都会错。但是一旦你更好地了解事情将如何形成,你就可以更好地做出这些猜测了。
就扩展源而言,我们在trunk下有一个Code目录,其中包含python的目录和各种其他语言的目录。就个人而言,我更倾向于下次尝试将任何扩展代码放入自己的存储库中。
话虽如此,我回到最初的观点:不要做太大的交易。把它放在似乎适合你的地方。如果你发现一些不起作用的东西,可以(而且应该)改变它。
使用 setuptools package_data支持,最好将非python数据捆绑在Python模块中>。我强烈建议使用命名空间包来创建多个项目可以使用的共享命名空间 - 就像在com.yourcompany.yourproject
中放置包的Java约定(并且能够拥有共享的com.yourcompany.utils
命名空间)。
重新分支和合并,如果你使用足够好的源代码控制系统,即使通过重命名,它也会处理合并; Bazaar 特别擅长这一点。
与此处的其他答案相反,我在+1 src
目录顶层(同时带有doc
和test
目录)。文档目录树的具体约定将根据您使用的内容而有所不同;例如, Sphinx 有自己的快速启动工具支持的约定。
请利用setuptools和pkg_resources;这使得其他项目更容易依赖于代码的特定版本(如果您使用<=>,则可以使用不同的非代码文件同时安装多个版本。)