Написание скрипта настройки
Примечание
Этот документ сохраняется только до тех пор, пока документация 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 подразумевает его применение ко всем пакетам ниже пакета package, поэтому случай 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 позволяет задавать каталог как абсолютный путь установки, но это не рекомендуется, так как это несовместимо с форматом упаковки колес. Никакая информация о каталоге из файлов не используется для определения конечного расположения устанавливаемого файла; используется только имя файла.
Вы можете указать опции 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) |
Примечания:
- Эти поля являются обязательными.
- Рекомендуется, чтобы версии имели вид главная.вторая[.поправочная[.дополнительная]].
- Должен быть указан либо автор, либо системный администратор. Если указан системный администратор, distutils отображает его как автора в
PKG-INFO. - Поле
long_descriptionиспользуется PyPI при публикации пакета для создания страницы проекта. - Поле
license— текст, указывающий лицензию, охватывающую пакет, если лицензия не выбрана из классификаторов «Лицензия» Trove. См. полеClassifier. Обратите внимание, что есть опция распределенияlicence, которая устарела, но всё ещё действует как псевдоним дляlicense. - Это поле должно быть списком.
- Действительные классификаторы перечислены на PyPI.
- Для обеспечения обратной совместимости это поле также принимает строку. Если вы передадите строку, разделенную запятыми
'foo, bar', она будет преобразована в['foo', 'bar']. В противном случае она будет преобразована в список из одной строки.
- ‘короткая строка’
-
Одна строка текста, не более 200 символов.
- ‘длинная строка’
-
Несколько строк простого текста в формате reStructuredText (см. http://docutils.sourceforge.net/).
- ‘список строк’
-
См. ниже.
Кодирование информации о версии — целое искусство. Пакеты Python обычно придерживаются формата версии главная.вторая[.поправочная][дополнительная]. Главная цифра равна 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–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/distutils/setupscript.html