diff --git a/Pyro5/serializers.py b/Pyro5/serializers.py index 82100ac..dee7f27 100644 --- a/Pyro5/serializers.py +++ b/Pyro5/serializers.py @@ -422,7 +422,7 @@ def dumps(self, data): return msgpack.packb(data, use_bin_type=True, default=self.default) def loadsCall(self, data): - return msgpack.unpackb(self._convertToBytes(data), raw=False, object_hook=self.object_hook) + return msgpack.unpackb(self._convertToBytes(data), raw=False, object_hook=self.object_hook, ext_hook=self.ext_hook) def loads(self, data): return msgpack.unpackb(self._convertToBytes(data), raw=False, object_hook=self.object_hook, ext_hook=self.ext_hook) diff --git a/tests/test_serialize.py b/tests/test_serialize.py index 6ab92b0..92b46c0 100644 --- a/tests/test_serialize.py +++ b/tests/test_serialize.py @@ -111,6 +111,49 @@ class TestJsonSerializer(TestSerpentSerializer): class TestMsgpackSerializer(TestSerpentSerializer): serializer = Pyro5.serializers.serializers["msgpack"] + import msgpack + + class Thing: + def __init__(self, value): + self.value = value + + class CustomMsgpackSerializer(Pyro5.serializers.MsgpackSerializer): + CUSTOM_EXT_CODE = 0x40 + + def default(self, obj): + if isinstance(obj, Thing): + return msgpack.ExtType(self.CUSTOM_EXT_CODE, str(obj.value).encode("ascii")) + return super().default(obj) + + def ext_hook(self, code, data): + if code == self.CUSTOM_EXT_CODE: + return Thing(int(data)) + return super().ext_hook(code, data) + + class TestMsgpackExtHookLoadsCall: + """Regression: MsgpackSerializer.loadsCall must honor ext_hook so + custom-ExtType-encoded call arguments round-trip when a subclass + overrides ext_hook for custom codes. Without the fix, loadsCall + returns raw msgpack.ExtType objects instead of decoded values.""" + + serializer = CustomMsgpackSerializer() + + def testLoadsCallHonorsExtHook(self): + data = self.serializer.dumpsCall("obj-id", "method-name", (Thing(42),), {}) + obj, method, vargs, kwargs = self.serializer.loadsCall(data) + assert obj == "obj-id" + assert method == "method-name" + assert isinstance(vargs[0], Thing) + assert vargs[0].value == 42 + assert kwargs == {} + + def testLoadsAlreadyHonorsExtHook(self): + """Sanity check on the sibling that always worked, so future + regressions on either method surface symmetrically.""" + data = self.serializer.dumps(Thing(7)) + back = self.serializer.loads(data) + assert isinstance(back, Thing) + assert back.value == 7 class TestSerializer2_serpent: SERIALIZER = "serpent"