> **Источник:** https://python-all.ru/3.4/distutils/examples.html
>
> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.

---

# 7. Примеры

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

> **См. также**
>
> **[Кулинарная книга Distutils](https://python-all.ru/3.4/distutils/examples.html)**
>
> Сборник рецептов, показывающих, как получить больше контроля над distutils.

## 7.1. Дистрибутив чистого Python (по модулям)

При распространении всего нескольких модулей, особенно если они не находятся в определённом пакете, их можно указать по отдельности с помощью параметра `py_modules` в скрипте setup.

В простейшем случае понадобятся два файла: скрипт setup и единственный распространяемый модуль `foo.py` в этом примере:

```python
<root>/
        setup.py
        foo.py
```

(На всех схемах в этом разделе *\<root\>* обозначает корневой каталог дистрибутива.) Минимальный скрипт setup для описания этой ситуации будет выглядеть так:

```python
from distutils.core import setup
setup(name='foo',
      version='1.0',
      py_modules=['foo'],
      )
```

Обратите внимание, что имя дистрибутива указывается независимо с помощью параметра `name`, и нет правила, что оно должно совпадать с именем единственного модуля в дистрибутиве (хотя это, вероятно, хорошее соглашение). Однако имя дистрибутива используется для генерации имён файлов, поэтому следует использовать только буквы, цифры, символы подчёркивания и дефисы.

Так как `py_modules` – это список, можно, конечно, указать несколько модулей, например, если распространяются модули `foo` и `bar`, то скрипт setup может выглядеть так:

```python
<root>/
        setup.py
        foo.py
        bar.py
```

а скрипт setup может быть таким:

```python
from distutils.core import setup
setup(name='foobar',
      version='1.0',
      py_modules=['foo', 'bar'],
      )
```

Вы можете поместить исходные файлы модулей в другой каталог, но если у вас достаточно модулей для этого, вероятно, проще указывать модули по пакетам, а не перечислять их по отдельности.

## 7.2. Дистрибутив чистого Python (по пакетам)

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

Скрипт setup из предыдущего примера можно также записать как

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

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

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

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

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

```python
from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'': 'src'},
      packages=[''],
      )
```

Однако чаще требуется распространять несколько модулей в одном пакете (или в подпакетах). Например, если модули `foo` и `bar` принадлежат пакету `foobar`, один из способов организовать дерево исходных кодов – это

```python
<root>/
        setup.py
        foobar/
                 __init__.py
                 foo.py
                 bar.py
```

Фактически это стандартная структура, ожидаемая Distutils, и та, что требует наименьших усилий для описания в вашем скрипте setup:

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

Если нужно разместить модули в каталогах, названных не по имени их пакета, то необходимо снова использовать параметр `package_dir`. Например, если каталог `src` содержит модули пакета `foobar`:

```python
<root>/
        setup.py
        src/
                 __init__.py
                 foo.py
                 bar.py
```

соответствующий скрипт setup будет таким:

```python
from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'foobar': 'src'},
      packages=['foobar'],
      )
```

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

```python
<root>/
        setup.py
        __init__.py
        foo.py
        bar.py
```

в этом случае скрипт setup будет таким:

```python
from distutils.core import setup
setup(name='foobar',
      version='1.0',
      package_dir={'foobar': ''},
      packages=['foobar'],
      )
```

(Пустая строка также обозначает текущий каталог.)

Если имеются подпакеты, они должны быть явно перечислены в `packages`, но любые записи в `package_dir` автоматически распространяются на подпакеты. (Другими словами, Distutils *не* сканирует дерево исходных кодов, пытаясь определить, какие каталоги соответствуют пакетам Python, по наличию файлов `__init__.py`.) Таким образом, если в стандартной структуре появляется подпакет:

```python
<root>/
        setup.py
        foobar/
                 __init__.py
                 foo.py
                 bar.py
                 subfoo/
                           __init__.py
                           blah.py
```

тогда соответствующий скрипт setup будет таким:

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

## 7.3. Одиночный модуль расширения

Модули расширения указываются с помощью параметра `ext_modules`. `package_dir` не влияет на то, где находятся исходные файлы расширений; он влияет только на исходники чистых модулей Python. Простейший случай – один модуль расширения в одном файле исходного кода на C:

```python
<root>/
        setup.py
        foo.c
```

Если расширение `foo` принадлежит корневому пакету, скрипт setup для этого может быть таким:

```python
from distutils.core import setup
from distutils.extension import Extension
setup(name='foobar',
      version='1.0',
      ext_modules=[Extension('foo', ['foo.c'])],
      )
```

Если расширение на самом деле принадлежит пакету, скажем `foopkg`, то

При той же самой структуре дерева исходных кодов это расширение можно поместить в пакет `foopkg`, просто изменив имя расширения:

```python
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. Проверка пакета

Команда `check` позволяет проверить, соответствует ли мета-информация пакета минимальным требованиям для сборки дистрибутива.

Чтобы запустить её, достаточно вызвать её с помощью скрипта `setup.py`. Если чего-то не хватает, `check` выведет предупреждение.

Рассмотрим пример с простым скриптом:

```python
from distutils.core import setup

setup(name='foobar')
```

При запуске команды `check` будут выведены некоторые предупреждения:

```python
$ 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
```

Если используется синтаксис reStructuredText в поле `long_description` и установлен [docutils](https://python-all.ru/3.4/distutils/examples.html), можно проверить корректность синтаксиса с помощью команды `check`, используя параметр `restructuredtext`.

Например, если скрипт `setup.py` изменён следующим образом:

```python
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
$ python setup.py check --restructuredtext
running check
warning: check: Title underline too short. (line 2)
warning: check: Could not finish the parsing.
```

## 7.5. Чтение метаданных

Функция [`distutils.core.setup()`](https://python-all.ru/3.4/distutils/apiref.html#distutils.core.setup) предоставляет интерфейс командной строки, позволяющий запрашивать поля мета-информации проекта через скрипт `setup.py` заданного проекта:

```python
$ python setup.py --name
distribute
```

Этот вызов считывает мета-информацию `name`, запуская функцию [`distutils.core.setup()`](https://python-all.ru/3.4/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`.

Этот статический файл можно прочитать с помощью класса `distutils.dist.DistributionMetadata` и его метода `read_pkg_file()`:

```python
>>> 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'
```

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

```python
>>> pkg_info_path = 'distribute-0.6.8-py2.7.egg-info'
>>> DistributionMetadata(pkg_info_path).name
'distribute'
```
