如何在 setup.py 中访问由 setuptools 的 install_requires 安装的模块?

How to access a module installed by setuptools' install_requires within setup.py?

我正在写一个 setup.py 来安装我的包 reboundx,它只有一个依赖项 rebound。我的包构建了一个扩展 libreboundx.so,需要 link 到 librebound.so in setup.py

rebxExt = Extension('libreboundx', libraries=['rebound'], library_dirs = [rebound_path]...)

我希望能够在 setup(...) 调用中使用 install_requires 来构建 reboundx 模块,以确保安装了正确版本的 rebound .有什么办法可以解决循环问题吗?

如果 rebound 没有安装,我会不知何故需要安装工具通过 install_requires 检测到它,安装 rebound,然后找到正确的路径并构建扩展 libreboundx.so.

(如果你喜欢这种方法,我可以进一步阐述) 考虑这个例子。我使用 ctypes 加载共享库。然后使用函数调用获取库的版本。

>>> import ctypes
>>> x = ctypes.cdll.LoadLibrary("libc.so.6")
>>> x.gnu_get_libc_version
<_FuncPtr object at 0x7f9a08e3e460>
>>> getversion = x.gnu_get_libc_version
>>> getversion.restype = ctypes.c_char_p
>>> getversion()
'2.19'

您可以使用 rebound.so 做类似的事情。使用 try 语句加载 librebound。 如果是版本错误或者在库的标准搜索路径中找不到,那就编译librebound。

#setup.py
import ctypes
try:
    x = ctypes.cdll.LoadLibrary("librebound.so")
    x.restype = ctypes.c_char_p
    version = x.get_version()
    major,minor = version.split(".")
    if int(major) < REQUIRED_VERSION:
        raise False, "LIBREBOUND VERSION %s IS NOT SUPPORTED"%(str(version))

except:
    pass
    #invoke rebound's makefile
    #probably install the new library to lib path.
    #link to new rebound 

rebxExt = Extension('libreboundx', libraries=['rebound'], library_dirs = [rebound_path]...)

您应该使用 setup_requires 参数 setup()。从文档中,

setup_requires

A string or list of strings specifying what other distributions need to be present in order for the setup script to run. setuptools will attempt to obtain these (even going so far as to download them using EasyInstall) before processing the rest of the setup script or commands. This argument is needed if you are using distutils extensions as part of your build process; for example, extensions that process setup() arguments and turn them into EGG-INFO metadata files.

(Note: projects listed in setup_requires will NOT be automatically installed on the system where the setup script is being run. They are simply downloaded to the ./.eggs directory if they’re not locally available already. If you want them to be installed, as well as being available when the setup script is run, you should add them to install_requires and setup_requires.)

https://pythonhosted.org/setuptools/setuptools.html#new-and-changed-setup-keywords

编辑: 它应该为 setup_requires 中的每个条目创建一个 egg 目录。 但是,我只是用反弹尝试了这个,它实际上无法在简单安装下构建。

$> python2 setup.py install
install_dir .
warning: no files found matching 'src/rebound.h'
src/rebound.c:38:21: fatal error: rebound.h: No such file or directory
compilation terminated.
Traceback (most recent call last):
  File "setup.py", line 187, in <module>
    url="http://www.mathics.github.io/",   # project home page, if any
  File "/usr/lib/python2.7/distutils/core.py", line 111, in setup
    _setup_distribution = dist = klass(attrs)
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/setuptools/dist.py", line 221, in __init__
    self.fetch_build_eggs(attrs.pop('setup_requires'))
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/setuptools/dist.py", line 245, in fetch_build_eggs
    parse_requirements(requires), installer=self.fetch_build_egg
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/pkg_resources.py", line 544, in resolve
    dist = best[req.key] = env.best_match(req, self, installer)
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/pkg_resources.py", line 786, in best_match
    return self.obtain(req, installer) # try and download/install
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/pkg_resources.py", line 798, in obtain
    return installer(requirement)
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/setuptools/dist.py", line 293, in fetch_build_egg
    return cmd.easy_install(req)
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/setuptools/command/easy_install.py", line 582, in easy_install
    return self.install_item(spec, dist.location, tmpdir, deps)
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/setuptools/command/easy_install.py", line 612, in install_item
    dists = self.install_eggs(spec, download, tmpdir)
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/setuptools/command/easy_install.py", line 802, in install_eggs
    return self.build_and_install(setup_script, setup_base)
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/setuptools/command/easy_install.py", line 1079, in build_and_install
    self.run_setup(setup_script, setup_base, args)
  File "/usr/lib/python2.7/site-packages/distribute-0.6.14-py2.7.egg/setuptools/command/easy_install.py", line 1070, in run_setup
    raise DistutilsError("Setup script exited with %s" % (v.args[0],))
distutils.errors.DistutilsError: Setup script exited with error: command 'gcc' failed with exit status 1

似乎 setuptools 确实通过 setup_requires 关键字实现了这一点,正如 sn6uv 所指出的那样。但是,我发现很难找到说明如何使用它的文档 and/or 示例。从我确实发现的情况来看,对于外行来说,它似乎也有点 delicate/complicated 。

其一,当使用 pip 时,setup_requires 中的依赖项安装与其他所有依赖项不同(参见 Install package which has setup_requires from local source distributions). It also seems like if you need to import the dependency within setup.py, you need to subclass the install command in order to delay the import until it's available (deep in a thread on the distutils mailing list arguing over setup_requires, Donald Stufft talks about this and links to an example: https://mail.python.org/pipermail/distutils-sig/2015-March/025882.html)。

所以我放弃并实现了类似于 goCards 建议的手动检查的东西。只是想我会 post 为遇到此问题的更有野心的人提供链接。