Skip to content

Commit 28513eb

Browse files
authored
Merge pull request #1238 from zariiii9003/io_typing
Improve IO typing
2 parents cb58f9a + 55c5fdf commit 28513eb

File tree

12 files changed

+131
-123
lines changed

12 files changed

+131
-123
lines changed

can/__init__.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,6 @@
2424
CanTimeoutError,
2525
)
2626

27-
from .io import Logger, SizedRotatingLogger, Printer, LogReader, MessageSync
28-
from .io import ASCWriter, ASCReader
29-
from .io import BLFReader, BLFWriter
30-
from .io import CanutilsLogReader, CanutilsLogWriter
31-
from .io import CSVWriter, CSVReader
32-
from .io import SqliteWriter, SqliteReader
33-
3427
from .util import set_logging_level
3528

3629
from .message import Message
@@ -42,6 +35,13 @@
4235
from .interface import Bus, detect_available_configs
4336
from .bit_timing import BitTiming
4437

38+
from .io import Logger, SizedRotatingLogger, Printer, LogReader, MessageSync
39+
from .io import ASCWriter, ASCReader
40+
from .io import BLFReader, BLFWriter
41+
from .io import CanutilsLogReader, CanutilsLogWriter
42+
from .io import CSVWriter, CSVReader
43+
from .io import SqliteWriter, SqliteReader
44+
4545
from .broadcastmanager import (
4646
CyclicSendTaskABC,
4747
LimitedDurationCyclicSendTaskABC,

can/io/asc.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,10 @@
1212
import time
1313
import logging
1414

15-
from .. import typechecking
1615
from ..message import Message
17-
from ..listener import Listener
1816
from ..util import channel2int
19-
from .generic import BaseIOHandler, FileIOMessageWriter
20-
from ..typechecking import AcceptedIOType
17+
from .generic import FileIOMessageWriter, MessageReader
18+
from ..typechecking import StringPathLike
2119

2220

2321
CAN_MSG_EXT = 0x80000000
@@ -28,7 +26,7 @@
2826
logger = logging.getLogger("can.io.asc")
2927

3028

31-
class ASCReader(BaseIOHandler):
29+
class ASCReader(MessageReader):
3230
"""
3331
Iterator of CAN messages from a ASC logging file. Meta data (comments,
3432
bus statistics, J1939 Transport Protocol messages) is ignored.
@@ -40,7 +38,7 @@ class ASCReader(BaseIOHandler):
4038

4139
def __init__(
4240
self,
43-
file: AcceptedIOType,
41+
file: Union[StringPathLike, TextIO],
4442
base: str = "hex",
4543
relative_timestamp: bool = True,
4644
) -> None:
@@ -248,7 +246,7 @@ def __iter__(self) -> Generator[Message, None, None]:
248246
self.stop()
249247

250248

251-
class ASCWriter(FileIOMessageWriter, Listener):
249+
class ASCWriter(FileIOMessageWriter):
252250
"""Logs CAN data to an ASCII log file (.asc).
253251
254252
The measurement starts with the timestamp of the first registered message.
@@ -287,7 +285,7 @@ class ASCWriter(FileIOMessageWriter, Listener):
287285

288286
def __init__(
289287
self,
290-
file: AcceptedIOType,
288+
file: Union[StringPathLike, TextIO],
291289
channel: int = 1,
292290
) -> None:
293291
"""

can/io/blf.py

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@
1717
import datetime
1818
import time
1919
import logging
20-
from typing import List, BinaryIO
20+
from typing import List, BinaryIO, Generator, Union
2121

2222
from ..message import Message
23-
from ..listener import Listener
2423
from ..util import len2dlc, dlc2len, channel2int
25-
from ..typechecking import AcceptedIOType
26-
from .generic import BaseIOHandler, FileIOMessageWriter
24+
from ..typechecking import StringPathLike
25+
from .generic import FileIOMessageWriter, MessageReader
2726

2827

2928
class BLFParseError(Exception):
@@ -131,7 +130,7 @@ def systemtime_to_timestamp(systemtime):
131130
return 0
132131

133132

134-
class BLFReader(BaseIOHandler):
133+
class BLFReader(MessageReader):
135134
"""
136135
Iterator of CAN messages from a Binary Logging File.
137136
@@ -141,7 +140,7 @@ class BLFReader(BaseIOHandler):
141140

142141
file: BinaryIO
143142

144-
def __init__(self, file: AcceptedIOType) -> None:
143+
def __init__(self, file: Union[StringPathLike, BinaryIO]) -> None:
145144
"""
146145
:param file: a path-like object or as file-like object to read from
147146
If this is a file-like object, is has to opened in binary
@@ -162,7 +161,7 @@ def __init__(self, file: AcceptedIOType) -> None:
162161
self._tail = b""
163162
self._pos = 0
164163

165-
def __iter__(self):
164+
def __iter__(self) -> Generator[Message, None, None]:
166165
while True:
167166
data = self.file.read(OBJ_HEADER_BASE_STRUCT.size)
168167
if not data:
@@ -349,7 +348,7 @@ def _parse_data(self, data):
349348
pos = next_pos
350349

351350

352-
class BLFWriter(FileIOMessageWriter, Listener):
351+
class BLFWriter(FileIOMessageWriter):
353352
"""
354353
Logs CAN data to a Binary Logging File compatible with Vector's tools.
355354
"""
@@ -364,7 +363,7 @@ class BLFWriter(FileIOMessageWriter, Listener):
364363

365364
def __init__(
366365
self,
367-
file: AcceptedIOType,
366+
file: Union[StringPathLike, BinaryIO],
368367
append: bool = False,
369368
channel: int = 1,
370369
compression_level: int = -1,

can/io/canutils.py

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
"""
66

77
import logging
8+
from typing import Generator, TextIO, Union
89

910
from can.message import Message
10-
from can.listener import Listener
11-
from .generic import BaseIOHandler, FileIOMessageWriter
12-
from ..typechecking import AcceptedIOType
11+
from .generic import FileIOMessageWriter, MessageReader
12+
from ..typechecking import AcceptedIOType, StringPathLike
1313

1414
log = logging.getLogger("can.io.canutils")
1515

@@ -22,7 +22,7 @@
2222
CANFD_ESI = 0x02
2323

2424

25-
class CanutilsLogReader(BaseIOHandler):
25+
class CanutilsLogReader(MessageReader):
2626
"""
2727
Iterator over CAN messages from a .log Logging File (candump -L).
2828
@@ -32,30 +32,37 @@ class CanutilsLogReader(BaseIOHandler):
3232
``(0.0) vcan0 001#8d00100100820100``
3333
"""
3434

35-
def __init__(self, file: AcceptedIOType) -> None:
35+
file: TextIO
36+
37+
def __init__(self, file: Union[StringPathLike, TextIO]) -> None:
3638
"""
3739
:param file: a path-like object or as file-like object to read from
3840
If this is a file-like object, is has to opened in text
3941
read mode, not binary read mode.
4042
"""
4143
super().__init__(file, mode="r")
4244

43-
def __iter__(self):
45+
def __iter__(self) -> Generator[Message, None, None]:
4446
for line in self.file:
4547

4648
# skip empty lines
4749
temp = line.strip()
4850
if not temp:
4951
continue
5052

51-
timestamp, channel, frame = temp.split()
52-
timestamp = float(timestamp[1:-1])
53-
canId, data = frame.split("#", maxsplit=1)
54-
if channel.isdigit():
55-
channel = int(channel)
53+
channel_string: str
54+
timestamp_string, channel_string, frame = temp.split()
55+
timestamp = float(timestamp_string[1:-1])
56+
can_id_string, data = frame.split("#", maxsplit=1)
57+
58+
channel: Union[int, str]
59+
if channel_string.isdigit():
60+
channel = int(channel_string)
61+
else:
62+
channel = channel_string
5663

57-
isExtended = len(canId) > 3
58-
canId = int(canId, 16)
64+
is_extended = len(can_id_string) > 3
65+
can_id = int(can_id_string, 16)
5966

6067
is_fd = False
6168
brs = False
@@ -69,43 +76,43 @@ def __iter__(self):
6976
data = data[2:]
7077

7178
if data and data[0].lower() == "r":
72-
isRemoteFrame = True
79+
is_remote_frame = True
7380

7481
if len(data) > 1:
7582
dlc = int(data[1:])
7683
else:
7784
dlc = 0
7885

79-
dataBin = None
86+
data_bin = None
8087
else:
81-
isRemoteFrame = False
88+
is_remote_frame = False
8289

8390
dlc = len(data) // 2
84-
dataBin = bytearray()
91+
data_bin = bytearray()
8592
for i in range(0, len(data), 2):
86-
dataBin.append(int(data[i : (i + 2)], 16))
93+
data_bin.append(int(data[i : (i + 2)], 16))
8794

88-
if canId & CAN_ERR_FLAG and canId & CAN_ERR_BUSERROR:
95+
if can_id & CAN_ERR_FLAG and can_id & CAN_ERR_BUSERROR:
8996
msg = Message(timestamp=timestamp, is_error_frame=True)
9097
else:
9198
msg = Message(
9299
timestamp=timestamp,
93-
arbitration_id=canId & 0x1FFFFFFF,
94-
is_extended_id=isExtended,
95-
is_remote_frame=isRemoteFrame,
100+
arbitration_id=can_id & 0x1FFFFFFF,
101+
is_extended_id=is_extended,
102+
is_remote_frame=is_remote_frame,
96103
is_fd=is_fd,
97104
bitrate_switch=brs,
98105
error_state_indicator=esi,
99106
dlc=dlc,
100-
data=dataBin,
107+
data=data_bin,
101108
channel=channel,
102109
)
103110
yield msg
104111

105112
self.stop()
106113

107114

108-
class CanutilsLogWriter(FileIOMessageWriter, Listener):
115+
class CanutilsLogWriter(FileIOMessageWriter):
109116
"""Logs CAN data to an ASCII log file (.log).
110117
This class is is compatible with "candump -L".
111118
@@ -115,7 +122,10 @@ class CanutilsLogWriter(FileIOMessageWriter, Listener):
115122
"""
116123

117124
def __init__(
118-
self, file: AcceptedIOType, channel: str = "vcan0", append: bool = False
125+
self,
126+
file: Union[StringPathLike, TextIO],
127+
channel: str = "vcan0",
128+
append: bool = False,
119129
):
120130
"""
121131
:param file: a path-like object or as file-like object to write to

can/io/csv.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010
"""
1111

1212
from base64 import b64encode, b64decode
13-
from typing import TextIO
13+
from typing import TextIO, Generator, Union
1414

1515
from can.message import Message
16-
from can.listener import Listener
17-
from .generic import BaseIOHandler, FileIOMessageWriter
18-
from ..typechecking import AcceptedIOType
16+
from .generic import FileIOMessageWriter, MessageReader
17+
from ..typechecking import StringPathLike
1918

2019

21-
class CSVReader(BaseIOHandler):
20+
class CSVReader(MessageReader):
2221
"""Iterator over CAN messages from a .csv file that was
2322
generated by :class:`~can.CSVWriter` or that uses the same
2423
format as described there. Assumes that there is a header
@@ -27,15 +26,17 @@ class CSVReader(BaseIOHandler):
2726
Any line separator is accepted.
2827
"""
2928

30-
def __init__(self, file: AcceptedIOType) -> None:
29+
file: TextIO
30+
31+
def __init__(self, file: Union[StringPathLike, TextIO]) -> None:
3132
"""
3233
:param file: a path-like object or as file-like object to read from
3334
If this is a file-like object, is has to opened in text
3435
read mode, not binary read mode.
3536
"""
3637
super().__init__(file, mode="r")
3738

38-
def __iter__(self):
39+
def __iter__(self) -> Generator[Message, None, None]:
3940
# skip the header line
4041
try:
4142
next(self.file)
@@ -62,7 +63,7 @@ def __iter__(self):
6263
self.stop()
6364

6465

65-
class CSVWriter(FileIOMessageWriter, Listener):
66+
class CSVWriter(FileIOMessageWriter):
6667
"""Writes a comma separated text file with a line for
6768
each message. Includes a header line.
6869
@@ -85,7 +86,9 @@ class CSVWriter(FileIOMessageWriter, Listener):
8586

8687
file: TextIO
8788

88-
def __init__(self, file: AcceptedIOType, append: bool = False) -> None:
89+
def __init__(
90+
self, file: Union[StringPathLike, TextIO], append: bool = False
91+
) -> None:
8992
"""
9093
:param file: a path-like object or a file-like object to write to.
9194
If this is a file-like object, is has to open in text

can/io/generic.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@
55
Optional,
66
cast,
77
Iterable,
8-
Union,
9-
TextIO,
10-
BinaryIO,
118
Type,
129
ContextManager,
1310
)
@@ -76,21 +73,23 @@ def stop(self) -> None:
7673
class MessageWriter(BaseIOHandler, can.Listener, metaclass=ABCMeta):
7774
"""The base class for all writers."""
7875

76+
file: Optional[can.typechecking.FileLike]
77+
7978

8079
# pylint: disable=abstract-method,too-few-public-methods
8180
class FileIOMessageWriter(MessageWriter, metaclass=ABCMeta):
8281
"""A specialized base class for all writers with file descriptors."""
8382

84-
file: Union[TextIO, BinaryIO]
83+
file: can.typechecking.FileLike
8584

8685
def __init__(self, file: can.typechecking.AcceptedIOType, mode: str = "rt") -> None:
87-
# Not possible with the type signature, but be verbose for user friendliness
86+
# Not possible with the type signature, but be verbose for user-friendliness
8887
if file is None:
8988
raise ValueError("The given file cannot be None")
9089

9190
super().__init__(file, mode)
9291

9392

9493
# pylint: disable=too-few-public-methods
95-
class MessageReader(BaseIOHandler, Iterable, metaclass=ABCMeta):
94+
class MessageReader(BaseIOHandler, Iterable[can.Message], metaclass=ABCMeta):
9695
"""The base class for all readers."""

0 commit comments

Comments
 (0)