66
77import can .typechecking
88
9- from abc import ABCMeta , abstractmethod
9+ from abc import ABC , ABCMeta , abstractmethod
1010import can
1111import logging
1212import threading
1313from time import time
1414from enum import Enum , auto
1515
16- from can .broadcastmanager import ThreadBasedCyclicSendTask
16+ from can .broadcastmanager import ThreadBasedCyclicSendTask , CyclicSendTaskABC
1717from can .message import Message
1818
1919LOG = logging .getLogger (__name__ )
@@ -61,7 +61,7 @@ def __init__(
6161 :param dict kwargs:
6262 Any backend dependent configurations are passed in this dictionary
6363 """
64- self ._periodic_tasks : List [can . broadcastmanager . CyclicSendTaskABC ] = []
64+ self ._periodic_tasks : List [_SelfRemovingCyclicTask ] = []
6565 self .set_filters (can_filters )
6666
6767 def __str__ (self ) -> str :
@@ -151,7 +151,7 @@ def _recv_internal(
151151 raise NotImplementedError ("Trying to read from a write only bus?" )
152152
153153 @abstractmethod
154- def send (self , msg : Message , timeout : Optional [float ] = None ):
154+ def send (self , msg : Message , timeout : Optional [float ] = None ) -> None :
155155 """Transmit a message to the CAN bus.
156156
157157 Override this method to enable the transmit path.
@@ -172,7 +172,7 @@ def send(self, msg: Message, timeout: Optional[float] = None):
172172
173173 def send_periodic (
174174 self ,
175- msgs : Union [Sequence [ Message ], Message ],
175+ msgs : Union [Message , Sequence [ Message ] ],
176176 period : float ,
177177 duration : Optional [float ] = None ,
178178 store_task : bool = True ,
@@ -188,7 +188,7 @@ def send_periodic(
188188 - the task's :meth:`CyclicTask.stop()` method is called.
189189
190190 :param msgs:
191- Messages to transmit
191+ Message(s) to transmit
192192 :param period:
193193 Period in seconds between each message
194194 :param duration:
@@ -215,26 +215,35 @@ def send_periodic(
215215 appropriate as the stopped tasks are still taking up memory as they
216216 are associated with the Bus instance.
217217 """
218- if not isinstance (msgs , (list , tuple )):
219- if isinstance (msgs , Message ):
220- msgs = [msgs ]
221- else :
222- raise ValueError ("Must be either a list, tuple, or a Message" )
223- if not msgs :
224- raise ValueError ("Must be at least a list or tuple of length 1" )
225- task = self ._send_periodic_internal (msgs , period , duration )
218+ if isinstance (msgs , Message ):
219+ msgs = [msgs ]
220+ elif isinstance (msgs , Sequence ):
221+ # A Sequence does not necessarily provide __bool__ we need to use len()
222+ if len (msgs ) == 0 :
223+ raise ValueError ("Must be a sequence at least of length 1" )
224+ else :
225+ raise ValueError ("Must be either a message or a sequence of messages" )
226+
227+ # Create a backend specific task; will be patched to a _SelfRemovingCyclicTask later
228+ task = cast (
229+ _SelfRemovingCyclicTask ,
230+ self ._send_periodic_internal (msgs , period , duration ),
231+ )
232+
226233 # we wrap the task's stop method to also remove it from the Bus's list of tasks
234+ periodic_tasks = self ._periodic_tasks
227235 original_stop_method = task .stop
228236
229- def wrapped_stop_method (remove_task = True ):
237+ def wrapped_stop_method (remove_task : bool = True ) -> None :
238+ nonlocal task , periodic_tasks , original_stop_method
230239 if remove_task :
231240 try :
232- self . _periodic_tasks .remove (task )
241+ periodic_tasks .remove (task )
233242 except ValueError :
234- pass
243+ pass # allow the task to be already removed
235244 original_stop_method ()
236245
237- setattr ( task , " stop" , wrapped_stop_method )
246+ task . stop = wrapped_stop_method # type: ignore
238247
239248 if store_task :
240249 self ._periodic_tasks .append (task )
@@ -273,13 +282,13 @@ def _send_periodic_internal(
273282 )
274283 return task
275284
276- def stop_all_periodic_tasks (self , remove_tasks = True ):
285+ def stop_all_periodic_tasks (self , remove_tasks : bool = True ) -> None :
277286 """Stop sending any messages that were started using **bus.send_periodic**.
278287
279288 .. note::
280289 The result is undefined if a single task throws an exception while being stopped.
281290
282- :param bool remove_tasks:
291+ :param remove_tasks:
283292 Stop tracking the stopped tasks.
284293 """
285294 for task in self ._periodic_tasks :
@@ -288,7 +297,7 @@ def stop_all_periodic_tasks(self, remove_tasks=True):
288297 task .stop (remove_task = False )
289298
290299 if remove_tasks :
291- self ._periodic_tasks = []
300+ self ._periodic_tasks . clear ()
292301
293302 def __iter__ (self ) -> Iterator [Message ]:
294303 """Allow iteration on messages as they are received.
@@ -317,7 +326,9 @@ def filters(self) -> Optional[can.typechecking.CanFilters]:
317326 def filters (self , filters : Optional [can .typechecking .CanFilters ]):
318327 self .set_filters (filters )
319328
320- def set_filters (self , filters : Optional [can .typechecking .CanFilters ] = None ):
329+ def set_filters (
330+ self , filters : Optional [can .typechecking .CanFilters ] = None
331+ ) -> None :
321332 """Apply filtering to all messages received by this Bus.
322333
323334 All messages that match at least one filter are returned.
@@ -342,7 +353,7 @@ def set_filters(self, filters: Optional[can.typechecking.CanFilters] = None):
342353 self ._filters = filters or None
343354 self ._apply_filters (self ._filters )
344355
345- def _apply_filters (self , filters : Optional [can .typechecking .CanFilters ]):
356+ def _apply_filters (self , filters : Optional [can .typechecking .CanFilters ]) -> None :
346357 """
347358 Hook for applying the filters to the underlying kernel or
348359 hardware if supported/implemented by the interface.
@@ -387,10 +398,10 @@ def _matches_filters(self, msg: Message) -> bool:
387398 # nothing matched
388399 return False
389400
390- def flush_tx_buffer (self ):
401+ def flush_tx_buffer (self ) -> None :
391402 """Discard every message that may be queued in the output buffer(s)."""
392403
393- def shutdown (self ):
404+ def shutdown (self ) -> None :
394405 """
395406 Called to carry out any interface specific cleanup required
396407 in shutting down a bus.
@@ -432,3 +443,10 @@ def _detect_available_configs() -> List[can.typechecking.AutoDetectedConfig]:
432443
433444 def fileno (self ) -> int :
434445 raise NotImplementedError ("fileno is not implemented using current CAN bus" )
446+
447+
448+ class _SelfRemovingCyclicTask (CyclicSendTaskABC , ABC ):
449+ """Removes itself from a bus. Only needed for typing :meth:`Bus._periodic_tasks`. Do not instantiate."""
450+
451+ def stop (self , remove_task : bool = True ) -> None :
452+ raise NotImplementedError ()
0 commit comments