Как установить 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-вопросе.