Merge pull request #954 from jmolz/fix-prompt-manager-bare-excepts

fix: avoid swallowing prompt manager interrupts
This commit is contained in:
cybermaggedon 2026-05-26 16:57:38 +01:00 committed by GitHub
commit 00dd7a4e14
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 43 additions and 10 deletions

View file

@ -7,7 +7,7 @@ including template rendering, term merging, JSON validation, and error handling.
import pytest
import json
from unittest.mock import AsyncMock, MagicMock, patch
from unittest.mock import AsyncMock
from trustgraph.template.prompt_manager import PromptManager, PromptConfiguration, Prompt
@ -344,6 +344,42 @@ class TestPromptManager:
assert pm.terms == {} # Default empty terms
assert len(pm.prompts) == 0
def test_load_config_does_not_swallow_keyboard_interrupt(self, monkeypatch):
"""KeyboardInterrupt should propagate out of config parsing."""
pm = PromptManager()
def interrupt(_value):
raise KeyboardInterrupt
monkeypatch.setattr("trustgraph.template.prompt_manager.json.loads", interrupt)
with pytest.raises(KeyboardInterrupt):
pm.load_config({"system": json.dumps("Test")})
@pytest.mark.asyncio
async def test_json_parse_does_not_swallow_system_exit(self):
"""SystemExit should propagate out of JSON response parsing."""
pm = PromptManager()
config = {
"system": json.dumps("Test"),
"template-index": json.dumps(["json_response"]),
"template.json_response": json.dumps({
"prompt": "Generate JSON",
"response-type": "json"
})
}
pm.load_config(config)
def exit_parse(_text):
raise SystemExit(2)
pm.parse_json = exit_parse
mock_llm = AsyncMock()
mock_llm.return_value = "{}"
with pytest.raises(SystemExit):
await pm.invoke("json_response", {}, mock_llm)
@pytest.mark.unit
class TestPromptManagerJsonl:

View file

@ -31,12 +31,12 @@ class PromptManager:
try:
system = json.loads(config["system"])
except:
except (KeyError, TypeError, json.JSONDecodeError):
system = "Be helpful."
try:
ix = json.loads(config["template-index"])
except:
except (KeyError, TypeError, json.JSONDecodeError):
ix = []
prompts = {}
@ -68,8 +68,8 @@ class PromptManager:
try:
self.system_template = ibis.Template(self.config.system_template)
except:
raise RuntimeError("Error in system template")
except Exception as e:
raise RuntimeError(f"Error in system template: {e}")
self.templates = {}
for k, v in self.prompts.items():
@ -136,8 +136,6 @@ class PromptManager:
terms = self.terms | self.prompts[id].terms | input
resp_type = self.prompts[id].response_type
return self.templates[id].render(terms)
async def invoke(self, id, input, llm):
@ -161,7 +159,7 @@ class PromptManager:
if resp_type == "json":
try:
obj = self.parse_json(resp)
except:
except (json.JSONDecodeError, TypeError):
logger.error(f"JSON parse failed: {resp}")
raise RuntimeError("JSON parse fail")
@ -195,4 +193,3 @@ class PromptManager:
return objects
raise RuntimeError(f"Response type {resp_type} not known")