我正在开发一个项目,我需要覆盖位于 ./lib/python3.9/site-packages" 目录中的文件。我有以下钩子,它使用
importlib.util
和 sys.meta_path.insert
:
覆盖文件位于
./lib_changes/*
。
Hook 适用于除
zope+intid.__init__.py
文件之外的所有以下文件
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
而不破坏其子模块的导入?有没有办法正确设置自定义导入挂钩来处理这种情况?
事实证明我走在正确的道路上。感谢 @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
..