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'