owenrusk.dev

errands

small jobs the runtime does for the studio.

git clone https://owenrusk.dev/errands.git

commit e54d8441b1718081d696efa73bf8ac3745f197ad
parent f40a7fead8ca92caae2bff2fed343c5d62a78289
author Owen Rusk <owen@papermothgames.com>
date   2025-05-06 11:50:03 -0500
tools: errors come back as json, not a traceback
errands/tools.py+5-2
tests/test_tools.py+5-0
diff --git a/errands/tools.py b/errands/tools.py
index ae2e847..03ccc38 100644
--- a/errands/tools.py
+++ b/errands/tools.py
@@ -41,6 +41,9 @@ def hosting_pay(amount: str) -> dict:
 
 
 def call(line: str) -> str:
-    request = json.loads(line)
-    result = TOOLS[request["tool"]](**request.get("args", {}))
+    try:
+        request = json.loads(line)
+        result = TOOLS[request["tool"]](**request.get("args", {}))
+    except Exception as error:
+        return json.dumps({"ok": False, "error": f"{type(error).__name__}: {error}"})
     return json.dumps({"ok": True, "result": result})
diff --git a/tests/test_tools.py b/tests/test_tools.py
index 8dba143..a6d22ab 100644
--- a/tests/test_tools.py
+++ b/tests/test_tools.py
@@ -13,5 +13,10 @@ class CallTest(unittest.TestCase):
             del tools.TOOLS["echo"]
         self.assertEqual(out, {"ok": True, "result": "hi"})
 
+    def test_errors_are_json(self):
+        out = json.loads(tools.call(json.dumps({"tool": "no_such_tool"})))
+        self.assertEqual(out, {"ok": False, "error": "KeyError: 'no_such_tool'"})
+        self.assertFalse(json.loads(tools.call("not json"))["ok"])
+
     def test_mail_tools_are_there(self):
         self.assertLessEqual({"mail_unread", "mail_reply", "mail_send"}, set(tools.TOOLS))