Skip to content

refactor: drop the runtime u8 = int shim; u8 is typing-only - #138

Merged
JPHutchins merged 1 commit into
screaming-goblinfrom
refactor/u8-without-shim
Sep 22, 2026
Merged

JPHutchins merged 1 commit into
screaming-goblinfrom
refactor/u8-without-shim

Conversation

@JPHutchins

Copy link
Copy Markdown
Collaborator

Warning

LLM Disclosure

This PR was authored by claude-opus-5-5[1m] on behalf of @JPHutchins. They asked why #137 shimmed u8 rather than declaring types-bits[rt], approved the cleaner fix below, and asked for it as its own small PR.

Note

Targets screaming-goblin, per the epic's rule (intercreate/smpmgr#103).

#137's if TYPE_CHECKING: from types_bits import u8 / else: u8 = int meant the checkers saw Literal[0..255] while the interpreter saw int. This removes the else branch: u8 now exists only for the type checkers.

+from __future__ import annotations
 if TYPE_CHECKING:
     from types_bits import u8
-else:  # `types_bits` is typing-only; reaching its values at runtime needs its `rt` extra
-    u8 = int
-    return cast(Iterator[u8], itertools.cycle(range(0x100)))
+    return cast("Iterator[u8]", itertools.cycle(range(0x100)))

No new dependency, and no difference between what the checkers see and what runs.

Why this works, and why not types-bits[rt]
  • Only two expressions evaluated u8 at runtime. One was the SMPClient.__init__ annotation (evaluated eagerly on 3.10–3.13); from __future__ import annotations now stores it as a string. The other was cast()'s first argument, which is an ordinary expression, not an annotation, so it is quoted.
  • smp itself declares types-bits<0.3,>=0.2 without rt, so the library that defines u8 treats it as typing-only. The extra would only pull in annotated-types (a pydantic dependency, evicted in refactor(mcuboot): replace pydantic dataclasses with stdlib dataclasses #136) to evaluate objects nothing reads: cast() is a no-op and the annotation is never inspected.
Verification
  • camas check: ruff, pydoclint, mypy, pyright, and tests all green.
  • camas matrix: green on 3.10, 3.11, 3.12, 3.13, 3.14.
  • A clean uv pip install . into a fresh 3.10 venv (what the transport-extras job does) has no annotated_types. import smpclient succeeds, wrapping_sequence() wraps 255 → 0, and u8 is unbound at runtime.

🤖 Generated with Claude Code

#137 bound `u8 = int` in an `else` branch of the `TYPE_CHECKING` import, so
the checkers saw `types_bits.u8` (`Literal[0..255]`) while the interpreter saw
`int`: an invisible divergence. The branch existed only because two expressions
evaluated `u8` at runtime: the `sequence: Iterator[u8] | None` annotation on
`SMPClient.__init__` (evaluated eagerly on 3.10-3.13) and the first argument of
`cast()` in `wrapping_sequence()`.

With `from __future__ import annotations`, annotations are stored as strings
and never evaluated. `cast()`'s first argument is an ordinary expression, not an
annotation, so it is quoted. Nothing binds `u8` at runtime any more, so the
`else` branch and its explanatory comment go together.

Why not declare `types-bits[rt]` instead: smp itself declares
`types-bits<0.3,>=0.2` without the `rt` extra, so the library that defines `u8`
treats it as typing-only. The extra only pulls in `annotated-types` (one of
pydantic's dependencies, which #136 evicted), and nothing in smpclient
validates a `u8` at runtime: `cast()` is a no-op, and the annotation is never
read.

Verified: `camas check` and `camas matrix` (3.10-3.14) green. A clean
`uv pip install .` into a fresh 3.10 venv (what the `transport-extras` CI job
does) has no `annotated_types`; `import smpclient` succeeds and
`wrapping_sequence()` wraps 255 -> 0, with `u8` unbound at runtime.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@JPHutchins
JPHutchins merged commit 4552624 into screaming-goblin Sep 22, 2026
28 checks passed
@JPHutchins
JPHutchins deleted the refactor/u8-without-shim branch September 22, 2026 23:04
@JPHutchins JPHutchins mentioned this pull request Sep 22, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant