-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconftest.py
More file actions
82 lines (72 loc) · 3.18 KB
/
Copy pathconftest.py
File metadata and controls
82 lines (72 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# SPDX-License-Identifier: MIT OR Apache-2.0
"""Shared pytest/conftest for the builder plugin.
Single source of truth for path resolution so every test + the standalone
verify.py agree on where the plugin and hermes-agent live, and on how to load
the plugin module headlessly. Previously each test file recomputed these paths
independently (with inconsistent parent-depth counts), which broke if the plugin
was ever nested differently.
"""
import importlib.util
import os
import sys
import tempfile
import types
import warnings
from pathlib import Path
import yaml
# Suppress StarletteDeprecationWarning at import time (before pytest's
# filterwarnings config takes effect). This comes from the starlette version
# Hermes core bundles; upgrading starlette here would diverge from the runtime.
try:
from starlette.exceptions import StarletteDeprecationWarning
warnings.filterwarnings("ignore", category=StarletteDeprecationWarning)
except ImportError:
pass
PLUGIN_DIR = Path(__file__).resolve().parent
# Hermes core (hermes-agent) lives at <HERMES_HOME>/hermes-agent. Resolve from
# the REAL HERMES_HOME (env or the conventional ~/.hermes default) BEFORE this
# module redirects HERMES_HOME to a throwaway test profile below. Resolving by
# relative parent depth breaks when the repo is checked out at a path other than
# <HERMES_HOME>/plugins/builder (e.g. CI's `plugin-src` checkout); resolving from
# HERMES_HOME keeps the same source of truth the plugin itself uses.
_REAL_HERMES_HOME = Path(os.environ.get("HERMES_HOME") or Path.home() / ".hermes")
HERMES_AGENT_DIR = _REAL_HERMES_HOME / "hermes-agent"
# Make the plugin and hermes-agent importable.
for p in (str(PLUGIN_DIR), str(HERMES_AGENT_DIR)):
if Path(p).exists() and p not in sys.path:
sys.path.insert(0, p)
# A throwaway profile so plugin discovery exercises the installed-user-plugin
# path without reading or writing the real Hermes profile. Discovery scans
# ``HERMES_HOME/plugins`` and user plugins are opt-in, so the isolated profile
# must contain both the plugin link and its enabled-config entry. Without this,
# tests that import model_tools only prove that an empty profile cannot discover
# builder.
TEST_HERMES_HOME = Path(tempfile.mkdtemp(prefix="builder-"))
os.environ["HERMES_HOME"] = str(TEST_HERMES_HOME)
(TEST_HERMES_HOME / "plugins").mkdir(parents=True)
(TEST_HERMES_HOME / "plugins" / "builder").symlink_to(
PLUGIN_DIR,
target_is_directory=True,
)
(TEST_HERMES_HOME / "config.yaml").write_text(
yaml.safe_dump({"plugins": {"enabled": ["builder"]}}),
encoding="utf-8",
)
def load_plugin(slug: str = "builder") -> types.ModuleType:
"""Load the plugin __init__ as a module without installing it."""
ns = "hermes_plugins"
if ns not in sys.modules:
pkg = types.ModuleType(ns)
pkg.__path__ = []
pkg.__package__ = ns
sys.modules[ns] = pkg
mn = f"{ns}.{slug}"
spec = importlib.util.spec_from_file_location(
mn, PLUGIN_DIR / "__init__.py", submodule_search_locations=[str(PLUGIN_DIR)]
)
mod = importlib.util.module_from_spec(spec)
mod.__package__ = mn
mod.__path__ = [str(PLUGIN_DIR)]
sys.modules[mn] = mod
spec.loader.exec_module(mod)
return mod