172 lines
5.8 KiB
Python
172 lines
5.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Copyright © IQ.Lvbs, apart of Project Teal Lvbs, All Rights Reserved, licensed under https://konn3kt.com/tos
|
|
"""
|
|
import importlib
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from types import ModuleType
|
|
|
|
|
|
class ProprietaryModuleMissing(ImportError):
|
|
pass
|
|
|
|
|
|
def _iter_proprietary_python_roots() -> list[Path]:
|
|
roots: list[Path] = []
|
|
|
|
env_root_raw = os.environ.get("IQPILOT_PROPRIETARY_ROOT", "").strip()
|
|
if env_root_raw:
|
|
roots.append(Path(env_root_raw))
|
|
|
|
repo_root = Path(__file__).resolve().parents[1]
|
|
# Primary runtime install path on device.
|
|
roots.append(repo_root / ".iqpilot")
|
|
# Dev/build artifact bundle roots.
|
|
roots.append(repo_root / "artifacts" / "iqpilot_model_selector_private")
|
|
roots.append(repo_root / "artifacts" / "iqpilot_navd_private")
|
|
roots.append(repo_root / "artifacts" / "iqpilot_hephaestusd_private")
|
|
roots.append(repo_root / "artifacts" / "iqpilot_alc_private")
|
|
|
|
return [root / "python" for root in roots]
|
|
|
|
|
|
def _iter_repo_roots() -> list[Path]:
|
|
roots: list[Path] = []
|
|
seen: set[Path] = set()
|
|
this_file = Path(__file__).resolve()
|
|
|
|
for parent in this_file.parents:
|
|
if parent in seen:
|
|
continue
|
|
|
|
if ((parent / "konn3kt_private").exists() or
|
|
(parent / "iqpilot" / "models_private_src").exists() or
|
|
(parent / "opendbc_repo").exists()):
|
|
roots.append(parent)
|
|
seen.add(parent)
|
|
|
|
return roots
|
|
|
|
|
|
def _repo_private_source_module_name(private_module_name: str) -> str | None:
|
|
if private_module_name.startswith("iqpilot_private.models."):
|
|
return private_module_name.replace("iqpilot_private.models.", "iqpilot.models_private_src.", 1)
|
|
if private_module_name.startswith("iqpilot_private.navd."):
|
|
return private_module_name.replace("iqpilot_private.navd.", "konn3kt_private.navd.", 1)
|
|
if private_module_name.startswith("iqpilot_private.konn3kt.hephaestus."):
|
|
return private_module_name.replace("iqpilot_private.konn3kt.hephaestus.", "konn3kt_private.hephaestus.", 1)
|
|
if private_module_name.startswith("iqpilot_private.konn3kt.iqlvbs."):
|
|
return private_module_name.replace("iqpilot_private.konn3kt.iqlvbs.", "konn3kt_private.iqlvbs.", 1)
|
|
return None
|
|
|
|
|
|
def _load_repo_private_source(private_module_name: str) -> ModuleType | None:
|
|
fallback_module_name = _repo_private_source_module_name(private_module_name)
|
|
if fallback_module_name is None:
|
|
return None
|
|
|
|
for repo_root in _iter_repo_roots():
|
|
repo_root_str = str(repo_root)
|
|
if repo_root_str not in sys.path:
|
|
sys.path.insert(0, repo_root_str)
|
|
|
|
try:
|
|
return importlib.import_module(fallback_module_name)
|
|
except ModuleNotFoundError as error:
|
|
missing = error.name or ""
|
|
if missing == fallback_module_name or missing.startswith(f"{fallback_module_name}."):
|
|
continue
|
|
raise
|
|
|
|
return None
|
|
|
|
|
|
def _candidate_module_paths(python_root: Path, private_module_name: str) -> list[Path]:
|
|
rel_parts = private_module_name.split(".")
|
|
module_base = python_root.joinpath(*rel_parts)
|
|
paths = [
|
|
module_base.with_suffix(".py"),
|
|
module_base.with_suffix(".pyc"),
|
|
]
|
|
paths.extend(module_base.parent.glob(f"{module_base.name}.*.so"))
|
|
paths.append(module_base / "__init__.py")
|
|
paths.append(module_base / "__init__.pyc")
|
|
paths.extend(module_base.glob("__init__.*.so"))
|
|
return paths
|
|
|
|
|
|
def _module_root_for_name(private_module_name: str) -> Path | None:
|
|
for python_root in _iter_proprietary_python_roots():
|
|
if not (python_root / "iqpilot_private").exists():
|
|
continue
|
|
if any(path.exists() for path in _candidate_module_paths(python_root, private_module_name)):
|
|
return python_root
|
|
return None
|
|
|
|
|
|
def _ensure_private_path(private_module_name: str) -> None:
|
|
resolved_root = _module_root_for_name(private_module_name)
|
|
if resolved_root is not None:
|
|
resolved_root_str = str(resolved_root)
|
|
if resolved_root_str not in sys.path:
|
|
sys.path.insert(0, resolved_root_str)
|
|
return
|
|
|
|
for python_root in _iter_proprietary_python_roots():
|
|
if (python_root / "iqpilot_private").exists():
|
|
python_root_str = str(python_root)
|
|
if python_root_str not in sys.path:
|
|
sys.path.insert(0, python_root_str)
|
|
return
|
|
|
|
|
|
def _is_private_module_missing(error: ModuleNotFoundError, private_module_name: str) -> bool:
|
|
missing = error.name or ""
|
|
parts = private_module_name.split(".")
|
|
valid_missing = {".".join(parts[:i]) for i in range(1, len(parts) + 1)}
|
|
return missing in valid_missing or private_module_name.startswith(f"{missing}.")
|
|
|
|
|
|
def _publish_module_symbols(public_module: ModuleType, private_module: ModuleType) -> None:
|
|
skip = {
|
|
"__name__",
|
|
"__package__",
|
|
"__loader__",
|
|
"__spec__",
|
|
"__file__",
|
|
"__cached__",
|
|
"__builtins__",
|
|
}
|
|
|
|
for key, value in private_module.__dict__.items():
|
|
if key in skip:
|
|
continue
|
|
public_module.__dict__[key] = value
|
|
|
|
public_module.__dict__["__private_module__"] = private_module.__name__
|
|
if "__all__" not in public_module.__dict__:
|
|
public_module.__dict__["__all__"] = [k for k in private_module.__dict__ if not k.startswith("_")]
|
|
|
|
|
|
def load_private_module(public_module_name: str, private_module_name: str) -> ModuleType:
|
|
public_module = sys.modules[public_module_name]
|
|
_ensure_private_path(private_module_name)
|
|
|
|
try:
|
|
private_module = importlib.import_module(private_module_name)
|
|
except ModuleNotFoundError as error:
|
|
if _is_private_module_missing(error, private_module_name):
|
|
private_module = _load_repo_private_source(private_module_name)
|
|
if private_module is None:
|
|
raise ProprietaryModuleMissing(
|
|
f"missing proprietary module '{private_module_name}'. "
|
|
"install the IQ Pilot private proprietary bundle into IQPILOT_PROPRIETARY_ROOT"
|
|
) from error
|
|
else:
|
|
raise
|
|
|
|
_publish_module_symbols(public_module, private_module)
|
|
return private_module
|