From 00f36d15f55358bca32fd949c014c6168df1bd1c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 28 May 2018 14:47:40 +0200 Subject: [PATCH 01/22] added a ThreadSafeBus based on the wrapt library --- can/bus.py | 6 +-- can/thread_safe_bus.py | 96 ++++++++++++++++++++++++++++++++++++++++++ setup.py | 1 + 3 files changed, 100 insertions(+), 3 deletions(-) create mode 100644 can/thread_safe_bus.py diff --git a/can/bus.py b/can/bus.py index 9e70cb67e..9271c0030 100644 --- a/can/bus.py +++ b/can/bus.py @@ -169,10 +169,10 @@ def send_periodic(self, msg, period, duration=None): least *duration* seconds. """ - if not hasattr(self, "_lock"): + if not hasattr(self, "_lock_send_periodic"): # Create a send lock for this bus - self._lock = threading.Lock() - return ThreadBasedCyclicSendTask(self, self._lock, msg, period, duration) + self._lock_send_periodic = threading.Lock() + return ThreadBasedCyclicSendTask(self, self._lock_send_periodic, msg, period, duration) def __iter__(self): """Allow iteration on messages as they are received. diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py new file mode 100644 index 000000000..10bc2c0f1 --- /dev/null +++ b/can/thread_safe_bus.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# coding: utf-8 + +""" +""" + +from __future__ import print_function, absolute_import + +from threading import RLock + +from wrapt import ObjectProxy, synchronized + +from can import Bus, BusABC + + +class NullContextManager(object): + """ + A context manager that does nothing at all. + """ + + def __init__(self, resource=None): + self.resource = resource + + def __enter__(self): + return self.resource + + def __exit__(self, *args): + pass + + +class ThreadSafeBus(ObjectProxy, BusABC): + """ + Contains a thread safe :class:`can.BusABC` implementation that + wraps around an existing interface instance. All public methods + of that base class are now safe to be called from multiple threads. + + Use this as a drop in replacement for :class:`~can.BusABC`. + + .. note:: + + This approach assumes that both :meth:`~can.BusABC.send` and + :meth:`~can.BusABC._recv_internal` of the underlying bus instance can be + called simultaneously, and that the methods uses :meth:`~can.BusABC._recv_internal` + instead of :meth:`~can.BusABC.recv` directly. + """ + + # init locks for sending and receiving + _lock_send = RLock() + _lock_recv = RLock() + + def __init__(self, *args, **kwargs): + # now, BusABC.send_periodic() does not need a lock anymore, but the + # implementation still requires a context manager + self.__wrapped__._lock_send_periodic = NullContextManager() + + def recv(self, timeout=None, *args, **kwargs): + with self._lock_recv: + return self.__wrapped__.recv(timeout=timeout, *args, **kwargs) + + def send(self, msg, timeout=None, *args, **kwargs): + with self._lock_send: + return self.__wrapped__.send(msg, timeout=timeout, *args, **kwargs) + + # send_periodic does not need a lock, see that method and the comment in this __init__ + + @property + def filters(self): + with self._lock_recv: + return self.__wrapped__.filters + + @filters.setter + def filters(self, filters): + with self._lock_recv: + self.__wrapped__.filters = filters + + def set_filters(self, can_filters=None, *args, **kwargs): + with self._lock_recv: + return self.__wrapped__.set_filters(can_filters=can_filters, *args, **kwargs) + + def flush_tx_buffer(self, *args, **kwargs): + with self._lock_send: + return self.__wrapped__.flush_tx_buffer(*args, **kwargs) + + def shutdown(self, *args, **kwargs): + with self._lock_send, self._lock_recv: + return self.__wrapped__.shutdown(*args, **kwargs) + + @property + def state(self): + with self._lock_send, self._lock_recv: + return self.__wrapped__.state + + @state.setter + def state(self, new_state): + with self._lock_send, self._lock_recv: + self.__wrapped__.state = new_state diff --git a/setup.py b/setup.py index 0af5e8577..c70c985ea 100644 --- a/setup.py +++ b/setup.py @@ -56,6 +56,7 @@ python_requires=">=2.7,!=3.0,!=3.1,!=3.2,!=3.3", install_requires=[ 'setuptools', + 'wrapt ~= 1.10', ] + (['subprocess32 ~= 3.2.7'] if version_info.major < 3 else []), extras_require={ 'serial': ['pyserial >= 3.0'], From e9370834476ff7765c5416eebb720021fbd48dd0 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 28 May 2018 15:03:22 +0200 Subject: [PATCH 02/22] add TheradSafeBus to root __init__.py --- can/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/can/__init__.py b/can/__init__.py index ce126ff95..3fa8c01d1 100644 --- a/can/__init__.py +++ b/can/__init__.py @@ -34,6 +34,7 @@ class CanError(IOError): from .message import Message from .bus import BusABC +from .thread_safe_bus import ThreadSafeBus from .notifier import Notifier from .interfaces import VALID_INTERFACES from . import interface From 62ecb4ec69e2be6da0e12f403562d27dc0b4096b Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 28 May 2018 15:28:28 +0200 Subject: [PATCH 03/22] added tests for ThreadSafeBus --- can/thread_safe_bus.py | 3 +- test/back2back_test.py | 71 +++++++++++++++++++++++++++++++++++------- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 10bc2c0f1..1b7d6aae2 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -10,7 +10,8 @@ from wrapt import ObjectProxy, synchronized -from can import Bus, BusABC +from .interface import Bus +from .bus import BusABC class NullContextManager(object): diff --git a/test/back2back_test.py b/test/back2back_test.py index 304bec690..e9ebc698a 100644 --- a/test/back2back_test.py +++ b/test/back2back_test.py @@ -10,10 +10,11 @@ import sys import unittest from time import sleep +from multiprocessing.dummy import Pool as ThreadPool -import can +import pytest -from .data.example_data import generate_message +import can from .config import * from .data.example_data import generate_message @@ -35,16 +36,16 @@ class Back2BackTestCase(unittest.TestCase): """ def setUp(self): - self.bus1 = can.interface.Bus(channel=CHANNEL_1, - bustype=INTERFACE_1, - bitrate=BITRATE, - fd=TEST_CAN_FD, - single_handle=True) - self.bus2 = can.interface.Bus(channel=CHANNEL_2, - bustype=INTERFACE_2, - bitrate=BITRATE, - fd=TEST_CAN_FD, - single_handle=True) + self.bus1 = can.Bus(channel=CHANNEL_1, + bustype=INTERFACE_1, + bitrate=BITRATE, + fd=TEST_CAN_FD, + single_handle=True) + self.bus2 = can.Bus(channel=CHANNEL_2, + bustype=INTERFACE_2, + bitrate=BITRATE, + fd=TEST_CAN_FD, + single_handle=True) def tearDown(self): self.bus1.shutdown() @@ -170,5 +171,51 @@ def test_basics(self): notifier.stop() +class TestThreadSafeBus(Back2BackTestCase): + """Does some testing that is better than nothing. + """ + + def setUp(self): + self.bus1 = can.ThreadSafeBus(channel=CHANNEL_1, + bustype=INTERFACE_1, + bitrate=BITRATE, + fd=TEST_CAN_FD, + single_handle=True) + self.bus2 = can.ThreadSafeBus(channel=CHANNEL_2, + bustype=INTERFACE_2, + bitrate=BITRATE, + fd=TEST_CAN_FD, + single_handle=True) + + @pytest.mark.timeout(5.0) + def test_concurrent_writes(self): + sender_pool = ThreadPool(100) + receiver_pool = ThreadPool(100) + + message = can.Message( + arbitration_id=0x123, + extended_id=True, + timestamp=121334.365, + data=[254, 255, 1, 2] + ) + workload = 1000 * [message] + + def sender(msg): + self.bus1.send(msg) + + def receiver(_): + result = self.bus2.recv(timeout=2.0) + self.assertIsNotNone(result) + self.assertEqual(result, message) + + sender_pool.map_async(sender, workload) + receiver_pool.map_async(receiver, len(workload) * [None]) + + sender_pool.close() + sender_pool.join() + receiver_pool.close() + receiver_pool.join() + + if __name__ == '__main__': unittest.main() From cd147dc4647a16d5c889c97ecf48e2a7a1e65352 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 28 May 2018 15:40:07 +0200 Subject: [PATCH 04/22] try to fix constructor of ThreadSafeBus --- can/thread_safe_bus.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 1b7d6aae2..2ffb8eda7 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -50,6 +50,8 @@ class ThreadSafeBus(ObjectProxy, BusABC): _lock_recv = RLock() def __init__(self, *args, **kwargs): + super(ThreadSafeBus, self).__init__(Bus(*args, **kwargs)) + # now, BusABC.send_periodic() does not need a lock anymore, but the # implementation still requires a context manager self.__wrapped__._lock_send_periodic = NullContextManager() From 1b8414e6ca9879be551690847fff2e7ae8644943 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 28 May 2018 15:53:09 +0200 Subject: [PATCH 05/22] try to use metaclasses correctly --- can/thread_safe_bus.py | 3 ++- setup.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 2ffb8eda7..133d118b1 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -8,6 +8,7 @@ from threading import RLock +import six from wrapt import ObjectProxy, synchronized from .interface import Bus @@ -29,7 +30,7 @@ def __exit__(self, *args): pass -class ThreadSafeBus(ObjectProxy, BusABC): +class ThreadSafeBus(six.with_metaclass(ObjectProxy, BusABC)): """ Contains a thread safe :class:`can.BusABC` implementation that wraps around an existing interface instance. All public methods diff --git a/setup.py b/setup.py index c70c985ea..9acfa1934 100644 --- a/setup.py +++ b/setup.py @@ -57,6 +57,7 @@ install_requires=[ 'setuptools', 'wrapt ~= 1.10', + 'six ~= 1.11', ] + (['subprocess32 ~= 3.2.7'] if version_info.major < 3 else []), extras_require={ 'serial': ['pyserial >= 3.0'], From 7b1f5c2f479324a95239eaa1a89fa4a776384245 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 28 May 2018 16:00:45 +0200 Subject: [PATCH 06/22] explicitly require pytest --- setup.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 9acfa1934..e124b505e 100644 --- a/setup.py +++ b/setup.py @@ -21,10 +21,11 @@ # Dependencies tests_require = [ - 'mock >= 2.0.0', - 'nose >= 1.3.7', - 'pytest-timeout >= 1.2.1', - 'pyserial >= 3.0' + 'mock ~= 2.0', + 'nose ~= 1.3.7', + 'pytest ~= 3.6', + 'pytest-timeout ~= 1.2', + 'pyserial ~= 3.0' ] setup( From b66c610728d009b7e23d663c94e906a7dc54c5d9 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 28 May 2018 17:26:41 +0200 Subject: [PATCH 07/22] attempt to fix metaclass in ThreadSafeBus --- can/thread_safe_bus.py | 8 +++++--- setup.py | 1 - 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 133d118b1..507aa5b28 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -6,10 +6,10 @@ from __future__ import print_function, absolute_import +from abc import ABCMeta from threading import RLock -import six -from wrapt import ObjectProxy, synchronized +from wrapt import ObjectProxy from .interface import Bus from .bus import BusABC @@ -30,7 +30,7 @@ def __exit__(self, *args): pass -class ThreadSafeBus(six.with_metaclass(ObjectProxy, BusABC)): +class ThreadSafeBus(ObjectProxy, BusABC): """ Contains a thread safe :class:`can.BusABC` implementation that wraps around an existing interface instance. All public methods @@ -46,6 +46,8 @@ class ThreadSafeBus(six.with_metaclass(ObjectProxy, BusABC)): instead of :meth:`~can.BusABC.recv` directly. """ + __metaclass__ = ABCMeta + # init locks for sending and receiving _lock_send = RLock() _lock_recv = RLock() diff --git a/setup.py b/setup.py index e124b505e..04dab4463 100644 --- a/setup.py +++ b/setup.py @@ -58,7 +58,6 @@ install_requires=[ 'setuptools', 'wrapt ~= 1.10', - 'six ~= 1.11', ] + (['subprocess32 ~= 3.2.7'] if version_info.major < 3 else []), extras_require={ 'serial': ['pyserial >= 3.0'], From 458e0542e59dc6b7eda31e9e050e8f14e594742c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 28 May 2018 17:30:01 +0200 Subject: [PATCH 08/22] removed BusABC from ThreadSafeBus parent class --- can/thread_safe_bus.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 507aa5b28..4238f8eb5 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -30,7 +30,7 @@ def __exit__(self, *args): pass -class ThreadSafeBus(ObjectProxy, BusABC): +class ThreadSafeBus(ObjectProxy): """ Contains a thread safe :class:`can.BusABC` implementation that wraps around an existing interface instance. All public methods @@ -46,8 +46,6 @@ class ThreadSafeBus(ObjectProxy, BusABC): instead of :meth:`~can.BusABC.recv` directly. """ - __metaclass__ = ABCMeta - # init locks for sending and receiving _lock_send = RLock() _lock_recv = RLock() From b1761d58965958206b4939d724a5071a8903a4d6 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Mon, 28 May 2018 18:08:53 +0200 Subject: [PATCH 09/22] doc --- can/thread_safe_bus.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index 4238f8eb5..e62efc24e 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -46,7 +46,7 @@ class ThreadSafeBus(ObjectProxy): instead of :meth:`~can.BusABC.recv` directly. """ - # init locks for sending and receiving + # init locks for sending and receiving separately _lock_send = RLock() _lock_recv = RLock() From bd6c5d6b147cf61964f4b72f3e8b086afc34e19c Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 30 May 2018 18:51:14 +0200 Subject: [PATCH 10/22] added documentation --- can/bus.py | 4 ++++ can/thread_safe_bus.py | 8 +++++--- doc/api.rst | 6 ++++-- doc/bus.rst | 20 ++++++++++++++++++-- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/can/bus.py b/can/bus.py index 9271c0030..ffe48280b 100644 --- a/can/bus.py +++ b/can/bus.py @@ -191,6 +191,10 @@ def __iter__(self): @property def filters(self): + """ + Modify the filters of this bus. See :meth:`~can.BusABC.set_filters` + for details. + """ return self._filters @filters.setter diff --git a/can/thread_safe_bus.py b/can/thread_safe_bus.py index e62efc24e..b161d47c9 100644 --- a/can/thread_safe_bus.py +++ b/can/thread_safe_bus.py @@ -35,14 +35,15 @@ class ThreadSafeBus(ObjectProxy): Contains a thread safe :class:`can.BusABC` implementation that wraps around an existing interface instance. All public methods of that base class are now safe to be called from multiple threads. + The send and receive methods are synchronized separately. - Use this as a drop in replacement for :class:`~can.BusABC`. + Use this as a drop-in replacement for :class:`~can.BusABC`. .. note:: This approach assumes that both :meth:`~can.BusABC.send` and :meth:`~can.BusABC._recv_internal` of the underlying bus instance can be - called simultaneously, and that the methods uses :meth:`~can.BusABC._recv_internal` + called simultaneously, and that the methods use :meth:`~can.BusABC._recv_internal` instead of :meth:`~can.BusABC.recv` directly. """ @@ -65,7 +66,8 @@ def send(self, msg, timeout=None, *args, **kwargs): with self._lock_send: return self.__wrapped__.send(msg, timeout=timeout, *args, **kwargs) - # send_periodic does not need a lock, see that method and the comment in this __init__ + # send_periodic does not need a lock, since the underlying + # `send` method is already synchronized @property def filters(self): diff --git a/doc/api.rst b/doc/api.rst index 3eca7ddc8..13adb2903 100644 --- a/doc/api.rst +++ b/doc/api.rst @@ -25,9 +25,12 @@ Utilities .. automodule:: can.util :members: +.. automethod:: can.detect_available_configs .. _notifier: - + + + Notifier -------- @@ -35,4 +38,3 @@ The Notifier object is used as a message distributor for a bus. .. autoclass:: can.Notifier :members: - diff --git a/doc/bus.rst b/doc/bus.rst index d00cd211e..c71662289 100644 --- a/doc/bus.rst +++ b/doc/bus.rst @@ -4,7 +4,9 @@ Bus --- The :class:`~can.Bus` class, as the name suggests, provides an abstraction of a CAN bus. -The bus provides a wrapper around a physical or virtual CAN Bus. +The bus provides an abstract wrapper around a physical or virtual CAN Bus. + +A thread safe bus wrapper is also available, see "Thread safe bus". Filtering @@ -37,10 +39,24 @@ Receiving ''''''''' Reading from the bus is achieved by either calling the :meth:`~can.BusABC.recv` method or -by directly iterating over the bus:: +by directly iterating over the bus: for msg in bus: print(msg.data) Alternatively the :class:`~can.Listener` api can be used, which is a list of :class:`~can.Listener` subclasses that receive notifications when new messages arrive. + +Thread safe bus +--------------- + +This thread safe version of the bus class can be used by multiple threads at once. +Sending and receiving is locked seperatly to avoid unnessesary delays. + +It can be used exactly like the normal :class:`~can.Bus` class: + + my_bus = can.Bus(interface='socketcan', channel='vcan0') + my_bus.send(...) + +.. autoclass:: can.ThreadSafeBus + :members: From 78368f33af7696aa1e7b4e9bfbf1d7af5f63bd62 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 6 Jun 2018 23:39:24 +0200 Subject: [PATCH 11/22] fix proposed doc chnages --- doc/bus.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/bus.rst b/doc/bus.rst index c71662289..621baed8f 100644 --- a/doc/bus.rst +++ b/doc/bus.rst @@ -39,7 +39,7 @@ Receiving ''''''''' Reading from the bus is achieved by either calling the :meth:`~can.BusABC.recv` method or -by directly iterating over the bus: +by directly iterating over the bus:: for msg in bus: print(msg.data) @@ -55,7 +55,7 @@ Sending and receiving is locked seperatly to avoid unnessesary delays. It can be used exactly like the normal :class:`~can.Bus` class: - my_bus = can.Bus(interface='socketcan', channel='vcan0') + my_bus = can.ThreadSafeBus(interface='socketcan', channel='vcan0') my_bus.send(...) .. autoclass:: can.ThreadSafeBus From c82eea69e22e899019c6f01573abc47c83f6356a Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 6 Jun 2018 23:44:19 +0200 Subject: [PATCH 12/22] remove setuptools dependency --- setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/setup.py b/setup.py index 04dab4463..34a952850 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,6 @@ # see https://www.python.org/dev/peps/pep-0345/#version-specifiers python_requires=">=2.7,!=3.0,!=3.1,!=3.2,!=3.3", install_requires=[ - 'setuptools', 'wrapt ~= 1.10', ] + (['subprocess32 ~= 3.2.7'] if version_info.major < 3 else []), extras_require={ From efa99dc3249e2b5914955191fb25abfc42d78f88 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 6 Jun 2018 23:44:37 +0200 Subject: [PATCH 13/22] debugging commit for CI server --- can/interfaces/socketcan/socketcan_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/can/interfaces/socketcan/socketcan_common.py b/can/interfaces/socketcan/socketcan_common.py index 4e904f774..54412d04e 100644 --- a/can/interfaces/socketcan/socketcan_common.py +++ b/can/interfaces/socketcan/socketcan_common.py @@ -10,7 +10,7 @@ import errno import struct import sys -if sys.version_info[0] < 3 and os.name == 'posix': +if False: # TODO (sys.version_info[0] < 3 and os.name == 'posix'): import subprocess32 as subprocess else: import subprocess From b68afa4ea4b7ca6af17942e72ec281c7fa3da246 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 6 Jun 2018 23:53:16 +0200 Subject: [PATCH 14/22] removed the subprocess32 dependency --- can/interfaces/socketcan/socketcan_common.py | 5 +---- setup.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/can/interfaces/socketcan/socketcan_common.py b/can/interfaces/socketcan/socketcan_common.py index 54412d04e..1b3cec9e5 100644 --- a/can/interfaces/socketcan/socketcan_common.py +++ b/can/interfaces/socketcan/socketcan_common.py @@ -10,10 +10,7 @@ import errno import struct import sys -if False: # TODO (sys.version_info[0] < 3 and os.name == 'posix'): - import subprocess32 as subprocess -else: - import subprocess +import subprocess import re from can.interfaces.socketcan.socketcan_constants import CAN_EFF_FLAG diff --git a/setup.py b/setup.py index 34a952850..60fee70d4 100644 --- a/setup.py +++ b/setup.py @@ -57,7 +57,7 @@ python_requires=">=2.7,!=3.0,!=3.1,!=3.2,!=3.3", install_requires=[ 'wrapt ~= 1.10', - ] + (['subprocess32 ~= 3.2.7'] if version_info.major < 3 else []), + ], extras_require={ 'serial': ['pyserial >= 3.0'], 'neovi': ['python-ics >= 2.8'], From fdd5d2ae4e42b5117e6cac139fbdb1a193a4dbd2 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Wed, 6 Jun 2018 23:53:55 +0200 Subject: [PATCH 15/22] fix typo in test file name --- test/{sockectan_helpers.py => socketcan_helpers.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename test/{sockectan_helpers.py => socketcan_helpers.py} (96%) diff --git a/test/sockectan_helpers.py b/test/socketcan_helpers.py similarity index 96% rename from test/sockectan_helpers.py rename to test/socketcan_helpers.py index 846de8647..a8e3488d8 100644 --- a/test/sockectan_helpers.py +++ b/test/socketcan_helpers.py @@ -21,7 +21,7 @@ class TestSocketCanHelpers(unittest.TestCase): def test_error_code_to_str(self): """ Check that the function does not crash & always - returns a least one character. + returns at least one character. """ # all possible & also some invalid error codes From 976276f7c03b79b428d5c60173a17794d993dbf7 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 7 Jun 2018 00:01:42 +0200 Subject: [PATCH 16/22] debugging commit --- test/socketcan_helpers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/socketcan_helpers.py b/test/socketcan_helpers.py index a8e3488d8..8ec52d5fe 100644 --- a/test/socketcan_helpers.py +++ b/test/socketcan_helpers.py @@ -17,6 +17,9 @@ class TestSocketCanHelpers(unittest.TestCase): + def test_nothing(self): + pass # added so that this TestCase gets recognized + @unittest.skipUnless(IS_LINUX, "socketcan is only available on Linux") def test_error_code_to_str(self): """ From 06bc2a100f0dd72eadb063f6ddfa53491ddbf0a3 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Thu, 7 Jun 2018 00:11:25 +0200 Subject: [PATCH 17/22] undo last debugging commit --- test/socketcan_helpers.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/socketcan_helpers.py b/test/socketcan_helpers.py index 8ec52d5fe..a8e3488d8 100644 --- a/test/socketcan_helpers.py +++ b/test/socketcan_helpers.py @@ -17,9 +17,6 @@ class TestSocketCanHelpers(unittest.TestCase): - def test_nothing(self): - pass # added so that this TestCase gets recognized - @unittest.skipUnless(IS_LINUX, "socketcan is only available on Linux") def test_error_code_to_str(self): """ From f3cae06614c68f4b3cc20af122adeba41effe7b1 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 8 Jun 2018 15:17:27 +0200 Subject: [PATCH 18/22] renamed socketcan helpers test --- test/{socketcan_helpers.py => test_socketcan_helpers.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename test/{socketcan_helpers.py => test_socketcan_helpers.py} (100%) diff --git a/test/socketcan_helpers.py b/test/test_socketcan_helpers.py similarity index 100% rename from test/socketcan_helpers.py rename to test/test_socketcan_helpers.py From 26ffd06dd09913d8c5878023ec89b0f7b9b5e88b Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 8 Jun 2018 15:26:07 +0200 Subject: [PATCH 19/22] documentation improvements --- doc/bus.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/bus.rst b/doc/bus.rst index 621baed8f..4aca097bb 100644 --- a/doc/bus.rst +++ b/doc/bus.rst @@ -6,7 +6,7 @@ Bus The :class:`~can.Bus` class, as the name suggests, provides an abstraction of a CAN bus. The bus provides an abstract wrapper around a physical or virtual CAN Bus. -A thread safe bus wrapper is also available, see "Thread safe bus". +A thread safe bus wrapper is also available, see `Thread safe bus`_. Filtering @@ -16,7 +16,6 @@ Message filtering can be set up for each bus. Where the interface supports it, t out in the hardware or kernel layer - not in Python. - API '''' @@ -35,6 +34,7 @@ Transmitting Writing to the bus is done by calling the :meth:`~can.BusABC.send()` method and passing a :class:`~can.Message` object. + Receiving ''''''''' @@ -47,16 +47,20 @@ by directly iterating over the bus:: Alternatively the :class:`~can.Listener` api can be used, which is a list of :class:`~can.Listener` subclasses that receive notifications when new messages arrive. + Thread safe bus --------------- -This thread safe version of the bus class can be used by multiple threads at once. +This thread safe version of the :class:`~can.Bus` class can be used by multiple threads at once. Sending and receiving is locked seperatly to avoid unnessesary delays. +Conflicting calls are executed by blocking until the bus is accessible. -It can be used exactly like the normal :class:`~can.Bus` class: +It can be used exactly like the normal :class:`~can.Bus`: + # 'socketcan' is only an exemple interface, it works with all the others too my_bus = can.ThreadSafeBus(interface='socketcan', channel='vcan0') my_bus.send(...) + my_bus.recv(...) .. autoclass:: can.ThreadSafeBus :members: From db0b20cd780debe73f3567c28773b5da3c3bb1d0 Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 8 Jun 2018 15:27:27 +0200 Subject: [PATCH 20/22] corrected unit test --- test/test_socketcan_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_socketcan_helpers.py b/test/test_socketcan_helpers.py index a8e3488d8..bc28806ac 100644 --- a/test/test_socketcan_helpers.py +++ b/test/test_socketcan_helpers.py @@ -25,7 +25,7 @@ def test_error_code_to_str(self): """ # all possible & also some invalid error codes - test_data = range(0, 256) + (-1, 256, 5235, 346264) + test_data = [range(0, 256)] + (-1, 256, 5235, 346264) for error_code in test_data: string = error_code_to_str(error_code) From 3737bb38f75bcbeae8569ac3baa9d6d020ec8c6f Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 8 Jun 2018 15:42:15 +0200 Subject: [PATCH 21/22] fix unit test #2 --- test/test_socketcan_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_socketcan_helpers.py b/test/test_socketcan_helpers.py index bc28806ac..8cd1066d3 100644 --- a/test/test_socketcan_helpers.py +++ b/test/test_socketcan_helpers.py @@ -25,7 +25,7 @@ def test_error_code_to_str(self): """ # all possible & also some invalid error codes - test_data = [range(0, 256)] + (-1, 256, 5235, 346264) + test_data = [range(0, 256)] + [-1, 256, 5235, 346264] for error_code in test_data: string = error_code_to_str(error_code) From 0142bb8db905eeccbcdfdc9fad21b8111cfad48b Mon Sep 17 00:00:00 2001 From: Felix Divo Date: Fri, 8 Jun 2018 15:52:49 +0200 Subject: [PATCH 22/22] fix unit test #3 --- test/test_socketcan_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_socketcan_helpers.py b/test/test_socketcan_helpers.py index 8cd1066d3..f33a4e28a 100644 --- a/test/test_socketcan_helpers.py +++ b/test/test_socketcan_helpers.py @@ -25,7 +25,7 @@ def test_error_code_to_str(self): """ # all possible & also some invalid error codes - test_data = [range(0, 256)] + [-1, 256, 5235, 346264] + test_data = list(range(0, 256)) + [-1, 256, 5235, 346264] for error_code in test_data: string = error_code_to_str(error_code)