Skip to content

Commit 0c34e50

Browse files
karldinghardbyte
authored andcommitted
Fix RecursionError in Message.__getattr__
__getattr__ incorrectly assumes that self._dict always exists. However, if self._dict doesn't exist, the function attempts to call __getattr__ again, which results in infinite recursion when serializing the object via pickle. This fixes the implementation of __getattr__ and adds a test to exercise pickling/unpickling the Message. Fixes #804
1 parent ed58372 commit 0c34e50

File tree

2 files changed

+34
-3
lines changed

2 files changed

+34
-3
lines changed

can/message.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ def __getattr__(self, key):
5353
# TODO keep this for a version, in order to not break old code
5454
# this entire method (as well as the _dict attribute in __slots__ and the __setattr__ method)
5555
# can be removed in 4.0
56-
# this method is only called if the attribute was not found elsewhere, like in __slots__
56+
# this method is only called if the attribute was not found elsewhere, like in __slots_
57+
if key not in self.__slots__:
58+
raise AttributeError
5759
try:
5860
warnings.warn("Custom attributes of messages are deprecated and will be removed in 4.0", DeprecationWarning)
5961
return self._dict[key]

test/test_message_class.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
import sys
66
from math import isinf, isnan
77
from copy import copy, deepcopy
8+
import pickle
89

910
from hypothesis import given, settings, reproduce_failure
1011
import hypothesis.strategies as st
1112

1213
from can import Message
1314

15+
from .message_helper import ComparingMessagesTestCase
16+
1417

1518
class TestMessageClass(unittest.TestCase):
1619
"""
@@ -70,7 +73,7 @@ def test_methods(self, **kwargs):
7073

7174
# check copies and equalities
7275
if is_valid:
73-
self.assertEqual(message, message)
76+
self.assertEqual(message, message)
7477
normal_copy = copy(message)
7578
deep_copy = deepcopy(message)
7679
for other in (normal_copy, deep_copy, message):
@@ -79,5 +82,31 @@ def test_methods(self, **kwargs):
7982
self.assertTrue(message.equals(other, timestamp_delta=0))
8083

8184

82-
if __name__ == '__main__':
85+
class MessageSerialization(unittest.TestCase, ComparingMessagesTestCase):
86+
def __init__(self, *args, **kwargs):
87+
unittest.TestCase.__init__(self, *args, **kwargs)
88+
ComparingMessagesTestCase.__init__(
89+
self, allowed_timestamp_delta=0.016, preserves_channel=True
90+
)
91+
92+
def test_serialization(self):
93+
message = Message(
94+
timestamp=1.0,
95+
arbitration_id=0x401,
96+
is_extended_id=False,
97+
is_remote_frame=False,
98+
is_error_frame=False,
99+
channel=1,
100+
dlc=6,
101+
data=bytearray([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]),
102+
is_fd=False,
103+
)
104+
105+
serialized = pickle.dumps(message, -1)
106+
deserialized = pickle.loads(serialized)
107+
108+
self.assertMessageEqual(message, deserialized)
109+
110+
111+
if __name__ == "__main__":
83112
unittest.main()

0 commit comments

Comments
 (0)