How to Bootstrap numpy installation in setup.py

前端 未结 7 1066
陌清茗
陌清茗 2020-12-02 12:12

I have a project which has a C extension which requires numpy. Ideally, I\'d like whoever downloads my project to just be able to run python setup.py install or

相关标签:
7条回答
  • 2020-12-02 12:52

    Perhaps a more practical solution is to just require numpy to be installed beforehand and import numpy inside a function scope. @coldfix solution works but compiling numpy takes forever. Much faster to pip install it first as a wheels package, especially now that we have wheels for most systems thanks to efforts like 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(),
    )
    
    0 讨论(0)
  • 2020-12-02 12:52

    I found a very easy solution in [this post][1]:

    Or you can stick to https://github.com/pypa/pip/issues/5761. Here you install cython and numpy using setuptools.dist before actual setup:

    from setuptools import dist
    dist.Distribution().fetch_build_eggs(['Cython>=0.15.1', 'numpy>=1.10'])
    

    Works well for me!

    0 讨论(0)
  • 2020-12-02 12:55

    The following works at least with numpy1.8 and 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'],
        ...
    )
    

    For a small explanation, see why it fails without the "hack", see this answer.

    Note, that using setup_requires has a subtle downside: numpy will not only be compiled before building extensions, but also when doing python setup.py --help, for example. To avoid this, you could check for command line options, like suggested in https://github.com/scipy/scipy/blob/master/setup.py#L205, but on the other hand I don't really think it's worth the effort.

    0 讨论(0)
  • 2020-12-02 12:57

    This is a fundamental problem with packages that need to use numpy (for distutils or get_include). I do not know of a way to "boot-strap" it using pip or easy-install.

    However, it is easy to make a conda package for your module and provide the list of dependencies so that someone can just do a conda install pkg-name which will download and install everything needed.

    Conda is available in Anaconda or in Miniconda (python + just conda).

    See this website: http://docs.continuum.io/conda/index.html or this slide-deck for more information: https://speakerdeck.com/teoliphant/packaging-and-deployment-with-conda

    0 讨论(0)
  • 2020-12-02 13:04

    @coldfix's solution doesn't work for Cython-extensions, if Cython isn't pre-installed on the target-machine, as it fails with the error

    error: unknown file type '.pyx' (from 'xxxxx/yyyyyy.pyx')

    The reason for the failure is the premature import of setuptools.command.build_ext, because when imported, it tries to use Cython's 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
    

    And normally setuptools is successful, because the import happens after setup_requirements are fulfilled. However by importing it already in setup.py, only fall back solution can be used, which doesn't know any about Cython.

    One possibility to bootstrap Cython alongside with numpy, would be to postpone the import of setuptools.command.build_ext with help of an indirection/proxy:

    # 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},
        ...
    )
    

    There are other possibilities, discussed for example in this SO-question.

    0 讨论(0)
  • 2020-12-02 13:08

    The key is to defer importing numpy until after it has been installed. A trick I learned from this pybind11 example is to import numpy in the __str__ method of a helper class (get_numpy_include below).

    from setuptools import setup, Extension
    
    class get_numpy_include(object):
        """Defer numpy.get_include() until after numpy is installed."""
    
        def __str__(self):
            import numpy
            return numpy.get_include()
    
    
    ext_modules = [Extension('vme', ['vme.c'], extra_link_args=['-lvme'],
                             include_dirs=[get_numpy_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'])
    
    0 讨论(0)
提交回复
热议问题