Для авторов нижестоящих пакетов#
Этот документ призван объяснить некоторые лучшие практики для создания пакета, зависящего от NumPy.
Понимание версионирования NumPy и стабильности API/ABI#
NumPy использует стандартный, PEP 440 совместимая схема версионирования:
major.minor.bugfix. A основной выпуск является крайне необычным, и если это произойдет,
скорее всего, это будет указывать на нарушение ABI. Выпуски NumPy 1.xx происходили с
2006 по 2023 год; NumPy 2.0 в начале 2024 года — это первый выпуск, который изменил
ABI (незначительные нарушения ABI для крайних случаев могли происходить несколько раз в минорных
выпусках).
Минорный версии выпускаются регулярно, обычно каждые 6 месяцев. Минорные
версии содержат новые функции, устаревания и удаления ранее
устаревшего кода. Исправление ошибки conf_tmp_path (атрибут numpy.distutils.ccompiler_opt.CCompilerOpt)
Важно знать, что NumPy, как и сам Python и большинство других известных научных проектов на Python, не не используйте семантическое версионирование. Вместо этого обратно несовместимые изменения API требуют предупреждений об устаревании как минимум для двух выпусков. Для получения дополнительных сведений см. NEP 23 — Политика обратной совместимости и устаревания.
NumPy предоставляет как Python API, так и C-API. C-API можно получить доступ напрямую или через инструменты, такие как Cython или f2py. Если ваш пакет использует C-API, важно понимать совместимость двоичного интерфейса приложения (ABI) NumPy: ABI NumPy совместим вперёд, но не назад. Это означает, что двоичные файлы, скомпилированные против старой версии NumPy, будут работать с новыми версиями, но двоичные файлы, скомпилированные против новой версии, не обязательно будут работать со старыми.
Модули также могут быть безопасно собраны для NumPy 2.0 или более поздней версии в
Режим abi3 CPython, что позволяет собрать с одной (минимально поддерживаемой) версией Python, но быть совместимым с более высокими версиями в той же серии (например, 3.x).
Это может значительно сократить количество колес, которые нужно собрать и
распространить. Для получения дополнительной информации и примеров см.
документация cibuildwheel.
Тестирование против основной ветки NumPy или предварительных выпусков#
Для крупных, активно поддерживаемых пакетов, зависящих от NumPy, мы рекомендуем тестировать на версии NumPy в разработке в CI. Чтобы упростить это, ночные сборки предоставляются как wheels на https://anaconda.org/scientific-python-nightly-wheels/. Пример команды установки:
pip install -U --pre --only-binary :all: -i https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy
Это помогает обнаружить регрессии в NumPy, которые требуют исправления до следующего выпуска NumPy. Кроме того, мы рекомендуем вызывать ошибки при предупреждениях в CI для этой работы, либо все предупреждения, либо, по крайней мере, DeprecationWarning и
FutureWarning. Это дает вам раннее предупреждение об изменениях в NumPy для
адаптации вашего кода.
Если вы хотите протестировать свои собственные сборки wheel с последней ночной сборкой NumPy
и используете cibuildwheel, вам может понадобиться что-то вроде этого в вашем конфигурационном файле CI:
CIBW_ENVIRONMENT: "PIP_PRE=1 PIP_EXTRA_INDEX_URL=https://pypi.anaconda.org/scientific-python-nightly-wheels/simple"
Добавление зависимости от NumPy#
Зависимость времени сборки#
Примечание
До NumPy 1.25 C-API NumPy был не предоставляется обратно совместимым способом по умолчанию. Это означает, что при компиляции с версией NumPy раньше 1.25 необходимо компилировать с самой старой версией, которую вы хотите поддерживать. Это можно сделать, используя oldest-supported-numpy. Пожалуйста, смотрите Документация NumPy 1.24.
Если пакет использует C-API NumPy напрямую или использует другой инструмент, зависящий от него, например Cython или Pythran, NumPy является время сборки зависимость пакета.
По умолчанию NumPy предоставляет API, обратно совместимый с самой ранней версией NumPy, которая поддерживает самую старую версию Python, поддерживаемую в настоящее время NumPy. Например, NumPy 1.25.0 поддерживает Python 3.9 и выше; а самая ранняя версия NumPy, поддерживающая Python 3.9, была 1.19. Поэтому мы гарантируем, что NumPy 1.25 при использовании настроек по умолчанию будет предоставлять C-API, совместимый с NumPy 1.19. (точная версия устанавливается во внутренних заголовочных файлах NumPy).
NumPy также обратно совместим для всех минорных выпусков, но мажорный выпуск потребует перекомпиляции (см. рекомендации для NumPy 2.0 далее).
Поведение по умолчанию можно настроить, например, добавив:
#define NPY_TARGET_VERSION NPY_1_22_API_VERSION
перед включением любых заголовков NumPy (или эквивалентного -D флаг компилятора) в каждом модуле расширения, который требует C-API NumPy.
Это в основном полезно, если вам нужно использовать недавно добавленный API ценой несовместимости со старыми версиями.
Если по какой-то причине вы хотите компилировать для текущей установленной версии NumPy по умолчанию, вы можете добавить:
#ifndef NPY_TARGET_VERSION
#define NPY_TARGET_VERSION NPY_API_VERSION
#endif
Что позволяет пользователю переопределить значение по умолчанию через -DNPY_TARGET_VERSION. Это определение должно быть согласованным для каждого модуля расширения (использование
import_array()) и также применяется к модулю umath.
При компиляции с NumPy вы должны добавить соответствующие ограничения версии
к вашему pyproject.toml (см. PEP 517). Поскольку ваше расширение не будет совместимо с новым основным выпуском NumPy и может быть несовместимо с очень старыми версиями.
Для пакетов conda-forge, пожалуйста, смотрите
здесь
для инструкций по объявлению зависимости от numpy при использовании C API.
Зависимость времени выполнения и диапазоны версий#
NumPy и многие основные научные пакеты Python договорились о графике прекращения поддержки старых версий Python и NumPy: NEP29. Мы рекомендуем всем пакетам, зависящим от NumPy, следовать рекомендациям в NEP 29.
Для зависимости времени выполнения, укажите границы версий с помощью
install_requires в setup.py (предполагая, что вы используете numpy.distutils или
setuptools для сборки).
Большинству библиотек, зависящих от NumPy, не потребуется устанавливать верхнюю границу версии: NumPy тщательно сохраняет обратную совместимость.
Тем не менее, если вы (a) проект, который гарантированно выпускает
часто, (b) используете большую часть API NumPy, и (c) беспокоитесь,
что изменения в NumPy могут сломать ваш код, вы можете установить
верхнюю границу с N не менее 3, и
MAJOR.MINOR являясь текущим релизом NumPy [*]. Если вы используете C-API NumPy (напрямую или через Cython), вы также можете зафиксировать текущую основную версию, чтобы предотвратить нарушение ABI. Обратите внимание, что установка верхней границы для NumPy может повлияет на возможность установки вашей библиотеки вместе с другими, более новыми пакетами.
Примечание
SciPy имеет больше документации о том, как он собирает wheels и работает со своими зависимостями времени сборки и выполнения здесь.
Сборка колес NumPy и SciPy в CI также может быть полезна в качестве справочника, её можно найти здесь для NumPy и здесь для SciPy.
Советы, специфичные для NumPy 2.0#
NumPy 2.0 — это релиз с нарушением ABI, однако он содержит поддержку сборки колес, которые работают как в 2.0, так и в релизах 1.xx. Важно понимать, что:
Когда вы собираете колеса для вашего пакета, используя версию NumPy 1.xx во время сборки, те не будет работать с NumPy 2.0.
Когда вы собираете wheels для вашего пакета, используя версию NumPy 2.x во время сборки, эти будет работать с NumPy 1.xx.
Первый раз, когда ABI NumPy для версии 2.0 гарантированно стабилен, будет выпуск первого кандидата на релиз 2.0 (т.е. 2.0.0rc1). Наши рекомендации по управлению зависимостью от NumPy следующие:
В основной (разрабатываемой) ветке вашего пакета не добавляйте никаких ограничений.
Если вы полагаетесь на C-API NumPy (например, через прямое использование в C/C++ или через код Cython, который использует NumPy), добавьте
numpy<2.0требование в метаданных зависимостей вашего пакета для релизов / в ветках релизов. Делайте это до тех пор, пока numpy2.0.0rc1выпущен, и вы можете нацелиться на него. Обоснование: C ABI NumPy изменится в версии 2.0, поэтому любые скомпилированные модули расширений, зависящие от NumPy, перестанут работать; их необходимо перекомпилировать.Если вы полагаетесь на большой API поверхности из Python API NumPy, также рассмотрите добавление того же
numpy<2.0требование к вашим метаданным до тех пор, пока вы не убедитесь, что ваш код обновлён для изменений в 2.0 (т.е., когда вы протестировали, что всё работает с2.0.0rc1). Обоснование: мы проведем значительную очистку API, удалив множество псевдонимов и устаревших/нерекомендуемых объектов (см., например, Руководство по переходу на NumPy 2.0 и NEP 52 — Очистка Python API для NumPy 2.0), поэтому, если вы не используете только современные/рекомендуемые функции и объекты, ваш код, вероятно, потребует по крайней мере некоторых корректировок.Планируйте выпуск собственных пакетов, которые зависят от
numpyвскоре после выпуска первого релиз-кандидата NumPy 2.0 (вероятно, около 1 февраля 2024). Обоснование: на этом этапе вы можете выпускать пакеты, которые будут работать как с версией 2.0, так и с 1.X, и, следовательно, ваши конечные пользователи не увидят значительных/никаких нарушений работы (вы хотитеpip install mypackageдля продолжения работы в день выхода NumPy 2.0).Как только
2.0.0rc1доступен, вы можете настроить свои метаданные вpyproject.tomlспособом, описанным ниже.
Есть два случая: вам нужно сохранить совместимость с numpy 1.xx, одновременно поддерживая
2.0, или вы можете отказаться от поддержки numpy 1.xx для новых выпусков
вашего пакета и поддерживать только >=2.0. Последнее проще, но может быть более
ограничительным для ваших пользователей. В этом случае просто добавьте numpy>=2.0 (или
numpy>=2.0.0rc1) в ваши требования к сборке и выполнению, и всё готово.
Мы сосредоточимся на «сохранении совместимости с 1.xx и 2.x», что теперь
немного сложнее.
Пример для пакета, использующего C-API NumPy (через C/Cython/и т.д.), который хочет поддерживать NumPy 1.23.5 и выше:
[build-system]
build-backend = ...
requires = [
# Note for packagers: this constraint is specific to wheels
# for PyPI; it is also supported to build against 1.xx still.
# If you do so, please ensure to include a `numpy<2.0`
# runtime requirement for those binary packages.
"numpy>=2.0.0rc1",
...
]
[project]
dependencies = [
"numpy>=1.23.5",
]
Мы рекомендуем иметь хотя бы одну задачу CI, которая собирает/устанавливает через wheel, а затем запускает тесты против самой старой версии numpy, которую поддерживает пакет. Например:
- name: Build wheel via wheel, then install it
run: |
python -m build # This will pull in numpy 2.0 in an isolated env
python -m pip install dist/*.whl
- name: Test against oldest supported numpy version
run: |
python -m pip install numpy==1.23.5
# now run test suite
Вышеуказанное работает только после того, как NumPy 2.0 станет доступен на PyPI. Если вы хотите протестировать с колесом NumPy 2.0-dev, вам необходимо использовать ночную сборку numpy (см. этот раздел выше) или собрать numpy из исходного кода.