_alias.py 2.0 KB

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