chore(deps): added `construct-typing` for typechecking
What changed, and why it matters
This commit only adds a development dependency called construct-typing to improve type checking and adjusts some type-checker ignore comments. It does not change how the Trezor firmware or tools behave at runtime, and it does not fix or introduce any security issue.
No action required; this is a routine tooling/dependency maintenance commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch adds construct-typing>=0.7.0 to pyproject.toml and uv.lock, and updates several # type: ignore / # pyright: ignore comments to match the new type-checker expectations. There are no functional code changes, no runtime behavior changes, and no security-relevant modifications.
Changed components
python/src/trezorlib/_internal/firmware_headers.pypython/src/trezorlib/cli/settings.pypython/src/trezorlib/construct_helpers.pypython/src/trezorlib/firmware/core.pypython/src/trezorlib/firmware/sanity_struct.pytests/device_tests/ethereum/test_definitions.pypyproject.tomluv.lockInspect captured patch +26 / −10
diff --git a/pyproject.toml b/pyproject.toml
index a5c7b648..55e27f03 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -60,6 +60,7 @@ dependencies = [
"pytest-retry>=1.7.0,<2",
"slh-dsa>=0.1.3,<0.2",
"bleak>=1.1.0",
+ "construct-typing>=0.7.0",
]
[dependency-groups]
diff --git a/python/src/trezorlib/_internal/firmware_headers.py b/python/src/trezorlib/_internal/firmware_headers.py
index 6b3562b5..93721ad5 100644
--- a/python/src/trezorlib/_internal/firmware_headers.py
+++ b/python/src/trezorlib/_internal/firmware_headers.py
@@ -313,7 +313,7 @@ class VendorHeader(firmware.VendorHeader, CosiSignedMixin):
DEV_KEYS: t.ClassVar[t.Sequence[bytes]] = _make_dev_keys(b"\x44", b"\x45")
SUBCON = c.Struct(
- *firmware.VendorHeader.SUBCON.subcons,
+ *firmware.VendorHeader.SUBCON.subcons, # type: ignore [Cannot access attribute]
c.Terminated,
)
@@ -516,7 +516,7 @@ class BootloaderV2Image(firmware.BootableImage):
raise ValueError("Sigmask specifies more public keys than provided.")
# Verify ed25519 signatures
- if mask.bit_count() != len( # pyright: ignore[reportAttributeAccessIssue] # bit_count() is not available with Python 3.9
+ if mask.bit_count() != len( # type: ignore [Cannot access attribute] # bit_count() is not available with Python 3.9
self.unauth.ec_signatures
):
raise ValueError("Sigmask does not specify valid number of ed25519 keys.")
@@ -533,7 +533,7 @@ class BootloaderV2Image(firmware.BootableImage):
sig_idx += 1
# Verify slh-dsa signatures
- if mask.bit_count() != len( # pyright: ignore[reportAttributeAccessIssue] # bit_count() is not available with Python 3.9
+ if mask.bit_count() != len( # type: ignore [Cannot access attribute] # bit_count() is not available with Python 3.9
self.unauth.slh_signatures
):
raise ValueError("Sigmask does not specify valid number of slh-dsa keys.")
diff --git a/python/src/trezorlib/cli/settings.py b/python/src/trezorlib/cli/settings.py
index 1af8f70f..26a4e814 100644
--- a/python/src/trezorlib/cli/settings.py
+++ b/python/src/trezorlib/cli/settings.py
@@ -156,7 +156,7 @@ def image_to_jpeg(filename: Path, width: int, height: int, quality: int = 90) ->
needs_regeneration = True
if filename.suffix in (".jpg", ".jpeg"):
- if image.info.get("progressive"): # pyright: ignore[reportAttributeAccessIssue]
+ if image.info.get("progressive"): # type: ignore [Cannot access attribute "info" for class "Image"]
needs_regeneration = True
if needs_regeneration:
diff --git a/python/src/trezorlib/construct_helpers.py b/python/src/trezorlib/construct_helpers.py
index fb5a769b..e19298ec 100644
--- a/python/src/trezorlib/construct_helpers.py
+++ b/python/src/trezorlib/construct_helpers.py
@@ -23,7 +23,7 @@ import construct
class EnumAdapter(construct.Adapter):
- def __init__(self, subcon: construct.Adapter, enum: type[Enum]) -> None:
+ def __init__(self, subcon: construct.Construct, enum: type[Enum]) -> None:
self.enum = enum
super().__init__(subcon)
@@ -40,7 +40,7 @@ class EnumAdapter(construct.Adapter):
class TupleAdapter(construct.Adapter):
- def __init__(self, *subcons: construct.Adapter) -> None:
+ def __init__(self, *subcons: construct.Construct) -> None:
super().__init__(construct.Sequence(*subcons))
def _encode(self, obj: t.Any, context: t.Any, path: t.Any) -> t.Any:
diff --git a/python/src/trezorlib/firmware/core.py b/python/src/trezorlib/firmware/core.py
index 4e1407ab..e558402c 100644
--- a/python/src/trezorlib/firmware/core.py
+++ b/python/src/trezorlib/firmware/core.py
@@ -97,7 +97,7 @@ class FirmwareHeader(SanityCheckedStruct):
"_end_offset" / c.Tell,
"_rebuild_header_len" / c.If(
- c.this.version[0] > 1,
+ c.this.version[0] > 1, # type: ignore [parameter "name" of type "str"]
c.Pointer(
c.this._start_offset + 4,
c.Rebuild(c.Int32ul, c.this._end_offset - c.this._start_offset)
diff --git a/python/src/trezorlib/firmware/sanity_struct.py b/python/src/trezorlib/firmware/sanity_struct.py
index 55e5c401..f0573ff4 100644
--- a/python/src/trezorlib/firmware/sanity_struct.py
+++ b/python/src/trezorlib/firmware/sanity_struct.py
@@ -97,7 +97,7 @@ class SanityCheckedStruct(Struct):
while isinstance(subcon, Transformed):
subcon = subcon.subcon
- subcon_fields: t.ItemsView[str, t.Any] = subcon._subcons.items()
+ subcon_fields: t.ItemsView[str, t.Any] = subcon._subcons.items() # type: ignore [Cannot access attribute]
except Exception as e:
errors.append(f"Failed to parse subcon fields. {e}")
return False
diff --git a/tests/device_tests/ethereum/test_definitions.py b/tests/device_tests/ethereum/test_definitions.py
index abc69b1c..567907c2 100644
--- a/tests/device_tests/ethereum/test_definitions.py
+++ b/tests/device_tests/ethereum/test_definitions.py
@@ -480,7 +480,7 @@ def make_label_checker(
if absent:
seen_absent.update(label for label in absent if label in text)
- on_page.seen = seen # type: ignore[attr-defined]
+ on_page.seen = seen # type: ignore [attr-defined]
def assert_all_seen() -> None:
assert seen == (expected or set())
diff --git a/uv.lock b/uv.lock
index 02dddf21..3ea17abe 100644
--- a/uv.lock
+++ b/uv.lock
@@ -8,7 +8,7 @@ resolution-markers = [
]
[options]
-exclude-newer = "2026-05-09T13:36:00.892620957Z"
+exclude-newer = "2026-05-27T13:22:26.419278496Z"
exclude-newer-span = "P30D"
[options.exclude-newer-package]
@@ -418,6 +418,19 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/9e/9a/bfe55ec4afaecb64512cc39f3a801e76fcc5660b5a98d6c193af527b5f23/construct_classes-0.2.2-py3-none-any.whl", hash = "sha256:bf616b174ad53adbd388beb4fc1a5af4ba42289b186e6a741142262ead337b3f", size = 4149, upload-time = "2025-08-26T14:32:46.945Z" },
]
+[[package]]
+name = "construct-typing"
+version = "0.7.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+ { name = "construct" },
+ { name = "typing-extensions" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/f6/ae/659fe4866d89ef5a3a65cddbdd7b35882f4feb72db383821965f2fcea934/construct_typing-0.7.0.tar.gz", hash = "sha256:71d110dedff39bd3b603c734077032a7065bc597a49db1f5b03a211d05dbac23", size = 45104, upload-time = "2025-10-27T19:30:29.614Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/8c/0c/2db6f7e1ae9795e436c6a0dc0bc38b12b8c8a228cb63203e24190b755b3b/construct_typing-0.7.0-py3-none-any.whl", hash = "sha256:c92383c6e8e5d07ba25811c8d5163820458d821e73bb1006541f43f89788646c", size = 24350, upload-time = "2025-10-27T19:30:27.505Z" },
+]
+
[[package]]
name = "coverage"
version = "7.13.5"
@@ -2202,6 +2215,7 @@ dependencies = [
{ name = "black" },
{ name = "bleak" },
{ name = "click" },
+ { name = "construct-typing" },
{ name = "coverage" },
{ name = "cryptography" },
{ name = "demjson3" },
@@ -2268,6 +2282,7 @@ requires-dist = [
{ name = "black", specifier = ">=24.2" },
{ name = "bleak", specifier = ">=1.1.0" },
{ name = "click", specifier = ">=8,<9" },
+ { name = "construct-typing", specifier = ">=0.7.0" },
{ name = "coverage", specifier = "==7.13.5" },
{ name = "cryptography" },
{ name = "demjson3" },
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.