Как установить Bootstrap numpy в setup.py
У меня есть проект с расширением C, который требует numpy. В идеале, мне бы хотелось, чтобы кто-либо загрузил мой проект, чтобы просто запустить python setup.py install
или использовать один вызов pip
. Проблема у меня в том, что в моем setup.py
мне нужно импортировать numpy, чтобы получить расположение заголовков, но я хотел бы, чтобы numpy был просто регулярным требованием в install_requires
, чтобы он автоматически загружался с Python Индекс пакета.
Вот пример того, что я пытаюсь сделать:
from setuptools import setup, Extension
import numpy as np
ext_modules = [Extension('vme', ['vme.c'], extra_link_args=['-lvme'],
include_dirs=[np.get_include()])]
setup(name='vme',
version='0.1',
description='Module for communicating over VME with CAEN digitizers.',
ext_modules=ext_modules,
install_requires=['numpy','pyzmq', 'Sphinx'])
Очевидно, я не могу import numpy
вверху, прежде чем он будет установлен. Я видел аргумент setup_requires
, переданный в setup()
, но не могу найти документацию о том, для чего он предназначен.
Возможно ли это?
Ответы
Ответ 1
Следующее работает по крайней мере с numpy1.8 и python {2.6,2.7,3.3}:
from setuptools import setup
from setuptools.command.build_ext import build_ext as _build_ext
class build_ext(_build_ext):
def finalize_options(self):
_build_ext.finalize_options(self)
# Prevent numpy from thinking it is still in its setup process:
__builtins__.__NUMPY_SETUP__ = False
import numpy
self.include_dirs.append(numpy.get_include())
setup(
...
cmdclass={'build_ext':build_ext},
setup_requires=['numpy'],
...
)
Для небольшого объяснения, посмотрите, почему он не работает без "взлома", посмотрите этот ответ.
Обратите внимание, что использование setup_requires
имеет небольшой недостаток: numpy будет компилироваться не только перед python setup.py --help
расширений, но и, например, при создании python setup.py --help
. Чтобы избежать этого, вы можете проверить параметры командной строки, как предложено в https://github.com/scipy/scipy/blob/master/setup.py#L205, но с другой стороны, я не думаю, что это стоит усилие.
Ответ 2
Это фундаментальная проблема с пакетами, которые должны использовать numpy (для distutils или get_include). Я не знаю, как "загрузиться" с помощью pip или easy-install.
Тем не менее, легко сделать пакет conda для вашего модуля и предоставить список зависимостей, так что кто-то может просто сделать pda-имя conda install, которое загрузит и установит все необходимое.
Конда доступна в Анаконде или в Миниконде (питон + просто Конда).
Посетите этот веб-сайт: http://docs.continuum.io/conda/index.html или эту слайд-колоду для получения дополнительной информации: https://speakerdeck.com/teoliphant/packaging-and-deployment-with-conda
Ответ 3
Чтобы заставить pip работать, вы можете сделать так же, как Scipy: https://github.com/scipy/scipy/blob/master/setup.py#L205
А именно, команда egg_info
должна быть передана в стандартные setuptools/distutils, но другие команды могут использовать numpy.distutils
.
Ответ 4
Возможно, более практичным решением является просто установить numpy, который будет установлен заранее, и import numpy
внутри области функций. Решение @coldfix работает, но компиляция numpy берет навсегда. Гораздо быстрее, чтобы установить его сначала как пакет колес, особенно теперь, когда у нас есть колеса для большинства систем благодаря усилиям, таким как manylinux.
from __future__ import print_function
import sys
import textwrap
import pkg_resources
from setuptools import setup, Extension
def is_installed(requirement):
try:
pkg_resources.require(requirement)
except pkg_resources.ResolutionError:
return False
else:
return True
if not is_installed('numpy>=1.11.0'):
print(textwrap.dedent("""
Error: numpy needs to be installed first. You can install it via:
$ pip install numpy
"""), file=sys.stderr)
exit(1)
def ext_modules():
import numpy as np
some_extention = Extension(..., include_dirs=[np.get_include()])
return [some_extention]
setup(
ext_modules=ext_modules(),
)
Ответ 5
Решение @coldfix не работает для расширений Cython, если Cython предварительно не установлен на целевой машине, так как он завершается с ошибкой
ошибка: неизвестный тип файла ".pyx" (из "xxxxx/yyyyyy.pyx")
Причиной сбоя является преждевременный импорт setuptools.command.build_ext
, потому что при импорте он пытается использовать Cython build_ext
-functionality:
try:
# Attempt to use Cython for building extensions, if available
from Cython.Distutils.build_ext import build_ext as _build_ext
# Additionally, assert that the compiler module will load
# also. Ref #1229.
__import__('Cython.Compiler.Main')
except ImportError:
_build_ext = _du_build_ext
И обычно setuptools успешен, потому что импорт происходит после выполнения setup_requirements
. Однако, импортируя его уже в setup.py
, можно использовать только запасное решение, которое ничего не знает о Cython.
Одной из возможностей начальной загрузки Cython
наряду с numpy было бы отложить импорт setuptools.command.build_ext
с помощью косвенной ссылки/прокси:
# factory function
def my_build_ext(pars):
# import delayed:
from setuptools.command.build_ext import build_ext as _build_ext#
# include_dirs adjusted:
class build_ext(_build_ext):
def finalize_options(self):
_build_ext.finalize_options(self)
# Prevent numpy from thinking it is still in its setup process:
__builtins__.__NUMPY_SETUP__ = False
import numpy
self.include_dirs.append(numpy.get_include())
#object returned:
return build_ext(pars)
...
setup(
...
cmdclass={'build_ext' : my_build_ext},
...
)
Есть и другие возможности, обсуждаемые, например, в этом SO-вопросе.