Conversation
|
Thanks for the review. All three findings addressed (pushed in
|
Both halves are solid now. The
On the disclosed overlap with #92090/#92122/#92516: keeping the |
|
Thanks for the re-review. All three nits are fixed and pushed (
Test results: |
…deps are missing The launcher (the top-level `hermes` entry script) now reports which interpreter it actually ran under, which dependency is missing, and the venv interpreter to use instead of dying with a bare ModuleNotFoundError (NousResearch#92882, NousResearch#90292, NousResearch#91504, NousResearch#80439). A ModuleNotFoundError for a `hermes_cli.*` module raised from inside the package is classified as an internal error with its real traceback, so a genuine bug in the checkout is never misreported as a missing dependency. Scope narrowed: the `Exec=` half (keeping the venv interpreter verbatim in the generated .desktop entry) landed on main via NousResearch#99492 / 6fe933e, which resolves the interpreter lexically, so this PR now carries only the launcher diagnostic half - no sibling PR touches the entry script.
b15fa34 to
81f7468
Compare
Problem
hermes desktop— and the generated~/.local/share/applications/hermes.desktop— die at import time with a bareModuleNotFoundError: No module named 'yaml'(or'dotenv') whenever the interpreter that ends up running thehermesentry script is not the install venv: a uv-managed CPython invoked directly (the issue's step 2), or anExec=whose interpreter escaped the venv.Scope (rebased after #99492 landed)
The
Exec=half of this PR — writingsys.executableverbatim inresolve_exec_command()instead ofPath(sys.executable).resolve()— is onmainnow: #99492 /6fe933e7099rewrotehermes_cli/linux_desktop_entry.pyto resolve the interpreter lexically (os.path.abspath(sys.executable)), which is the same fix by a better route. That half has been dropped here and the branch rebased onto currentmain, so the PR is no longer conflicting.What remains is the half no sibling PR touches: the repo's
hermesentry script. It does a barefrom hermes_cli.main import main, so any invocation under a dep-less interpreter surfaces as an opaque import traceback with no hint about which interpreter ran or where the deps live.Fix
The
hermeslauncher catches an import-timeModuleNotFoundErrorand exits 1 with an actionable diagnostic: the running interpreter (sys.executable), the missing module, and — when discoverable next to the checkout (venv/.venv+pyvenv.cfg) — the expected venv interpreter and the exact command to re-run.A
ModuleNotFoundErrorfor ahermes_cli.*module raised from a file inside the package is classified as an internal error (reported with the real traceback, never as a missing dependency), so the launcher guard cannot mask an import bug inhermes_cliitself.Re-review fixes (Enough1122 nits)
_report_launcher_failurerenders the re-run command withshlex.join(sys.argv[1:])instead of" ".join(...), sohermes gateway run --model "a b"comes out as... --model 'a b'and copy-pastes verbatim instead of flattening into two arguments.raise ... from errattribution —_classify_import_failureclassifies by the innermost frame of the deepest traceback in the__cause__chain, so an import failure re-raised viaraise ... from erris attributed to its original import site rather than to whatever re-raised it (which could sit outside the package and demote an internal bug to a "missing dependency"). The contract is pinned in the docstring.exc.nameis empty, the wholeNo module named 'x'sentence used to land in themissing dependency:field; it is now normalized to the bare module tokenx.Evidence / regression tests (RED pre-fix, GREEN with this patch)
test_launcher_fails_actionably_when_deps_are_missing— runs./hermesunderpython -S(site-packages stripped). Pre-fix stderr is the issue's exactTraceback ... No module named 'yaml'(samemain.py:723); post-fix it names the running interpreter + missing module + fix, with noTracebackand noModuleNotFoundError.test_classify_flags_hermes_cli_internal_import_as_bug/test_classify_reports_missing_third_party_dep/test_classify_hermes_cli_name_from_outside_is_not_internal— the import-failure classifier: ahermes_cli.*module missing from inside the package is an internal bug; a missing external module (yaml, dotenv, …) is a dependency problem even when the failing import sits insidehermes_cli; the samehermes_cli.*failure raised from outside the package is not internal.test_classify_attributes_re_raised_import_to_cause_chain— araise ... from err-wrapped import failure whose re-raise site is outsidehermes_cliis still classified as internal via the__cause__chain.test_classify_normalizes_unnamed_module_error_to_module_token—No module named 'x'with emptyexc.namenormalizes tox.test_report_launcher_failure_missing_dependency_field_is_bare_token— the report field carries only the module token.test_report_launcher_failure_fix_command_is_shell_quoted— argv entries with spaces must be shell-quoted in the suggested fix and round-trip through a POSIX shell.test_report_launcher_failure_distinguishes_internal_bug— the internal-bug report says "internal error", never "missing dependency".test_launcher_flags_internal_import_bug_not_missing_deps— end-to-end: a checkout whosehermes_cliimports its own missing submodule exits 1 with the internal-error diagnostic + real traceback, and is never misreported as a missing dependency.test_launcher_still_runs_cli_when_deps_are_present—./hermes --helpstill exits 0 with deps installed; the guard does not change the healthy path.Local runs on this rebased head:
pytest tests/hermes_cli/test_launcher.py→ 12 passed; the same suite againstmain'shermesscript → 10 failed, 2 passed (RED before / GREEN after);ruff check+ruff format --checkclean.Fixes #92882.