Skip to content

Commit 7081ec9

Browse files
committed
Merge remote-tracking branch 'upstream/develop' into develop
2 parents 04ddd8a + 270224c commit 7081ec9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+701
-595
lines changed

.mergify.yml

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
queue_rules:
22
- name: default
33
conditions:
4-
- "status-success=test,format" # "GitHub Actions works slightly differently [...], only the job name is used."
4+
- "status-success=test" # "GitHub Actions works slightly differently [...], only the job name is used."
5+
- "status-success=format"
56

67
pull_request_rules:
78
- name: Automatic merge passing PR on up to date branch with approving CR
@@ -10,7 +11,8 @@ pull_request_rules:
1011
- "#approved-reviews-by>=1"
1112
- "#review-requested=0"
1213
- "#changes-requested-reviews-by=0"
13-
- "status-success=test,format"
14+
- "status-success=test"
15+
- "status-success=format"
1416
- "label!=work-in-progress"
1517
actions:
1618
queue:

can/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

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

15-
rc: Dict[str, Any] = dict()
15+
rc: Dict[str, Any] = {}
1616

1717
from .listener import Listener, BufferedReader, RedirectReader, AsyncBufferedReader
1818

can/exceptions.py

+19
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/canalystii.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def __init__(
5353
elif isinstance(channel, int):
5454
self.channels = [channel]
5555
else: # Sequence[int]
56-
self.channels = [c for c in channel]
56+
self.channels = list(channel)
5757

5858
self.rx_queue = collections.deque(
5959
maxlen=rx_queue_size

can/interfaces/cantact.py

+64-46
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,20 @@
77
from unittest.mock import Mock
88

99
from can import BusABC, Message
10+
from ..exceptions import (
11+
CanInitializationError,
12+
CanInterfaceNotImplementedError,
13+
error_check,
14+
)
1015

1116
logger = logging.getLogger(__name__)
1217

1318
try:
1419
import cantact
1520
except ImportError:
21+
cantact = None
1622
logger.warning(
17-
"The CANtact module is not installed. Install it using `python3 -m pip install cantact`"
23+
"The CANtact module is not installed. Install it using `python -m pip install cantact`"
1824
)
1925

2026

@@ -25,13 +31,15 @@ class CantactBus(BusABC):
2531
def _detect_available_configs():
2632
try:
2733
interface = cantact.Interface()
28-
except (NameError, SystemError):
29-
# couldn't import cantact, so no configurations are available
34+
except (NameError, SystemError, AttributeError):
35+
logger.debug(
36+
"Could not import or instantiate cantact, so no configurations are available"
37+
)
3038
return []
3139

3240
channels = []
3341
for i in range(0, interface.channel_count()):
34-
channels.append({"interface": "cantact", "channel": "ch:%d" % i})
42+
channels.append({"interface": "cantact", "channel": f"ch:{i}"})
3543
return channels
3644

3745
def __init__(
@@ -42,7 +50,7 @@ def __init__(
4250
monitor=False,
4351
bit_timing=None,
4452
_testing=False,
45-
**kwargs
53+
**kwargs,
4654
):
4755
"""
4856
:param int channel:
@@ -58,36 +66,45 @@ def __init__(
5866
if _testing:
5967
self.interface = MockInterface()
6068
else:
61-
self.interface = cantact.Interface()
69+
if cantact is None:
70+
raise CanInterfaceNotImplementedError(
71+
"The CANtact module is not installed. Install it using `python -m pip install cantact`"
72+
)
73+
with error_check(
74+
"Cannot create the cantact.Interface", CanInitializationError
75+
):
76+
self.interface = cantact.Interface()
6277

6378
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()
79+
self.channel_info = f"CANtact: ch:{channel}"
80+
81+
# Configure the interface
82+
with error_check("Cannot setup the cantact.Interface", CanInitializationError):
83+
if bit_timing is None:
84+
# use bitrate
85+
self.interface.set_bitrate(int(channel), int(bitrate))
86+
else:
87+
# use custom bit timing
88+
self.interface.set_bit_timing(
89+
int(channel),
90+
int(bit_timing.brp),
91+
int(bit_timing.tseg1),
92+
int(bit_timing.tseg2),
93+
int(bit_timing.sjw),
94+
)
95+
self.interface.set_enabled(int(channel), True)
96+
self.interface.set_monitor(int(channel), monitor)
97+
self.interface.start()
8298

8399
super().__init__(
84100
channel=channel, bitrate=bitrate, poll_interval=poll_interval, **kwargs
85101
)
86102

87103
def _recv_internal(self, timeout):
88-
frame = self.interface.recv(int(timeout * 1000))
104+
with error_check("Cannot receive message"):
105+
frame = self.interface.recv(int(timeout * 1000))
89106
if frame is None:
90-
# timeout occured
107+
# timeout occurred
91108
return None, False
92109

93110
msg = Message(
@@ -103,31 +120,33 @@ def _recv_internal(self, timeout):
103120
return msg, False
104121

105122
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-
)
123+
with error_check("Cannot send message"):
124+
self.interface.send(
125+
self.channel,
126+
msg.arbitration_id,
127+
bool(msg.is_extended_id),
128+
bool(msg.is_remote_frame),
129+
msg.dlc,
130+
msg.data,
131+
)
114132

115133
def shutdown(self):
116-
self.interface.stop()
134+
with error_check("Cannot shutdown interface"):
135+
self.interface.stop()
117136

118137

119138
def mock_recv(timeout):
120139
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
140+
return {
141+
"id": 0x123,
142+
"extended": False,
143+
"timestamp": time.time(),
144+
"loopback": False,
145+
"rtr": False,
146+
"dlc": 8,
147+
"data": [1, 2, 3, 4, 5, 6, 7, 8],
148+
"channel": 0,
149+
}
131150
else:
132151
# simulate timeout when timeout = 0
133152
return None
@@ -144,7 +163,6 @@ class MockInterface:
144163
set_bit_timing = Mock()
145164
set_enabled = Mock()
146165
set_monitor = Mock()
147-
start = Mock()
148166
stop = Mock()
149167
send = Mock()
150168
channel_count = Mock(return_value=1)

can/interfaces/ics_neovi/neovi_bus.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -226,8 +226,8 @@ def channel_to_netid(channel_name_or_id):
226226
channel = getattr(ics, netid)
227227
else:
228228
raise ValueError(
229-
"channel must be an integer or " "a valid ICS channel name"
230-
)
229+
"channel must be an integer or a valid ICS channel name"
230+
) from None
231231
return channel
232232

233233
@staticmethod

can/interfaces/ixxat/canlib.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -604,12 +604,12 @@ def _inWaiting(self):
604604
return 1
605605

606606
def flush_tx_buffer(self):
607-
""" Flushes the transmit buffer on the IXXAT """
607+
"""Flushes the transmit buffer on the IXXAT"""
608608
# TODO #64: no timeout?
609609
_canlib.canChannelWaitTxEvent(self._channel_handle, constants.INFINITE)
610610

611611
def _recv_internal(self, timeout):
612-
""" Read a message from IXXAT device. """
612+
"""Read a message from IXXAT device."""
613613

614614
# TODO: handling CAN error messages?
615615
data_received = False

can/interfaces/ixxat/exceptions.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@
2121

2222

2323
class VCITimeout(CanTimeoutError):
24-
""" Wraps the VCI_E_TIMEOUT error """
24+
"""Wraps the VCI_E_TIMEOUT error"""
2525

2626

2727
class VCIError(CanOperationError):
28-
""" Try to display errors that occur within the wrapped C library nicely. """
28+
"""Try to display errors that occur within the wrapped C library nicely."""
2929

3030

3131
class VCIRxQueueEmptyError(VCIError):
32-
""" Wraps the VCI_E_RXQUEUE_EMPTY error """
32+
"""Wraps the VCI_E_RXQUEUE_EMPTY error"""
3333

3434
def __init__(self):
3535
super().__init__("Receive queue is empty")

can/interfaces/kvaser/canlib.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -713,11 +713,7 @@ def get_channel_info(channel):
713713
ctypes.sizeof(number),
714714
)
715715

716-
return "%s, S/N %d (#%d)" % (
717-
name.value.decode("ascii"),
718-
serial.value,
719-
number.value + 1,
720-
)
716+
return f"{name.value.decode('ascii')}, S/N {serial.value} (#{number.value + 1})"
721717

722718

723719
init_kvaser_library()

can/interfaces/neousys/neousys.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747

4848

4949
class NeousysCanSetup(Structure):
50-
""" C CAN Setup struct """
50+
"""C CAN Setup struct"""
5151

5252
_fields_ = [
5353
("bitRate", c_uint),
@@ -58,7 +58,7 @@ class NeousysCanSetup(Structure):
5858

5959

6060
class NeousysCanMsg(Structure):
61-
""" C CAN Message struct """
61+
"""C CAN Message struct"""
6262

6363
_fields_ = [
6464
("id", c_uint),
@@ -75,7 +75,7 @@ class NeousysCanMsg(Structure):
7575
# valid:1~4, Resynchronization Jump Width in time quanta
7676
# valid:1~1023, CAN_CLK divider used to determine time quanta
7777
class NeousysCanBitClk(Structure):
78-
""" C CAN BIT Clock struct """
78+
"""C CAN BIT Clock struct"""
7979

8080
_fields_ = [
8181
("syncPropPhase1Seg", c_ushort),

can/interfaces/nixnet.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
)
3131
except ImportError:
3232
logger.error("Error, NIXNET python module cannot be loaded.")
33-
raise ImportError()
33+
raise
3434
else:
3535
logger.error("NI-XNET interface is only available on Windows systems")
3636
raise NotImplementedError("NiXNET is not supported on not Win32 platforms")

can/interfaces/seeedstudio/seeedstudio.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def __init__(
113113
"could not create the serial device"
114114
) from error
115115

116-
super(SeeedBus, self).__init__(channel=channel, *args, **kwargs)
116+
super().__init__(channel=channel, *args, **kwargs)
117117
self.init_frame()
118118

119119
def shutdown(self):

0 commit comments

Comments
 (0)