style(python): update types to pyright 1.1.404
What changed, and why it matters
This is a routine code-style and type-annotation cleanup in the Python part of the Trezor firmware repository. It updates type hints to satisfy a newer version of the pyright type checker, replaces some manual success checks with a helper, and makes a few small return-type changes (for example, returning bytes instead of bytearray from some transport reads). There is no indication of a security bug being fixed or introduced.
No security action required. Treat as a normal development/style commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit updates Python type annotations across 22 files to pass pyright 1.1.404. Changes include: adding return types, switching from typing.Tuple to built-in tuple, adding from future import annotations, adjusting flake8 annotation ignore codes, replacing explicit isinstance(resp, messages.Success) checks with session.call(…, expect=messages.Success) or messages.Success.ensure_isinstance(resp), and converting a few bytearray returns to bytes in BLE/UDP transports. No cryptographic, protocol, or access-control logic is substantively altered.
Changed components
python/src/trezorlibpython/tools/pybridge.pypython/setup.cfgInspect captured patch +113 / −116
diff --git a/python/setup.cfg b/python/setup.cfg
index 7ff9af9b..694d4ec4 100644
--- a/python/setup.cfg
+++ b/python/setup.cfg
@@ -24,12 +24,14 @@ ignore =
E741,
# W503: line break before binary operator
W503,
- # flake8-annotations
- ANN,
+ # Annotations plugin: type annotation for self and cls
+ ANN101, ANN102,
+ # Annotations plugin: opinionated warnings
+ ANN4,
per-file-ignores =
helper-scripts/*:I
- tools/*:I
- tests/*:I
+ tools/*:I,A
+ tests/*:I,A
known-modules = libusb1:[usb1],hidapi:[hid],PyQt5:[PyQt5.QtWidgets,PyQt5.QtGui,PyQt5.QtCore],noiseprotocol:[noise]
[isort]
diff --git a/python/src/trezorlib/_internal/emulator.py b/python/src/trezorlib/_internal/emulator.py
index 7ccce124..07c3193f 100644
--- a/python/src/trezorlib/_internal/emulator.py
+++ b/python/src/trezorlib/_internal/emulator.py
@@ -32,7 +32,7 @@ EMULATOR_WAIT_TIME = 60
_RUNNING_PIDS = set()
-def _cleanup_pids():
+def _cleanup_pids() -> None:
for process in _RUNNING_PIDS:
process.kill()
diff --git a/python/src/trezorlib/_internal/firmware_headers.py b/python/src/trezorlib/_internal/firmware_headers.py
index 3802ee7a..c8b60458 100644
--- a/python/src/trezorlib/_internal/firmware_headers.py
+++ b/python/src/trezorlib/_internal/firmware_headers.py
@@ -14,6 +14,8 @@
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
+from __future__ import annotations
+
import typing as t
from copy import copy
from dataclasses import asdict
@@ -85,7 +87,7 @@ def _format_container(
truncate_after: t.Optional[int] = 64,
truncate_to: t.Optional[int] = 32,
) -> str:
- def mostly_printable(bytes: bytes) -> bool:
+ def mostly_printable(bytes: bytes | bytearray) -> bool:
if not bytes:
return True
printable = sum(1 for byte in bytes if 0x20 <= byte <= 0x7E)
diff --git a/python/src/trezorlib/_internal/translations.py b/python/src/trezorlib/_internal/translations.py
index e9d9d4e7..d06e9f8a 100644
--- a/python/src/trezorlib/_internal/translations.py
+++ b/python/src/trezorlib/_internal/translations.py
@@ -297,26 +297,26 @@ class TranslationsBlob(Struct):
# fmt: on
@property
- def header(self):
+ def header(self) -> Header:
return Header.parse(self.header_bytes)
@property
- def proof(self):
+ def proof(self) -> Proof:
return Proof.parse(self.proof_bytes)
@proof.setter
- def proof(self, proof: Proof):
+ def proof(self, proof: Proof) -> None:
self.proof_bytes = proof.build()
@property
- def translation_chunks(self):
+ def translation_chunks(self) -> list[TranslatedStringsChunk]:
return [
TranslatedStringsChunk.parse(chunk)
for chunk in self.payload.translations_chunks_bytes
]
@property
- def fonts(self):
+ def fonts(self) -> FontsTable:
return FontsTable.parse(self.payload.fonts_bytes)
def build(self) -> bytes:
@@ -335,7 +335,7 @@ ALL_LAYOUTS = frozenset(LayoutType) - {LayoutType.T1}
ALL_LAYOUT_NAMES = frozenset(layout.name for layout in ALL_LAYOUTS)
-def check_blob(lang_data: JsonDef):
+def check_blob(lang_data: JsonDef) -> None:
json_header: JsonHeader = lang_data["header"]
lang_version = f"{json_header['language']} v{json_header['version']}"
diff --git a/python/src/trezorlib/ble.py b/python/src/trezorlib/ble.py
index c761aff8..b469d567 100644
--- a/python/src/trezorlib/ble.py
+++ b/python/src/trezorlib/ble.py
@@ -22,14 +22,5 @@ if t.TYPE_CHECKING:
from .transport.session import Session
-def unpair(
- session: "Session",
- all: bool,
-):
-
- resp = session.call(messages.BleUnpair(all=all))
-
- if isinstance(resp, messages.Success):
- return
- else:
- raise RuntimeError(f"Unexpected message {resp}")
+def unpair(session: "Session", all: bool) -> None:
+ session.call(messages.BleUnpair(all=all), expect=messages.Success)
diff --git a/python/src/trezorlib/cli/__init__.py b/python/src/trezorlib/cli/__init__.py
index 7b51b411..be3750ed 100644
--- a/python/src/trezorlib/cli/__init__.py
+++ b/python/src/trezorlib/cli/__init__.py
@@ -244,7 +244,9 @@ class TrezorConnection:
seedless_session = client.get_seedless_session()
return seedless_session
- def _connection_context(self, connect_fn: t.Callable[[], t.Any]):
+ def _connection_context(
+ self, connect_fn: t.Callable[[], R]
+ ) -> t.Generator[R, None, None]:
try:
conn = connect_fn()
except Exception as e:
@@ -263,7 +265,7 @@ class TrezorConnection:
# other exceptions may cause a traceback
@contextmanager
- def client_context(self):
+ def client_context(self) -> t.Generator[TrezorClient, None, None]:
"""Get a client instance as a context manager. Handle errors in a manner
appropriate for end-users.
@@ -280,7 +282,7 @@ class TrezorConnection:
derive_cardano: bool = False,
seedless: bool = False,
must_resume: bool = False,
- ):
+ ) -> t.Generator[Session, None, None]:
yield from self._connection_context(
self.get_seedless_session
if seedless
@@ -291,7 +293,7 @@ class TrezorConnection:
)
)
- def _print_exception(self, exc: Exception, message: str):
+ def _print_exception(self, exc: Exception, message: str) -> None:
LOG.debug(message, exc_info=True)
message = f"{message}: {exc.__class__.__name__}"
if description := str(exc):
diff --git a/python/src/trezorlib/client.py b/python/src/trezorlib/client.py
index e6e20831..7d2ca6c0 100644
--- a/python/src/trezorlib/client.py
+++ b/python/src/trezorlib/client.py
@@ -90,7 +90,7 @@ class TrezorClient:
LOG.info(f"creating client instance for device: {transport.get_path()}")
# Here, self.model could be set to None. Unless _init_device is False, it will
# get correctly reconfigured as part of the init_device flow.
- self._model = model # type: ignore ["None" is incompatible with "TrezorModel"]
+ self._model = model # type: ignore ["None" is not assignable to "TrezorModel"]
if self._model:
self.mapping = self.model.default_mapping
else:
@@ -339,7 +339,7 @@ class TrezorClient:
protocol = ProtocolV2Channel(self.transport, self.mapping)
return protocol
- def reset_protocol(self):
+ def reset_protocol(self) -> None:
if self._protocol_version == ProtocolVersion.V1:
self.protocol = ProtocolV1Channel(self.transport, self.mapping)
elif self._protocol_version == ProtocolVersion.V2:
diff --git a/python/src/trezorlib/debuglink.py b/python/src/trezorlib/debuglink.py
index 804c4e7c..24a81db2 100644
--- a/python/src/trezorlib/debuglink.py
+++ b/python/src/trezorlib/debuglink.py
@@ -913,7 +913,7 @@ class DebugLink:
im.save(img_location)
self.t1_screenshot_counter += 1
- def check_gc_info(self, fail_on_gc_leak: bool = True):
+ def check_gc_info(self, fail_on_gc_leak: bool = True) -> None:
"""Fetch GC heap information and check for leaks."""
if not self.has_gc_info:
return
@@ -982,7 +982,7 @@ class DebugUI:
self.passphrase = None
self.reset_input_flow()
- def reset_input_flow(self):
+ def reset_input_flow(self) -> None:
self.input_flow: InputFlowType | object = self.default_input_flow()
next(self.input_flow) # start default input flow generator
@@ -1434,7 +1434,7 @@ class TrezorClientDebugLink(TrezorClient):
assert isinstance(self.protocol, ProtocolV2Channel)
self.protocol.sync_responses()
- def mnemonic_callback(self, _) -> str:
+ def mnemonic_callback(self, _: t.Any) -> str:
word, pos = self.debug.read_recovery_word()
if word:
return word
@@ -1771,7 +1771,7 @@ class DisplayStyle(Enum):
class ScreenButtons:
- def __init__(self, layout_type: LayoutType):
+ def __init__(self, layout_type: LayoutType) -> None:
assert layout_type in (LayoutType.Bolt, LayoutType.Delizia, LayoutType.Eckhart)
self.layout_type = layout_type
@@ -2087,7 +2087,7 @@ PASSPHRASE_SPECIAL = ("_<>", ".:@", "/|\\", "!()", "+%&", "-[]", "?{}", ",'`", "
class ButtonActions:
- def __init__(self, debuglink: DebugLink):
+ def __init__(self, debuglink: DebugLink) -> None:
self.debuglink = debuglink
def _passphrase_choices(self, char: str) -> "tuple[str, ...]":
diff --git a/python/src/trezorlib/device.py b/python/src/trezorlib/device.py
index 2d018494..0a6c669a 100644
--- a/python/src/trezorlib/device.py
+++ b/python/src/trezorlib/device.py
@@ -233,7 +233,7 @@ def recover(
return _deprecation_retval_helper(res)
-def is_slip39_backup_type(backup_type: messages.BackupType):
+def is_slip39_backup_type(backup_type: messages.BackupType) -> bool:
return backup_type in (
messages.BackupType.Slip39_Basic,
messages.BackupType.Slip39_Advanced,
diff --git a/python/src/trezorlib/exceptions.py b/python/src/trezorlib/exceptions.py
index 3c591a2f..4713d7a0 100644
--- a/python/src/trezorlib/exceptions.py
+++ b/python/src/trezorlib/exceptions.py
@@ -93,7 +93,7 @@ class FailedSessionResumption(TrezorException):
Raised when `trezorctl -s <sesssion_id>` is used or `TREZOR_SESSION_ID = <session_id>`
is set and resumption of session with the `session_id` fails."""
- def __init__(self, received_session_id: bytes | None = None):
+ def __init__(self, received_session_id: bytes | None = None) -> None:
# We keep the session id that was received from Trezor for test purposes
self.received_session_id = received_session_id
super().__init__("Failed to resume session")
diff --git a/python/src/trezorlib/firmware/__init__.py b/python/src/trezorlib/firmware/__init__.py
index 77047cc3..af8cb369 100644
--- a/python/src/trezorlib/firmware/__init__.py
+++ b/python/src/trezorlib/firmware/__init__.py
@@ -81,7 +81,7 @@ def update(
session: Session,
data: bytes,
progress_update: t.Callable[[int], t.Any] = lambda _: None,
-):
+) -> None:
if session.features.bootloader_mode is False:
raise RuntimeError("Device must be in bootloader mode")
@@ -89,12 +89,10 @@ def update(
# TREZORv1 method
if isinstance(resp, messages.Success):
- resp = session.call(messages.FirmwareUpload(payload=data))
+ resp = session.call(
+ messages.FirmwareUpload(payload=data), expect=messages.Success
+ )
progress_update(len(data))
- if isinstance(resp, messages.Success):
- return
- else:
- raise RuntimeError(f"Unexpected result {resp}")
# TREZORv2 method
while isinstance(resp, messages.FirmwareRequest):
@@ -104,10 +102,7 @@ def update(
resp = session.call(messages.FirmwareUpload(payload=payload, hash=digest))
progress_update(length)
- if isinstance(resp, messages.Success):
- return
- else:
- raise RuntimeError(f"Unexpected message {resp}")
+ messages.Success.ensure_isinstance(resp)
def get_hash(session: Session, challenge: bytes | None) -> bytes:
diff --git a/python/src/trezorlib/protobuf.py b/python/src/trezorlib/protobuf.py
index 3406c17f..25f5d2c1 100644
--- a/python/src/trezorlib/protobuf.py
+++ b/python/src/trezorlib/protobuf.py
@@ -47,7 +47,7 @@ MAX_FIELD_SIZE = 1024 * 1024 # 1 MB
class Reader(tx.Protocol):
- def readinto(self, __buf: bytearray) -> int:
+ def readinto(self, buf: bytearray, /) -> int:
"""
Reads exactly `len(buffer)` bytes into `buffer`. Returns number of bytes read,
or 0 if it cannot read that much.
@@ -56,7 +56,7 @@ class Reader(tx.Protocol):
class Writer(tx.Protocol):
- def write(self, __buf: bytes) -> int:
+ def write(self, buf: bytes | bytearray | memoryview, /) -> int:
"""
Writes all bytes from `buffer`, or raises `EOFError`
"""
@@ -165,8 +165,7 @@ class Field:
def py_type(self) -> type:
if self._py_type is None:
self._py_type = self._resolve_type()
- # pyright issue https://github.com/microsoft/pyright/issues/8136
- return self._py_type # type: ignore [Type "Unknown | None"]
+ return self._py_type
def _resolve_type(self) -> type:
# look for a type in the builtins
@@ -341,7 +340,7 @@ class LimitedReader:
self.reader = reader
self.limit = limit
- def readinto(self, buf: bytearray) -> int:
+ def readinto(self, buf: bytearray, /) -> int:
if self.limit < len(buf):
return 0
else:
@@ -354,7 +353,7 @@ class CountingWriter:
def __init__(self) -> None:
self.size = 0
- def write(self, buf: bytes) -> int:
+ def write(self, buf: bytes | bytearray | memoryview, /) -> int:
nwritten = len(buf)
self.size += nwritten
return nwritten
@@ -562,7 +561,7 @@ def format_message(
truncate_after: int | None = 256,
truncate_to: int | None = 64,
) -> str:
- def mostly_printable(bytes: bytes) -> bool:
+ def mostly_printable(bytes: bytes | bytearray) -> bool:
if not bytes:
return True
printable = sum(1 for byte in bytes if 0x20 <= byte <= 0x7E)
diff --git a/python/src/trezorlib/stellar.py b/python/src/trezorlib/stellar.py
index 4296f22c..552eb4ab 100644
--- a/python/src/trezorlib/stellar.py
+++ b/python/src/trezorlib/stellar.py
@@ -280,7 +280,7 @@ def _read_operation(op: "Operation") -> "StellarMessageType":
raise ValueError(f"Unknown operation type: {op.__class__.__name__}")
-def _raise_if_account_muxed_id_exists(account: "MuxedAccount"):
+def _raise_if_account_muxed_id_exists(account: "MuxedAccount") -> None:
# Currently Trezor firmware does not support MuxedAccount,
# so we throw an exception here.
if account.account_muxed_id is not None:
diff --git a/python/src/trezorlib/tools.py b/python/src/trezorlib/tools.py
index 7272c8ad..3826fc8a 100644
--- a/python/src/trezorlib/tools.py
+++ b/python/src/trezorlib/tools.py
@@ -476,12 +476,12 @@ class EnumAdapter(construct.Adapter):
self.enum = enum
super().__init__(subcon)
- def _encode(self, obj: Any, ctx: Any, path: Any):
+ def _encode(self, obj: Any, ctx: Any, path: Any) -> Any:
if isinstance(obj, self.enum):
return obj.value
return obj
- def _decode(self, obj: Any, ctx: Any, path: Any):
+ def _decode(self, obj: Any, ctx: Any, path: Any) -> Any:
try:
return self.enum(obj)
except ValueError:
@@ -492,8 +492,8 @@ class TupleAdapter(construct.Adapter):
def __init__(self, *subcons: Any) -> None:
super().__init__(construct.Sequence(*subcons))
- def _encode(self, obj: Any, ctx: Any, path: Any):
+ def _encode(self, obj: Any, ctx: Any, path: Any) -> Any:
return obj
- def _decode(self, obj: Any, ctx: Any, path: Any):
+ def _decode(self, obj: Any, ctx: Any, path: Any) -> Any:
return tuple(obj)
diff --git a/python/src/trezorlib/transport/ble.py b/python/src/trezorlib/transport/ble.py
index d4acaee1..5dd1f0b7 100644
--- a/python/src/trezorlib/transport/ble.py
+++ b/python/src/trezorlib/transport/ble.py
@@ -18,17 +18,17 @@ from __future__ import annotations
import asyncio
import atexit
import logging
+import typing as t
from dataclasses import dataclass
from multiprocessing import Pipe, Process
from multiprocessing.connection import Connection
-from typing import TYPE_CHECKING, Any, Iterable
from ..log import DUMP_PACKETS
from ..models import T3W1
from . import Timeout, Transport, TransportException
from .udp import UdpTransport
-if TYPE_CHECKING:
+if t.TYPE_CHECKING:
from ..models import TrezorModel
try:
@@ -71,8 +71,8 @@ class BleTransport(Transport):
@classmethod
def enumerate(
- cls, models: Iterable[TrezorModel] | None = None
- ) -> Iterable[BleTransport]:
+ cls, models: t.Iterable[TrezorModel] | None = None
+ ) -> t.Iterable[BleTransport]:
# TODO use manufacturer_data
if models and T3W1 not in models:
return []
@@ -114,7 +114,7 @@ class BleTransport(Transport):
LOG.log(DUMP_PACKETS, f"received packet: {chunk.hex()}")
if len(chunk) not in (64, 244):
LOG.error(f"{__name__}: unexpected chunk size: {len(chunk)}")
- return bytearray(chunk)
+ return bytes(chunk)
@classmethod
def ble_proxy(cls) -> BleProxy:
@@ -124,10 +124,10 @@ class BleTransport(Transport):
class BleProxy:
- pipe: Connection | None = None
+ pipe: Connection[t.Any, t.Any] | None = None
process: Process | None = None
- def __init__(self):
+ def __init__(self) -> None:
if not BLEAK_IMPORTED:
raise RuntimeError("Bleak library not available, BLE support disabled")
@@ -141,8 +141,8 @@ class BleProxy:
atexit.register(self._shutdown)
- def __getattr__(self, name: str):
- def f(*args: Any, **kwargs: Any):
+ def __getattr__(self, name: str) -> t.Callable[..., t.Any]:
+ def f(*args: t.Any, **kwargs: t.Any) -> t.Any:
assert self.pipe is not None
self.pipe.send((name, args, kwargs))
result = self.pipe.recv()
@@ -152,7 +152,7 @@ class BleProxy:
return f
- def _shutdown(self):
+ def _shutdown(self) -> None:
if self.pipe is not None:
try:
self.pipe.send(("shutdown", [], {}))
@@ -172,7 +172,7 @@ class Peripheral:
queue: asyncio.Queue | None = None
@property
- def address(self):
+ def address(self) -> str:
return self.device.address
@@ -180,10 +180,10 @@ class BleAsync:
class Shutdown(Exception):
pass
- def __init__(self, pipe: Connection):
+ def __init__(self, pipe: Connection) -> None:
asyncio.run(self.main(pipe))
- async def main(self, pipe: Connection):
+ async def main(self, pipe: Connection) -> None:
self.devices = {}
self.did_scan = False
LOG.debug("async BLE process started")
@@ -195,7 +195,7 @@ class BleAsync:
await self.disconnect(address)
# returns after shutdown, or raises an exception
- async def _main_loop(self, pipe: Connection):
+ async def _main_loop(self, pipe: Connection) -> None:
while True:
await ready(pipe)
cmd, args, kwargs = pipe.recv()
@@ -247,7 +247,7 @@ class BleAsync:
(periph.address, periph.device.name) for periph in self.devices.values()
]
- async def connect(self, address: str):
+ async def connect(self, address: str) -> None:
if not self.did_scan:
await self.scan()
@@ -259,7 +259,7 @@ class BleAsync:
LOG.debug(f"Already connected to {periph.address}")
return
- async def disconnect_callback(client: BleakClient):
+ async def disconnect_callback(client: BleakClient) -> None:
LOG.error(f"Got disconnected from {periph.address}")
self.devices[address].client = None
self.devices[address].queue = None
@@ -289,7 +289,9 @@ class BleAsync:
queue = asyncio.Queue()
- async def read_callback(characteristic: BleakGATTCharacteristic, data: bytes):
+ async def read_callback(
+ characteristic: BleakGATTCharacteristic, data: bytearray
+ ) -> None:
await queue.put(data)
await client.start_notify(TREZOR_CHARACTERISTIC_TX, read_callback)
@@ -297,7 +299,7 @@ class BleAsync:
periph.queue = queue
LOG.info(f"Connected to {client.address}")
- async def disconnect(self, address: str):
+ async def disconnect(self, address: str) -> None:
periph = self.devices.get(address)
if not periph or not periph.client:
return
@@ -315,24 +317,24 @@ class BleAsync:
periph.client = None
periph.queue = None
- async def read(self, address: str, timeout: float | None):
+ async def read(self, address: str, timeout: float | None) -> bytes:
periph = self.devices[address]
try:
return await asyncio.wait_for(periph.queue.get(), timeout=timeout)
except (TimeoutError, asyncio.TimeoutError):
raise Timeout(f"Timeout reading BLE packet ({timeout}s)")
- async def write(self, address: str, chunk: bytes):
+ async def write(self, address: str, chunk: bytes) -> None:
periph = self.devices[address]
await periph.client.write_gatt_char(
TREZOR_CHARACTERISTIC_RX, chunk, response=False
)
- async def shutdown(self):
+ async def shutdown(self) -> None:
raise self.Shutdown
-async def ready(f: Any, write: bool = False):
+async def ready(f: Connection, write: bool = False) -> None:
"""Asynchronously wait for file-like object to become ready for reading or writing."""
fd = f.fileno()
loop = asyncio.get_event_loop()
@@ -340,14 +342,14 @@ async def ready(f: Any, write: bool = False):
if write:
- def callback():
+ def callback() -> None:
event.set()
loop.remove_writer(fd)
loop.add_writer(fd, callback)
else:
- def callback():
+ def callback() -> None:
event.set()
loop.remove_reader(fd)
diff --git a/python/src/trezorlib/transport/thp/curve25519.py b/python/src/trezorlib/transport/thp/curve25519.py
index a5eace41..7f8bfb5b 100644
--- a/python/src/trezorlib/transport/thp/curve25519.py
+++ b/python/src/trezorlib/transport/thp/curve25519.py
@@ -14,7 +14,7 @@
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
-from typing import Tuple
+from __future__ import annotations
p = 2**255 - 19
J = 486662
@@ -65,13 +65,13 @@ def get_public_key(private_key: bytes) -> bytes:
return multiply(private_key, base_point)
-def multiply(private_scalar: bytes, public_point: bytes):
+def multiply(private_scalar: bytes, public_point: bytes) -> bytes:
# X25519 from
# https://datatracker.ietf.org/doc/html/rfc7748#section-5
def ladder_operation(
x1: int, x2: int, z2: int, x3: int, z3: int
- ) -> Tuple[int, int, int, int]:
+ ) -> tuple[int, int, int, int]:
# https://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html#ladder-ladd-1987-m-3
# (x4, z4) = 2 * (x2, z2)
# (x5, z5) = (x2, z2) + (x3, z3)
@@ -98,7 +98,7 @@ def multiply(private_scalar: bytes, public_point: bytes):
return x4, z4, x5, z5
- def conditional_swap(first: int, second: int, condition: int):
+ def conditional_swap(first: int, second: int, condition: int) -> tuple[int, int]:
# Returns (second, first) if condition is true and (first, second) otherwise
# Must be implemented in a way that it is constant time
true_mask = -condition
@@ -136,7 +136,7 @@ def elligator2(point: bytes) -> bytes:
# map_to_curve_elligator2_curve25519 from
# https://www.rfc-editor.org/rfc/rfc9380.html#ell2-opt
- def conditional_move(first: int, second: int, condition: bool):
+ def conditional_move(first: int, second: int, condition: bool) -> int:
# Returns second if condition is true and first otherwise
# Must be implemented in a way that it is constant time
true_mask = -condition
diff --git a/python/src/trezorlib/transport/thp/message_header.py b/python/src/trezorlib/transport/thp/message_header.py
index 2f33c7f0..525522f9 100644
--- a/python/src/trezorlib/transport/thp/message_header.py
+++ b/python/src/trezorlib/transport/thp/message_header.py
@@ -14,8 +14,12 @@
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
+from __future__ import annotations
+
import struct
+from typing_extensions import Self
+
CODEC_V1 = 0x3F
CONTINUATION_PACKET = 0x80
HANDSHAKE_INIT_REQ = 0x00
@@ -77,13 +81,13 @@ class MessageHeader:
def is_ack(self) -> bool:
return self.ctrl_byte & ACK_MASK == ACK_MESSAGE
- def is_channel_allocation_response(self):
+ def is_channel_allocation_response(self) -> bool:
return (
self.cid == BROADCAST_CHANNEL_ID
and self.ctrl_byte == _CHANNEL_ALLOCATION_RES
)
- def is_pong(self):
+ def is_pong(self) -> bool:
return self.cid == BROADCAST_CHANNEL_ID and self.ctrl_byte == PONG
def is_handshake_init_response(self) -> bool:
@@ -96,13 +100,13 @@ class MessageHeader:
return self.ctrl_byte & DATA_MASK == ENCRYPTED_TRANSPORT
@classmethod
- def get_error_header(cls, cid: int, length: int):
+ def get_error_header(cls, cid: int, length: int) -> Self:
return cls(_ERROR, cid, length)
@classmethod
- def get_channel_allocation_request_header(cls, length: int):
+ def get_channel_allocation_request_header(cls, length: int) -> Self:
return cls(CHANNEL_ALLOCATION_REQ, BROADCAST_CHANNEL_ID, length)
@classmethod
- def get_ping_header(cls, length: int):
+ def get_ping_header(cls, length: int) -> Self:
return cls(PING, BROADCAST_CHANNEL_ID, length)
diff --git a/python/src/trezorlib/transport/thp/protocol_v1.py b/python/src/trezorlib/transport/thp/protocol_v1.py
index 03b0f102..bc2ff26c 100644
--- a/python/src/trezorlib/transport/thp/protocol_v1.py
+++ b/python/src/trezorlib/transport/thp/protocol_v1.py
@@ -89,7 +89,7 @@ class ProtocolV1Channel(Channel):
self.transport.write_chunk(chunk)
buffer = buffer[chunk_size - 1 :]
- def _read(self, timeout: float | None = None) -> t.Tuple[int, bytes]:
+ def _read(self, timeout: float | None = None) -> tuple[int, bytes]:
if timeout is None:
timeout = self._DEFAULT_READ_TIMEOUT
@@ -105,14 +105,14 @@ class ProtocolV1Channel(Channel):
while len(buffer) < datalen:
buffer.extend(self.read_next(timeout=timeout))
- return msg_type, buffer[:datalen]
+ return msg_type, bytes(buffer[:datalen])
- def read_chunkless(self, timeout: float | None = None) -> t.Tuple[int, bytes]:
+ def read_chunkless(self, timeout: float | None = None) -> tuple[int, bytes]:
data = self.transport.read_chunk(timeout=timeout)
msg_type, datalen = struct.unpack(">HL", data[: self.HEADER_LEN])
return msg_type, data[self.HEADER_LEN : self.HEADER_LEN + datalen]
- def read_first(self, timeout: float | None = None) -> t.Tuple[int, int, bytes]:
+ def read_first(self, timeout: float | None = None) -> tuple[int, int, bytes]:
chunk = self.transport.read_chunk(timeout=timeout)
if chunk[:3] != b"?##":
raise UnexpectedMagicError(chunk.hex())
diff --git a/python/src/trezorlib/transport/thp/protocol_v2.py b/python/src/trezorlib/transport/thp/protocol_v2.py
index 28f8f690..b200a60f 100644
--- a/python/src/trezorlib/transport/thp/protocol_v2.py
+++ b/python/src/trezorlib/transport/thp/protocol_v2.py
@@ -112,7 +112,7 @@ class ProtocolV2Channel(Channel):
self,
message: protobuf.MessageType,
session_id: int = DEFAULT_SESSION_ID,
- ):
+ ) -> None:
message_type, message_data = self.mapping.encode(message)
self._encrypt_and_write(session_id, message_type, message_data)
self._read_ack()
@@ -153,7 +153,7 @@ class ProtocolV2Channel(Channel):
self.channel_id = cid
self.device_properties = dp
- def _send_channel_allocation_request(self, nonce: bytes):
+ def _send_channel_allocation_request(self, nonce: bytes) -> None:
thp_io.write_payload_to_wire_and_add_checksum(
self.transport,
MessageHeader.get_channel_allocation_request_header(
@@ -273,12 +273,12 @@ class ProtocolV2Channel(Channel):
self._send_ack_bit(bit=1)
self._is_paired = bool(int.from_bytes(trezor_state, "big"))
- def _read_ack(self):
+ def _read_ack(self) -> None:
header, payload = self._read_until_valid_crc_check()
if not header.is_ack() or len(payload) > 0:
LOG.error("Received message is not a valid ACK")
- def _send_ack_bit(self, bit: int):
+ def _send_ack_bit(self, bit: int) -> None:
if bit not in (0, 1):
raise ValueError("Invalid ACK bit")
LOG.debug(f"sending ack {bit}")
diff --git a/python/src/trezorlib/transport/thp/thp_io.py b/python/src/trezorlib/transport/thp/thp_io.py
index ba05248c..e0dd60aa 100644
--- a/python/src/trezorlib/transport/thp/thp_io.py
+++ b/python/src/trezorlib/transport/thp/thp_io.py
@@ -17,7 +17,6 @@
from __future__ import annotations
import struct
-from typing import Tuple
from ...exceptions import ThpError
from .. import Transport
@@ -33,28 +32,29 @@ CONTINUATION_PACKET = 0x80
def write_payload_to_wire_and_add_checksum(
- transport: Transport, header: MessageHeader, transport_payload: bytes
-):
- chksum: bytes = checksum.compute(header.to_bytes_init() + transport_payload)
- data = transport_payload + chksum
+ transport: Transport, header: MessageHeader, payload: bytes
+) -> None:
+ chksum = checksum.compute(header.to_bytes_init() + payload)
+ data = payload + chksum
if len(data) > MAX_PAYLOAD_LEN:
raise RuntimeError("Message too large")
write_payload_to_wire(transport, header, data)
def write_payload_to_wire(
- transport: Transport, header: MessageHeader, transport_payload: bytes
-):
- buffer = bytearray(transport_payload)
+ transport: Transport, header: MessageHeader, payload: bytes
+) -> None:
if transport.CHUNK_SIZE is None:
- transport.write_chunk(buffer)
+ transport.write_chunk(payload)
return
- chunk = header.to_bytes_init() + buffer[: transport.CHUNK_SIZE - INIT_HEADER_LENGTH]
+ chunk = (
+ header.to_bytes_init() + payload[: transport.CHUNK_SIZE - INIT_HEADER_LENGTH]
+ )
chunk = chunk.ljust(transport.CHUNK_SIZE, b"\x00")
transport.write_chunk(chunk)
- buffer = buffer[transport.CHUNK_SIZE - INIT_HEADER_LENGTH :]
+ buffer = payload[transport.CHUNK_SIZE - INIT_HEADER_LENGTH :]
while buffer:
chunk = (
header.to_bytes_cont() + buffer[: transport.CHUNK_SIZE - CONT_HEADER_LENGTH]
@@ -66,7 +66,7 @@ def write_payload_to_wire(
def read(
transport: Transport, timeout: float | None = None
-) -> Tuple[MessageHeader, bytes, bytes]:
+) -> tuple[MessageHeader, bytes, bytes]:
"""
Reads from the given wire transport.
@@ -90,12 +90,12 @@ def read(
msg_data = buffer[:data_len]
chksum = buffer[data_len : data_len + checksum.CHECKSUM_LENGTH]
- return (header, msg_data, chksum)
+ return (header, bytes(msg_data), bytes(chksum))
def read_first(
transport: Transport, timeout: float | None = None
-) -> Tuple[MessageHeader, bytes]:
+) -> tuple[MessageHeader, bytes]:
chunk = transport.read_chunk(timeout)
try:
ctrl_byte, cid, data_length = struct.unpack(
diff --git a/python/src/trezorlib/transport/udp.py b/python/src/trezorlib/transport/udp.py
index 4634a367..37676ee5 100644
--- a/python/src/trezorlib/transport/udp.py
+++ b/python/src/trezorlib/transport/udp.py
@@ -125,7 +125,7 @@ class UdpTransport(Transport):
LOG.log(DUMP_PACKETS, f"received packet: {chunk.hex()}")
if len(chunk) != 64:
raise TransportException(f"Unexpected chunk size: {len(chunk)}")
- return bytearray(chunk)
+ return chunk
def find_debug(self) -> "UdpTransport":
host, port = self.device
diff --git a/python/tools/pybridge.py b/python/tools/pybridge.py
index f95b2fe8..60e58f83 100644
--- a/python/tools/pybridge.py
+++ b/python/tools/pybridge.py
@@ -227,7 +227,7 @@ def do_enumerate():
def do_acquire(path: str, sid: str):
check_origin()
if sid == "null":
- sid = None # type: ignore [is incompatible with declared type]
+ sid = None # type: ignore [is not assignable to declared type]
trezor = Transport.find(path)
if trezor is None:
response.status = 404
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.