Skip to content

explore: drive the CLI from an argtree Cli spec#31

Draft
JPHutchins wants to merge 1 commit into
mainfrom
explore/argtree-cli
Draft

explore: drive the CLI from an argtree Cli spec#31
JPHutchins wants to merge 1 commit into
mainfrom
explore/argtree-cli

Conversation

@JPHutchins
Copy link
Copy Markdown
Owner

What this is

An exploration (not merge-ready — see Blockers) of replacing camas's hand-rolled argparse parser with a declarative argtree Cli spec, and consuming the typed result in dispatch instead of an Any-typed argparse.Namespace.

⛔ Blockers — resolve these argtree issues first

This PR rides on workarounds that should become first-class argtree support before merge:

What changed

File Change
src/camas/main/parser.py Cli NamedTuple spec (one field per arg); build_parser builds it via argtree and adopts the actions into CamasArgumentParser through parents=; new reconstruct() wraps from_namespace. Excluded from mypyc.
src/camas/main/dispatch.py Reads a typed Cli instead of poking the namespace; resolves the env-dependent --effects default here (None ⇒ absent ⇒ default_effects_expr(), "" ⇒ list Effects).
setup.py parser.py added to _main_excluded (mypyc).
pyproject.toml argtree added to runtime deps and [build-system] requires.
tests/main/test_parser.py Read the typed Cli via reconstruct rather than the raw namespace.

The declarative spec is the core; three runtime/state-dependent behaviors stay imperative around it — the dynamic per-matrix-axis --PY flags, the task | expression positional metavar, and the --effects default. That "declarative core + imperative shell" shape is the honest result: argtree is great for the static surface, but camas's parser is unusually dynamic.

Why bother — the payoff

The win isn't the prettier spec; it's types. dispatch now consumes Cli (expression: str | None, dry_run: bool, …) instead of an argparse.Namespace where every field is Any. Under five strict type-checkers that removes a class of implicit-Any reads.

Verification (all green)

  • --help output byte-identical to the current parser
  • 639 tests pass, 100% coverage maintained
  • All 5 type-checkers clean — mypy, pyright, ty, zuban, pyrefly
  • mypyc wheel builds and runs end-to-end: compiled dispatch.so → interpreted parser.py → argtree get_type_hints(Cli), including dynamically-added --PY axis flags

Open question for review

Is the typed-dispatch readability worth (a) a new runtime dependency on a 0.x library and (b) parser.py no longer being mypyc-compiled? Leaning yes as an experiment; not an obvious slam-dunk over the working argparse.

🤖 Generated with Claude Code

Port camas.main.parser's hand-rolled argparse construction to a declarative
argtree `Cli` NamedTuple, and consume the typed result in dispatch instead of
an `Any`-typed argparse.Namespace.

- parser.py: `Cli` spec built by argtree, bridged to CamasArgumentParser
  (custom --help) via argparse `parents=`. The dynamic per-axis flags, the
  state-dependent positional metavar, and the env-dependent --effects default
  stay imperative around the declarative core.
- dispatch.py: read a typed `Cli` (reconstruct/from_namespace) rather than
  poking the namespace; resolve the --effects env default here.
- parser.py excluded from mypyc — argtree resolves field types at runtime via
  get_type_hints, which mypyc erases — and argtree added to build-system
  requires so the compiled dispatcher's import still type-checks.

Behavior unchanged: --help byte-identical, 639 tests pass, 100% coverage, all
five type-checkers clean, and the mypyc wheel builds and runs end-to-end.

Blocked on argtree enhancements before this is merge-ready:
JPHutchins/argtree#9, JPHutchins/argtree#10, JPHutchins/argtree#11.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Jun 1, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (b6131bd) to head (2b9b1a3).

Additional details and impacted files
@@            Coverage Diff            @@
##              main       #31   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           54        54           
  Lines         5103      5116   +13     
  Branches       275       277    +2     
=========================================
+ Hits          5103      5116   +13     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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