Skip to content

Complete the type annotations and turn on the CI gates - #24

Merged
ESultanik merged 1 commit into
system-randomfrom
typing-complete
Aug 7, 2026
Merged

Complete the type annotations and turn on the CI gates#24
ESultanik merged 1 commit into
system-randomfrom
typing-complete

Conversation

@ESultanik

Copy link
Copy Markdown
Owner

Stacked on #23.

The package shipped py.typed while being only partly annotated, so consumers got Any from anything unannotated — worse than no marker, because it looks checked. Every function now has a return annotation (116 of 116), ruff and ty are clean, and both run as blocking CI jobs alongside ruff format --check.

The ANN rules were held back when ruff was first configured, because turning them on against a partly annotated tree would have buried every other finding. They are enabled now, except in tests/, where annotating each test function adds noise without adding safety.

Modernisation

Dict/List/Tuple/Optional/Union become PEP 585/604 builtins and X | None; Generator[X, None, None] becomes Iterator[X]; class Encrypter(object) loses its base. The *TypeHint aliases are now TypeAlias declarations with comments explaining what they hold, and StatusCallbackTypeHint refers to the StatusCallback Protocol rather than a bare Callable.

Fixes that fell out of making the checker agree

  • IOWrapper classified its source into an object, which untyped every use of it and needed four type: ignores. It now has _as_path, _as_bytes, and _as_stream accessors that validate rather than assume, so a wrong-kind access raises a LenticryptError instead of an AttributeError further along.
  • encode() returned bytearray while its caller was annotated bytes; it now returns bytes, which also lets it be yielded straight into the block stream.
  • decode() accepted BinaryIO, which the internal IO streams do not nominally satisfy; widened to IO.
  • DictionaryEncrypter.__init__ was a *args, **kwargs pass-through that typed all its parameters as object. It declares the real signature.
  • ProgressBar.__init__ likewise took *args, **kwargs and forwarded them.

Also replaces the 160-character block-header comment with a readable diagram, and gives is_power2 a real docstring.

CI

ty has no --strict flag, so CI uses --error-on-warning, which is the equivalent gate.

Suppressions

No type: ignore anywhere. Five noqas remain, each with a written reason: the deliberately-seeded RNG, two interface parameters a particular override does not need, FrozenDict's dict-compatible constructor, and the CLI's error log that intentionally omits a traceback.

256 passed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01367hFob9sd4xpDmVT4uoFy

The package shipped `py.typed` while being only partly annotated, so consumers
got `Any` from anything unannotated -- worse than no marker, because it looks
checked. Every function now has a return annotation (116 of 116), `ruff` and
`ty` are clean, and both run as blocking CI jobs alongside `ruff format --check`.

The `ANN` rules were held back when ruff was first configured, because turning
them on against a partly annotated tree would have buried every other finding.
They are enabled now, except in `tests/`, where annotating each test function
adds noise without adding safety.

Modernisation across the package: `Dict`/`List`/`Tuple`/`Optional`/`Union` become
PEP 585/604 builtins and `X | None`; `Generator[X, None, None]` becomes
`Iterator[X]`; `class Encrypter(object)` loses its base. The `*TypeHint` aliases
are now `TypeAlias` declarations with comments explaining what they hold, and
`StatusCallbackTypeHint` refers to the `StatusCallback` Protocol rather than a
bare `Callable`.

Several fixes fell out of making the checker agree:

  * `IOWrapper` classified its source into an `object`, which untyped every use
    of it and needed four `type: ignore`s. It now has `_as_path`, `_as_bytes` and
    `_as_stream` accessors that *validate* rather than assume, so a wrong-kind
    access raises a LenticryptError instead of an AttributeError further along.
  * `encode()` returned `bytearray` while its caller was annotated `bytes`; it
    now returns `bytes`, which also lets it be yielded straight into the block
    stream.
  * `decode()` accepted `BinaryIO`, which the internal `IO` streams do not
    nominally satisfy; widened to `IO`.
  * `DictionaryEncrypter.__init__` was a `*args, **kwargs` pass-through that
    typed all its parameters as `object`. It declares the real signature.
  * `ProgressBar.__init__` likewise took `*args, **kwargs` and forwarded them.

Also replaces the 160-character block-header comment with a readable diagram,
and gives `is_power2` a real docstring.

`ty` has no `--strict` flag, so CI uses `--error-on-warning`, which is the
equivalent gate.

No `type: ignore` anywhere. Five `noqa`s remain, each with a written reason: the
deliberately-seeded RNG, two interface parameters that a particular override does
not need, `FrozenDict`'s dict-compatible constructor, and the CLI's error log
that intentionally omits a traceback.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01367hFob9sd4xpDmVT4uoFy
@ESultanik
ESultanik merged commit c4f5677 into system-random Aug 7, 2026
12 checks passed
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