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






Не имеет большого значения. Все, что делает вас счастливым, будет работать. Глупых правил не так много, потому что проекты 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.
Посмотрите на Django как на хороший пример.
Я не склонен считать Django хорошим примером - трюки с sys.path - это моментальный DQ в моей книге.
@ Чарльз Даффи: "хитрости с sys.path"? Можете дать ссылку или цитату?
Я сторонник каталога src.
повторные «уловки»: Django добавляет родительский элемент корневой папки проекта в sys.path, так что модули могут быть импортированы как «из класса импорта project.app.module» или «из класса импорта app.module».
О, мне нравится этот трюк, и я использую его сейчас. Я хочу поместить общий модуль в другой каталог, и я не хочу устанавливать модуль в масштабе всей системы, а также не хочу просить людей изменять PYTHONPATH вручную. Если люди не предложат что-то лучшее, я думаю, что это на самом деле самый чистый путь.
Недавно я создал инструмент, который поможет вам запустить проект на Python с нуля. введя некоторую информацию, например: имя_пакета, имя_пользователя github, лицензия, версия Python, которую вы хотите поддерживать. И имея virtualenv, тест, покрытие кода, документ, развертывание документа, публикацию в pypi, тестирование нескольких версий Python (tox) из коробки! И он совместим с WINDOWS / MACOS / LINUX. Проверьте это: pypi.python.org/pypi/pygitrepo
cuft -> cruft *.
Что касается уловок Django, я считаю, что они удалили это с версии 1.4. (См. это обсуждение.)
«/scripts или /bin»: я бы не стал этого делать. Вы можете определить entry_points в setup.py и иметь cli.py или что-то подобное как часть пакета.
Данные, отличные от Python, лучше всего объединять внутри ваших модулей Python с помощью поддержки package_data в setuptools. Одна вещь, которую я настоятельно рекомендую, - это использовать пакеты пространств имен для создания общих пространств имен, которые могут использовать несколько проектов - во многом подобно соглашению Java о размещении пакетов в com.yourcompany.yourproject (и возможности иметь общее пространство имен com.yourcompany.utils).
Что касается ветвления и слияния, если вы используете достаточно хорошую систему управления версиями, она будет обрабатывать слияния даже при переименовании; Базар особенно хорош в этом.
В отличие от некоторых других ответов здесь, я +1 к каталогу верхнего уровня src (вместе с каталогами doc и test). Конкретные соглашения для деревьев каталогов документации будут различаться в зависимости от того, что вы используете; Например, Сфинкс имеет свои собственные соглашения, которые поддерживает его инструмент быстрого запуска.
Пожалуйста, используйте setuptools и pkg_resources; это упрощает другим проектам использование определенных версий вашего кода (и одновременную установку нескольких версий с разными файлами, не являющимися файлами кода, если вы используете package_data).
По моему опыту, это просто вопрос повторения. Поместите свои данные и код туда, где, по вашему мнению, они находятся. Скорее всего, вы все равно ошибетесь. Но как только вы получите лучшее представление о том, как именно будут развиваться события, вы будете в гораздо лучшем положении, чтобы делать такие предположения.
Что касается источников расширений, у нас есть каталог кода в основной строке, который содержит каталог для python и каталог для различных других языков. Лично я более склонен в следующий раз попытаться поместить любой код расширения в его собственный репозиторий.
Сказав это, я возвращаюсь к своей исходной точке: не придавайте этому большого значения. Положите его туда, где вам кажется, что это работает. Если вы обнаружите, что что-то не работает, это можно (и нужно) изменить.
Ага. Я стараюсь быть «питонистом» в этом отношении: явное лучше, чем неявное. Иерархии каталогов читаются / проверяются больше, чем они записываются. Так далее..
Этот сообщение в блоге Жана-Поля Кальдерона обычно задается как ответ в #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/binand put your executables there, if you have any. Don't give them a.pyextension, 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 emptyTwisted/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 withTwisted/twisted/test/__init__.py. Place tests in files likeTwisted/twisted/test/test_internet.py.- add
Twisted/READMEandTwisted/setup.pyto explain and install your software, respectively, if you're feeling nice.Don't:
- put your source in a directory called
srcorlib. 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__.pyand 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 мог импортировать ваш модуль или пакет, не заставляя пользователя добавлять каталог, содержащий его, в свой путь импорта». Хорошо знать!
Дело в том, что здесь не упоминается важная часть документа проекта, где ее разместить.
Запутался в том, что «поместите исходный код в каталог с именем src или lib. Это затрудняет запуск без установки.». Что бы установили? Это имя каталога, которое вызывает проблему, или тот факт, что это вообще подкаталог?
«Некоторые люди будут утверждать, что вы должны распространять свои тесты внутри самого модуля - я не согласен. Это часто увеличивает сложность для ваших пользователей; многие наборы тестов часто требуют дополнительных зависимостей и контекстов времени выполнения». python-guide-pt-br.readthedocs.io/en/latest/writing/structur e /…
@ JackO'Connor Я сбит с толку. Как выполнить импорт в основную функцию, определенную в Twisted/twisted, из исполняемого файла в Twisted/bin без изменения путей?
@GoodDeeds Я думаю, что стандартный способ - заставить пользователя установить библиотеку либо глобально, либо в каком-то виртуальном сервере, который они используют, чтобы импорт работал. Иногда мне нравится включать сценарий bash, который устанавливает необходимый PYTHONPATH и выполняет код локально, чтобы помочь в разработке, но я не уверен, что делают другие люди.
«Это затрудняет работу без установки». - это точка
Мне кажется ироничным, что в этом примере в качестве имени проекта используется Twisted, поскольку официальный Библиотека Twisted теперь использует макет src, что противоречит первой рекомендации «Не делайте этого»: «поместите исходный код в каталог с именем src или lib. Это делает его трудно запустить без установки ". В этом весь смысл (см. Статья Ионела Кристиана Марьеша).
Сделайте: «поместите исходный код в каталог с именем src или lib».
Собственно "Это затрудняет работу без установки". это огромное преимущество. Все должны устанавливать pip install -e, тогда не будет двусмысленности и шансов протестировать то, что, по вашему мнению, вы тестируете.
Раздел «не делать» звучит чрезвычайно питонично, однако, как и многие (ленивые) практики сообщества Python, подвержены ошибкам и случайным сложностям - как также отмечали некоторые комментаторы.
Согласно Структура файловой системы проекта Python Жан-Поля Кальдероне:
Project/
|-- bin/
| |-- project
|
|-- project/
| |-- test/
| | |-- __init__.py
| | |-- test_main.py
| |
| |-- __init__.py
| |-- main.py
|
|-- setup.py
|-- README
Project/project/? Ах, второй - имя пакета.
Беспечно проигнорировав это, мне в конце концов пришлось перетасовать мои вещи, чтобы они выглядели вот так, чтобы упростить создание RPM. Настоятельно рекомендую всем использовать эту настройку по умолчанию, если у вас нет действительно веской причины не делать этого.
как исполняемый файл в папке bin ссылается на модуль проекта? (Я не думаю, что синтаксис Python позволяет использовать ../ в заявлении include)
@ThorSummoner Это на самом деле очень хороший вопрос для меня, помимо настройки PYTHON_PATH, я не знаю, как это сделать. Мне кажется, что эта структура делает запуск сценария запуска проекта излишне сложным (размещение файла «проект» непосредственно в проекте / устраняет необходимость настраивать PYTHON_PATH).
На самом деле Python поддерживает подъем по каталогам; См. stackoverflow.com/a/1054281/1695680.
@ThorSummoner Это работает только тогда, когда остается внутри одного пакета. Чтобы относительный импорт работал здесь, вам понадобится файл __init__.py как в папке bin, так и в верхней папке Project.
@ThorSummoner Просто. Вы устанавливаете пакет! (pip install -e /path/to/Project)
Было бы здорово, если бы кто-нибудь заархивировал образец этого макета с помощью hello.py и hello-test.py и сделал его доступным для нас, новичков.
наличие папки /bin с исполняемыми файлами звучит для меня как очень плохая практика: у вас проблемы с импортом, как упоминалось выше, и вам приходится возиться с PYTHONPATH; Я предпочитаю использовать entry_points
Каково соглашение об именах для каталога проекта для python PascalCase или этого случая?
@Kroltan Не могли бы вы объяснить, что происходит после того, как pip install установит пакет, который позволяет вам запускать исполняемый файл из папки bin, который красиво ссылается на модуль проекта?
@Bloke Ядром является флаг -e, который устанавливает пакет как редактируемый, то есть устанавливает его как ссылки на фактическую папку проекта. Затем исполняемый файл может просто import project получить доступ к модулю.
Этот поток довольно старый, но я могу пролить свет на вопрос помещения исполняемого файла project в bin. По-видимому, это актуально для систем Linux, где исполняемый файл обычно должен быть сценарием оболочки (с тем же именем, что и ваш Python проект). Подробнее здесь.
Этот пост кажется устаревшим, я не нашел ни одного проекта, который бы использовал эту структуру. Не могли бы вы указать мне на некоторые? Или, может быть, добавить в ответ заголовок о том, что он старый?
Проверьте Открытый исходный код проекта 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 Не могли бы вы расширить свою идею? Вы говорите о том, чтобы поставить Makefile на один уровень с setup.py? Итак, если я вас правильно понимаю, make env автоматизирует создание нового venv и установку в него пакетов ...?
@ Сент-Антарио точно. Как уже упоминалось, у меня обычно есть «тестовая» цель для запуска тестов, а иногда и «релизная» цель, которая смотрит на текущий тег, строит колесо и отправляет его в pypi.
Попробуйте запустить проект, используя шаблон python_boilerplate. Он в значительной степени соответствует передовым методам (например, те, кто здесь), но лучше подходит в том случае, если вы обнаружите, что в какой-то момент захотите разделить свой проект на более чем одно яйцо (и поверьте мне, с любыми проектами, кроме простейших, вы это сделаете. ситуация, когда вам нужно использовать локально измененную версию чужой библиотеки).
Куда вы положили источник?
PROJECT_ROOT/src/<egg_name>.Куда вы кладете скрипты запуска приложений?
entry_point в одном из яиц.Куда вы положили хлам проекта IDE?
PROJECT_ROOT/.<something> в корне проекта, и это нормально.Где вы ставите модульные / приемочные испытания?
PROJECT_ROOT/src/<egg_name>/tests. Я лично предпочитаю использовать py.test для их запуска.Куда вы помещаете данные, не относящиеся к Python, например файлы конфигурации?
pkg_resources из setuptools или, начиная с Python 3.7, через модуль importlib.resources из стандартной библиотеки.PROJECT_ROOT/config. Для развертывания могут быть разные варианты. В Windows можно использовать %APP_DATA%/<app-name>/config, в Linux - /etc/<app-name> или /opt/<app-name>/config.PROJECT_ROOT/var во время разработки и в /var во время развертывания Linux.PROJECT_ROOT/src/<egg_name>/nativeДокументация обычно идет в PROJECT_ROOT/doc или PROJECT_ROOT/src/<egg_name>/doc (это зависит от того, рассматриваете ли вы некоторые из яиц как отдельные большие проекты). Некоторая дополнительная конфигурация будет в файлах типа PROJECT_ROOT/buildout.cfg и PROJECT_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, который обрабатывает вашу любимую логику загрузки конфигурации за вас и, возможно, даже позволяет пользователю изменять настройки во время выполнения. В целом, однако, я думаю, что это проблема, заслуживающая отдельного вопроса (который, возможно, задавали раньше где-то здесь).
Какой отличный ответ, особенно в части данных, отличных от Python, спасибо!
У "Python Packaging Authority" есть образец проекта:
https://github.com/pypa/sampleproject
Это образец проекта, который существует как помощь в Руководстве пользователя Python Packaging Tutorial on Packaging and Distributing Projects.
+ тенденция к структуре root/src/*: github.com/pypa/sampleproject/commit/…
рекомендации по структуре проекта также см. в setuptools.readthedocs.io/_/downloads/en/latest/pdf
Любые проекты Python с открытым исходным кодом, которые вы бы порекомендовали эмулировать их структуру каталогов?