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


730

想象一下,您想使用Python开发非平凡的最终用户桌面(非Web)应用程序。构造项目文件夹层次结构的最佳方法是什么?

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

特别是:

  1. 您将源放在哪里?
  2. 您将应用程序启动脚本放在哪里?
  3. 您将IDE项目放在哪里?
  4. 您将单元/验收测试放在哪里?
  5. 您将非Python数据(例如配置文件)放在哪里?
  6. 您在哪里将非Python来源(例如C ++)用于pyd / so二进制扩展模块?

Answers:


376

没什么大不了的。令您快乐的一切都会起作用。没有很多愚蠢的规则,因为Python项目可以很简单。

  • /scripts/bin那种命令行界面的东西
  • /tests 为您的测试
  • /lib 用于您的C语言库
  • /doc 对于大多数文档
  • /apidoc 用于Epydoc生成的API文档。

顶级目录可以包含自述文件,配置文件和其他内容。

困难的选择是是否使用/src树。Python没有区别/src/lib/bin如Java或C具有。

由于/src某些人认为顶层目录没有意义,因此顶层目录可以是应用程序的顶层体系结构。

  • /foo
  • /bar
  • /baz

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

这样,另一个项目PYTHONPATH可以包括/path/to/quux/foo重用QUUX.foo模块。

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


23
您会建议仿真任何开源python项目的目录结构吗?
Lance Rushing

4
看看Django就是一个很好的例子。
S.Lott,

33
我不倾向于认为Django是一个很好的例子-用sys.path玩把戏是我书中的即时DQ。
查尔斯·达菲

18
re“技巧”:Django将根项目文件夹的父级添加到sys.path中,以便可以将模块作为“从project.app.module导入文件夹”或“从app.module导入文件夹”导入。
乔纳森·哈特利

3
哦,我喜欢这个技巧,现在正在使用。我想将共享模块放在另一个目录中,我不想在系统范围内安装模块,也不想让人们手动修改PYTHONPATH。除非人们提出更好的建议,否则我认为这实际上是最干净的方法。
吴永伟

242

根据Jean-Paul Calderone的Python项目文件系统结构

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

23
Project/project/?啊,第二个是包裹名称。
Cees Timmerman

44
bin文件夹中的可执行文件如何引用项目模块?(我认为python语法不允许../在include语句中使用)
ThorSummoner 2014年

8
@ThorSummoner简单。您安装了软件包!(pip install -e /path/to/Project
Kroltan 2014年

22
如果有人使用hello.py和hello-test.py压缩此布局的样本并将其提供给我们newbs,那将是非常棒的。
jeremyjjbrown 2015年

8
@Bloke核心是-e标志,该标志将程序包安装为可编辑的程序包,即,将其安装为指向实际项目文件夹的链接。可执行文件然后只能import project访问该模块。
Kroltan '16

231

博客由让-保罗·Calderone的岗位如Freenode上的#python答案通常是给出。

Python项目的文件系统结构

做:

  • 为目录命名与您的项目相关的名称。例如,如果您的项目名为“ Twisted”,请为其源文件命名顶级目录Twisted。发行时,应包括版本号后缀:Twisted-2.5
  • 创建目录Twisted/bin,然后将可执行文件放在此处(如果有)。.py即使它们是Python源文件,也不要给它们扩展名。除了在项目中其他地方定义的main函数的导入和调用外,不要在其中添加任何代码。(略有起皱:由于在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/READMETwisted/setup.py来解释和安装软件。

别:

  • 将您的源代码放在一个名为src或的目录中lib。这使得不安装就很难运行。
  • 将测试放到Python包之外。这使得很难针对已安装的版本运行测试。
  • 创建一个包,只有拥有__init__.py,然后把所有的代码放入__init__.py。只需制作一个模块而不是一个包,就更简单了。
  • 尝试提出一些神奇的技巧,以使Python能够导入您的模块或包,而无需用户将包含它的目录添加到其导入路径(通过PYTHONPATH或其他机制)。您将无法正确处理所有情况,并且当您的软件无法在其环境中运行时,用户会生您的气。

25
这正是我所需要的。“不要试图提出魔术般的技巧,以使Python能够导入模块或包,而无需用户将包含它的目录添加到其导入路径。” 很高兴知道!
杰克·奥康纳

1
问题是,这没有提及项目的重要文档部分将其放置在何处。
lpapp 2014年

14
对“将您的源放入名为src或lib的目录中感到困惑。这使得不安装就很难运行”。将安装什么?是导致问题的目录名称,还是根本就是子目录的事实?
彼得·埃里希

3
“有些人会断言,您应该在模块本身内分发测试–我不同意。这通常会增加用户的复杂性;许多测试套件通常需要附加的依赖项和运行时上下文。” python-guide-pt-br.readthedocs.io/zh-CN/latest/writing/structure/…– endolith 2015
6

2
“这使得不安装就很难运行。” -就是重点
Nick T

123

以正确的方式查看Open Sourcing Python项目

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

设置项目时,布局(或目录结构)对于正确设置很重要。合理的布局意味着潜在的贡献者不必花大量的时间寻找代码。文件位置很直观。由于我们正在处理现有项目,因此这意味着您可能需要移动一些内容。

让我们从顶部开始。大多数项目都有许多顶级文件(例如setup.py,README.md,requirements.txt等)。每个项目应具有三个目录:

  • 包含项目文档的docs目录
  • 以项目名称命名的目录,用于存储实际的Python包
  • 在两个位置之一中的测试目录
    • 在包含测试代码和资源的包目录下
    • 作为独立的顶层目录为了更好地了解文件的组织方式,以下是我的一个项目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目录。


4
我这样做,但更多的是:我有一个顶层Makefile,目标文件是一个使'virtualenv env自动化的'env'目标;./env/bin/pip install -r requirements.txt; ./env/bin/python setup.py开发”,通常也是一个依赖于env的“测试”目标,它还会安装测试依赖项,然后运行py.test。
pjz 2014年

@pjz您能否扩大您的想法?您是在谈论与Makefile处于同一水平setup.py吗?因此,如果我了解您正确地make env自动创建新文件venv并将其安装到其中...?
圣安东尼奥

完全是@ St.Antario。如前所述,我通常也有一个“测试”目标来运行测试,有时还有一个“发行”目标,它查看当前标签并构建一个轮子并将其发送给pypi。
pjz


19

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

  • 您将源放在哪里?

    • 对于大型项目,将源分成几个鸡蛋是有意义的。每个鸡蛋将在下作为单独的setuptools-layout放置PROJECT_ROOT/src/<egg_name>
  • 您将应用程序启动脚本放在哪里?

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

    • 取决于IDE。他们中的许多人将自己的东西保存PROJECT_ROOT/.<something>在项目的根目录中,这很好。
  • 您将单元/验收测试放在哪里?

    • 每个鸡蛋都有单独的一组测试,并保存在其PROJECT_ROOT/src/<egg_name>/tests目录中。我个人更喜欢使用py.test它们来运行它们。
  • 您将非Python数据(例如配置文件)放在哪里?

    • 这取决于。可能有不同类型的非Python数据。
      • “资源”,即必须包装在一个鸡蛋中的数据。该数据进入包名称空间中某个位置的相应egg目录。可以通过pkg_resources从中的包使用它,也可以从标准库中setuptoolsimportlib.resources模块通过Python 3.7开始使用。
      • “配置文件”,即非Python文件,它们被视为项目源文件的外部文件,但在应用程序开始运行时必须使用一些值进行初始化。在开发过程中,我更喜欢将此类文件保存在中PROJECT_ROOT/config。对于部署,可以有多种选择。在Windows %APP_DATA%/<app-name>/config上,可以在Linux /etc/<app-name>或上使用/opt/<app-name>/config
      • 生成的文件,即应用程序在执行期间可以创建或修改的文件。我希望PROJECT_ROOT/var在开发/var期间以及在Linux部署期间保留它们。
  • 您在哪里将非Python来源(例如C ++)用于pyd / so二进制扩展模块?
    • 进入 PROJECT_ROOT/src/<egg_name>/native

文件通常会放入PROJECT_ROOT/docPROJECT_ROOT/src/<egg_name>/doc(取决于您是否将某些鸡蛋视为一个单独的大型项目)。一些其他配置将在PROJECT_ROOT/buildout.cfg和文件中PROJECT_ROOT/setup.cfg


感谢您的答复!您为我澄清了很多事情!我只有一个问题:可以嵌套鸡蛋吗?
Shookie 2014年

不,在将.egg文件存储在其他.egg文件中的意义上,您不能“嵌套”鸡蛋,并希望这会很有用[除非您要处理的确很奇怪]。但是,您可以做的是创建“虚拟”鸡蛋-空的程序包,不提供任何有用的代码,而是在其依赖项列表中列出其他程序包。这样,当用户尝试安装此类软件包时,他将递归安装许多相关的鸡蛋。
KT。

@KT您能否详细说明如何处理生成的数据?特别是,您(在代码中)如何区分开发和部署?我想您有一些base_data_location变量,但是如何适当设置呢?
cmyr

1
我猜想您是在谈论“运行时数据”-人们经常将其放在/ var / packagename或〜/ .packagename / var下,或诸如此类。在大多数情况下,这些选择足以满足您的默认需求,您的用户无需更改。如果您想让这种行为得到调整,那么选择就很多了,我认为没有一个万能的最佳实践。典型选择:a)〜/ .packagename / configfile,b)导出MY_PACKAGE_CONFIG = / path / to / configfile c)命令行选项或函数参数d)这些选项的组合。
KT。

请注意,通常在某个地方有一个Singleton Config类,它可以为您处理您喜欢的配置加载逻辑,甚至可以让用户在运行时修改设置。不过,总的来说,我认为这是一个值得单独回答的问题(可能在此之前有人问过)。
KT。

15

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

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

话虽如此,我回到了我的初始观点:不要做太大的事情。将其放在似乎对您有用的位置。如果发现不起作用,则可以(并且应该)对其进行更改。


是的 我试图对此保持“ Python式”:显式优于隐式。目录层次结构的读取/检查要比其编写的要多。等
eric 2015年

10

最好使用setuptools中package_data支持将非Python数据捆绑到您的Python模块中。我强烈建议您使用名称空间包来创建多个项目可以使用的共享名称空间,这很像Java约定(将软件包放入其中并能够拥有一个共享名称空间)。com.yourcompany.yourprojectcom.yourcompany.utils

重新分支和合并,如果您使用足够好的源代码控制系统,它将通过重命名来处理合并;集市在这方面尤其擅长。

与这里的其他答案相反,我对拥有src顶级目录(带有doctest目录并在旁边)+1 。文档目录树的特定约定将根据您所使用的内容而有所不同。例如,Sphinx有其快速启动工具支持的自己的约定。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.