# -*- coding: utf-8 -*- r"""旧导入前缀 → 新导入前缀的**别名**(仅用于模块实现包搬迁的过渡期;下个版本删除)。 背景(用户令 2026-09-28 新结构):实现包由 `<模块>/common//` 上提到 `<模块>//`。 旧写法 `from app_X.common.app_X_guanlan.api import …` 要在一个版本内继续可用,且**不能产生两份模块对象** (两份 = 两份全局状态,isinstance/缓存/单例会静默失效)。 做法:往 `sys.meta_path` 装一个只认旧前缀的 finder;命中时把 `create_module` 直接指向新前缀的模块对象, 于是 `sys.modules[旧名] is sys.modules[新名]`(同名同体),子模块也逐个这样映射,不预先 import 任何实现。 """ from __future__ import annotations import importlib import importlib.abc import importlib.util import sys class _PrefixAlias(importlib.abc.MetaPathFinder, importlib.abc.Loader): def __init__(self, old: str, new: str) -> None: self.old = old self.new = new def find_spec(self, fullname, path=None, target=None): # noqa: D102 if fullname == self.old or fullname.startswith(self.old + '.'): return importlib.util.spec_from_loader(fullname, self) return None def create_module(self, spec): # noqa: D102 new_name = self.new + spec.name[len(self.old):] mod = importlib.import_module(new_name) sys.modules[spec.name] = mod # 同名同体 return mod def exec_module(self, module) -> None: # noqa: D102 pass # 实现已执行过,无需再跑 def alias_prefix(old: str, new: str) -> None: """把 `old` 前缀(含其子模块)别名到 `new` 前缀;重复调用幂等。""" for f in sys.meta_path: if isinstance(f, _PrefixAlias) and f.old == old and f.new == new: return sys.meta_path.insert(0, _PrefixAlias(old, new)) __all__ = ['alias_prefix']