如何在不破坏 Python 中子模块导入的情况下覆盖包的 __init__.py?

问题描述 投票:0回答:1

我正在开发一个项目,我需要覆盖位于 ./lib/python3.9/site-packages" 目录中的文件。我有以下钩子,它使用

importlib.util
sys.meta_path.insert
:

  • 覆盖文件位于

    ./lib_changes/*

  • Hook 适用于除

    zope+intid.__init__.py
    文件之外的所有以下文件

enter image description here

import re
import sys
import importlib.util
import os
from Zope2.Startup.serve import main
import logging

LOGGER = logging.getLogger("RunWSGI")

def generate_override_mapping(lib_changes_dir):
    override_mapping = {}
    for filename in os.listdir(lib_changes_dir):
        if filename.endswith(".py"):
            module_name = filename[:-3].replace("+", ".")
            # Special handling for __init__.py files
            if module_name.endswith(".__init__"):
                module_name = module_name[:-9]
            override_mapping[module_name] = os.path.join(lib_changes_dir, filename)
    return override_mapping

class CustomImportHook:
    def __init__(self, override_mapping):
        self.override_mapping = override_mapping
        self.override_counter = 0

    def find_spec(self, fullname, path=None, target=None):
        
        if fullname in self.override_mapping:
            override_path = self.override_mapping[fullname]
            LOGGER.info(f"{fullname} Overide by {override_path}")
            self.override_counter += 1

            return importlib.util.spec_from_file_location(fullname, override_path)

        return None


lib_changes_dir = '/opt/projects/Plone/site/backend/lib_changes'
override_mapping = generate_override_mapping(lib_changes_dir)

sys.meta_path.insert(0, CustomImportHook(override_mapping))

if __name__ == '__main__':
    sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
    sys.exit(main())

问题:我试图使用位于不同目录中的自定义实现来覆盖

zope.intid.__init__.py
(或任何
.__init__.py
文件)。但是,我遇到了一个问题,即无法正确识别包子模块,从而导致 ModuleNotFoundError 错误。例如,一旦 Hook 成功地将
zope.intid.__init__.py
的实现替换为位于
./lib_changes
的实现,诸如
import zope.intid.interfaces
之类的改进将不再起作用(错误:
ModuleNotFoundError: No module named 'zope.intid.interfaces'; 'zope.intid' is not a package

问题:如何正确覆盖 zope.intid 包(或任何

__init__.py
包文件)的
.__init__.py
而不破坏其子模块的导入?有没有办法正确设置自定义导入挂钩来处理这种情况?

python overriding python-importlib site-packages
1个回答
0
投票

事实证明我走在正确的道路上。感谢 @metatoaster,我遇到的问题是由于修补后的模块无法在导入解析问题时退回到默认模块位置。关键是还提供一组其他路径来尝试通过 的

submodule_search_locations
spec_from_file_location
功能:

..
class CustomImportHook:
def __init__(self, override_mapping):
    self.override_mapping = override_mapping
    self.override_counter = 0

def find_spec(self, fullname, path=None, target=None):

    if fullname in self.override_mapping:
        override_path = self.override_mapping[fullname]
        LOGGER.info(f"{fullname} Overide by {override_path}")
        self.override_counter += 1
                    
        return importlib.util.spec_from_file_location(
            fullname, 
            override_path,
            submodule_search_locations = [f'/../lib/python3.9/site-packages/<path-to-default-package']
        )

    return None
..
© www.soinside.com 2019 - 2024. All rights reserved.