Написание скрипта установки
Примечание
Этот документ сохраняется только до тех пор, пока документация setuptools в https://setuptools.readthedocs.io/en/latest/setuptools.html самостоятельно не охватывает всю релевантную информацию, которая в настоящее время включена здесь.
Скрипт установки является центром всей деятельности по созданию, распространению и установке модулей с использованием Distutils. Основное назначение скрипта установки — описать распределение вашего модуля для Distutils, чтобы различные команды, работающие с вашими модулями, действовали правильно. Как мы видели в разделе Простой пример выше, скрипт установки состоит главным образом из вызова setup(), и большая часть информации, предоставляемой разработчику модуля Distutils, предоставляется в качестве ключевых аргументов для setup().
Вот несколько более сложный пример, за которым мы будем следить в ближайших разделах: собственный скрипт установки Distutils. (Помните, что хотя Distutils включены в Python 1.6 и более поздних версиях, они также существуют независимо, чтобы пользователи Python 1.5.2 могли использовать их для установки других распределений модулей. Собственный скрипт установки Distutils, показанный здесь, используется для установки пакета в Python 1.5.2.)
#!/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'],
)
Существует всего два отличия между этим и тривиальным распределением одного файла, представленным в разделе Простой пример: больше метаданных и указание чистых модулей Python по пакетам, а не по модулям. Это важно, так как Distutils состоит из пары десятков модулей, разделенных (на данный момент) на два пакета; явный список каждого модуля был бы утомительным для генерации и сложным для поддержки. Дополнительную информацию о дополнительных метаданных см. в разделе Дополнительные метаданные.
Обратите внимание, что все имена файлов и каталогов (файлы или каталоги), указанные в скрипте установки, должны быть записаны с использованием соглашения Unix, т. е. с разделителями «/». Distutils позаботится о преобразовании этого платформонезависимого представления в то, что подходит для вашей текущей платформы, перед фактическим использованием имени файла. Это делает ваш скрипт установки переносимым между операционными системами, что, конечно, является одной из основных целей Distutils. В этом духе все имена файлов в этом документе разделяются с помощью символа «/».
Конечно, это относится только к именам файлов, передаваемым функциям Distutils. Если вы, например, используете стандартные функции Python, такие как glob.glob() или os.listdir() для указания файлов, вам следует быть внимательным, чтобы написать переносимый код вместо жёсткой кодировки разделителей путей:
glob.glob(os.path.join('mydir', 'subdir', '*.html'))
os.listdir(os.path.join('mydir', 'subdir'))
2.1. Перечисление целых пакетов
Опция packages сообщает Distutils обработать (сгенерировать, распространить, установить и т. д.) все чистые модули Python, найденные в каждом пакете, упомянутом в списке packages. Для этого, конечно, должна быть взаимосвязь между именами пакетов и каталогами в файловой системе. По умолчанию соответствие является наиболее очевидным, т. е. пакет distutils находится в каталоге distutils относительно корня распределения. Таким образом, когда вы говорите packages = ['foo'] в вашем скрипте установки, вы обещаете, что Distutils найдёт файл foo/__init__.py (который может быть написан по-другому в вашей системе, но вы понимаете идею) относительно каталога, в котором находится ваш скрипт установки. Если вы нарушите это обещание, Distutils выдаст предупреждение, но всё равно обработает неверный пакет.
Если вы используете другое соглашение для организации своего каталога исходных файлов, это не проблема: вам просто нужно указать опцию package_dir для уведомления Distutils о вашем соглашении. Например, предположим, что вы храните весь исходный код Python в lib, так что модули в «корневом пакете» (т. е. не в любом пакете) находятся в lib, модули в пакете foo находятся в lib/foo, и так далее. Тогда вы бы поместили
package_dir = {'': 'lib'}
в свой скрипт установки. Ключами этого словаря являются имена пакетов, а пустое имя пакета обозначает корневой пакет. Значения — это имена каталогов относительно корня вашего распределения. В данном случае, когда вы говорите packages =
['foo'], вы обещаете, что файл lib/foo/__init__.py существует.
Другое возможное соглашение — поместить пакет foo прямо в lib, пакет foo.bar в lib/bar и т. д. Это было бы записано в скрипте установки как
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. Перечисление отдельных модулей
Для небольшого распределения модулей вы можете предпочесть перечислить все модули вместо перечисления пакетов, особенно в случае одного модуля, который помещается в «корневой пакет» (т. е. без пакета вообще). Этот самый простой случай был показан в разделе Простой пример; вот немного более сложный пример:
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, каждый из которых описывает отдельный модуль расширения. Предположим, что ваш дистрибутив включает один модуль расширения, называемый foo и реализованный с помощью foo.c. Если дополнительные инструкции для компилятора/линкера не нужны, описание этого расширения довольно простое:
Extension('foo', ['foo.c'])
Класс Extension можно импортировать из distutils.core вместе с setup(). Таким образом, скрипт настройки для дистрибутива модулей, содержащего только это одно расширение и ничего больше, может быть таким:
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 всегда — имя расширения, включая любые имена пакетов. Например,
Extension('foo', ['src/foo1.c', 'src/foo2.c'])
описывает расширение, которое находится в корневом пакете, в то время как
Extension('pkg.foo', ['src/foo1.c', 'src/foo2.c'])
описывает то же расширение в пакете pkg. Исходные файлы и результирующий объектный код идентичны в обоих случаях; единственное различие заключается в том, где в файловой системе (и, следовательно, где в иерархии пространства имен Python) находится результирующее расширение.
Если у вас есть несколько расширений, все в одном пакете (или все под одним базовым пакетом), используйте ключевой аргумент ext_package для setup(). Например,
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 — список исходных файлов. Поскольку Distutils в настоящее время поддерживают только расширения C, C++ и Objective-C, это обычно файлы исходных кодов C/C++/Objective-C. (Убедитесь, что вы используете соответствующие расширения для различения файлов исходного кода C++; .cc и .cpp, похоже, распознаются компиляторами как Unix, так и Windows.)
Однако, вы также можете включить в список файлы интерфейса SWIG (.i); команда build_ext знает, как работать с расширениями SWIG: она запустит SWIG на файле интерфейса и скомпилирует полученный файл C/C++ в ваше расширение.
Несмотря на это предупреждение, опции для SWIG могут в настоящее время передаваться так:
setup(...,
ext_modules=[Extension('_foo', ['foo.i'],
swig_opts=['-modern', '-I../include'])],
py_modules=['foo'],
)
Или в командной строке так:
> python setup.py build_ext --swig-opts="-modern -I../include"
На некоторых платформах вы можете включать не исходные файлы, которые обрабатываются компилятором и включаются в ваше расширение. В настоящее время это просто означает текстовые файлы сообщений Windows (.mc) и файлы определения ресурсов (.rc) для Visual C++. Они будут скомпилированы в двоичные файлы ресурсов (.res) и связаны с исполняемым файлом.
2.3.3. Опции препроцессора
Три необязательных аргумента к Extension помогут, если вам нужно указать каталоги для поиска заголовочных файлов или макросы препроцессора для определения/отмены определения: include_dirs, define_macros, и undef_macros.
Например, если ваше расширение требует заголовочных файлов в каталоге include в корне вашем дистрибутива, используйте опцию include_dirs:
Extension('foo', ['foo.c'], include_dirs=['include'])
Вы можете указать абсолютные каталоги; если вы знаете, что ваше расширение будет построено только на системах Unix с установленным X11R6 в /usr, вы можете обойтись
Extension('foo', ['foo.c'], include_dirs=['/usr/include/X11'])
Следует избегать использования такого непереносимого кода, если вы планируете распространять свой код: вероятно, лучше написать код на C, например
#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, например
#include <Numerical/arrayobject.h>
Если вы должны поместить каталог включения Numerical прямо в свой путь поиска заголовков, вы можете найти этот каталог, используя модуль Distutils distutils.sysconfig:
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 — это просто список макросов для отмены определения.
Например:
Extension(...,
define_macros=[('NDEBUG', '1'),
('HAVE_STRFTIME', None)],
undef_macros=['HAVE_FOO', 'HAVE_BAR'])
эквивалентно наличию этого в начале каждого файла исходного кода C:
#define NDEBUG 1 #define HAVE_STRFTIME #undef HAVE_FOO #undef HAVE_BAR
2.3.4. Опции библиотек
Вы также можете указать библиотеки, с которыми необходимо связать ваше расширение при построении, и каталоги для поиска этих библиотек. Опция libraries — список библиотек для связывания, library_dirs — список каталогов для поиска библиотек во время связывания, а runtime_library_dirs — список каталогов для поиска общих (динамически загружаемых) библиотек во время выполнения.
Например, если вам нужно связаться с библиотеками, которые, как известно, находятся в стандартном пути поиска библиотек на целевых системах
Extension(...,
libraries=['gdbm', 'readline'])
Если вам нужно связаться с библиотеками в нестандартном расположении, вам нужно будет включить расположение в library_dirs:
Extension(...,
library_dirs=['/usr/X11R6/lib'],
libraries=['X11', 'Xt'])
(Опять же, этот тип непереносимого конструкта следует избегать, если вы планируете распространять свой код.)
2.3.5. Другие опции
Остались ещё некоторые опции, которые можно использовать для обработки специальных случаев.
Опция optional — булево значение; если оно истинно, ошибка сборки в расширении не прервёт процесс сборки, а вместо этого просто не установит сбоя расширение.
Опция extra_objects — список объектных файлов, которые будут переданы компоновщику. Эти файлы не должны иметь расширений, так как используется стандартное расширение для компилятора.
extra_compile_args и extra_link_args можно использовать для указания дополнительных опций командной строки для командных строк соответствующих компилятора и компоновщика.
export_symbols полезна только на Windows. Она может содержать список символов (функций или переменных), которые нужно экспортировать. Эта опция не нужна при построении скомпилированных расширений: Distutils автоматически добавит initmodule в список экспортируемых символов.
Опция depends — список файлов, от которых зависит расширение (например, заголовочные файлы). Команда сборки вызовет компилятор по исходным файлам для перестройки расширения, если какой-либо из этих файлов был изменён после предыдущей сборки.
2.4. Взаимосвязи между дистрибутивами и пакетами
Дистрибутив может быть связан с пакетами тремя конкретными способами:
- Он может требовать пакеты или модули.
- Он может предоставлять пакеты или модули.
- Он может устаревать пакеты или модули.
Эти взаимосвязи могут быть указаны с использованием ключевых аргументов функции distutils.core.setup().
Зависимости от других Python-модулей и пакетов могут быть указаны, передав ключевой аргумент requires функции setup(). Значение должно быть списком строк. Каждая строка указывает пакет, который требуется, и необязательно, какие версии достаточны.
Чтобы указать, что требуется любая версия модуля или пакета, строка должна состоять только из имени модуля или пакета. Примеры включают 'mymodule' и 'xml.parsers.expat'.
Если требуются определенные версии, можно указать последовательность квалификаторов в скобках. Каждый квалификатор может состоять из оператора сравнения и номера версии. Допустимые операторы сравнения:
< > == <= >= !=
Их можно комбинировать, используя несколько квалификаторов, разделенных запятыми (и необязательными пробелами). В этом случае должны быть удовлетворены все квалификаторы; для объединения оценок используется логическое И.
Давайте рассмотрим несколько примеров:
Выражение Requires | Описание |
|---|---|
| Только версия |
| Любая версия после |
Теперь, когда мы можем указывать зависимости, нам также необходимо указать, что мы предоставляем, чтобы другие дистрибутивы могли потребовать. Это делается с помощью ключевого аргумента provides функции setup(). Значение этого ключевого аргумента — список строк, каждая из которых называет Python-модуль или пакет и, необязательно, идентифицирует версию. Если версия не указана, предполагается, что она соответствует версии дистрибутива.
Некоторые примеры:
Выражение Provides | Описание |
|---|---|
| Предоставляем |
| Предоставляем |
Пакет может объявить, что он устаревает другие пакеты, используя ключевой аргумент obsoletes. Значение для этого аналогично значению ключевого аргумента requires: список строк, задающих спецификаторы модулей или пакетов. Каждый спецификатор состоит из имени модуля или пакета, за которым необязательно следует один или несколько квалификаторов версии. Квалификаторы версии даются в скобках после имени модуля или пакета.
Версии, определенные квалификаторами, являются теми, которые устарели дистрибутивом, который описывается. Если квалификаторы не указаны, понимается, что все версии указанного модуля или пакета устарели.
2.5. Установка скриптов
До сих пор мы работали с чистыми и нечистыми Python-модулями, которые обычно не выполняются сами по себе, а импортируются скриптами.
Скрипты — это файлы, содержащие исходный код Python, предназначенные для запуска из командной строки. Для работы со скриптами Distutils не требуется ничего сложного. Единственная интересная особенность заключается в том, что если первая строка скрипта начинается с #! и содержит слово «python», Distutils скорректирует первую строку, чтобы она ссылалась на текущее местоположение интерпретатора. По умолчанию она заменяется на текущее местоположение интерпретатора. Параметр --executable (или -e) позволит явно переопределить путь к интерпретатору.
Параметр scripts просто представляет собой список файлов, которые должны обрабатываться таким образом. Из скрипта настройки PyXML:
setup(...,
scripts=['scripts/xmlproc_parse', 'scripts/xmlproc_val']
)
Изменено в версии 3.1: Все скрипты также будут добавлены в файл MANIFEST если шаблон не предоставлен. См. Указание файлов для распространения.
2.6. Установка данных пакета
Часто необходимо устанавливать дополнительные файлы в пакет. Эти файлы часто представляют собой данные, тесно связанные с реализацией пакета, или текстовые файлы, содержащие документацию, которая может быть интересна программистам, использующим пакет. Эти файлы называются данными пакета.
Данные пакета могут быть добавлены в пакеты с помощью ключевого аргумента package_data функции setup(). Значение должно быть отображением имени пакета на список имен относительных путей, которые должны быть скопированы в пакет. Пути интерпретируются относительно каталога, содержащего пакет (информация из отображения package_dir используется при необходимости); то есть ожидается, что файлы будут частью пакета в исходных каталогах. Они также могут содержать шаблоны glob.
Имена путей могут содержать части каталогов; все необходимые каталоги будут созданы при установке.
Например, если пакет должен содержать подкаталог с несколькими файлами данных, файлы могут быть организованы следующим образом в дереве исходных файлов:
setup.py
src/
mypkg/
__init__.py
module.py
data/
tables.dat
spoons.dat
forks.dat
Соответствующий вызов функции setup() может быть:
setup(...,
packages=['mypkg'],
package_dir={'mypkg': 'src/mypkg'},
package_data={'mypkg': ['data/*.dat']},
)
Изменено в версии 3.1: Все файлы, которые соответствуют package_data будут добавлены в файл MANIFEST если шаблон не предоставлен. См. Указание файлов для распространения.
2.7. Установка дополнительных файлов
Параметр data_files можно использовать для указания дополнительных файлов, необходимых для дистрибутива модуля: конфигурационные файлы, каталоги сообщений, файлы данных, все, что не попадает в предыдущие категории.
data_files задает последовательность пар (каталог, файлы) следующим образом:
setup(...,
data_files=[('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
('config', ['cfg/data.cfg'])],
)
Каждая пара (каталог, файлы) в последовательности указывает каталог установки и файлы для установки там.
Каждое имя файла в файлах интерпретируется относительно файла setup.py вверху дистрибутива исходных файлов пакета. Обратите внимание, что вы можете указать каталог, где будут установлены файлы данных, но вы не можете переименовывать сами файлы данных.
Каталог должен быть относительным путем. Он интерпретируется относительно префикса установки (Python's sys.prefix для системных установок; site.USER_BASE для пользовательских установок). Distutils допускает, чтобы каталог был абсолютным путем установки, но это не рекомендуется, так как это несовместимо с форматом упаковки wheel. Информация о каталоге из файлов не используется для определения конечного расположения установленного файла; используется только имя файла.
Можно указать параметры data_files как простой последовательностью файлов без указания целевого каталога, но это не рекомендуется, и команда install будет выводить предупреждение в этом случае. Чтобы установить файлы данных непосредственно в целевой каталог, в качестве каталога следует указать пустую строку.
Изменено в версии 3.1: Все файлы, которые соответствуют data_files будут добавлены в файл MANIFEST если шаблон не предоставлен. См. Указание файлов для распространения.
2.8. Дополнительные метаданные
Скрипт настройки может содержать дополнительные метаданные помимо имени и версии. Эта информация включает:
Метаданные | Описание | Значение | Примечания |
|---|---|---|---|
| имя пакета | короткая строка | (1) |
| версия данного релиза | короткая строка | (1)(2) |
| имя автора пакета | короткая строка | (3) |
| электронный адрес автора пакета | электронный адрес | (3) |
| имя системного администратора пакета | короткая строка | (3) |
| электронный адрес системного администратора пакета | электронный адрес | (3) |
| домашняя страница пакета | URL | (1) |
| краткое описание пакета | короткая строка | |
| более подробное описание пакета | длинная строка | (4) |
| место, где можно загрузить пакет | URL | |
| список классификаторов | список строк | (6)(7) |
| список платформ | список строк | (6)(8) |
| список ключевых слов | список строк | (6)(8) |
| лицензия пакета | короткая строка | (5) |
Примечания:
- Эти поля обязательны.
- Рекомендуется, чтобы версии имели формат major.minor[.patch[.sub]].
- Должен быть указан либо автор, либо системный администратор. Если указан системный администратор, distutils отображает его как автора в
PKG-INFO. - Поле
long_descriptionиспользуется PyPI при публикации пакета для создания страницы проекта. - Поле
license— текст, указывающий лицензию, покрывающую пакет, если лицензия не выбрана из классификаторов Трове «Лицензия». См. полеClassifier. Обратите внимание, что существует устаревшее, но всё ещё действующее как псевдоним дляlicense, распределениеlicence. - Это поле должно быть списком.
- Список допустимых классификаторов представлен на PyPI.
- Для обеспечения обратной совместимости это поле также принимает строку. Если вы передаёте строку, разделённую запятыми
'foo, bar', она будет преобразована в['foo', 'bar']. В противном случае она будет преобразована в список из одной строки.
- ‘короткая строка’
-
Одна строка текста, не более 200 символов.
- ‘длинная строка’
-
Несколько строк простого текста в формате reStructuredText (см. http://docutils.sourceforge.net/).
- ‘список строк’
-
См. ниже.
Кодирование информации о версии — это искусство само по себе. Пакеты Python обычно следуют формату версии major.minor[.patch][sub]. Главный номер равен 0 для первоначальных, экспериментальных релизов программного обеспечения. Он увеличивается для релизов, которые представляют собой основные этапы в пакете. Вспомогательный номер увеличивается, когда в пакет добавляются важные новые функции. Номер исправления увеличивается при выпуске исправлений ошибок. Дополнительная информация о версии иногда используется для обозначения дополнительных релизов. Это «a1,a2,…,aN» (для альфа-релизов, где функциональность и API могут измениться), «b1,b2,…,bN» (для бета-релизов, которые исправляют только ошибки) и «pr1,pr2,…,prN» (для окончательных релизов дорелизного тестирования). Некоторые примеры:
- 0.1.0
-
первый, экспериментальный релиз пакета
- 1.0.1a2
-
второй альфа-релиз первой исправляющей версии 1.0
classifiers должно быть указано в списке:
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',
],
)
Изменено в версии 3.7: setup теперь выдает предупреждение, когда classifiers, keywords или platforms поля не указаны как список или строка.
2.9. Отладка скрипта настройки
Иногда что-то идет не так, и скрипт настройки не делает того, что хочет разработчик.
Distutils перехватывает любые исключения при выполнении скрипта настройки и выводит простое сообщение об ошибке перед завершением скрипта. Цель этого поведения — не вводить в заблуждение администраторов, которые мало знают о Python и пытаются установить пакет. Если они получат длинный трассировку стека из глубины Distutils, они могут подумать, что пакет или установка Python сломаны, потому что они не дочитают до конца и не увидят, что это проблема с правами доступа.
С другой стороны, это не помогает разработчику найти причину сбоя. В этих целях переменная среды DISTUTILS_DEBUG может быть установлена на любое значение, кроме пустой строки, и distutils теперь будет выводить подробную информацию о выполняемых действиях, выводить полную трассировку стека при возникновении исключения и выводить всю командную строку при сбое внешней программы (например, компилятора C).
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/distutils/setupscript.html