Skip to content

Fully implement modern type hinting, checked with ty in CI#50

Merged
Korijn merged 1 commit into
masterfrom
claude/typing
Jul 10, 2026
Merged

Fully implement modern type hinting, checked with ty in CI#50
Korijn merged 1 commit into
masterfrom
claude/typing

Conversation

@Korijn

@Korijn Korijn commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Track C of the roadmap — the analog of observ #184 + #185, delivered as one PR instead of the planned stacked pair: the runtime changes typing needed turned out to be two trivial lines (an assert and a metadata fix), not enough to justify a standalone "runtime cleanups" PR.

Typing

  • Operation TypedDicts in types.pyAddOperation, RemoveOperation, ReplaceOperation and the Operation union — used across the public signatures: diff() and produce() now advertise tuple[..., list[Operation], list[Operation]], apply/iapply take list[Operation], and to_json/to_str_paths are precisely typed.
  • PEP 604 unions and builtin generics everywhere via from __future__ import annotations; the runtime floor stays Python 3.9 (typing-only imports live in a TYPE_CHECKING block, and nothing new evaluates at runtime).
  • py.typed marker shipped in the wheel (verified in the built wheel) plus the Typing :: Typed classifier.
  • Genuine annotation bugs fixed, as observ's pass also found: Pointer.evaluate claimed to return tuple[Diffable, ...] but parent is None at the root; PatchRecorder.record_add annotated reverse_path: Pointer with a None default.
  • Duck-typed code stays honest: the iapply interpreter and diff()'s dispatch intentionally accept container look-alikes (observ proxies), so they unpack into Any/cast at one commented boundary instead of pretending to be nominally typed.

Toolchain

  • ty in a dedicated ty dependency group (and in dev), [tool.ty] scoped to patchdiff/ with python-version = "3.9" so 3.9-incompatible typing constructs are caught by the checker.
  • A dedicated Typecheck job in CI, installing only the ty group, added to publish's needs.

Runtime changes (annotation-driven, behavior-preserving)

  • _Proxy._detach makes its caller-guarded invariant explicit with an assert.
  • pyproject metadata fix: description was literally "MIT"; it's now a real description, plus license = "MIT" (the wheel now carries License-Expression: MIT).

Verification

  • ty check: 26 diagnostics on master → 0 on this branch.
  • Full test suite passes at 100% coverage on 3.10–3.14 locally (3.9 isn't installable in this environment; ty's 3.9 target and the CI matrix cover it).
  • ruff check/format pass; the docs build stays warning-free with the new signatures rendered by mkdocstrings; built wheel inspected for py.typed and metadata.

🤖 Generated with Claude Code

https://claude.ai/code/session_016Mu9vEBwU4fQLi8ZgkgdS2


Generated by Claude Code

Closes the typing item of the 1.0.0 roadmap (Track C), following
fork-tongue/observ#184/#185.

Typing:
- Operation TypedDicts in types.py (AddOperation, RemoveOperation,
  ReplaceOperation and the Operation union), used across diff, apply,
  serialize and produce signatures: diff() and produce() now advertise
  tuple[..., list[Operation], list[Operation]]
- PEP 604 unions and builtin generics everywhere via from __future__
  import annotations (the runtime floor stays Python 3.9; typing-only
  imports live in a TYPE_CHECKING block)
- py.typed marker shipped in the wheel, Typing :: Typed classifier
- Genuine annotation bugs fixed: Pointer.evaluate returned
  tuple[Diffable, ...] but parent is None at the root;
  PatchRecorder.record_add annotated reverse_path: Pointer with a None
  default
- Duck-typed code kept honest instead of over-narrowed: the iapply
  interpreter and the diff() dispatch unpack into Any/cast at the
  boundary, with comments explaining why

Toolchain:
- ty in a dedicated dependency group (and in dev), [tool.ty] config
  scoped to patchdiff/ with python-version = "3.9" so 3.9-incompatible
  typing constructs are caught
- Typecheck job in CI, added to publish's needs

Runtime changes (annotation-driven, behavior-preserving):
- _Proxy._detach makes its caller-guarded invariant explicit with an
  assert
- pyproject metadata fix: description was literally "MIT"; now a real
  description plus license = "MIT"

ty check: 26 diagnostics on the previous code, 0 after. Tests pass at
100% coverage on 3.10-3.14 locally; docs build stays warning-free with
the new signatures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Mu9vEBwU4fQLi8ZgkgdS2
@Korijn Korijn merged commit cb3f5d1 into master Jul 10, 2026
11 checks passed
@Korijn Korijn deleted the claude/typing branch July 10, 2026 21:48
Korijn pushed a commit that referenced this pull request Jul 11, 2026
PR #51 added int(key) in Pointer.evaluate before PR #50's typing
landed; merged together they leave master's Typecheck job red (key is
Hashable, int() wants str-ish). Cast at the conversion site — the
retry only runs for parsed string tokens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Mu9vEBwU4fQLi8ZgkgdS2
Korijn pushed a commit that referenced this pull request Jul 11, 2026
PR #51 added int(key) in Pointer.evaluate before PR #50's typing
landed; merged together they leave master's Typecheck job red (key is
Hashable, int() wants str-ish). Cast at the conversion site — the
retry only runs for parsed string tokens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Mu9vEBwU4fQLi8ZgkgdS2
Korijn added a commit that referenced this pull request Jul 11, 2026
PR #51 added int(key) in Pointer.evaluate before PR #50's typing
landed; merged together they leave master's Typecheck job red (key is
Hashable, int() wants str-ish). Cast at the conversion site — the
retry only runs for parsed string tokens.


Claude-Session: https://claude.ai/code/session_016Mu9vEBwU4fQLi8ZgkgdS2

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants