Skip to content

Commit 7bc904e

Browse files
authored
Improve can.io type annotations (#1951)
1 parent 32f07c3 commit 7bc904e

File tree

20 files changed

+373
-238
lines changed

20 files changed

+373
-238
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ jobs:
7676
- name: Set up Python
7777
uses: actions/setup-python@v5
7878
with:
79-
python-version: "3.10"
79+
python-version: "3.13"
8080
- name: Install dependencies
8181
run: |
8282
python -m pip install --upgrade pip

can/_entry_points.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ def read_entry_points(group: str) -> list[_EntryPoint]:
3030
def read_entry_points(group: str) -> list[_EntryPoint]:
3131
return [
3232
_EntryPoint(ep.name, *ep.value.split(":", maxsplit=1))
33-
for ep in entry_points().get(group, [])
33+
for ep in entry_points().get(group, []) # pylint: disable=no-member
3434
]

can/io/asc.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ class ASCReader(TextIOMessageReader):
3939
bus statistics, J1939 Transport Protocol messages) is ignored.
4040
"""
4141

42-
file: TextIO
43-
4442
def __init__(
4543
self,
4644
file: Union[StringPathLike, TextIO],
@@ -322,8 +320,6 @@ class ASCWriter(TextIOMessageWriter):
322320
It the first message does not have a timestamp, it is set to zero.
323321
"""
324322

325-
file: TextIO
326-
327323
FORMAT_MESSAGE = "{channel} {id:<15} {dir:<4} {dtype} {data}"
328324
FORMAT_MESSAGE_FD = " ".join(
329325
[

can/io/blf.py

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@
1717
import struct
1818
import time
1919
import zlib
20-
from collections.abc import Generator
20+
from collections.abc import Generator, Iterator
2121
from decimal import Decimal
2222
from typing import Any, BinaryIO, Optional, Union, cast
2323

2424
from ..message import Message
2525
from ..typechecking import StringPathLike
2626
from ..util import channel2int, dlc2len, len2dlc
27-
from .generic import BinaryIOMessageReader, FileIOMessageWriter
27+
from .generic import BinaryIOMessageReader, BinaryIOMessageWriter
2828

2929
TSystemTime = tuple[int, int, int, int, int, int, int, int]
3030

@@ -104,7 +104,7 @@ class BLFParseError(Exception):
104104
TIME_ONE_NANS_FACTOR = Decimal("1e-9")
105105

106106

107-
def timestamp_to_systemtime(timestamp: float) -> TSystemTime:
107+
def timestamp_to_systemtime(timestamp: Optional[float]) -> TSystemTime:
108108
if timestamp is None or timestamp < 631152000:
109109
# Probably not a Unix timestamp
110110
return 0, 0, 0, 0, 0, 0, 0, 0
@@ -146,8 +146,6 @@ class BLFReader(BinaryIOMessageReader):
146146
silently ignored.
147147
"""
148148

149-
file: BinaryIO
150-
151149
def __init__(
152150
self,
153151
file: Union[StringPathLike, BinaryIO],
@@ -206,7 +204,7 @@ def __iter__(self) -> Generator[Message, None, None]:
206204
yield from self._parse_container(data)
207205
self.stop()
208206

209-
def _parse_container(self, data):
207+
def _parse_container(self, data: bytes) -> Iterator[Message]:
210208
if self._tail:
211209
data = b"".join((self._tail, data))
212210
try:
@@ -217,7 +215,7 @@ def _parse_container(self, data):
217215
# Save the remaining data that could not be processed
218216
self._tail = data[self._pos :]
219217

220-
def _parse_data(self, data):
218+
def _parse_data(self, data: bytes) -> Iterator[Message]:
221219
"""Optimized inner loop by making local copies of global variables
222220
and class members and hardcoding some values."""
223221
unpack_obj_header_base = OBJ_HEADER_BASE_STRUCT.unpack_from
@@ -375,13 +373,11 @@ def _parse_data(self, data):
375373
pos = next_pos
376374

377375

378-
class BLFWriter(FileIOMessageWriter):
376+
class BLFWriter(BinaryIOMessageWriter):
379377
"""
380378
Logs CAN data to a Binary Logging File compatible with Vector's tools.
381379
"""
382380

383-
file: BinaryIO
384-
385381
#: Max log container size of uncompressed data
386382
max_container_size = 128 * 1024
387383

@@ -412,14 +408,12 @@ def __init__(
412408
Z_DEFAULT_COMPRESSION represents a default compromise between
413409
speed and compression (currently equivalent to level 6).
414410
"""
415-
mode = "rb+" if append else "wb"
416411
try:
417-
super().__init__(file, mode=mode)
412+
super().__init__(file, mode="rb+" if append else "wb")
418413
except FileNotFoundError:
419414
# Trying to append to a non-existing file, create a new one
420415
append = False
421-
mode = "wb"
422-
super().__init__(file, mode=mode)
416+
super().__init__(file, mode="wb")
423417
assert self.file is not None
424418
self.channel = channel
425419
self.compression_level = compression_level
@@ -452,7 +446,7 @@ def __init__(
452446
# Write a default header which will be updated when stopped
453447
self._write_header(FILE_HEADER_SIZE)
454448

455-
def _write_header(self, filesize):
449+
def _write_header(self, filesize: int) -> None:
456450
header = [b"LOGG", FILE_HEADER_SIZE, self.application_id, 0, 0, 0, 2, 6, 8, 1]
457451
# The meaning of "count of objects read" is unknown
458452
header.extend([filesize, self.uncompressed_size, self.object_count, 0])
@@ -462,7 +456,7 @@ def _write_header(self, filesize):
462456
# Pad to header size
463457
self.file.write(b"\x00" * (FILE_HEADER_SIZE - FILE_HEADER_STRUCT.size))
464458

465-
def on_message_received(self, msg):
459+
def on_message_received(self, msg: Message) -> None:
466460
channel = channel2int(msg.channel)
467461
if channel is None:
468462
channel = self.channel
@@ -514,7 +508,7 @@ def on_message_received(self, msg):
514508
data = CAN_MSG_STRUCT.pack(channel, flags, msg.dlc, arb_id, can_data)
515509
self._add_object(CAN_MESSAGE, data, msg.timestamp)
516510

517-
def log_event(self, text, timestamp=None):
511+
def log_event(self, text: str, timestamp: Optional[float] = None) -> None:
518512
"""Add an arbitrary message to the log file as a global marker.
519513
520514
:param str text:
@@ -525,17 +519,19 @@ def log_event(self, text, timestamp=None):
525519
"""
526520
try:
527521
# Only works on Windows
528-
text = text.encode("mbcs")
522+
encoded = text.encode("mbcs")
529523
except LookupError:
530-
text = text.encode("ascii")
524+
encoded = text.encode("ascii")
531525
comment = b"Added by python-can"
532526
marker = b"python-can"
533527
data = GLOBAL_MARKER_STRUCT.pack(
534-
0, 0xFFFFFF, 0xFF3300, 0, len(text), len(marker), len(comment)
528+
0, 0xFFFFFF, 0xFF3300, 0, len(encoded), len(marker), len(comment)
535529
)
536-
self._add_object(GLOBAL_MARKER, data + text + marker + comment, timestamp)
530+
self._add_object(GLOBAL_MARKER, data + encoded + marker + comment, timestamp)
537531

538-
def _add_object(self, obj_type, data, timestamp=None):
532+
def _add_object(
533+
self, obj_type: int, data: bytes, timestamp: Optional[float] = None
534+
) -> None:
539535
if timestamp is None:
540536
timestamp = self.stop_timestamp or time.time()
541537
if self.start_timestamp is None:
@@ -564,7 +560,7 @@ def _add_object(self, obj_type, data, timestamp=None):
564560
if self._buffer_size >= self.max_container_size:
565561
self._flush()
566562

567-
def _flush(self):
563+
def _flush(self) -> None:
568564
"""Compresses and writes data in the buffer to file."""
569565
if self.file.closed:
570566
return
@@ -578,7 +574,7 @@ def _flush(self):
578574
self._buffer = [tail]
579575
self._buffer_size = len(tail)
580576
if not self.compression_level:
581-
data = uncompressed_data
577+
data: "Union[bytes, memoryview[int]]" = uncompressed_data # noqa: UP037
582578
method = NO_COMPRESSION
583579
else:
584580
data = zlib.compress(uncompressed_data, self.compression_level)
@@ -601,7 +597,7 @@ def file_size(self) -> int:
601597
"""Return an estimate of the current file size in bytes."""
602598
return self.file.tell() + self._buffer_size
603599

604-
def stop(self):
600+
def stop(self) -> None:
605601
"""Stops logging and closes the file."""
606602
self._flush()
607603
if self.file.seekable():

can/io/canutils.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import logging
88
from collections.abc import Generator
9-
from typing import Any, TextIO, Union
9+
from typing import Any, Optional, TextIO, Union
1010

1111
from can.message import Message
1212

@@ -34,8 +34,6 @@ class CanutilsLogReader(TextIOMessageReader):
3434
``(0.0) vcan0 001#8d00100100820100``
3535
"""
3636

37-
file: TextIO
38-
3937
def __init__(
4038
self,
4139
file: Union[StringPathLike, TextIO],
@@ -148,13 +146,12 @@ def __init__(
148146
:param bool append: if set to `True` messages are appended to
149147
the file, else the file is truncated
150148
"""
151-
mode = "a" if append else "w"
152-
super().__init__(file, mode=mode)
149+
super().__init__(file, mode="a" if append else "w")
153150

154151
self.channel = channel
155-
self.last_timestamp = None
152+
self.last_timestamp: Optional[float] = None
156153

157-
def on_message_received(self, msg):
154+
def on_message_received(self, msg: Message) -> None:
158155
# this is the case for the very first message:
159156
if self.last_timestamp is None:
160157
self.last_timestamp = msg.timestamp or 0.0

can/io/csv.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ class CSVReader(TextIOMessageReader):
2828
Any line separator is accepted.
2929
"""
3030

31-
file: TextIO
32-
3331
def __init__(
3432
self,
3533
file: Union[StringPathLike, TextIO],
@@ -89,8 +87,6 @@ class CSVWriter(TextIOMessageWriter):
8987
Each line is terminated with a platform specific line separator.
9088
"""
9189

92-
file: TextIO
93-
9490
def __init__(
9591
self,
9692
file: Union[StringPathLike, TextIO],
@@ -106,8 +102,7 @@ def __init__(
106102
the file is truncated and starts with a newly
107103
written header line
108104
"""
109-
mode = "a" if append else "w"
110-
super().__init__(file, mode=mode)
105+
super().__init__(file, mode="a" if append else "w")
111106

112107
# Write a header row
113108
if not append:

0 commit comments

Comments
 (0)