From 23f1bd813a9c2dc086d68182d72e284608eccf39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Noel?= Date: Fri, 27 Jun 2025 10:32:06 +0200 Subject: [PATCH 1/3] varlink.error: Instantiate MethodNotFound from VarlinkError.new --- varlink/error.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/varlink/error.py b/varlink/error.py index d72e872..255537d 100644 --- a/varlink/error.py +++ b/varlink/error.py @@ -32,6 +32,9 @@ def new(cls, message, namespaced=False): elif message["error"] == "org.varlink.service.MethodNotImplemented": return MethodNotImplemented.new(message, namespaced) + elif message["error"] == "org.varlink.service.MethodNotFound": + return MethodNotFound.new(message, namespaced) + else: return cls(message, namespaced) From 912fc6aa161f7d259b307a261346075f9ca19e1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Behrmann?= Date: Sat, 8 Nov 2025 12:51:25 +0100 Subject: [PATCH 2/3] client: drop namespaced from VarlinkError.new in _next_varlink_message The relevant code is in essence the following decoded = json.loads(message) if "error" in decoded and decoded["error"] is not None: raise VarlinkError.new(decoded) where message is at first an incoming Varlink message that is string-encoded JSON. This means at this point the thing being handed to VarlinkError.new is a dictionary. The namespaced keyword being passed in before, would tell VarlinkError to handle the incoming thing as a SimpleNamespace, thus leading to breakage. An alternative at this point might be to instead add an object hook if namespaced is true, but since the instantiation of the error object will directly normalise this to a dictionary and the relevant methods on the error have their own namespace options, this seems gratuitous. --- varlink/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/varlink/client.py b/varlink/client.py index fd92b5c..157a5e4 100644 --- a/varlink/client.py +++ b/varlink/client.py @@ -87,7 +87,7 @@ def _next_varlink_message(self): if "error" in message and message["error"] is not None: self._in_use = False - e = VarlinkError.new(message, self._namespaced) + e = VarlinkError.new(message) raise e else: return message["parameters"], ("continues" in message) and message["continues"] From 5d6005a8a0f9d3103e3bf16f3288949b427e8f03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Noel?= Date: Sat, 8 Nov 2025 12:46:09 +0100 Subject: [PATCH 3/3] tests: Adds a new test file checking errors serialization/deserialization. --- varlink/tests/test_error.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 varlink/tests/test_error.py diff --git a/varlink/tests/test_error.py b/varlink/tests/test_error.py new file mode 100644 index 0000000..b61c28b --- /dev/null +++ b/varlink/tests/test_error.py @@ -0,0 +1,34 @@ +import json +import unittest + +import varlink + + +class OneMessageClientHandler(varlink.ClientInterfaceHandler): + def __init__(self, interface, namespaced, next_message): + # No interface but we do not use them + super().__init__(interface, namespaced) + self.next_message = next_message + + def _next_message(self): + yield self.next_message + + +class TestError(unittest.TestCase): + def test_pack_unpack(self): + dummy_if = varlink.Interface("interface org.example.dummy") + for error in [ + varlink.InterfaceNotFound("org.varlink.notfound"), + varlink.MethodNotFound("Method"), + varlink.MethodNotImplemented("Abstract"), + varlink.InvalidParameter("Struct.param"), + ]: + for namespaced in (True, False): + with self.subTest(error=error, namespaced=namespaced): + # encode error + encoded = json.dumps(error, cls=varlink.VarlinkEncoder) + + # Emulates the client receiving an error + handler = OneMessageClientHandler(dummy_if, namespaced, encoded) + with self.assertRaises(error.__class__): + handler._next_varlink_message()