我应该如何处理在我的 setup.py 脚本中导入第三方库?

Posted

技术标签:

【中文标题】我应该如何处理在我的 setup.py 脚本中导入第三方库?【英文标题】:How should I handle importing third-party libraries within my setup.py script? 【发布时间】:2017-11-02 15:53:23 【问题描述】:

我正在开发一个 Python 应用程序,并且正在发布一个分支。我在公司服务器上设置了一个 PyPI 服务器,并将我的包的源代码分发复制到它上面。

我检查了该软件包是否托管在服务器上,然后尝试将其安装在我的本地开发机器上。我最终得到了这个输出:

$ pip3 install --trusted-host 172.16.1.92 -i http://172.16.1.92:5001/simple/ <my-package>
Collecting <my-package>
  Downloading http://172.16.1.92:5001/packages/<my-package>-0.2.0.zip
    Complete output from command python setup.py egg_info:
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
      File "C:\Users\<me>\AppData\Local\Temp\pip-build-ubb3jkpr\<my-package>\setup.py", line 9, in <module>
        import appdirs
    ModuleNotFoundError: No module named 'appdirs'

    ----------------------------------------
Command "python setup.py egg_info" failed with error code 1 in C:\Users\<me>\AppData\Local\Temp\pip-build-ubb3jkpr\<my-package>\

原因是我试图在我的setup.py 中导入一个第三方库appdirs,这是我计算data_files 参数到setup() 所必需的:

try:
    from setuptools import setup
except ImportError:
    from distutils.core import setup

import os
from collections import defaultdict

import appdirs
from <my-package>.version import __version__ as <my-package>_version

APP_NAME = '<my-app>'
APP_AUTHOR = '<company>'
SYSTEM_COMPONENT_PLUGIN_DIR = os.path.join(appdirs.user_data_dir(APP_NAME, APP_AUTHOR), 'components')

# ...

setup(
    # ...
    data_files=component_files,
)

但是,我的本地开发机器上没有安装appdirs,我也不希望最终用户拥有它。

setup.py 中依赖这样的第三方库是否可以接受,如果可以,使用它们的推荐方法是什么?有没有办法可以确保在 setup.py 中导入 appdirs 之前安装它,或者我应该只记录 appdirs 是安装我的包所需的包?

【问题讨论】:

您是否尝试过使用setup_requires?见pip.readthedocs.io/en/1.4.1/… @PeterBrittain 有没有关于如何使用它的示例?我是否应该进行两次setup() 调用,一次在setup.py 的顶部附近只有setup_requires=['appdirs'],而我已经在底部? 这并不常见...您可以在***.com/questions/37471313/setup-requires-with-cython 中找到一个简单的示例。这开始显示python.org/dev/peps/pep-0518 中提到的各种箍,它突出了使用 pip 执行此操作的另一种选择,应该很快就会可用。 【参考方案1】:

我在此答案中忽略了许可问题。在真正发布之前,您肯定需要考虑这些因素。

在 setup.py 中依赖这样的第三方库是否可以接受

是的,这是可以接受的,但通常应该尽量减少这些,特别是如果这些模块对最终用户没有明显用途。没有人喜欢拥有他们不需要或使用的软件包。

推荐的使用方法是什么?

基本上有3个选项:

引导它们(例如使用pip 以编程方式安装包)。例如 setuptools provides an ez_setup.py file 可用于引导 setuptools。也许可以自定义下载和安装appdirs

在您的项目中包含它们(尤其是如果它是一个小包)。例如appdirs 基本上只是一个单一的文件模块。在您的项目中很容易复制和维护。这样做时要非常小心许可问题!

在无法导入它们并让用户安装它们时优雅地失败。例如:

try:
    import appdirs
except ImportError:
    raise ImportError('this package requires "appdirs" to be installed. '
                      'Install it first: "pip install appdirs".')

【讨论】:

看起来appdirs 在 MIT 下获得许可,因此在三个选项中,第二个选项似乎最吸引人。理想情况下,setup_requires(@PeterBrittain 推荐)看起来是最好的解决方案,但我不确定如何使用它。 嗯,这取决于许可证、大小、稳定性以及是否还需要它作为运行时依赖项来决定哪种方法是“最吸引人的”。我个人不喜欢setup_requires,因为使用某些本地安装包的方式很烦人(不可能)(我认为python setup.py installpython setup.py developpip install . 不起作用)。 appdirs 在作为本地模块运行时似乎会奇怪地解析路径,所以我选择了第三个选项。【参考方案2】:

如果import 失败,您可以使用pip 以编程方式安装软件包:

try:
    import appdirs
except ImportError:
    import pip
    pip.main(['install', 'appdirs'])
    import appdirs

在某些情况下,您可能需要使用importlib__import__pip.main 之后导入包或引用PATH 变量。如果用户真的想在安装之前安装该软件包,那么包含验证也是值得的。

我使用了很多来自 "Installing python module within code" 的示例,我还没有亲自尝试在 setup.py 文件中使用它,但看起来它可以解决您的问题。

【讨论】:

我很好奇为什么这被否决了。对于我个人遇到的问题,这似乎是一种无痛且实用的解决方案。【参考方案3】:

您可以在依赖项列表中提及 install_requires。请查看 python 打包指南here。您还可以提供一个 requirements.txt 文件,以便可以使用“pip install -r”立即运行它

【讨论】:

我不确定install_requires 会有什么帮助,你是说setup_requires 吗?然而,根据我的经验,要做到正确是很棘手的。尤其是如果不能保证 setuptools 已安装并且给定 try: from setuptools import setup 这不是保证。 如果没记错的话,setup_requires 只是下载依赖,而install_requires 安装它们,我同意应该导入安装工具。 @Venu:要求setuptools 能够安装你的包是可以的。如果您使用pip 安装,则setuptools 已捆绑。我只想在这里使用setuptoolssetup_requires

以上是关于我应该如何处理在我的 setup.py 脚本中导入第三方库?的主要内容,如果未能解决你的问题,请参考以下文章

您如何处理在 IIS 上运行的网站的计划任务?

在 Grafana 中,我们如何处理在图表/面板中返回 0 行的查询?

Python Selenium - 如何处理在WebDriverWait完成之后才显示的警报?

如何处理在 HTML5 画布上绘制的形状并更改其属性?

如何处理在应用程序有效期内结束自动续订的订阅

我应该以啥顺序在我的共享模块中导入模块