6. Примеры Distutils¶
Примечание
Этот документ сохраняется исключительно до тех пор, пока документация setuptools
на сайте https://setuptools.readthedocs.io/en/latest/setuptools.html не будет независимо охватывать всю соответствующую информацию, включенную сюда в настоящее время.
В этой главе приведен ряд базовых примеров, которые помогут начать работу с distutils. Дополнительную информацию об использовании distutils можно найти в Distutils Cookbook.
См.также
- Distutils Cookbook
Коллекция рецептов, показывающих, как добиться большего контроля над distutils.
6.1. Распространение чистого Python (по модулям)¶
Если вы распространяете всего пару модулей, особенно если они не входят в конкретный пакет, вы можете указать их по отдельности, используя опцию py_modules
в скрипте установки.
В простейшем случае у вас будет два файла, о которых нужно беспокоиться: скрипт установки и единственный распространяемый модуль, foo.py
в данном примере:
<root>/
setup.py
foo.py
(Во всех схемах этого раздела <root> будет означать корневой каталог дистрибутива). Минимальный сценарий настройки, описывающий эту ситуацию, будет выглядеть так:
from distutils.core import setup
setup(name='foo',
version='1.0',
py_modules=['foo'],
)
Обратите внимание, что имя дистрибутива задается независимо с помощью опции name
, и нет правила, которое бы гласило, что оно должно быть таким же, как имя единственного модуля в дистрибутиве (хотя, вероятно, это хорошее соглашение, которого следует придерживаться). Однако имя дистрибутива используется для генерации имен файлов, поэтому следует придерживаться букв, цифр, подчеркиваний и дефисов.
Поскольку py_modules
- это список, вы, конечно, можете указать несколько модулей, например, если вы распространяете модули foo
и bar
, ваша установка может выглядеть следующим образом:
<root>/
setup.py
foo.py
bar.py
и сценарий установки может быть
from distutils.core import setup
setup(name='foobar',
version='1.0',
py_modules=['foo', 'bar'],
)
Вы можете поместить исходные файлы модулей в другой каталог, но если у вас достаточно модулей для этого, вероятно, проще указывать модули по пакетам, а не перечислять их по отдельности.
6.2. Чистый дистрибутив Python (по пакетам)¶
Если вам нужно распространить более пары модулей, особенно если они находятся в нескольких пакетах, вероятно, проще указывать целые пакеты, а не отдельные модули. Это работает, даже если ваши модули не находятся в пакете; вы можете просто указать Distutils обрабатывать модули из корневого пакета, и это работает так же, как и любой другой пакет (за исключением того, что вам не нужно иметь файл __init__.py
).
Сценарий настройки из последнего примера также можно записать как
from distutils.core import setup
setup(name='foobar',
version='1.0',
packages=[''],
)
(Пустая строка означает корневой пакет).
Если эти два файла перемещены в подкаталог, но остаются в корневом пакете, например:
<root>/
setup.py
src/ foo.py
bar.py
то вы все равно укажете корневой пакет, но вам придется указать Distutils, где находятся исходные файлы корневого пакета:
from distutils.core import setup
setup(name='foobar',
version='1.0',
package_dir={'': 'src'},
packages=[''],
)
Однако чаще всего вы хотите распространять несколько модулей в одном пакете (или в подпакетах). Например, если модули foo
и bar
принадлежат пакету foobar
, один из способов компоновки дерева исходных текстов выглядит так
<root>/
setup.py
foobar/
__init__.py
foo.py
bar.py
На самом деле это макет по умолчанию, ожидаемый Distutils, и тот, который требует меньше всего работы для описания в вашем сценарии установки:
from distutils.core import setup
setup(name='foobar',
version='1.0',
packages=['foobar'],
)
Если вы хотите поместить модули в каталоги, не названные по имени их пакета, то вам нужно снова использовать опцию package_dir
. Например, если каталог src
содержит модули в пакете foobar
:
<root>/
setup.py
src/
__init__.py
foo.py
bar.py
подходящим сценарием настройки будет
from distutils.core import setup
setup(name='foobar',
version='1.0',
package_dir={'foobar': 'src'},
packages=['foobar'],
)
Или вы можете поместить модули из вашего основного пакета прямо в корень дистрибутива:
<root>/
setup.py
__init__.py
foo.py
bar.py
в этом случае ваш сценарий настройки будет
from distutils.core import setup
setup(name='foobar',
version='1.0',
package_dir={'foobar': ''},
packages=['foobar'],
)
(Пустая строка также обозначает текущий каталог).
Если у вас есть подпакеты, они должны быть явно перечислены в packages
, но любые записи в package_dir
автоматически распространяются на подпакеты. (Другими словами, Distutils не сканирует ваше дерево исходных текстов, пытаясь выяснить, какие каталоги соответствуют пакетам Python, ища файлы __init__.py
). Таким образом, если в схеме по умолчанию выращивается подпакет:
<root>/
setup.py
foobar/
__init__.py
foo.py
bar.py
subfoo/
__init__.py
blah.py
то соответствующий сценарий настройки будет
from distutils.core import setup
setup(name='foobar',
version='1.0',
packages=['foobar', 'foobar.subfoo'],
)
6.3. Одиночный модуль расширения¶
Модули расширения задаются с помощью опции ext_modules
. package_dir
не влияет на то, где будут найдены исходные файлы расширений; она влияет только на исходные тексты чистых модулей Python. Простейший случай - один модуль расширения в одном исходном файле на языке Си - выглядит так:
<root>/
setup.py
foo.c
Если расширение foo
принадлежит корневому пакету, сценарий установки для него может быть
from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
version='1.0',
ext_modules=[Extension('foo', ['foo.c'])],
)
Если расширение действительно принадлежит пакету, скажем foopkg
, то
С точно таким же расположением дерева исходников это расширение можно поместить в пакет foopkg
, просто изменив имя расширения:
from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
version='1.0',
ext_modules=[Extension('foopkg.foo', ['foo.c'])],
)
6.4. Проверка пакета¶
Команда check
позволяет проверить, соответствуют ли мета-данные вашего пакета минимальным требованиям для сборки дистрибутива.
Чтобы запустить его, просто вызовите его с помощью вашего скрипта setup.py
. Если чего-то не хватает, check
выдаст предупреждение.
Рассмотрим пример с простым сценарием:
from distutils.core import setup
setup(name='foobar')
Выполнение команды check
приведет к появлению некоторых предупреждений:
$ python setup.py check
running check
warning: check: missing required meta-data: version, url
warning: check: missing meta-data: either (author and author_email) or
(maintainer and maintainer_email) should be supplied
Если вы используете синтаксис reStructuredText в поле long_description
и docutils установлен, вы можете проверить, в порядке ли синтаксис, с помощью команды check
, используя опцию restructuredtext
.
Например, если сценарий setup.py
изменить следующим образом:
from distutils.core import setup
desc = """\
My description
==============
This is the description of the ``foobar`` package.
"""
setup(name='foobar', version='1', author='tarek',
author_email='tarek@ziade.org',
url='http://example.com', long_description=desc)
Если длинное описание нарушено, check
сможет обнаружить это с помощью парсера docutils
:
$ python setup.py check --restructuredtext
running check
warning: check: Title underline too short. (line 2)
warning: check: Could not finish the parsing.
6.5. Чтение метаданных¶
Функция distutils.core.setup()
предоставляет интерфейс командной строки, который позволяет запрашивать поля метаданных проекта через сценарий setup.py
данного проекта:
$ python setup.py --name
distribute
Этот вызов считывает метаданные name
, выполняя функцию distutils.core.setup()
. Хотя, когда исходный или бинарный дистрибутив создается с помощью Distutils, поля метаданных записываются в статический файл PKG-INFO
. Когда проект на основе Distutils устанавливается в Python, файл PKG-INFO
копируется вместе с модулями и пакетами дистрибутива под именем NAME-VERSION-pyX.X.egg-info
, где NAME
- имя проекта, VERSION
его версия, определенная в метаданных, и pyX.X
мажорная и минорная версия Python типа 2.7
или 3.2
.
Вы можете прочитать обратно этот статический файл, используя класс distutils.dist.DistributionMetadata
и его метод read_pkg_file()
:
>>> from distutils.dist import DistributionMetadata
>>> metadata = DistributionMetadata()
>>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info'))
>>> metadata.name
'distribute'
>>> metadata.version
'0.6.8'
>>> metadata.description
'Easily download, build, install, upgrade, and uninstall Python packages'
Обратите внимание, что класс также может быть инстанцирован с указанием пути к файлу метаданных для загрузки его значений:
>>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info'
>>> DistributionMetadata(pkg_info_path).name
'distribute'