Files
yakpanel-core/class/public/hook_import.py
2026-04-07 02:04:22 +05:30

149 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Hook __import__
import builtins
import os
import sys
import public
import public.PluginLoader as plugin_loader
import types
import traceback
if 'class_v2/' not in sys.path and 'class_v2' not in sys.path:
sys.path.insert(0, 'class_v2/')
__basedir = public.get_panel_path()
__hooked = False
old__import__ = builtins.__import__
def hook_import():
global __hooked, __basedir
if __hooked:
return
def _aap__import__(name, globals = None, locals = None, fromlist = (), level = 0):
try:
return old__import__(name, globals, locals, fromlist, level)
except SyntaxError:
panel_path = __basedir
# 处理相对导入
if level > 0:
if not globals or '__package__' not in globals:
raise ImportError("Attempted relative import with no known parent package")
package = globals.get('__package__') or globals.get('__name__', '').rpartition('.')[0]
if not package and level > 0:
raise ImportError("Attempted relative import with no known parent package")
if level > 1:
parent_parts = package.split('.')
if len(parent_parts) < level - 1:
raise ImportError("Attempted relative import beyond top-level package")
package = '.'.join(parent_parts[:-level + 1])
absolute_name = f"{package}.{name}" if name else package
else:
absolute_name = name
# 如果模块已加载,直接返回
if absolute_name in sys.modules:
if fromlist is None or len(fromlist) == 0:
return sys.modules[absolute_name]
is_loaded = True
for name in fromlist:
if name == '*':
continue
if not hasattr(sys.modules[absolute_name], name):
is_loaded = False
break
if is_loaded:
return sys.modules[absolute_name]
module_path_part = absolute_name.replace('.', '/')
is_package_import = bool(fromlist)
for p in set(sys.path):
base_path = os.path.join(panel_path, p)
potential_file_path = os.path.realpath(os.path.join(base_path, module_path_part + '.py'))
potential_dir_path = os.path.realpath(os.path.join(base_path, module_path_part))
if os.path.isdir(potential_dir_path):
init_py = os.path.join(potential_dir_path, '__init__.py')
if os.path.exists(init_py) and os.path.getsize(init_py) > 10:
# 如果 __init__.py 存在并且不为空,作为常规包加载
top_module = plugin_loader.get_module(init_py)
else:
# 如果 __init__.py 不存在,创建一个空的模块对象来代表这个包
top_module = types.ModuleType(absolute_name)
top_module.__file__ = potential_dir_path
top_module.__path__ = [potential_dir_path]
top_module.__package__ = absolute_name
# 将创建的空模块放入 sys.modules 缓存
sys.modules[absolute_name] = top_module
if is_package_import:
for sub_module_name in fromlist:
if sub_module_name == '*':
continue
sub_module_path = os.path.join(potential_dir_path, sub_module_name + '.py')
if os.path.exists(sub_module_path):
try:
sub_module = plugin_loader.get_module(sub_module_path)
setattr(top_module, sub_module_name, sub_module)
except:
traceback.print_exc()
pass
return top_module
elif os.path.exists(potential_file_path):
m = plugin_loader.get_module(potential_file_path)
# 规范化子模块对象属性
parts = absolute_name.split('.')
m.__name__ = absolute_name
m.__file__ = potential_file_path
m.__package__ = '.'.join(parts[:-1]) if len(parts) > 1 else ''
# 注册完整模块名到 sys.modules
sys.modules[absolute_name] = m
# 确保父包链存在并把子模块设置为父包属性(父包的 __path__ 指向模块文件所在目录)
for i in range(1, len(parts)):
parent_name = '.'.join(parts[:i])
child_name = parts[i]
if parent_name not in sys.modules:
parent = types.ModuleType(parent_name)
parent.__package__ = parent_name
# 将父包的 __path__ 指向包含子模块的目录(保守设置)
parent.__path__ = [os.path.dirname(potential_file_path)]
sys.modules[parent_name] = parent
else:
parent = sys.modules[parent_name]
# 设置父包对下级模块的属性引用(使用已经注册的模块对象)
child_full = '.'.join(parts[:i + 1])
child_mod = sys.modules.get(child_full)
if child_mod is not None:
setattr(parent, child_name, child_mod)
# 如果导入没有指定 fromlist即通常的 `import a.b.c`),返回顶级包对象以匹配 CPython 行为
is_package_import = bool(fromlist)
if not is_package_import:
top_name = parts[0]
return sys.modules.get(top_name, m)
# 有 fromlist 时返回子模块对象
return m
raise
builtins.__import__ = _aap__import__
__hooked = True