如何在Python setup.py中基于用户偏好动态安装依赖包
实现setup.py交互式选择安装依赖的方案
你遇到的EOFError是因为pip install默认在非交互上下文运行,会屏蔽标准输入流,直接在setup.py全局作用域调用input()无法读取到用户输入,所以触发读文件末尾的报错。正确的实现方式是自定义install命令类,将交互逻辑放到安装执行阶段触发。
实现步骤
1. 自定义安装命令类
继承setuptools自带的install命令类,重写run方法实现交互逻辑,同时兼容非交互场景避免报错:
import sys import setuptools from setuptools.command.install import install class CustomInstall(install): def run(self): # 判断当前是否为交互终端,避免CI/CD等非交互场景触发异常 is_interactive = sys.stdin.isatty() and sys.stdout.isatty() optional_deps = [] if is_interactive: # 询问是否安装numpy,可根据需求扩展更多可选依赖的询问 while True: choice = input("是否安装numpy?[y/n] 默认y: ").strip().lower() if choice in ('', 'y', 'yes'): optional_deps.append('numpy>=1.21.0') break elif choice in ('n', 'no'): break print("输入无效,请输入y或n") # 将用户选中的可选依赖追加到安装列表 self.distribution.install_requires.extend(optional_deps) # 执行原生安装逻辑 super().run()
2. 修改setup配置
在原有setup参数中新增cmdclass配置,指定使用自定义的安装类,原install_requires保留必须的基础依赖即可:
# 基础依赖(必须安装,无需询问) requirements = ["requests>=2.28.0", "click>=8.0.0"] setuptools.setup( name="backbone", version="1", entry_points={ "console_scripts": [ 'backbone_cli=backbone.backbone:main' ] }, author="Mostafa Ghadimi", author_email="<my_email>", long_description=long_description, long_description_content_type="text/markdown", install_requires=requirements, include_package_data=True, url="https://github.com/mostafaghadimi/backbone", classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", ], packages=setuptools.find_packages(), python_requires=">=3.6", license='MIT', # 新增自定义命令类配置 cmdclass={ 'install': CustomInstall } )
注意事项
- 非交互场景(比如CI/CD流水线、加了静默参数的安装)下,脚本会自动跳过询问,不会安装可选依赖,如有需要可以调整逻辑修改默认安装规则。
- 如果后续切换到pyproject.toml的标准化打包配置,更推荐使用官方支持的
optional-dependencies配置可选依赖,让用户通过pip install backbone[all]的方式手动选择扩展依赖,更符合Python打包规范。
内容的提问来源于stack exchange,提问作者Mostafa Ghadimi
相关产品推荐
相关产品推荐

