examples.md
1> **Источник:** https://python-all.ru/3.3/distutils/examples.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# 7. Примеры89Эта глава содержит несколько базовых примеров, которые помогут начать работу с distutils. Дополнительную информацию об использовании distutils можно найти в Distutils Cookbook.1011> **См. также**12>13> **[Кулинарная книга Distutils](https://python-all.ru/3.3/distutils/examples.html)**14>15> Сборник рецептов, показывающих, как получить больше контроля над distutils.1617## 7.1. Дистрибутив чистого Python (по модулям)1819Если распространяется всего несколько модулей, особенно если они не находятся в определённом пакете, их можно указать по отдельности с помощью опции *py\_modules* в скрипте setup.2021В простейшем случае понадобятся два файла: скрипт setup и единственный распространяемый модуль `foo.py` в этом примере:2223```python24<root>/25 setup.py26 foo.py27```2829(На всех схемах в этом разделе *\<root\>* обозначает корневой каталог дистрибутива.) Минимальный скрипт setup для описания этой ситуации будет выглядеть так:3031```python32from distutils.core import setup33setup(name='foo',34 version='1.0',35 py_modules=['foo'],36 )37```3839Обратите внимание, что имя дистрибутива указывается независимо с помощью опции *name*, и нет правила, которое бы гласило, что оно должно совпадать с именем единственного модуля в дистрибутиве (хотя это, вероятно, хорошее соглашение, которому стоит следовать). Однако имя дистрибутива используется для генерации имён файлов, поэтому следует использовать только буквы, цифры, символы подчёркивания и дефисы.4041Поскольку *py\_modules* является списком, можно, конечно, указать несколько модулей, например, если распространяются модули `foo` и `bar`, ваш скрипт setup может выглядеть так:4243```python44<root>/45 setup.py46 foo.py47 bar.py48```4950а скрипт setup может быть таким:5152```python53from distutils.core import setup54setup(name='foobar',55 version='1.0',56 py_modules=['foo', 'bar'],57 )58```5960Вы можете поместить исходные файлы модулей в другой каталог, но если у вас достаточно модулей для этого, вероятно, проще указывать модули по пакетам, а не перечислять их по отдельности.6162## 7.2. Дистрибутив чистого Python (по пакетам)6364Если нужно распространять более нескольких модулей, особенно если они находятся в нескольких пакетах, вероятно, проще указать целые пакеты, а не отдельные модули. Это работает даже если модули не находятся в пакете; можно просто указать Distutils обрабатывать модули из корневого пакета, и это работает так же, как с любым другим пакетом (за исключением того, что не нужно иметь файл `__init__.py`).6566Скрипт setup из предыдущего примера можно также записать как6768```python69from distutils.core import setup70setup(name='foobar',71 version='1.0',72 packages=[''],73 )74```7576(Пустая строка обозначает корневой пакет.)7778Если эти два файла перемещены в подкаталог, но остаются в корневом пакете, например:7980```python81<root>/82 setup.py83 src/ foo.py84 bar.py85```8687тогда вы по-прежнему указываете корневой пакет, но должны сообщить Distutils, где находятся исходные файлы в корневом пакете:8889```python90from distutils.core import setup91setup(name='foobar',92 version='1.0',93 package_dir={'': 'src'},94 packages=[''],95 )96```9798Однако чаще требуется распространять несколько модулей в одном пакете (или в подпакетах). Например, если модули `foo` и `bar` принадлежат пакету `foobar`, один из способов организовать дерево исходных кодов – это99100```python101<root>/102 setup.py103 foobar/104 __init__.py105 foo.py106 bar.py107```108109Фактически это стандартная структура, ожидаемая Distutils, и та, что требует наименьших усилий для описания в вашем скрипте setup:110111```python112from distutils.core import setup113setup(name='foobar',114 version='1.0',115 packages=['foobar'],116 )117```118119Если требуется поместить модули в каталоги, имена которых не соответствуют их пакету, то необходимо снова использовать опцию *package\_dir*. Например, если каталог `src` содержит модули пакета `foobar`:120121```python122<root>/123 setup.py124 src/125 __init__.py126 foo.py127 bar.py128```129130соответствующий скрипт setup будет таким:131132```python133from distutils.core import setup134setup(name='foobar',135 version='1.0',136 package_dir={'foobar': 'src'},137 packages=['foobar'],138 )139```140141Или же можно поместить модули из главного пакета прямо в корневой каталог дистрибутива:142143```python144<root>/145 setup.py146 __init__.py147 foo.py148 bar.py149```150151в этом случае скрипт setup будет таким:152153```python154from distutils.core import setup155setup(name='foobar',156 version='1.0',157 package_dir={'foobar': ''},158 packages=['foobar'],159 )160```161162(Пустая строка также обозначает текущий каталог.)163164Если имеются вложенные пакеты, они должны быть явно перечислены в *packages*, но любые записи в *package\_dir* автоматически распространяются на вложенные пакеты. (Иными словами, Distutils *не* сканирует ваше дерево исходников, пытаясь выяснить, какие каталоги соответствуют пакетам Python, путём поиска файлов `__init__.py`.) Таким образом, если в стандартной структуре появляется вложенный пакет:165166```python167<root>/168 setup.py169 foobar/170 __init__.py171 foo.py172 bar.py173 subfoo/174 __init__.py175 blah.py176```177178тогда соответствующий скрипт setup будет таким:179180```python181from distutils.core import setup182setup(name='foobar',183 version='1.0',184 packages=['foobar', 'foobar.subfoo'],185 )186```187188(И снова пустая строка в *package\_dir* обозначает текущий каталог.)189190## 7.3. Одиночный модуль расширения191192Модули расширения указываются с помощью опции *ext\_modules*. Опция *package\_dir* не влияет на то, где находятся исходные файлы расширения; она влияет только на исходные файлы чистых модулей Python. Простейший случай – один модуль расширения в одном файле исходного кода на C – выглядит так:193194```python195<root>/196 setup.py197 foo.c198```199200Если расширение `foo` принадлежит корневому пакету, скрипт setup для этого может быть таким:201202```python203from distutils.core import setup204from distutils.extension import Extension205setup(name='foobar',206 version='1.0',207 ext_modules=[Extension('foo', ['foo.c'])],208 )209```210211Если расширение на самом деле принадлежит пакету, скажем `foopkg`, то212213При той же самой структуре дерева исходных кодов это расширение можно поместить в пакет `foopkg`, просто изменив имя расширения:214215```python216from distutils.core import setup217from distutils.extension import Extension218setup(name='foobar',219 version='1.0',220 ext_modules=[Extension('foopkg.foo', ['foo.c'])],221 )222```223224## 7.4. Проверка пакета225226Команда `check` позволяет проверить, соответствует ли мета-информация пакета минимальным требованиям для сборки дистрибутива.227228Чтобы запустить её, достаточно вызвать её с помощью скрипта `setup.py`. Если чего-то не хватает, `check` выведет предупреждение.229230Рассмотрим пример с простым скриптом:231232```python233from distutils.core import setup234235setup(name='foobar')236```237238При запуске команды `check` будут выведены некоторые предупреждения:239240```python241$ python setup.py check242running check243warning: check: missing required meta-data: version, url244warning: check: missing meta-data: either (author and author_email) or245 (maintainer and maintainer_email) must be supplied246```247248Если используется синтаксис reStructuredText в поле `long_description` и установлен [docutils](https://python-all.ru/3.3/distutils/examples.html), можно проверить корректность синтаксиса с помощью команды `check`, используя параметр `restructuredtext`.249250Например, если скрипт `setup.py` изменён следующим образом:251252```python253from distutils.core import setup254255desc = """\256My description257=============258259This is the description of the ``foobar`` package.260"""261262setup(name='foobar', version='1', author='tarek',263 author_email='tarek@ziade.org',264 url='http://example.com', long_description=desc)265```266267Если длинное описание содержит ошибки, `check` сможет обнаружить их, используя анализатор `docutils`:268269```python270$ python setup.py check --restructuredtext271running check272warning: check: Title underline too short. (line 2)273warning: check: Could not finish the parsing.274```275276## 7.5. Чтение метаданных277278Функция [`distutils.core.setup()`](https://python-all.ru/3.3/distutils/apiref.html#distutils.core.setup) предоставляет интерфейс командной строки, позволяющий запрашивать поля метаданных проекта через скрипт *setup.py* данного проекта:279280```python281$ python setup.py --name282distribute283```284285Этот вызов считывает метаданные *name* путём выполнения функции [`distutils.core.setup()`](https://python-all.ru/3.3/distutils/apiref.html#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*.286287Этот статический файл можно прочитать с помощью класса `distutils.dist.DistributionMetadata` и его метода `read_pkg_file()`:288289```python290>>> from distutils.dist import DistributionMetadata291>>> metadata = DistributionMetadata()292>>> metadata.read_pkg_file(open('distribute-0.6.8-py2.7.egg-info'))293>>> metadata.name294'distribute'295>>> metadata.version296'0.6.8'297>>> metadata.description298'Easily download, build, install, upgrade, and uninstall Python packages'299```300301Обратите внимание, что класс также может быть создан с путём к файлу мета-информации для загрузки его значений:302303```python304>>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info'305>>> DistributionMetadata(pkg_info_path).name306'distribute'307```308