6. Примеры дистрибутивов

Примечание

Этот документ будет храниться только до тех пор, пока в документации setuptools по адресу https://setuptools.readthedocs.io/en/latest/setuptools.html самостоятельно не будет представлена вся соответствующая информация, которая в настоящее время включена в этот документ.

В этой главе приведен ряд основных примеров, которые помогут начать работу с distutils. Дополнительную информацию об использовании distutils можно найти в книге Distutils Cookbook.

См.также

Distutils Cookbook

Коллекция рецептов, показывающих, как добиться большего контроля над дистрибутивами.

6.1. Дистрибутив Pure 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. Дистрибутив Pure Python (по пакетам)

Если у вас есть несколько модулей для распространения, особенно если они находятся в нескольких пакетах, вероятно, проще указать целые пакеты, а не отдельные модули. Это работает, даже если ваши модули не находятся в пакете; вы можете просто указать Distutils обрабатывать модули из корневого пакета, и это работает так же, как и любой другой пакет (за исключением того, что вам не обязательно иметь файл __init__.py).

Сценарий установки из последнего примера также можно было бы записать следующим образом

from distutils.core import setup
setup(name='foobar',
      version='1.0',
      packages=[''],
      )

(Пустая строка обозначает корневой пакет.)

Если эти два файла перемещены в подкаталог, но остаются в корневом пакете, например:

<root>/
        setup.py
        src/      foo.py
                  bar.py

тогда вы все равно указали бы корневой пакет, но вы должны указать дистрибутивам, где находятся исходные файлы в корневом пакете:

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

На самом деле это макет по умолчанию, ожидаемый дистрибутивами, и тот, который требует наименьших усилий для описания в вашем сценарии установки:

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. Простейший случай, когда один модуль расширения находится в одном исходном файле C, - это:

<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. Когда на Python устанавливается проект, основанный на Distutils, файл 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'
Вернуться на верх