Skip to content

Commit c151e7c

Browse files
committed
Specific Exceptions: Adapting cantace interface
Part of #1046. Also publicly exposes a new exception helper: `error_check` that was previously part of the `usb2can` interface.
1 parent ec00893 commit c151e7c

File tree

3 files changed

+76
-53
lines changed

3 files changed

+76
-53
lines changed

can/exceptions.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@
1515
:class:`ValueError`. This should always be documented for the function at hand.
1616
"""
1717

18+
19+
from contextlib import contextmanager
20+
1821
from typing import Optional
22+
from typing import Type
1923

2024

2125
class CanError(Exception):
@@ -96,3 +100,18 @@ class CanTimeoutError(CanError, TimeoutError):
96100
- Some message could not be sent after the timeout elapsed
97101
- No message was read within the given time
98102
"""
103+
104+
105+
@contextmanager
106+
def error_check(
107+
error_message: Optional[str] = None,
108+
exception_type: Type[CanError] = CanOperationError,
109+
) -> None:
110+
"""Catches any exceptions and turns them into the new type while preserving the stack trace."""
111+
try:
112+
yield
113+
except Exception as error:
114+
if error_message is None:
115+
raise exception_type(str(error)) from error
116+
else:
117+
raise exception_type(error_message) from error

can/interfaces/cantact.py

Lines changed: 56 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@
77
from unittest.mock import Mock
88

99
from can import BusABC, Message
10+
from ..exceptions import CanInitializationError, error_check
1011

1112
logger = logging.getLogger(__name__)
1213

1314
try:
1415
import cantact
1516
except ImportError:
17+
cantact = None
1618
logger.warning(
17-
"The CANtact module is not installed. Install it using `python3 -m pip install cantact`"
19+
"The CANtact module is not installed. Install it using `python -m pip install cantact`"
1820
)
1921

2022

@@ -26,7 +28,7 @@ def _detect_available_configs():
2628
try:
2729
interface = cantact.Interface()
2830
except (NameError, SystemError):
29-
# couldn't import cantact, so no configurations are available
31+
logger.debug("Could not import cantact, so no configurations are available")
3032
return []
3133

3234
channels = []
@@ -42,7 +44,7 @@ def __init__(
4244
monitor=False,
4345
bit_timing=None,
4446
_testing=False,
45-
**kwargs
47+
**kwargs,
4648
):
4749
"""
4850
:param int channel:
@@ -58,36 +60,45 @@ def __init__(
5860
if _testing:
5961
self.interface = MockInterface()
6062
else:
61-
self.interface = cantact.Interface()
63+
if cantact is None:
64+
raise CanInitializationError(
65+
"The CANtact module is not installed. Install it using `python -m pip install cantact`"
66+
)
67+
with error_check(
68+
"Cannot create the cantact.Interface", CanInitializationError
69+
):
70+
self.interface = cantact.Interface()
6271

6372
self.channel = int(channel)
64-
self.channel_info = "CANtact: ch:%s" % channel
65-
66-
# configure the interface
67-
if bit_timing is None:
68-
# use bitrate
69-
self.interface.set_bitrate(int(channel), int(bitrate))
70-
else:
71-
# use custom bit timing
72-
self.interface.set_bit_timing(
73-
int(channel),
74-
int(bit_timing.brp),
75-
int(bit_timing.tseg1),
76-
int(bit_timing.tseg2),
77-
int(bit_timing.sjw),
78-
)
79-
self.interface.set_enabled(int(channel), True)
80-
self.interface.set_monitor(int(channel), monitor)
81-
self.interface.start()
73+
self.channel_info = f"CANtact: ch:{channel}"
74+
75+
# Configure the interface
76+
with error_check("Cannot setup the cantact.Interface", CanInitializationError):
77+
if bit_timing is None:
78+
# use bitrate
79+
self.interface.set_bitrate(int(channel), int(bitrate))
80+
else:
81+
# use custom bit timing
82+
self.interface.set_bit_timing(
83+
int(channel),
84+
int(bit_timing.brp),
85+
int(bit_timing.tseg1),
86+
int(bit_timing.tseg2),
87+
int(bit_timing.sjw),
88+
)
89+
self.interface.set_enabled(int(channel), True)
90+
self.interface.set_monitor(int(channel), monitor)
91+
self.interface.start()
8292

8393
super().__init__(
8494
channel=channel, bitrate=bitrate, poll_interval=poll_interval, **kwargs
8595
)
8696

8797
def _recv_internal(self, timeout):
88-
frame = self.interface.recv(int(timeout * 1000))
98+
with error_check("Cannot receive message"):
99+
frame = self.interface.recv(int(timeout * 1000))
89100
if frame is None:
90-
# timeout occured
101+
# timeout occurred
91102
return None, False
92103

93104
msg = Message(
@@ -103,31 +114,33 @@ def _recv_internal(self, timeout):
103114
return msg, False
104115

105116
def send(self, msg, timeout=None):
106-
self.interface.send(
107-
self.channel,
108-
msg.arbitration_id,
109-
bool(msg.is_extended_id),
110-
bool(msg.is_remote_frame),
111-
msg.dlc,
112-
msg.data,
113-
)
117+
with error_check("Cannot send message"):
118+
self.interface.send(
119+
self.channel,
120+
msg.arbitration_id,
121+
bool(msg.is_extended_id),
122+
bool(msg.is_remote_frame),
123+
msg.dlc,
124+
msg.data,
125+
)
114126

115127
def shutdown(self):
116-
self.interface.stop()
128+
with error_check("Cannot shutdown interface"):
129+
self.interface.stop()
117130

118131

119132
def mock_recv(timeout):
120133
if timeout > 0:
121-
frame = {}
122-
frame["id"] = 0x123
123-
frame["extended"] = False
124-
frame["timestamp"] = time.time()
125-
frame["loopback"] = False
126-
frame["rtr"] = False
127-
frame["dlc"] = 8
128-
frame["data"] = [1, 2, 3, 4, 5, 6, 7, 8]
129-
frame["channel"] = 0
130-
return frame
134+
return {
135+
"id": 0x123,
136+
"extended": False,
137+
"timestamp": time.time(),
138+
"loopback": False,
139+
"rtr": False,
140+
"dlc": 8,
141+
"data": [1, 2, 3, 4, 5, 6, 7, 8],
142+
"channel": 0,
143+
}
131144
else:
132145
# simulate timeout when timeout = 0
133146
return None
@@ -144,7 +157,6 @@ class MockInterface:
144157
set_bit_timing = Mock()
145158
set_enabled = Mock()
146159
set_monitor = Mock()
147-
start = Mock()
148160
stop = Mock()
149161
send = Mock()
150162
channel_count = Mock(return_value=1)

can/interfaces/usb2can/usb2canabstractionlayer.py

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
from ctypes import *
77
from enum import IntEnum
88
import logging
9-
from contextlib import contextmanager
109

1110
import can
11+
from ...exceptions import error_check
1212

1313
log = logging.getLogger("can.usb2can")
1414

@@ -102,14 +102,6 @@ class CanalMsg(Structure):
102102
]
103103

104104

105-
@contextmanager
106-
def error_check(error_message: str) -> None:
107-
try:
108-
yield
109-
except Exception as error:
110-
raise can.CanOperationError(error_message) from error
111-
112-
113105
class Usb2CanAbstractionLayer:
114106
"""A low level wrapper around the usb2can library.
115107

0 commit comments

Comments
 (0)