You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改继承build_py的自定义构建类以在临时目录构建并解决报错?

问题描述

我有一个setup.py文件,其中包含继承自build_py的自定义构建类,为适配旧环境,优先从distutils导入,失败则用setuptools:

try:
    warnings.filterwarnings('ignore', 
        message='.*distutils package is deprecated.*', 
        category=DeprecationWarning)
    from distutils.core import setup
    from distutils.command.build_py import build_py
except:
    from setuptools import setup
    from setuptools.command.build_py import build_py

class MyBuild(build_py):

    #... 现有自定义代码...

    #... 新增自定义代码...

    def run(self):
        build_py.run(self)

if __name__ == '__main__':

    setup(
        name="...",
        version="...",
        #...
        cmdclass={'build_py': MyBuild}
    )

在VM客户端执行pip install .时失败(客户端外执行正常),原因是pip尝试在源目录创建构建目录,触发权限问题。环境为Windows系统,Python 3.9/3.10/3.11,pip 22.3.1/23.3版本均存在该问题;使用旧版pip会出现ImportError: cannot import name 'Mapping' from 'collections'错误。

我尝试通过tempfile.gettempdir()获取临时目录作为构建目录,在自定义类中添加了以下代码:

def initialize_options(self):
        self.build_base = None
        self.build_lib = None
        print('Set self.build_base and self.build_lib to None')
        
    def finalize_options(self):
        if not self.build_base:
            self.build_base = tempfile.gettempdir()
            print('Set self.build_base to ', self.build_base)
            
        if not self.build_lib:
            self.build_lib = os.path.join(self.build_base, 'lib')
            print('Set self.build_lib to ', self.build_lib)

但执行pip install .时抛出异常:

Installing build dependencies ... done
  Getting requirements to build wheel ... error
  error: subprocess-exited-with-error

  × Getting requirements to build wheel did not run successfully.
  │ exit code: 1
  ╰─> [55 lines of output]
      Set self.build_base and self.build_lib to None
      Set self.build_base to  <...>\AppData\Local\Temp
      Set self.build_lib to  <...>\AppData\Local\Temp\lib
      Traceback (most recent call last):
        File "C:\pyvenv\<...>\Lib\site-packages\pip\_vendor\pep517\in_process\_in_process.py", line 351, in <module>
          main()
        File "C:\pyvenv\<...>\Lib\site-packages\pip\_vendor\pep517\in_process\_in_process.py", line 333, in main
          json_out['return_val'] = hook(**hook_input['kwargs'])
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "C:\pyvenv\<...>\Lib\site-packages\pip\_vendor\pep517\in_process\_in_process.py", line 118, in get_requires_for_build_wheel
          return hook(config_settings)
                 ^^^^^^^^^^^^^^^^^^^^^
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\build_meta.py", line 355, in get_requires_for_build_wheel
          return self._get_build_requires(config_settings, requirements=['wheel'])
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\build_meta.py", line 325, in _get_build_requires
          self.run_setup()
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\build_meta.py", line 341, in run_setup
          exec(code, locals())
        File "<string>", line 138, in <module>
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\_distutils\core.py", line 185, in setup
          return run_commands(dist)
                 ^^^^^^^^^^^^^^^^^^
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\_distutils\core.py", line 201, in run_commands
          dist.run_commands()
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\_distutils\dist.py", line 969, in run_commands
          self.run_command(cmd)
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\dist.py", line 989, in run_command
          super().run_command(command)
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\_distutils\dist.py", line 988, in run_command
          cmd_obj.run()
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\command\egg_info.py", line 318, in run
          self.find_sources()
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\command\egg_info.py", line 326, in find_sources
          mm.run()
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\command\egg_info.py", line 548, in run
          self.add_defaults()
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\command\egg_info.py", line 586, in add_defaults
          sdist.add_defaults(self)
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\command\sdist.py", line 113, in add_defaults
          super().add_defaults()
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\_distutils\command\sdist.py", line 249, in add_defaults
          self._add_defaults_python()
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\command\sdist.py", line 125, in _add_defaults_python
          self.filelist.extend(build_py.get_source_files())
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\_distutils\command\build_py.py", line 303, in get_source_files
          return [module[-1] for module in self.find_all_modules()]
                                           ^^^^^^^^^^^^^^^^^^^^^^^
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\_distutils\command\build_py.py", line 293, in find_all_modules
          if self.py_modules:
             ^^^^^^^^^^^^^^^
        File "C:\<...>\AppData\Local\Temp\pip-build-env-42o2vdhl\overlay\Lib\site-packages\setuptools\_distutils\cmd.py", line 107, in __getattr__
          raise AttributeError(attr)
      AttributeError: py_modules. Did you mean: 'find_modules'?
      [end of output]

尝试在finalize_options末尾添加如下代码时,又陷入递归循环:

# self.set_undefined_options('build_py', ('build_base', 'build_base'),
            # ('build_lib', 'build_lib'))

求正确的实现方式。

解决方案

问题出在重写initialize_options和finalize_options时,没有调用父类的对应方法,导致父类中负责初始化的属性(比如py_modules)未被设置,进而触发AttributeError。正确做法是先调用父类方法完成基础初始化,再覆盖自定义配置。

另外需要提前导入tempfile和os模块,避免导入错误。修改后的完整代码如下:

import os
import tempfile
import warnings

try:
    warnings.filterwarnings('ignore', 
        message='.*distutils package is deprecated.*', 
        category=DeprecationWarning)
    from distutils.core import setup
    from distutils.command.build_py import build_py
except:
    from setuptools import setup
    from setuptools.command.build_py import build_py

class MyBuild(build_py):
    #... 现有自定义代码...

    def initialize_options(self):
        # 先调用父类初始化方法,确保所有默认属性被设置
        super().initialize_options()
        # 重置build_base为None,后续在finalize中自定义
        self.build_base = None

    def finalize_options(self):
        # 如果build_base未设置,使用系统临时目录
        if not self.build_base:
            self.build_base = tempfile.gettempdir()
        # 调用父类finalize方法,让父类基于build_base处理其他依赖属性
        super().finalize_options()
        # 若需要自定义build_lib,可在此覆盖父类默认值
        # self.build_lib = os.path.join(self.build_base, 'custom_lib')

    def run(self):
        build_py.run(self)

if __name__ == '__main__':
    setup(
        name="...",
        version="...",
        #...
        cmdclass={'build_py': MyBuild}
    )

关键说明

  • 必须调用父类方法:initialize_options和finalize_options中先执行父类方法,确保py_modules、package_dir等必要属性完成初始化,避免属性缺失错误。
  • 避免递归循环:不要使用set_undefined_options('build_py', ...),当前类是build_py的子类,该操作会触发递归调用自身。
  • 临时目录的使用:将build_base设为系统临时目录后,父类的finalize_options会自动生成build_lib的默认路径(通常为build_base/lib),无需手动设置,若有特殊需求再覆盖即可。

这样修改后,pip会使用系统临时目录作为构建目录,避开源目录的权限问题,同时保证所有构建所需属性都被正确初始化。


内容的提问来源于stack exchange,提问作者Alan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 03:35:54