Какая структура проекта лучше всего подходит для приложения Python?

Представьте, что вы хотите разработать нетривиальное настольное (не веб) приложение для конечных пользователей на Python. Как лучше всего структурировать иерархию папок проекта?

Желательными функциями являются простота обслуживания, совместимость с IDE, пригодность для ветвления / слияния системы управления версиями и легкое создание пакетов установки.

В частности:

  1. Куда вы положили источник?
  2. Куда вы кладете скрипты запуска приложений?
  3. Куда вы положили хлам проекта IDE?
  4. Где вы ставите модульные / приемочные испытания?
  5. Куда вы помещаете данные, не относящиеся к Python, например файлы конфигурации?
  6. Где вы размещаете источники, отличные от Python, такие как C++ для модулей двоичного расширения pyd / so?
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
785
0
426 829
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Ответ принят как подходящий

Не имеет большого значения. Все, что делает вас счастливым, будет работать. Глупых правил не так много, потому что проекты Python могут быть простыми.

  • /scripts или /bin для такого рода интерфейсов командной строки
  • /tests для ваших тестов
  • /lib для ваших библиотек на языке C
  • /doc для большей части документации
  • /apidoc для документации API, созданной Epydoc.

А каталог верхнего уровня может содержать 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.

Любые проекты Python с открытым исходным кодом, которые вы бы порекомендовали эмулировать их структуру каталогов?

Lance Rushing 05.10.2009 21:02

Посмотрите на Django как на хороший пример.

S.Lott 05.10.2009 23:08

Я не склонен считать Django хорошим примером - трюки с sys.path - это моментальный DQ в моей книге.

Charles Duffy 29.10.2010 20:20

@ Чарльз Даффи: "хитрости с sys.path"? Можете дать ссылку или цитату?

S.Lott 30.10.2010 16:40

Я сторонник каталога src.

Raffi Khatchadourian 05.11.2011 02:33

повторные «уловки»: Django добавляет родительский элемент корневой папки проекта в sys.path, так что модули могут быть импортированы как «из класса импорта project.app.module» или «из класса импорта app.module».

Jonathan Hartley 07.11.2011 11:12

О, мне нравится этот трюк, и я использую его сейчас. Я хочу поместить общий модуль в другой каталог, и я не хочу устанавливать модуль в масштабе всей системы, а также не хочу просить людей изменять PYTHONPATH вручную. Если люди не предложат что-то лучшее, я думаю, что это на самом деле самый чистый путь.

Yongwei Wu 05.07.2017 12:39

Недавно я создал инструмент, который поможет вам запустить проект на Python с нуля. введя некоторую информацию, например: имя_пакета, имя_пользователя github, лицензия, версия Python, которую вы хотите поддерживать. И имея virtualenv, тест, покрытие кода, документ, развертывание документа, публикацию в pypi, тестирование нескольких версий Python (tox) из коробки! И он совместим с WINDOWS / MACOS / LINUX. Проверьте это: pypi.python.org/pypi/pygitrepo

MacSanhe 15.10.2017 03:34

cuft -> cruft *.

Collin Bell 24.10.2017 22:11

Что касается уловок Django, я считаю, что они удалили это с версии 1.4. (См. это обсуждение.)

user1071847 09.02.2018 18:36

«/scripts или /bin»: я бы не стал этого делать. Вы можете определить entry_points в setup.py и иметь cli.py или что-то подобное как часть пакета.

Martin Thoma 31.01.2020 11:15

Данные, отличные от Python, лучше всего объединять внутри ваших модулей Python с помощью поддержки package_data в setuptools. Одна вещь, которую я настоятельно рекомендую, - это использовать пакеты пространств имен для создания общих пространств имен, которые могут использовать несколько проектов - во многом подобно соглашению Java о размещении пакетов в com.yourcompany.yourproject (и возможности иметь общее пространство имен com.yourcompany.utils).

Что касается ветвления и слияния, если вы используете достаточно хорошую систему управления версиями, она будет обрабатывать слияния даже при переименовании; Базар особенно хорош в этом.

В отличие от некоторых других ответов здесь, я +1 к каталогу верхнего уровня src (вместе с каталогами doc и test). Конкретные соглашения для деревьев каталогов документации будут различаться в зависимости от того, что вы используете; Например, Сфинкс имеет свои собственные соглашения, которые поддерживает его инструмент быстрого запуска.

Пожалуйста, используйте setuptools и pkg_resources; это упрощает другим проектам использование определенных версий вашего кода (и одновременную установку нескольких версий с разными файлами, не являющимися файлами кода, если вы используете package_data).

По моему опыту, это просто вопрос повторения. Поместите свои данные и код туда, где, по вашему мнению, они находятся. Скорее всего, вы все равно ошибетесь. Но как только вы получите лучшее представление о том, как именно будут развиваться события, вы будете в гораздо лучшем положении, чтобы делать такие предположения.

Что касается источников расширений, у нас есть каталог кода в основной строке, который содержит каталог для python и каталог для различных других языков. Лично я более склонен в следующий раз попытаться поместить любой код расширения в его собственный репозиторий.

Сказав это, я возвращаюсь к своей исходной точке: не придавайте этому большого значения. Положите его туда, где вам кажется, что это работает. Если вы обнаружите, что что-то не работает, это можно (и нужно) изменить.

Ага. Я стараюсь быть «питонистом» в этом отношении: явное лучше, чем неявное. Иерархии каталогов читаются / проверяются больше, чем они записываются. Так далее..

eric 30.04.2015 03:19

Этот сообщение в блоге Жана-Поля Кальдерона обычно задается как ответ в #python на Freenode.

Filesystem structure of a Python project

Do:

  • name the directory something related to your project. For example, if your project is named "Twisted", name the top-level directory for its source files Twisted. When you do releases, you should include a version number suffix: Twisted-2.5.
  • create a directory Twisted/bin and put your executables there, if you have any. Don't give them a .py extension, even if they are Python source files. Don't put any code in them except an import of and call to a main function defined somewhere else in your projects. (Slight wrinkle: since on Windows, the interpreter is selected by the file extension, your Windows users actually do want the .py extension. So, when you package for Windows, you may want to add it. Unfortunately there's no easy distutils trick that I know of to automate this process. Considering that on POSIX the .py extension is a only a wart, whereas on Windows the lack is an actual bug, if your userbase includes Windows users, you may want to opt to just have the .py extension everywhere.)
  • If your project is expressable as a single Python source file, then put it into the directory and name it something related to your project. For example, Twisted/twisted.py. If you need multiple source files, create a package instead (Twisted/twisted/, with an empty Twisted/twisted/__init__.py) and place your source files in it. For example, Twisted/twisted/internet.py.
  • put your unit tests in a sub-package of your package (note - this means that the single Python source file option above was a trick - you always need at least one other file for your unit tests). For example, Twisted/twisted/test/. Of course, make it a package with Twisted/twisted/test/__init__.py. Place tests in files like Twisted/twisted/test/test_internet.py.
  • add Twisted/README and Twisted/setup.py to explain and install your software, respectively, if you're feeling nice.

Don't:

  • put your source in a directory called src or lib. This makes it hard to run without installing.
  • put your tests outside of your Python package. This makes it hard to run the tests against an installed version.
  • create a package that only has a __init__.py and then put all your code into __init__.py. Just make a module instead of a package, it's simpler.
  • try to come up with magical hacks to make Python able to import your module or package without having the user add the directory containing it to their import path (either via PYTHONPATH or some other mechanism). You will not correctly handle all cases and users will get angry at you when your software doesn't work in their environment.

Это было именно то, что мне нужно. «НЕ пытайтесь придумать волшебные уловки, чтобы Python мог импортировать ваш модуль или пакет, не заставляя пользователя добавлять каталог, содержащий его, в свой путь импорта». Хорошо знать!

Jack O'Connor 28.02.2013 09:18

Дело в том, что здесь не упоминается важная часть документа проекта, где ее разместить.

lpapp 02.09.2014 20:10

Запутался в том, что «поместите исходный код в каталог с именем src или lib. Это затрудняет запуск без установки.». Что бы установили? Это имя каталога, которое вызывает проблему, или тот факт, что это вообще подкаталог?

Peter Ehrlich 04.02.2015 01:25

«Некоторые люди будут утверждать, что вы должны распространять свои тесты внутри самого модуля - я не согласен. Это часто увеличивает сложность для ваших пользователей; многие наборы тестов часто требуют дополнительных зависимостей и контекстов времени выполнения». python-guide-pt-br.readthedocs.io/en/latest/writing/structur e /…

endolith 11.06.2017 19:19

@ JackO'Connor Я сбит с толку. Как выполнить импорт в основную функцию, определенную в Twisted/twisted, из исполняемого файла в Twisted/bin без изменения путей?

GoodDeeds 27.06.2018 16:41

@GoodDeeds Я думаю, что стандартный способ - заставить пользователя установить библиотеку либо глобально, либо в каком-то виртуальном сервере, который они используют, чтобы импорт работал. Иногда мне нравится включать сценарий bash, который устанавливает необходимый PYTHONPATH и выполняет код локально, чтобы помочь в разработке, но я не уверен, что делают другие люди.

Jack O'Connor 28.06.2018 17:42

«Это затрудняет работу без установки». - это точка

Nick T 12.07.2019 00:23

Мне кажется ироничным, что в этом примере в качестве имени проекта используется Twisted, поскольку официальный Библиотека Twisted теперь использует макет src, что противоречит первой рекомендации «Не делайте этого»: «поместите исходный код в каталог с именем src или lib. Это делает его трудно запустить без установки ". В этом весь смысл (см. Статья Ионела Кристиана Марьеша).

Maggyero 22.10.2019 16:09

Сделайте: «поместите исходный код в каталог с именем src или lib».

Gustav Rasmussen 21.06.2020 12:15

Собственно "Это затрудняет работу без установки". это огромное преимущество. Все должны устанавливать pip install -e, тогда не будет двусмысленности и шансов протестировать то, что, по вашему мнению, вы тестируете.

SwimBikeRun 18.09.2020 01:40

Раздел «не делать» звучит чрезвычайно питонично, однако, как и многие (ленивые) практики сообщества Python, подвержены ошибкам и случайным сложностям - как также отмечали некоторые комментаторы.

olyk 27.02.2021 20:51

Согласно Структура файловой системы проекта Python Жан-Поля Кальдероне:

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

Project/project/? Ах, второй - имя пакета.

Cees Timmerman 30.11.2011 20:50

Беспечно проигнорировав это, мне в конце концов пришлось перетасовать мои вещи, чтобы они выглядели вот так, чтобы упростить создание RPM. Настоятельно рекомендую всем использовать эту настройку по умолчанию, если у вас нет действительно веской причины не делать этого.

saccharine 10.12.2013 03:33

как исполняемый файл в папке bin ссылается на модуль проекта? (Я не думаю, что синтаксис Python позволяет использовать ../ в заявлении include)

ThorSummoner 08.06.2014 10:46

@ThorSummoner Это на самом деле очень хороший вопрос для меня, помимо настройки PYTHON_PATH, я не знаю, как это сделать. Мне кажется, что эта структура делает запуск сценария запуска проекта излишне сложным (размещение файла «проект» непосредственно в проекте / устраняет необходимость настраивать PYTHON_PATH).

Ad N 17.10.2014 19:26

На самом деле Python поддерживает подъем по каталогам; См. stackoverflow.com/a/1054281/1695680.

ThorSummoner 17.10.2014 21:03

@ThorSummoner Это работает только тогда, когда остается внутри одного пакета. Чтобы относительный импорт работал здесь, вам понадобится файл __init__.py как в папке bin, так и в верхней папке Project.

Yonatan 02.11.2014 17:13

@ThorSummoner Просто. Вы устанавливаете пакет! (pip install -e /path/to/Project)

Kroltan 19.12.2014 20:27

Было бы здорово, если бы кто-нибудь заархивировал образец этого макета с помощью hello.py и hello-test.py и сделал его доступным для нас, новичков.

jeremyjjbrown 15.01.2015 17:36

наличие папки /bin с исполняемыми файлами звучит для меня как очень плохая практика: у вас проблемы с импортом, как упоминалось выше, и вам приходится возиться с PYTHONPATH; Я предпочитаю использовать entry_points

Tomas Tomecek 09.04.2015 11:19

Каково соглашение об именах для каталога проекта для python PascalCase или этого случая?

user5650203 12.12.2015 13:54

@Kroltan Не могли бы вы объяснить, что происходит после того, как pip install установит пакет, который позволяет вам запускать исполняемый файл из папки bin, который красиво ссылается на модуль проекта?

Bloke 14.03.2016 14:39

@Bloke Ядром является флаг -e, который устанавливает пакет как редактируемый, то есть устанавливает его как ссылки на фактическую папку проекта. Затем исполняемый файл может просто import project получить доступ к модулю.

Kroltan 14.03.2016 15:15

Этот поток довольно старый, но я могу пролить свет на вопрос помещения исполняемого файла project в bin. По-видимому, это актуально для систем Linux, где исполняемый файл обычно должен быть сценарием оболочки (с тем же именем, что и ваш Python проект). Подробнее здесь.

trk 22.08.2018 11:13

Этот пост кажется устаревшим, я не нашел ни одного проекта, который бы использовал эту структуру. Не могли бы вы указать мне на некоторые? Или, может быть, добавить в ответ заголовок о том, что он старый?

Mayou36 08.03.2020 12:37

Проверьте Открытый исходный код проекта Python в правильном направлении.

Позвольте мне процитировать макет проекта часть этой замечательной статьи:

When setting up a project, the layout (or directory structure) is important to get right. A sensible layout means that potential contributors don't have to spend forever hunting for a piece of code; file locations are intuitive. Since we're dealing with an existing project, it means you'll probably need to move some stuff around.

Let's start at the top. Most projects have a number of top-level files (like setup.py, README.md, requirements.txt, etc). There are then three directories that every project should have:

  • A docs directory containing project documentation
  • A directory named with the project's name which stores the actual Python package
  • A test directory in one of two places
    • Under the package directory containing test code and resources
    • As a stand-alone top level directory To get a better sense of how your files should be organized, here's a simplified snapshot of the layout for one of my projects, 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

As you can see, there are some top level files, a docs directory (generated is an empty directory where sphinx will put the generated documentation), a sandman directory, and a test directory under sandman.

Я делаю это, но более того: у меня есть Makefile верхнего уровня с целью env, которая автоматизирует virtualenv env; ./env/bin/pip install -r requirements.txt; ./env/bin/python setup.py develop ', а также обычно цель' test ', которая зависит от env, а также устанавливает тестовые зависимости, а затем запускает py.test.

pjz 10.11.2014 06:12

@pjz Не могли бы вы расширить свою идею? Вы говорите о том, чтобы поставить Makefile на один уровень с setup.py? Итак, если я вас правильно понимаю, make env автоматизирует создание нового venv и установку в него пакетов ...?

St.Antario 28.11.2019 10:01

@ Сент-Антарио точно. Как уже упоминалось, у меня обычно есть «тестовая» цель для запуска тестов, а иногда и «релизная» цель, которая смотрит на текущий тег, строит колесо и отправляет его в pypi.

pjz 09.01.2020 05:08

Попробуйте запустить проект, используя шаблон python_boilerplate. Он в значительной степени соответствует передовым методам (например, те, кто здесь), но лучше подходит в том случае, если вы обнаружите, что в какой-то момент захотите разделить свой проект на более чем одно яйцо (и поверьте мне, с любыми проектами, кроме простейших, вы это сделаете. ситуация, когда вам нужно использовать локально измененную версию чужой библиотеки).

  • Куда вы положили источник?

    • Для прилично больших проектов имеет смысл разбить исходник на несколько яиц. Каждое яйцо будет идти как отдельный макет setuptools под 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 из setuptools или, начиная с Python 3.7, через модуль importlib.resources из стандартной библиотеки.
      • "Конфиг-файлы", то есть файлы, отличные от 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/doc или PROJECT_ROOT/src/<egg_name>/doc (это зависит от того, рассматриваете ли вы некоторые из яиц как отдельные большие проекты). Некоторая дополнительная конфигурация будет в файлах типа PROJECT_ROOT/buildout.cfg и PROJECT_ROOT/setup.cfg.

Спасибо за отличный ответ! Вы мне многое прояснили! У меня только один вопрос: могут ли яйца быть вложенными?

Shookie 19.08.2014 20:02

Нет, вы не можете «вкладывать» яйца в том смысле, что храните файлы .egg в других файлах .egg и надеетесь, что это будет очень полезно [если вы не замышляете что-то действительно странное]. Однако вы можете создавать «виртуальные» яйца - пустые пакеты, которые не предоставляют никакого полезного кода, но перечисляют другие пакеты в своих списках зависимостей. Таким образом, когда пользователь пытается установить такой пакет, он рекурсивно устанавливает множество зависимых яиц.

KT. 11.09.2014 21:08

@KT не могли бы вы рассказать немного о том, как вы обрабатываете сгенерированные данные? В частности, как вы (в коде) различаете разработку и развертывание? Я полагаю, у вас есть переменная base_data_location, но как ее правильно установить?

cmyr 04.04.2016 00:20

Я полагаю, вы говорите о «данных времени выполнения» - что-то, что люди часто помещают в / var / packagename или ~ / .packagename / var, или еще много чего. В большинстве случаев этих вариантов достаточно по умолчанию, и ваши пользователи не хотят их менять. Если вы хотите разрешить настройку этого поведения, вариантов довольно много, и я не думаю, что существует единая универсальная передовая практика. Типичный выбор: a) ~ / .packagename / configfile, b) export MY_PACKAGE_CONFIG = / path / to / configfile c) параметры командной строки или параметры функции d) их комбинация.

KT. 04.04.2016 00:44

Обратите внимание, что довольно часто где-то есть одноэлементный класс Config, который обрабатывает вашу любимую логику загрузки конфигурации за вас и, возможно, даже позволяет пользователю изменять настройки во время выполнения. В целом, однако, я думаю, что это проблема, заслуживающая отдельного вопроса (который, возможно, задавали раньше где-то здесь).

KT. 04.04.2016 00:44

Какой отличный ответ, особенно в части данных, отличных от Python, спасибо!

Maggyero 22.10.2019 21:19

У "Python Packaging Authority" есть образец проекта:

https://github.com/pypa/sampleproject

Это образец проекта, который существует как помощь в Руководстве пользователя Python Packaging Tutorial on Packaging and Distributing Projects.

+ тенденция к структуре root/src/*: github.com/pypa/sampleproject/commit/…

qrtLs 02.02.2020 19:35

рекомендации по структуре проекта также см. в setuptools.readthedocs.io/_/downloads/en/latest/pdf

qrtLs 02.02.2020 19:45

Другие вопросы по теме