Содержание страницы
7. Примеры¶Examples
Эта глава содержит несколько базовых примеров, которые помогут начать работу с distutils. Дополнительную информацию об использовании distutils можно найти в Distutils Cookbook.
См. также
- Кулинарная книга Distutils
- Сборник рецептов, показывающих, как получить больше контроля над distutils.
7.1. Дистрибутив чистого Python (по модулям)¶Pure Python distribution (by module)
Если вы распространяете всего несколько модулей, особенно если они не находятся
в определённом пакете, вы можете указать их по отдельности с помощью
параметра py_modules в скрипте setup.
В простейшем случае вам понадобится всего два файла: скрипт setup и
единственный модуль, который вы распространяете, foo.py в этом примере:
<root>/
setup.py
foo.py
(На всех схемах в этом разделе <root> обозначает корневой каталог дистрибутива.) Минимальный скрипт setup для описания этой ситуации будет выглядеть так:
from distutils.core import setup
setup(name='foo',
version='1.0',
py_modules=['foo'],
)
Обратите внимание, что имя дистрибутива задаётся независимо с помощью
параметра name, и нет правила, что оно должно совпадать с
именем единственного модуля в дистрибутиве (хотя это, вероятно, хорошее
соглашение). Однако имя дистрибутива используется для генерации
имён файлов, поэтому следует использовать только буквы, цифры, символы подчёркивания и дефисы.
Поскольку py_modules – это список, вы, конечно, можете указать несколько
модулей, например, если вы распространяете модули foo и bar, ваш
setup может выглядеть так:
<root>/
setup.py
foo.py
bar.py
а скрипт setup может быть таким:
from distutils.core import setup
setup(name='foobar',
version='1.0',
py_modules=['foo', 'bar'],
)
Вы можете поместить исходные файлы модулей в другой каталог, но если у вас достаточно модулей для этого, вероятно, проще указывать модули по пакетам, а не перечислять их по отдельности.
7.2. Дистрибутив чистого Python (по пакетам)¶Pure Python distribution (by package)
Если вам нужно распространять больше пары модулей, особенно если они находятся
в нескольких пакетах, вероятно, проще указывать целые пакеты, а не
отдельные модули. Это работает даже если ваши модули не в пакете; вы
можете просто указать Distutils обрабатывать модули из корневого пакета, и это
будет работать так же, как с любым другим пакетом (за исключением того, что вам не нужен
файл __init__.py).
Скрипт setup из предыдущего примера можно также записать как
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, и та, что требует наименьших усилий для описания в вашем скрипте setup:
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
соответствующий скрипт setup будет таким:
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
в этом случае скрипт setup будет таким:
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
тогда соответствующий скрипт setup будет таким:
from distutils.core import setup
setup(name='foobar',
version='1.0',
packages=['foobar', 'foobar.subfoo'],
)
7.3. Одиночный модуль расширения¶Single extension module
Модули расширения задаются с помощью параметра ext_modules. package_dir не влияет на расположение исходных файлов расширения; он влияет только на исходные файлы чистых модулей Python. Самый простой случай – один модуль расширения в одном C-файле:
<root>/
setup.py
foo.c
Если расширение foo принадлежит корневому пакету, скрипт setup для него может быть таким:
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'])],
)
7.4. Проверка пакета¶Checking a package
Команда 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) must be supplied
Если в поле long_description используется синтаксис reStructuredText и установлены 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.
7.5. Чтение метаданных¶Reading the metadata
Функция 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 – major и minor версия 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'