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

---

# 2. Написание сценария установки

Сценарий установки является центром всей деятельности по сборке, распространению и установке модулей с помощью Distutils. Основная цель сценария установки – описать ваш дистрибутив модулей для Distutils, чтобы различные команды, работающие с вашими модулями, делали правильные вещи. Как мы видели в разделе [A Simple Example](https://python-all.ru/3.6/distutils/introduction.html#distutils-simple-example) выше, сценарий установки состоит в основном из вызова `setup()`, и большая часть информации, передаваемой Distutils разработчиком модуля, передаётся в виде именованных аргументов `setup()`.

Вот немного более сложный пример, который мы будем рассматривать в следующих нескольких разделах: собственный сценарий установки Distutils. (Имейте в виду, что хотя Distutils входят в состав Python 1.6 и более поздних версий, они также существуют независимо, чтобы пользователи Python 1.5.2 могли использовать их для установки других дистрибутивов модулей. Собственный сценарий установки Distutils, показанный здесь, используется для установки пакета в Python 1.5.2.)

```python
#!/usr/bin/env python

from distutils.core import setup

setup(name='Distutils',
      version='1.0',
      description='Python Distribution Utilities',
      author='Greg Ward',
      author_email='gward@python.net',
      url='https://www.python.org/sigs/distutils-sig/',
      packages=['distutils', 'distutils.command'],
     )
```

Есть только два отличия от тривиального однофайлового дистрибутива, представленного в разделе [A Simple Example](https://python-all.ru/3.6/distutils/introduction.html#distutils-simple-example): больше метаданных и указание чистых модулей Python по пакетам, а не по модулям. Это важно, поскольку Distutils состоят из нескольких десятков модулей, разбитых (пока) на два пакета; явный список всех модулей было бы утомительно составлять и сложно поддерживать. Дополнительную информацию о метаданных см. в разделе [Additional meta-data](https://python-all.ru/3.6/distutils/setupscript.html#meta-data).

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

Это, конечно, относится только к путям, передаваемым функциям Distutils. Если вы, например, используете стандартные функции Python, такие как [`glob.glob()`](https://python-all.ru/3.6/library/glob.html#glob.glob) или [`os.listdir()`](https://python-all.ru/3.6/library/os.html#os.listdir) для указания файлов, будьте внимательны и пишите переносимый код вместо жёсткого кодирования разделителей пути:

```python
glob.glob(os.path.join('mydir', 'subdir', '*.html'))
os.listdir(os.path.join('mydir', 'subdir'))
```

## 2.1. Перечисление целых пакетов

Опция `packages` указывает Distutils обрабатывать (собирать, распространять, устанавливать и т. д.) все чистые модули Python, найденные в каждом пакете, упомянутом в списке `packages`. Для этого, конечно, должно быть соответствие между именами пакетов и каталогами в файловой системе. Соответствие по умолчанию – самое очевидное: пакет [`distutils`](https://python-all.ru/3.6/library/distutils.html#module-distutils) находится в каталоге `distutils` относительно корня дистрибутива. Таким образом, когда вы пишете `packages = ['foo']` в своём сценарии установки, вы обещаете, что Distutils найдёт файл `foo/__init__.py` (название может отличаться в вашей системе, но идея понятна) относительно каталога, где находится ваш сценарий установки. Если вы нарушите это обещание, Distutils выдаст предупреждение, но всё равно обработает проблемный пакет.

Если вы используете другую конвенцию для организации исходного каталога, это не проблема: нужно просто указать опцию `package_dir`, чтобы сообщить Distutils о вашей конвенции. Например, предположим, что вы храните все исходники Python в `lib`, так что модули в «корневом пакете» (т. е. не входящие ни в какой пакет) находятся в `lib`, модули пакета `foo` – в `lib/foo`, и так далее. Тогда вы укажете

```python
package_dir = {'': 'lib'}
```

в своём сценарии установки. Ключами этого словаря являются имена пакетов, а пустое имя пакета означает корневой пакет. Значения – имена каталогов относительно корня вашего дистрибутива. В этом случае, когда вы пишете `packages = ['foo']`, вы обещаете, что файл `lib/foo/__init__.py` существует.

Другая возможная конвенция – поместить пакет `foo` прямо в `lib`, пакет `foo.bar` в `lib/bar`, и т. д. Это можно записать в сценарии установки как

```python
package_dir = {'foo': 'lib'}
```

Запись `package: dir` в словаре `package_dir` неявно применяется ко всем пакетам ниже *пакета*, поэтому случай `foo.bar` обрабатывается автоматически. В этом примере наличие `packages = ['foo', 'foo.bar']` указывает Distutils искать `lib/__init__.py` и `lib/bar/__init__.py`. (Имейте в виду, что хотя `package_dir` применяется рекурсивно, необходимо явно перечислить все пакеты в `packages`: Distutils *не* будет рекурсивно сканировать дерево исходных текстов в поисках каталогов с файлом `__init__.py`.)

## 2.2. Перечисление отдельных модулей

Для небольшого дистрибутива модулей может быть удобнее перечислить все модули, а не пакеты – особенно случай одного модуля, который находится в «корневом пакете» (то есть вообще без пакета). Этот простейший случай был показан в разделе [Простой пример](https://python-all.ru/3.6/distutils/introduction.html#distutils-simple-example); вот немного более сложный пример:

```python
py_modules = ['mod1', 'pkg.mod2']
```

Здесь описываются два модуля: один в «корневом» пакете, другой – в пакете `pkg`. По умолчанию соответствие пакетов и каталогов подразумевает, что эти два модуля находятся в `mod1.py` и `pkg/mod2.py`, а также существует `pkg/__init__.py`. И снова можно переопределить соответствие пакетов и каталогов с помощью опции `package_dir`.

## 2.3. Описание модулей расширения

Так же как написание модулей расширения Python немного сложнее, чем написание чистых модулей Python, их описание для Distutils тоже немного сложнее. В отличие от чистых модулей, недостаточно просто перечислить модули или пакеты и ожидать, что Distutils сам найдёт нужные файлы; необходимо указать имя расширения, исходные файлы и любые требования к компиляции/компоновке (каталоги включения, библиотеки для компоновки и т.д.).

Все это делается через ещё один именованный аргумент для `setup()` – опцию `ext_modules`. `ext_modules` – это просто список экземпляров [`Extension`](https://python-all.ru/3.6/distutils/apiref.html#distutils.core.Extension), каждый из которых описывает один модуль расширения. Предположим, ваш дистрибутив содержит одно расширение с именем `foo`, реализованное в `foo.c`. Если не требуется дополнительных указаний компилятору/компоновщику, описание этого расширения довольно просто:

```python
Extension('foo', ['foo.c'])
```

Класс `Extension` можно импортировать из [`distutils.core`](https://python-all.ru/3.6/distutils/apiref.html#module-distutils.core) вместе с `setup()`. Таким образом, сценарий setup для дистрибутива модуля, содержащего только это одно расширение и ничего больше, может выглядеть так:

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

Класс `Extension` (точнее, лежащий в основе механизм сборки расширений, реализованный командой **build\_ext**) обеспечивает большую гибкость при описании расширений Python, что объясняется в следующих разделах.

### 2.3.1. Имена расширений и пакеты

Первым аргументом конструктора [`Extension`](https://python-all.ru/3.6/distutils/apiref.html#distutils.core.Extension) всегда является имя расширения, включая любые имена пакетов. Например,

```python
Extension('foo', ['src/foo1.c', 'src/foo2.c'])
```

описывает расширение, находящееся в корневом пакете, тогда как

```python
Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])
```

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

Если у вас есть несколько расширений в одном пакете (или все под одним базовым пакетом), используйте именованный аргумент `ext_package` для `setup()`. Например,

```python
setup(...,
      ext_package='pkg',
      ext_modules=[Extension('foo', ['foo.c']),
                   Extension('subpkg.bar', ['bar.c'])],
     )
```

скомпилирует `foo.c` в расширение `pkg.foo`, а `bar.c` – в `pkg.subpkg.bar`.

### 2.3.2. Исходные файлы расширения

Второй аргумент конструктора [`Extension`](https://python-all.ru/3.6/distutils/apiref.html#distutils.core.Extension) – это список исходных файлов. Поскольку Distutils в настоящее время поддерживает только расширения на C, C++ и Objective-C, это обычно исходные файлы на C/C++/Objective-C. (Не забудьте использовать соответствующие расширения для файлов C++: `.cc` и `.cpp`, похоже, распознаются компиляторами как Unix, так и Windows.)

Однако вы также можете включить в список файлы интерфейсов SWIG (`.i`); команда **build\_ext** умеет работать с расширениями SWIG: она запускает SWIG для файла интерфейса и компилирует полученный C/C++ файл в ваше расширение.

Несмотря на это предупреждение, опции для SWIG в настоящее время можно передавать так:

```python
setup(...,
      ext_modules=[Extension('_foo', ['foo.i'],
                             swig_opts=['-modern', '-I../include'])],
      py_modules=['foo'],
     )
```

Или в командной строке так:

```python
> python setup.py build_ext --swig-opts="-modern -I../include"
```

На некоторых платформах можно включать неисходные файлы, которые обрабатываются компилятором и включаются в ваше расширение. В настоящее время это относится только к файлам текстов сообщений Windows (`.mc`) и файлам определения ресурсов (`.rc`) для Visual C++. Они будут скомпилированы в двоичные файлы ресурсов (`.res`) и слинкованы в исполняемый файл.

### 2.3.3. Опции препроцессора

Три необязательных аргумента для [`Extension`](https://python-all.ru/3.6/distutils/apiref.html#distutils.core.Extension) помогут, если нужно указать каталоги включения для поиска или макросы препроцессора для определения/отмены: `include_dirs`, `define_macros` и `undef_macros`.

Например, если вашему расширению требуются заголовочные файлы в каталоге `include` в корне вашего дистрибутива, используйте опцию `include_dirs`:

```python
Extension('foo', ['foo.c'], include_dirs=['include'])
```

Там можно указать абсолютные пути; если вы знаете, что ваше расширение будет собираться только на Unix-системах с X11R6, установленным в `/usr`, можно обойтись

```python
Extension('foo', ['foo.c'], include_dirs=['/usr/include/X11'])
```

Следует избегать такого непереносимого использования, если вы планируете распространять свой код: вероятно, лучше написать код на C так:

```python
#include <X11/Xlib.h>
```

Если вам нужно включить заголовочные файлы из другого расширения Python, можно воспользоваться тем, что заголовочные файлы устанавливаются согласованным образом командой Distutils **install\_headers**. Например, заголовочные файлы Numerical Python устанавливаются (в стандартной установке Unix) в `/usr/local/include/python1.5/Numerical`. (Точное расположение будет различаться в зависимости от платформы и установки Python.) Поскольку каталог включения Python – `/usr/local/include/python1.5` в данном случае – всегда включён в путь поиска при сборке расширений Python, лучший подход – написать код на C так:

```python
#include <Numerical/arrayobject.h>
```

Если вам всё же нужно поместить каталог включения `Numerical` непосредственно в путь поиска заголовков, вы можете найти этот каталог с помощью модуля Distutils [`distutils.sysconfig`](https://python-all.ru/3.6/distutils/apiref.html#module-distutils.sysconfig):

```python
from distutils.sysconfig import get_python_inc
incdir = os.path.join(get_python_inc(plat_specific=1), 'Numerical')
setup(...,
      Extension(..., include_dirs=[incdir]),
      )
```

Несмотря на то, что это вполне переносимо – будет работать в любой установке Python, независимо от платформы, – вероятно, проще просто написать код на C разумным способом.

Можно определять и отменять макросы препроцессора с помощью опций `define_macros` и `undef_macros`. `define_macros` принимает список кортежей `(name, value)`, где `name` – имя определяемого макроса (строка), а `value` – его значение: строка или `None`. (Определение макроса `FOO` как `None` эквивалентно простому `#define FOO` в исходном коде C: в большинстве компиляторов это устанавливает `FOO` в строку `1`.) `undef_macros` – это просто список макросов для отмены.

Например:

```python
Extension(...,
          define_macros=[('NDEBUG', '1'),
                         ('HAVE_STRFTIME', None)],
          undef_macros=['HAVE_FOO', 'HAVE_BAR'])
```

is the equivalent of having this at the top of every C source file:

```python
#define NDEBUG 1
#define HAVE_STRFTIME
#undef HAVE_FOO
#undef HAVE_BAR
```

### 2.3.4. Библиотечные опции

Также можно указать библиотеки, с которыми нужно компоновать расширение при сборке, и каталоги для поиска этих библиотек. Опция `libraries` задаёт список библиотек для компоновки, `library_dirs` – список каталогов для поиска библиотек на этапе компоновки, а `runtime_library_dirs` – список каталогов для поиска общих (динамически загружаемых) библиотек во время выполнения.

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

```python
Extension(...,
          libraries=['gdbm', 'readline'])
```

Если нужно скомпоновать расширение с библиотеками в нестандартном расположении, необходимо указать это расположение в `library_dirs`:

```python
Extension(...,
          library_dirs=['/usr/X11R6/lib'],
          libraries=['X11', 'Xt'])
```

(Повторим: подобные непереносимые конструкции следует избегать, если планируется распространять свой код.)

### 2.3.5. Прочие опции

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

Опция `optional` является логической; если она равна true, сбой сборки расширения не прервёт процесс сборки, а просто не приведёт к установке ошибочного расширения.

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

`extra_compile_args` и `extra_link_args` можно использовать для указания дополнительных параметров командной строки для компилятора и компоновщика соответственно.

`export_symbols` применим только в Windows. Он может содержать список символов (функций или переменных), подлежащих экспорту. Эта опция не нужна при сборке скомпилированных расширений: Distutils автоматически добавит `initmodule` в список экспортируемых символов.

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

## 2.4. Взаимосвязи между дистрибутивами и пакетами

Дистрибутив может относиться к пакетам тремя определёнными способами:

1. Он может требовать пакеты или модули.
2. Он может предоставлять пакеты или модули.
3. Он может заменять (делать устаревшими) пакеты или модули.

Эти взаимосвязи можно задать с помощью именованных аргументов функции [`distutils.core.setup()`](https://python-all.ru/3.6/distutils/apiref.html#distutils.core.setup).

Зависимости от других модулей и пакетов Python можно указать, передав именованный аргумент *requires* функции `setup()`. Значение должно быть списком строк. Каждая строка задаёт необходимый пакет и, опционально, какие версии достаточны.

Чтобы указать, что требуется любая версия модуля или пакета, строка должна состоять только из имени модуля или пакета. Примеры: `'mymodule'` и `'xml.parsers.expat'`.

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

```python
<    >    ==
<=   >=   !=
```

Их можно комбинировать, используя несколько квалификаторов, разделённых запятыми (и необязательными пробелами). В этом случае все квалификаторы должны совпадать; используется логическое И для объединения результатов.

Рассмотрим несколько примеров:

| Выражение Requires | Пояснение |
| --- | --- |
| `==1.0` | Совместима только версия `1.0` |
| `>1.0, !=1.5.1, <2.0` | Любая версия после `1.0` и до `2.0` совместима, за исключением `1.5.1` |

Теперь, когда мы умеем указывать зависимости, нам также нужно уметь указывать, что мы предоставляем и что могут требовать другие дистрибутивы. Это делается с помощью именованного аргумента *provides* функции `setup()`. Значение этого аргумента – список строк, каждая из которых указывает имя модуля или пакета и, опционально, версию. Если версия не указана, считается, что она совпадает с версией дистрибутива.

Несколько примеров:

| Выражение Provides | Пояснение |
| --- | --- |
| `mypkg` | Предоставить `mypkg`, используя версию дистрибутива |
| `mypkg (1.1)` | Предоставить `mypkg` версии 1.1, независимо от версии дистрибутива |

Пакет может объявлять, что он заменяет (делает устаревшими) другие пакеты, с помощью именованного аргумента *obsoletes*. Его значение аналогично значению аргумента *requires*: список строк, задающих спецификаторы модуля или пакета. Каждый спецификатор состоит из имени модуля или пакета, за которым необязательно следуют один или несколько квалификаторов версии. Версионные квалификаторы указываются в скобках после имени модуля или пакета.

Версии, указанные квалификаторами, считаются заменяемыми (устаревшими) данным дистрибутивом. Если квалификаторы не указаны, считаются устаревшими все версии указанного модуля или пакета.

## 2.5. Установка скриптов

До сих пор мы имели дело с чистыми и нечистыми модулями Python, которые обычно не запускаются сами по себе, а импортируются скриптами.

Скрипты – это файлы, содержащие исходный код Python, предназначенные для запуска из командной строки. Скрипты не требуют от Distutils каких-либо сложных действий. Единственная умная особенность: если первая строка скрипта начинается с `#!` и содержит слово «python», Distutils изменит первую строку так, чтобы она указывала на текущий интерпретатор. По умолчанию она заменяется на путь к текущему интерпретатору. Опция `--executable` (или `-e`) позволяет явно переопределить путь к интерпретатору.

Опция `scripts` – это просто список файлов, которые обрабатываются таким образом. Из скрипта установки PyXML:

```python
setup(...,
      scripts=['scripts/xmlproc_parse', 'scripts/xmlproc_val']
      )
```

Изменено в версии 3.1: Все скрипты также будут добавлены в файл `MANIFEST`, если не предоставлен шаблон. См. [Указание файлов для распространения](https://python-all.ru/3.6/distutils/sourcedist.html#manifest).

## 2.6. Установка данных пакета

Часто в пакет нужно установить дополнительные файлы. Эти файлы – обычно данные, тесно связанные с реализацией пакета, или текстовые файлы с документацией, которая может быть полезна программистам, использующим пакет. Такие файлы называются *данными пакета*.

Данные пакета можно добавить в пакеты с помощью ключевого аргумента `package_data` функции `setup()`. Значением должно быть отображение имени пакета на список относительных путей, которые нужно скопировать в пакет. Пути интерпретируются относительно каталога, содержащего пакет (при необходимости используется информация из отображения `package_dir`); то есть ожидается, что файлы являются частью пакета в исходных каталогах. Они также могут содержать glob-шаблоны.

Имена путей могут содержать части каталогов; все необходимые каталоги будут созданы при установке.

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

```python
setup.py
src/
    mypkg/
        __init__.py
        module.py
        data/
            tables.dat
            spoons.dat
            forks.dat
```

Соответствующий вызов `setup()` может быть таким:

```python
setup(...,
      packages=['mypkg'],
      package_dir={'mypkg': 'src/mypkg'},
      package_data={'mypkg': ['data/*.dat']},
      )
```

Изменено в версии 3.1: Все файлы, соответствующие `package_data`, будут добавлены в файл `MANIFEST`, если не предоставлен шаблон. См. [Указание файлов для распространения](https://python-all.ru/3.6/distutils/sourcedist.html#manifest).

## 2.7. Установка дополнительных файлов

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

`data_files` задаёт последовательность пар (*каталог*, *файлы*) следующим образом:

```python
setup(...,
      data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
                  ('config', ['cfg/data.cfg']),
                  ('/etc/init.d', ['init-script'])]
     )
```

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

Каждая пара (*каталог*, *файлы*) в последовательности задаёт каталог установки и файлы для установки туда. Если *каталог* указан относительным путём, он интерпретируется относительно префикса установки (`sys.prefix` для пакетов на чистом Python, `sys.exec_prefix` для пакетов, содержащих модули расширения). Каждое имя файла из *файлы* интерпретируется относительно `setup.py`-скрипта, находящегося в корне дистрибутива исходного кода пакета. Информация о каталоге из *файлы* не используется для определения конечного положения устанавливаемого файла; используется только имя файла.

Можно указать опцию `data_files` как простую последовательность файлов без указания целевого каталога, но это не рекомендуется, и команда **install** выведет предупреждение. Чтобы установить файлы данных непосредственно в целевой каталог, в качестве каталога нужно задать пустую строку.

Изменено в версии 3.1: Все файлы, соответствующие `data_files`, будут добавлены в файл `MANIFEST`, если не предоставлен шаблон. См. [Указание файлов для распространения](https://python-all.ru/3.6/distutils/sourcedist.html#manifest).

## 2.8. Дополнительные метаданные

Скрипт setup может включать дополнительные метаданные помимо имени и версии. Эта информация включает:

| Метаданные | Описание | Значение | Примечания |
| --- | --- | --- | --- |
| `name` | имя пакета | короткая строка | (1) |
| `version` | версия этого релиза | короткая строка | (1)(2) |
| `author` | имя автора пакета | короткая строка | (3) |
| `author_email` | адрес электронной почты автора пакета | адрес электронной почты | (3) |
| `maintainer` | имя сопровождающего пакета | короткая строка | (3) |
| `maintainer_email` | адрес электронной почты сопровождающего пакет | адрес электронной почты | (3) |
| `url` | домашняя страница пакета | URL | (1) |
| `description` | краткое описание пакета | короткая строка |  |
| `long_description` | подробное описание пакета | длинная строка | (5) |
| `download_url` | расположение, откуда можно загрузить пакет | URL | (4) |
| `classifiers` | список классификаторов | список строк | (4) |
| `platforms` | список платформ | список строк |  |
| `license` | лицензия для пакета | короткая строка | (6) |

Примечания:

1. Эти поля обязательны.
2. Рекомендуется, чтобы версии имели формат *major.minor\[.patch\[.sub\]\]*.
3. Должен быть указан либо автор, либо сопровождающий. Если указан сопровождающий, distutils указывает его как автора в `PKG-INFO`.
4. Эти поля не следует использовать, если пакет должен быть совместим с версиями Python до 2.2.3 или 2.3. Список доступен на [сайте PyPI](https://python-all.ru/3.6/distutils/setupscript.html).
5. Поле `long_description` используется PyPI при [регистрации](https://python-all.ru/3.6/distutils/packageindex.html#package-register) пакета, чтобы [создать его домашнюю страницу](https://python-all.ru/3.6/distutils/packageindex.html#package-display).
6. Поле `license` – это текст, указывающий лицензию, покрывающую пакет, если лицензия не выбрана из классификаторов Trove «License». См. поле `Classifier`. Обратите внимание, что существует опция распространения `licence`, которая устарела, но всё ещё является псевдонимом для `license`.

**«короткая строка»**

Одна строка текста, не более 200 символов.

**«длинная строка»**

Несколько строк обычного текста в формате reStructuredText (см. [http://docutils.sourceforge.net/](https://python-all.ru/3.6/distutils/setupscript.html)).

**«список строк»**

См. ниже.

Кодирование информации о версии – само по себе искусство. Пакеты Python обычно придерживаются формата версии *major.minor\[.patch\]\[sub\]*. Основной номер (major) равен 0 для начальных экспериментальных выпусков ПО. Он увеличивается для выпусков, представляющих собой важные вехи в пакете. Дополнительный номер (minor) увеличивается, когда в пакет добавляются важные новые функции. Номер патча увеличивается при выпусках с исправлением ошибок. Иногда дополнительная информация о версии в конце используется для обозначения подвыпусков. Это «a1,a2,…,aN» (для альфа-версий, где функциональность и API могут меняться), «b1,b2,…,bN» (для бета-версий, которые только исправляют ошибки) и «pr1,pr2,…,prN» (для финального тестирования перед релизом). Некоторые примеры:

**0.1.0**

первый экспериментальный выпуск пакета

**1.0.1a2**

второй альфа-выпуск первой патч-версии 1.0

`classifiers` задаются в списке Python:

```python
setup(...,
      classifiers=[
          'Development Status :: 4 - Beta',
          'Environment :: Console',
          'Environment :: Web Environment',
          'Intended Audience :: End Users/Desktop',
          'Intended Audience :: Developers',
          'Intended Audience :: System Administrators',
          'License :: OSI Approved :: Python Software Foundation License',
          'Operating System :: MacOS :: MacOS X',
          'Operating System :: Microsoft :: Windows',
          'Operating System :: POSIX',
          'Programming Language :: Python',
          'Topic :: Communications :: Email',
          'Topic :: Office/Business',
          'Topic :: Software Development :: Bug Tracking',
          ],
      )
```

## 2.9. Отладка скрипта установки

Иногда что-то идет не так, и скрипт установки делает не то, что хочет разработчик.

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

С другой стороны, это не помогает разработчику найти причину сбоя. Для этой цели переменная окружения `DISTUTILS_DEBUG` может быть установлена в любое значение, кроме пустой строки, и distutils теперь будет выводить подробную информацию о том, что он делает, выводить полную трассировку при возникновении исключения и выводить всю командную строку при сбое внешней программы (например, компилятора C).
