examples.md
1> **Источник:** https://python-all.ru/3.10/distutils/examples.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 6. Примеры использования Distutils89> **Примечание**10>11> Этот документ сохраняется только до тех пор, пока `setuptools` документация по адресу [https://setuptools.readthedocs.io/en/latest/setuptools.html](https://python-all.ru/3.10/distutils/examples.html) не станет самостоятельно охватывать всю актуальную информацию, приведённую здесь.1213Эта глава содержит несколько базовых примеров, которые помогут начать работу с distutils. Дополнительную информацию об использовании distutils можно найти в Distutils Cookbook.1415> **См. также**16>17> **[Кулинарная книга Distutils](https://python-all.ru/3.10/distutils/examples.html)**18>19> Сборник рецептов, показывающих, как получить больше контроля над distutils.2021## 6.1. Распространение чистого Python (по модулям)2223Если вы распространяете всего несколько модулей, особенно если они не находятся в определённом пакете, вы можете указать их по отдельности с помощью параметра `py_modules` в скрипте setup.2425В простейшем случае вам понадобится всего два файла: скрипт setup и единственный модуль, который вы распространяете, `foo.py` в этом примере:2627```python28<root>/29 setup.py30 foo.py31```3233(На всех схемах в этом разделе *\<root\>* обозначает корневой каталог дистрибутива.) Минимальный скрипт setup для описания этой ситуации будет выглядеть так:3435```python36from distutils.core import setup37setup(name='foo',38 version='1.0',39 py_modules=['foo'],40 )41```4243Обратите внимание, что имя дистрибутива задаётся независимо с помощью параметра `name`, и нет правила, что оно должно совпадать с именем единственного модуля в дистрибутиве (хотя это, вероятно, хорошее соглашение). Однако имя дистрибутива используется для генерации имён файлов, поэтому следует использовать только буквы, цифры, символы подчёркивания и дефисы.4445Поскольку `py_modules` – это список, вы, конечно, можете указать несколько модулей, например, если вы распространяете модули `foo` и `bar`, ваш setup может выглядеть так:4647```python48<root>/49 setup.py50 foo.py51 bar.py52```5354а скрипт setup может быть таким:5556```python57from distutils.core import setup58setup(name='foobar',59 version='1.0',60 py_modules=['foo', 'bar'],61 )62```6364Вы можете поместить исходные файлы модулей в другой каталог, но если у вас достаточно модулей для этого, вероятно, проще указывать модули по пакетам, а не перечислять их по отдельности.6566## 6.2. Распространение чистого Python (по пакетам)6768Если вам нужно распространять больше пары модулей, особенно если они находятся в нескольких пакетах, вероятно, проще указывать целые пакеты, а не отдельные модули. Это работает даже если ваши модули не в пакете; вы можете просто указать Distutils обрабатывать модули из корневого пакета, и это будет работать так же, как с любым другим пакетом (за исключением того, что вам не нужен файл `__init__.py`).6970Скрипт setup из предыдущего примера можно также записать как7172```python73from distutils.core import setup74setup(name='foobar',75 version='1.0',76 packages=[''],77 )78```7980(Пустая строка обозначает корневой пакет.)8182Если эти два файла перемещены в подкаталог, но остаются в корневом пакете, например:8384```python85<root>/86 setup.py87 src/ foo.py88 bar.py89```9091тогда вы по-прежнему указываете корневой пакет, но должны сообщить Distutils, где находятся исходные файлы в корневом пакете:9293```python94from distutils.core import setup95setup(name='foobar',96 version='1.0',97 package_dir={'': 'src'},98 packages=[''],99 )100```101102Однако чаще требуется распространять несколько модулей в одном пакете (или в подпакетах). Например, если модули `foo` и `bar` принадлежат пакету `foobar`, один из способов организовать дерево исходников:103104```python105<root>/106 setup.py107 foobar/108 __init__.py109 foo.py110 bar.py111```112113Фактически это стандартная структура, ожидаемая Distutils, и та, что требует наименьших усилий для описания в вашем скрипте setup:114115```python116from distutils.core import setup117setup(name='foobar',118 version='1.0',119 packages=['foobar'],120 )121```122123Если модули нужно поместить в каталоги, не названные по имени пакета, то следует снова использовать параметр `package_dir`. Например, если каталог `src` содержит модули пакета `foobar`:124125```python126<root>/127 setup.py128 src/129 __init__.py130 foo.py131 bar.py132```133134соответствующий скрипт setup будет таким:135136```python137from distutils.core import setup138setup(name='foobar',139 version='1.0',140 package_dir={'foobar': 'src'},141 packages=['foobar'],142 )143```144145Или же можно поместить модули из главного пакета прямо в корневой каталог дистрибутива:146147```python148<root>/149 setup.py150 __init__.py151 foo.py152 bar.py153```154155в этом случае скрипт setup будет таким:156157```python158from distutils.core import setup159setup(name='foobar',160 version='1.0',161 package_dir={'foobar': ''},162 packages=['foobar'],163 )164```165166(Пустая строка также обозначает текущий каталог.)167168Если есть подпакеты, они должны быть явно перечислены в `packages`, но все записи в `package_dir` автоматически распространяются на подпакеты. (Другими словами, Distutils *не* сканирует дерево исходных файлов, пытаясь выяснить, какие каталоги соответствуют пакетам Python по наличию файлов `__init__.py`.) Таким образом, если в стандартной структуре появляется подпакет:169170```python171<root>/172 setup.py173 foobar/174 __init__.py175 foo.py176 bar.py177 subfoo/178 __init__.py179 blah.py180```181182тогда соответствующий скрипт setup будет таким:183184```python185from distutils.core import setup186setup(name='foobar',187 version='1.0',188 packages=['foobar', 'foobar.subfoo'],189 )190```191192## 6.3. Один модуль расширения193194Модули расширения задаются с помощью параметра `ext_modules`. `package_dir` не влияет на расположение исходных файлов расширения; он влияет только на исходные файлы чистых модулей Python. Самый простой случай – один модуль расширения в одном C-файле:195196```python197<root>/198 setup.py199 foo.c200```201202Если расширение `foo` принадлежит корневому пакету, скрипт setup для него может быть таким:203204```python205from distutils.core import setup206from distutils.extension import Extension207setup(name='foobar',208 version='1.0',209 ext_modules=[Extension('foo', ['foo.c'])],210 )211```212213Если расширение на самом деле принадлежит пакету, например `foopkg`, то:214215При той же структуре дерева исходных файлов это расширение можно поместить в пакет `foopkg`, просто изменив имя расширения:216217```python218from distutils.core import setup219from distutils.extension import Extension220setup(name='foobar',221 version='1.0',222 ext_modules=[Extension('foopkg.foo', ['foo.c'])],223 )224```225226## 6.4. Проверка пакета227228Команда `check` позволяет проверить, соответствуют ли метаданные пакета минимальным требованиям для сборки дистрибутива.229230Чтобы запустить её, достаточно вызвать её с помощью скрипта `setup.py`. Если чего-то не хватает, `check` выведет предупреждение.231232Рассмотрим пример с простым скриптом:233234```python235from distutils.core import setup236237setup(name='foobar')238```239240Запуск команды `check` выведет некоторые предупреждения:241242```console243$ python setup.py check244running check245warning: check: missing required meta-data: version, url246warning: check: missing meta-data: either (author and author_email) or247 (maintainer and maintainer_email) should be supplied248```249250Если в поле `long_description` используется синтаксис reStructuredText и установлены [docutils](https://python-all.ru/3.10/distutils/examples.html), можно проверить корректность синтаксиса командой `check` с параметром `restructuredtext`.251252Например, если скрипт `setup.py` изменить следующим образом:253254```python255from distutils.core import setup256257desc = """\258My description259==============260261This is the description of the ``foobar`` package.262"""263264setup(name='foobar', version='1', author='tarek',265 author_email='tarek@ziade.org',266 url='http://example.com', long_description=desc)267```268269Если длинное описание содержит ошибки, `check` сможет их обнаружить с помощью синтаксического анализатора `docutils`:270271```console272$ python setup.py check --restructuredtext273running check274warning: check: Title underline too short. (line 2)275warning: check: Could not finish the parsing.276```277278## 6.5. Чтение метаданных279280Функция [`distutils.core.setup()`](https://python-all.ru/3.10/distutils/apiref.html#distutils.core.setup) предоставляет интерфейс командной строки, позволяющий запрашивать поля метаданных проекта через скрипт `setup.py` этого проекта:281282```console283$ python setup.py --name284distribute285```286287Этот вызов читает метаданные `name`, запуская функцию [`distutils.core.setup()`](https://python-all.ru/3.10/distutils/apiref.html#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`.288289Этот статический файл можно прочитать с помощью класса `distutils.dist.DistributionMetadata` и его метода `read_pkg_file()`:290291```python292>>> from distutils.dist import DistributionMetadata293>>> metadata = DistributionMetadata()294>>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info'))295>>> metadata.name296'distribute'297>>> metadata.version298'0.6.8'299>>> metadata.description300'Easily download, build, install, upgrade, and uninstall Python packages'301```302303Обратите внимание, что класс также можно инстанцировать с путём к файлу метаданных, чтобы загрузить его значения:304305```python306>>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info'307>>> DistributionMetadata(pkg_info_path).name308'distribute'309```310