compat(plugins): warn once per name when a plugin resolves an old import path; lint step restored in CI

Every PLUGIN-COMPAT __getattr__ now calls hermes_cli.plugin_compat.warn_once(facade, name, target) before
resolving, emitting a HermesPluginCompatWarning (FutureWarning) once per process per name: old path, new
path, removal target. Importing a facade for its live API stays silent; only resolving a moved name warns.
COMPAT_MANIFEST.md documents the warning and how to silence it during migration.

Verified the runtime never routes through a pointer: every entry point (run_agent, cli, hermes_cli.main,
gateway.run, tui_gateway.server, web_server, model_tools + tool discovery, hermes_state, cron.scheduler,
browser_tool, mcp_tool, kanban, auth) imports clean and `hermes doctor` runs end to end with the warning
promoted to an error.

Also restores the check_compat_pointers CI step to .github/workflows/lint.yml, which a0be177aac dropped
when the compat layer was regenerated (the lint script itself was present; the workflow step was not).

hermes_cli/plugin_compat.py, tests/test_plugin_compat_warning.py and the two-line insert per facade are
part of the compat layer and go away with it.
This commit is contained in:
Teknium
2026-09-04 00:15:16 -07:00
parent 4ab96371d2
commit d63e380324
143 changed files with 376 additions and 1 deletions
+6
View File
@@ -178,3 +178,9 @@ jobs:
- name: Run footgun checker
run: python scripts/check-windows-footguns.py --all
# The Sep 2026 decomposition kept old import paths alive for external plugins
# (PLUGIN-COMPAT blocks, see COMPAT_MANIFEST.md). They are removed on schedule by
# reverting one commit, so in-tree code must never depend on them.
- name: Forbid in-tree use of plugin-compat pointers
run: python scripts/check_compat_pointers.py
+5
View File
@@ -10,6 +10,11 @@ date, by reverting that commit. Update your plugin to import from the `new locat
Nothing inside this repository is allowed to use these pointers (`scripts/check_compat_pointers.py`
fails CI if it does).
**You will see a warning.** The first time a process resolves a name through one of these blocks, Hermes emits a
`HermesPluginCompatWarning` (a `FutureWarning`) naming the old path, the new path, and the removal target — once per
name per process. Fix the import and it goes away. To silence during migration:
`python -W ignore::hermes_cli.plugin_compat.HermesPluginCompatWarning` or `warnings.filterwarnings("ignore", category=HermesPluginCompatWarning)`.
**Scope.** Only PUBLIC names (no leading underscore) that were defined or imported at module top level
before the decomposition are covered. Private names (`_foo`, `_TG_NAME_LIMIT`, `_clamp_telegram_names`,
...) were never part of any surface and are NOT restored; a plugin that patched or imported one must move
+2
View File
@@ -1005,5 +1005,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -2322,5 +2322,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -713,5 +713,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -80,5 +80,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -4912,5 +4912,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1594,5 +1594,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -66,5 +66,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -187,5 +187,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -181,5 +181,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1605,5 +1605,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -205,5 +205,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -633,5 +633,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -377,5 +377,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -354,5 +354,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -727,5 +727,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -101,5 +101,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -54,5 +54,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -65,5 +65,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -65,5 +65,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1023,5 +1023,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -65,5 +65,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -220,5 +220,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -4646,5 +4646,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -3889,5 +3889,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -77,5 +77,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -305,5 +305,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -327,5 +327,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -129,5 +129,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -281,5 +281,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -994,5 +994,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -612,5 +612,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -5506,5 +5506,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1216,5 +1216,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1269,5 +1269,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -954,5 +954,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -100,5 +100,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -2229,5 +2229,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -234,5 +234,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -720,5 +720,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -3905,5 +3905,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -243,5 +243,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -198,5 +198,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -228,5 +228,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -6190,5 +6190,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -4276,5 +4276,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -317,5 +317,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -299,5 +299,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -3427,5 +3427,7 @@ def __getattr__(name): # PEP 562 — chained onto the module's own __getattr__
if target is None:
return _plugin_compat_prev_getattr(name)
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -236,5 +236,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1071,5 +1071,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1518,5 +1518,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -2468,5 +2468,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -741,5 +741,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
@@ -1085,5 +1085,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -413,5 +413,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+38
View File
@@ -0,0 +1,38 @@
"""Once-per-process notice for external plugins still importing from pre-decomposition module paths.
The Sep 2026 decomposition (PR #102117) moved most of Hermes's internals into ``<stem>_<topic>`` sibling
modules. Old paths keep resolving through ``PLUGIN-COMPAT`` blocks so plugins have time to update; each
resolution through such a block calls :func:`warn_once` so the plugin author sees, exactly once per process
per name, where the code went and when the old path disappears. Internal Hermes code never reaches this
(``scripts/check_compat_pointers.py`` fails CI if it does).
This module is part of the compat layer and is removed with it.
"""
from __future__ import annotations
import os
import warnings
# Set by the maintainers when the removal is scheduled; surfaces in every warning and in COMPAT_MANIFEST.md.
COMPAT_REMOVAL = os.environ.get("HERMES_PLUGIN_COMPAT_REMOVAL", "the next minor release after the announced date (see COMPAT_MANIFEST.md)")
class HermesPluginCompatWarning(FutureWarning):
"""A plugin imported a name from its pre-decomposition module path."""
_seen: set[tuple[str, str]] = set()
def warn_once(facade: str, name: str, target_module: str, target_name: str) -> None:
key = (facade, name)
if key in _seen:
return
_seen.add(key)
new = f"{target_module}.{target_name}" if target_name != name else f"{target_module}.{name}"
warnings.warn(
f"hermes plugin compat: `{facade}.{name}` moved to `{new}`. The old path is kept only for external "
f"plugins and is removed in {COMPAT_REMOVAL}; update your import.",
HermesPluginCompatWarning,
stacklevel=3,
)
+2
View File
@@ -2096,5 +2096,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -476,5 +476,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -887,5 +887,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -807,5 +807,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -378,5 +378,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1082,5 +1082,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -455,5 +455,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -702,5 +702,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1823,5 +1823,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -244,5 +244,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1364,5 +1364,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -177,5 +177,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -133,5 +133,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -86,5 +86,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -280,5 +280,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -173,5 +173,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -180,5 +180,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
@@ -317,5 +317,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -174,5 +174,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -130,5 +130,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -538,5 +538,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -163,5 +163,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -339,5 +339,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1014,5 +1014,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -611,5 +611,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -773,5 +773,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -7370,5 +7370,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -465,5 +465,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -1633,5 +1633,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -362,5 +362,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -821,5 +821,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -6519,5 +6519,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -932,5 +932,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -53,5 +53,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -333,5 +333,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -336,5 +336,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -145,5 +145,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -65,5 +65,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -235,5 +235,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -86,5 +86,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -343,5 +343,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----
+2
View File
@@ -102,5 +102,7 @@ def __getattr__(name): # PEP 562 — lazy so no import cycles
if target is None:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
import importlib
from hermes_cli.plugin_compat import warn_once
warn_once(__name__, name, *target)
return getattr(importlib.import_module(target[0]), target[1])
# ---- END PLUGIN-COMPAT ----

Some files were not shown because too many files have changed in this diff Show More