
当使用pip install命令安装Python包,特别是包含自定义构建逻辑的本地包时,可能会遇到一个看似矛盾的错误:ModuleNotFoundError,即使该模块(例如numpy)已经通过pip全局安装。这通常发生在包的构建阶段(例如,创建wheel文件时)。
问题的核心在于现代pip和构建后端(如setuptools)为了确保构建过程的纯净性和可重复性,会默认在一个隔离的构建环境中执行。这意味着,在构建过程中,系统全局安装的任何包都不会自动对构建脚本可见。如果你的setup.py文件或自定义构建脚本(例如,在mycustominstaller/__init__.py中)在执行时需要某个特定的包(比如numpy)来完成构建任务,但该包没有在隔离环境中被显式声明和安装,就会导致ModuleNotFoundError。
错误日志中清晰地显示了这一过程:
Building wheels for collected packages: mypackagename ... Running command Building wheel for mypackagename (pyproject.toml) ... ModuleNotFoundError: No module named 'numpy' error: subprocess-exited-with-error
这表明numpy是在尝试构建wheel时,由mypackagename内部的某个脚本(如mycustominstaller/__init__.py)导入失败。
解决此问题的关键是告知构建系统,在进入实际的项目构建阶段之前,需要预先安装哪些依赖。根据你的项目是使用传统的setup.py还是现代的pyproject.toml进行配置,有两种主要方法:
如果你的项目主要通过setup.py进行配置,并且依赖于setuptools,你可以使用setup()函数中的setup_requires参数来声明构建时依赖。setup_requires是一个列表,其中包含在运行setup.py脚本本身之前需要安装的包。
示例 setup.py:
from setuptools import setup, find_packages
setup(
    name='mypackagename',
    version='0.1.0',
    packages=find_packages(),
    # 声明构建时所需的依赖
    setup_requires=[
        'numpy',  # 例如,如果你的构建脚本需要numpy
        'setuptools_scm', # 如果你使用setuptools_scm来管理版本
    ],
    # 声明运行时所需的依赖
    install_requires=[
        # 'some_runtime_dependency',
    ],
    author='Your Name',
    author_email='your.email@example.com',
    description='A custom Python package',
    long_description=open('README.md').read(),
    long_description_content_type='text/markdown',
    url='https://github.com/yourusername/mypackagename',
    classifiers=[
        'Programming Language :: Python :: 3',
        'License :: OSI Approved :: MIT License',
        'Operating System :: OS Independent',
    ],
    python_requires='>=3.7',
)在这个例子中,numpy被添加到setup_requires中。当pip尝试构建mypackagename时,它会首先在隔离环境中安装numpy,然后才执行setup.py中的自定义构建逻辑。
对于遵循PEP 517/518规范的现代Python项目,pyproject.toml是声明项目元数据和构建配置的首选方式。构建时依赖应该在[build-system]部分的requires字段中声明。这种方式更加通用,不限于setuptools作为构建后端。
示例 pyproject.toml:
[build-system]
requires = ["setuptools>=61.0", "wheel", "numpy"] # 声明构建时所需的依赖
build-backend = "setuptools.build_meta"
[project]
name = "mypackagename"
version = "0.1.0"
description = "A custom Python package"
readme = "README.md"
authors = [
    { name = "Your Name", email = "your.email@example.com" },
]
license = { file = "LICENSE" }
requires-python = ">=3.7"
dependencies = [
    # "some-runtime-dependency", # 声明运行时所需的依赖
]
[project.urls]
Homepage = "https://github.com/yourusername/mypackagename"
Repository = "https://github.com/yourusername/mypackagename"在这个pyproject.toml文件中,numpy被包含在build-system.requires列表中。setuptools>=61.0和wheel是setuptools构建后端通常需要的标准依赖。当pip检测到pyproject.toml并尝试构建项目时,它会首先在隔离环境中安装这些requires中列出的包,然后再调用build-backend来执行实际的构建。
setup_requires vs. install_requires / build-system.requires vs. project.dependencies:
隔离环境的优势: 虽然隔离环境可能带来初始的困惑,但它确保了包的构建过程是独立且可预测的,不受本地环境中特定包版本的影响,从而提高了包的可靠性和可移植性。
pip install -e . 的行为: 在开发过程中,你可能使用pip install -e .进行可编辑安装。这种情况下,setup_requires或build-system.requires仍然是重要的,因为即使是可编辑安装,也可能涉及到构建步骤(例如,编译C扩展)。
虚拟环境: 始终建议在虚拟环境(如venv或conda环境)中进行Python开发和包安装。这有助于管理项目依赖,避免全局Python环境的混乱。即使在虚拟环境中,上述构建时依赖的声明仍然是必要的,因为构建隔离环境是pip的特性,与虚拟环境是正交的。
自定义构建逻辑: 如果你的项目包含复杂的自定义构建逻辑,并且这些逻辑依赖于特定的Python模块,那么将这些模块添加到setup_requires或build-system.requires是确保构建成功的关键一步。
当pip install在构建阶段报告ModuleNotFoundError时,这通常是由于pip在隔离构建环境中运行,而你的构建脚本又依赖于未在该环境中声明的包。通过在setup.py中使用setup_requires或在pyproject.toml中使用build-system.requires显式声明这些构建时依赖,可以有效地解决这一问题,确保你的Python包能够顺利构建和安装。推荐使用pyproject.toml进行现代Python项目的配置,因为它提供了更清晰和更标准的构建依赖声明方式。
以上就是解决pip安装时构建阶段依赖缺失的教程的详细内容,更多请关注php中文网其它相关文章!
                        
                        每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
                Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号