Skip to content

feat: Experimental annotated argparse#1666

Open
KelvinChung2000 wants to merge 26 commits into
mainfrom
feat/annotated-argparse
Open

feat: Experimental annotated argparse#1666
KelvinChung2000 wants to merge 26 commits into
mainfrom
feat/annotated-argparse

Conversation

@KelvinChung2000
Copy link
Copy Markdown

Adds @with_annotated, a type-hint-driven alternative to @with_argparser that builds the parser automatically from a command's signature (positional/option inference, enum/literal/path/collection handling, subcommands, groups, mutex). Marked experimental.

  • New module cmd2/annotated.py plus Argument / Option metadata classes exported from cmd2
  • Underscored param names auto-dasherize in generated flags (dry_run--dry-run); opt out via Option("--my_flag")
  • Dedicated docs page docs/features/annotated.md with an experimental admonition; argument_processing.md keeps a short pointer
  • Example app examples/annotated_example.py and test suite in tests/test_annotated.py

Adds @with_annotated decorator that builds argparse parsers from type-annotated
function signatures. Supports Annotated[T, Argument(...)] / Annotated[T, Option(...)]
metadata, automatic positional/option detection, optional unwrapping, collections,
enums, literals, Path completion, subcommands via subcommand_to=, base_command=True
with cmd2_handler dispatch, and argument/mutually-exclusive groups.

- New module cmd2/annotated.py with Argument, Option, with_annotated, and
  build_parser_from_function helpers
- Comprehensive test suite in tests/test_annotated.py
- Example in examples/annotated_example.py
- Docs updates in docs/features/argument_processing.md
@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.54%. Comparing base (e2c061c) to head (a4db7ce).

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #1666    +/-   ##
========================================
  Coverage   99.53%   99.54%            
========================================
  Files          22       23     +1     
  Lines        4928     5680   +752     
========================================
+ Hits         4905     5654   +749     
- Misses         23       26     +3     
Flag Coverage Δ
unittests 99.54% <100.00%> (+<0.01%) ⬆️

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.

@tleonhardt
Copy link
Copy Markdown
Member

I plan to review this PR this weekend. @kmvanbrunt @bambu I'd very much appreciate your feedback as well.

@tleonhardt tleonhardt added this to the 4.0.0 milestone May 15, 2026
Comment thread cmd2/annotated.py Outdated
Comment thread cmd2/annotated.py Outdated
Comment thread cmd2/annotated.py Outdated
Comment thread examples/annotated_example.py
Comment thread cmd2/annotated.py Outdated
@kmvanbrunt
Copy link
Copy Markdown
Member

@KelvinChung2000

How can I do the following?

  1. Set the title and description for an argument group?
  2. Set the description and epilog for a parser?
  3. Set a custom help formatter class for a parser?
  4. Use a custom parser class?

Comment thread cmd2/__init__.py Outdated
KelvinChung2000 and others added 8 commits May 19, 2026 15:14
- aliases param: Sequence[str] = () to match as_subcommand_to()
- update aliases None checks now that it defaults to ()
- type with_annotated via @overload (no longer untyped decorator)
- drop experimental annotated exports from cmd2/__init__.py
- import from cmd2.annotated in example/tests/docs; fix example mypy
- Group(*members, title=, description=) for titled argument-group
sections
  (groups= now accepts bare tuples or Group)
- description= and epilog= for the generated parser
- formatter_class= for a custom help formatter
- parser_class= for a custom parser class

Includes tests, example command, and docs.
Adds VerbatimHelpFormatter and StrictArgumentParser subclasses and a
do_report command so all four parser-customization features have a
runnable demonstration, not just docs/tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop bare tuple[str, ...] support; entries must be Group instances.
Removes the _group_members shim and adds an explicit TypeError guard
(_require_group) so a wrong type fails clearly instead of with an
AttributeError. Updates tests and docs accordingly.
Adds a do_export command so mutually_exclusive_groups has a runnable
demo alongside the existing groups demo.
Comment thread cmd2/annotated.py Outdated
@tleonhardt tleonhardt changed the title feat: Experiemtal annotated argparse feat: Experimental annotated argparse May 20, 2026
Comment thread cmd2/annotated.py
Comment thread cmd2/annotated.py
Comment thread cmd2/annotated.py Outdated
Comment thread cmd2/annotated.py Outdated
Comment thread cmd2/annotated.py Outdated
Comment thread cmd2/annotated.py Outdated
Comment thread cmd2/annotated.py Outdated
@KelvinChung2000
Copy link
Copy Markdown
Author

I have updated the implementation to be as declarative as possible. So, in terms of maintenance or edge cases, you can add/ban them by just adding a new rule.

Hopefully, I have now covered most of the missing features.

@KelvinChung2000
Copy link
Copy Markdown
Author

Actually, do you want me to split the mega file and store it under something like cmd2/annotated? I can move out some of the "noise" like changes to potentially make things easier to review. This sort of large diff commit, GitHub is just not that well designed for it.

@tleonhardt
Copy link
Copy Markdown
Member

@KelvinChung2000 I'm flexible. I can see an argument for keeping it all self-contained within one file.

But I could also see the argument for splitting it into multiple files underneath the cmd2.annotated namespace - basically into its own sub-package.

@kmvanbrunt Would you have a preference here?

Comment thread docs/features/argument_processing.md Outdated
Comment thread cmd2/annotated.py Outdated
@tleonhardt
Copy link
Copy Markdown
Member

@KelvinChung2000 I chatted with @kmvanbrunt - for now please leave everything in the one big file annotated.py. Later if things grow, we can consider breaking up into multiple files.

Also:
- Fix minor edge case silent failure bug
- Add description of this new feature to CHANGELOG under an "Experimental Features" section for the 4.0.0 release
@tleonhardt
Copy link
Copy Markdown
Member

@KelvinChung2000 I pushed a change to add @with_annotated to the top level namespace after chatting with @kmvanbrunt about it.

I also pushed a fix for the minor edge case bug I mentioned last time.

From my perspective, this is good to merge. If anyone has any further comments or recommendations, put them forth sooner rather than later. I'll merge this a day or two from now if there are no further suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants