Source code for hexrd.module_map

# The following dynamically generates aliases for the remapped modules based
# on the file_map
from collections import defaultdict
import importlib
import importlib.abc
import importlib.machinery
from pathlib import Path
import sys
from typing import Union


[docs]def path_to_module(path: Path) -> str: """ Convert a path to a module name. e.g. * "package_remapper/remapper.py" -> "package_remapper.remapper" * "package_remapper/__init__.py" -> "package_remapper" """ if path.suffix not in (".py", ""): raise ValueError(f"Expected a .py file, got {path}") path = path.with_suffix("") if path.parts[-1] == "__init__": path = path.parent return path.as_posix().replace("/", ".")
HEXRD_PACKAGE_PATH = Path(__file__).parent file_map: dict[Path, list[Path]] = defaultdict(list) with open(HEXRD_PACKAGE_PATH / "file_table.tsv", "r") as f: for line in f: if not line.strip(): continue kv = line.strip().split() if len(kv) != 2: continue k, v = line.strip().split() file_map[Path(k)].append(Path(v)) module_map: dict[str, tuple[str, Path]] = {} for old_path, new_paths in file_map.items(): if old_path.suffix not in ("", ".py") or not "hexrd" in old_path.parts: continue old_module_path = path_to_module(old_path) # Default to pick the core module if it exists. Otherwise pick the first one. selected_path = new_paths[0] for new_path in new_paths: if 'core' in new_path.parts: selected_path = new_path break module_map[old_module_path] = ( path_to_module(selected_path), HEXRD_PACKAGE_PATH.parent / selected_path, )
[docs]class ModuleAlias: def __init__(self, current_path: list[str]): self.current_path = current_path def __getattr__(self, name): full_path = self.current_path + [name] full_name = ".".join(full_path) if full_name in module_map: module, _fp = module_map[full_name] if isinstance(module, ModuleAlias): return module else: return importlib.import_module(module) current_module = ".".join(self.current_path) raise AttributeError(f"Module `{current_module}` has no attribute {name}")
flattened_module_map: dict[str, Union[ModuleAlias, str]] = {} for key, (mapped_module, _mapped_fp) in module_map.items(): parts = mapped_module.split(".") for i in range(len(parts) - 1): module = ".".join(parts[: i + 1]) if module not in flattened_module_map: flattened_module_map[module] = ModuleAlias(parts[:i]) flattened_module_map[key] = mapped_module
[docs]def get(alias: str) -> Union[ModuleAlias, str, None]: """ Returns the the module or an alias to it if it exists. """ if alias in flattened_module_map: return flattened_module_map[alias] return None
[docs]class ModuleSpecWithParent(importlib.machinery.ModuleSpec): def __init__(self, name, loader, *, origin=None, parent=None, is_package=False): super().__init__(name, loader, origin=origin, is_package=is_package) self._parent = parent @property def parent(self): return self._parent
[docs]class ModuleAliasImporter(importlib.abc.MetaPathFinder, importlib.abc.Loader):
[docs] def find_spec(self, fullname, path, target=None): if fullname in module_map: mapped_module, mapped_fp = module_map[fullname] # We only want to remap modules that go somewhere else. # If we are already trying to import something that exists, let # the other importers take care of it so we don't just loop forever. if fullname == mapped_module: return None return importlib.machinery.ModuleSpec( fullname, self, origin=mapped_fp.as_posix(), is_package=mapped_fp.name == "__init__.py", ) return None
[docs] def load_module(self, fullname): """ This is a deprecated implementation path, but it is a lot easier to do override it this way than to override it with create and exec_module. """ if fullname not in module_map: raise ImportError(f"Module {fullname} not found in module_map") mapped_module, _mapped_fp = module_map[fullname] base_mod = importlib.import_module(mapped_module) extra_candidates: list[str] = [] for old_path, new_paths in file_map.items(): if old_path.suffix not in ("", ".py") or not "hexrd" in old_path.parts: continue try: old_mod = path_to_module(old_path) except ValueError: continue if old_mod == fullname or old_mod.startswith(fullname + "."): for p in new_paths: candidate = path_to_module(p) if candidate != mapped_module: extra_candidates.append(candidate) if extra_candidates: seen = set() deduped: list[str] = [] for c in extra_candidates: if c not in seen: seen.add(c) deduped.append(c) for candidate in deduped: try: cand_mod = importlib.import_module(candidate) except Exception: continue if hasattr(base_mod, "__path__") and hasattr(cand_mod, "__path__"): try: for p in list(cand_mod.__path__): if p not in base_mod.__path__: base_mod.__path__.append(p) except Exception: pass base_all = getattr(base_mod, "__all__", None) cand_all = getattr(cand_mod, "__all__", None) if cand_all: if base_all is None: base_mod.__all__ = list(cand_all) else: for name in cand_all: if name not in base_all: base_all.append(name) base_mod.__all__ = base_all for name, val in cand_mod.__dict__.items(): if name in ( "__name__", "__file__", "__package__", "__path__", "__loader__", "__spec__", ): continue if name not in base_mod.__dict__: base_mod.__dict__[name] = val sys.modules[fullname] = base_mod return sys.modules[fullname]
# We need to redirect __all__ attempts to import hexrd things into our own # handler. sys.meta_path.insert(0, ModuleAliasImporter())