99"""
1010
1111from __future__ import absolute_import , division
12-
12+
1313import warnings
1414from copy import deepcopy
15+ from math import isinf , isnan
16+
1517
1618class Message (object ):
1719 """
@@ -53,7 +55,7 @@ def __getattr__(self, key):
5355 # can be removed in 4.0
5456 # this method is only called if the attribute was not found elsewhere, like in __slots__
5557 try :
56- warnings .warn ("Custom attributes of messages are deprecated and will be removed in the next major version " , DeprecationWarning )
58+ warnings .warn ("Custom attributes of messages are deprecated and will be removed in 4.0 " , DeprecationWarning )
5759 return self ._dict [key ]
5860 except KeyError :
5961 raise AttributeError ("'message' object has no attribute '{}'" .format (key ))
@@ -63,26 +65,26 @@ def __setattr__(self, key, value):
6365 try :
6466 super (Message , self ).__setattr__ (key , value )
6567 except AttributeError :
66- warnings .warn ("Custom attributes of messages are deprecated and will be removed in the next major version " , DeprecationWarning )
68+ warnings .warn ("Custom attributes of messages are deprecated and will be removed in 4.0 " , DeprecationWarning )
6769 self ._dict [key ] = value
6870
6971 @property
7072 def id_type (self ):
7173 # TODO remove in 4.0
72- warnings .warn ("Message.id_type is deprecated, use is_extended_id" , DeprecationWarning )
74+ warnings .warn ("Message.id_type is deprecated and will be removed in 4.0 , use is_extended_id instead " , DeprecationWarning )
7375 return self .is_extended_id
7476
7577 @id_type .setter
7678 def id_type (self , value ):
7779 # TODO remove in 4.0
78- warnings .warn ("Message.id_type is deprecated, use is_extended_id" , DeprecationWarning )
80+ warnings .warn ("Message.id_type is deprecated and will be removed in 4.0 , use is_extended_id instead " , DeprecationWarning )
7981 self .is_extended_id = value
8082
8183 def __init__ (self , timestamp = 0.0 , arbitration_id = 0 , is_extended_id = None ,
8284 is_remote_frame = False , is_error_frame = False , channel = None ,
8385 dlc = None , data = None ,
8486 is_fd = False , bitrate_switch = False , error_state_indicator = False ,
85- extended_id = True , # deprecated in 3.x, removed in 4.x
87+ extended_id = None , # deprecated in 3.x, TODO remove in 4.x
8688 check = False ):
8789 """
8890 To create a message object, simply provide any of the below attributes
@@ -100,10 +102,15 @@ def __init__(self, timestamp=0.0, arbitration_id=0, is_extended_id=None,
100102
101103 self .timestamp = timestamp
102104 self .arbitration_id = arbitration_id
105+
106+ if extended_id is not None :
107+ # TODO remove in 4.0
108+ warnings .warn ("The extended_id parameter is deprecated and will be removed in 4.0, use is_extended_id instead" , DeprecationWarning )
109+
103110 if is_extended_id is not None :
104111 self .is_extended_id = is_extended_id
105112 else :
106- self .is_extended_id = extended_id
113+ self .is_extended_id = True if extended_id is None else extended_id
107114
108115 self .is_remote_frame = is_remote_frame
109116 self .is_error_frame = is_error_frame
@@ -162,18 +169,19 @@ def __str__(self):
162169 field_strings .append (" " * 24 )
163170
164171 if (self .data is not None ) and (self .data .isalnum ()):
165- try :
166- field_strings .append ("'{}'" .format (self .data .decode ('utf-8' )))
167- except UnicodeError :
168- pass
172+ field_strings .append ("'{}'" .format (self .data .decode ('utf-8' , 'replace' )))
169173
170174 if self .channel is not None :
171- field_strings .append ("Channel: {}" .format (self .channel ))
175+ try :
176+ field_strings .append ("Channel: {}" .format (self .channel ))
177+ except UnicodeEncodeError :
178+ pass
172179
173180 return " " .join (field_strings ).strip ()
174181
175182 def __len__ (self ):
176- return len (self .data )
183+ # return the dlc such that it also works on remote frames
184+ return self .dlc
177185
178186 def __bool__ (self ):
179187 # For Python 3
@@ -255,33 +263,47 @@ def _check(self):
255263 """Checks if the message parameters are valid.
256264 Assumes that the types are already correct.
257265
258- :raises AssertionError : iff one or more attributes are invalid
266+ :raises ValueError : iff one or more attributes are invalid
259267 """
260268
261- assert 0.0 <= self .timestamp , "the timestamp may not be negative"
269+ if self .timestamp < 0.0 :
270+ raise ValueError ("the timestamp may not be negative" )
271+ if isinf (self .timestamp ):
272+ raise ValueError ("the timestamp may not be infinite" )
273+ if isnan (self .timestamp ):
274+ raise ValueError ("the timestamp may not be NaN" )
262275
263- assert not ( self .is_remote_frame and self .is_error_frame ), \
264- "a message cannot be a remote and an error frame at the sane time"
276+ if self .is_remote_frame and self .is_error_frame :
277+ raise ValueError ( "a message cannot be a remote and an error frame at the sane time" )
265278
266- assert 0 <= self .arbitration_id , "arbitration IDs may not be negative"
279+ if self .arbitration_id < 0 :
280+ raise ValueError ("arbitration IDs may not be negative" )
267281
268282 if self .is_extended_id :
269- assert self .arbitration_id < 0x20000000 , "Extended arbitration IDs must be less than 2^29"
270- else :
271- assert self .arbitration_id < 0x800 , "Normal arbitration IDs must be less than 2^11"
283+ if 0x20000000 <= self .arbitration_id :
284+ raise ValueError ("Extended arbitration IDs must be less than 2^29" )
285+ elif 0x800 <= self .arbitration_id :
286+ raise ValueError ("Normal arbitration IDs must be less than 2^11" )
272287
273- assert 0 <= self .dlc , "DLC may not be negative"
288+ if self .dlc < 0 :
289+ raise ValueError ("DLC may not be negative" )
274290 if self .is_fd :
275- assert self .dlc <= 64 , "DLC was {} but it should be <= 64 for CAN FD frames" .format (self .dlc )
276- else :
277- assert self .dlc <= 8 , "DLC was {} but it should be <= 8 for normal CAN frames" .format (self .dlc )
291+ if 64 < self .dlc :
292+ raise ValueError ("DLC was {} but it should be <= 64 for CAN FD frames" .format (self .dlc ))
293+ elif 8 < self .dlc :
294+ raise ValueError ("DLC was {} but it should be <= 8 for normal CAN frames" .format (self .dlc ))
278295
279- if not self .is_remote_frame :
280- assert self .dlc == len (self .data ), "the length of the DLC and the length of the data must match up"
296+ if self .is_remote_frame :
297+ if self .data is not None and len (self .data ) != 0 :
298+ raise ValueError ("remote frames may not carry any data" )
299+ elif self .dlc != len (self .data ):
300+ raise ValueError ("the DLC and the length of the data must match up for non remote frames" )
281301
282302 if not self .is_fd :
283- assert not self .bitrate_switch , "bitrate switch is only allowed for CAN FD frames"
284- assert not self .error_state_indicator , "error stat indicator is only allowed for CAN FD frames"
303+ if self .bitrate_switch :
304+ raise ValueError ("bitrate switch is only allowed for CAN FD frames" )
305+ if self .error_state_indicator :
306+ raise ValueError ("error stat indicator is only allowed for CAN FD frames" )
285307
286308 def equals (self , other , timestamp_delta = 1.0e-6 ):
287309 """
@@ -299,7 +321,7 @@ def equals(self, other, timestamp_delta=1.0e-6):
299321 # see https://github.com/hardbyte/python-can/pull/413 for a discussion
300322 # on why a delta of 1.0e-6 was chosen
301323 return (
302- # check for identity first
324+ # check for identity first and finish fast
303325 self is other or
304326 # then check for equality by value
305327 (
0 commit comments