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'
Вернуться на верх