feat: Experimental annotated argparse#1666
Conversation
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 Report✅ All modified and coverable lines are covered by tests. 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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
|
I plan to review this PR this weekend. @kmvanbrunt @bambu I'd very much appreciate your feedback as well. |
|
How can I do the following?
|
- 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.
…ally subtle conversion edge-case bug
|
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. |
|
Actually, do you want me to split the mega file and store it under something like |
|
@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 @kmvanbrunt Would you have a preference here? |
|
@KelvinChung2000 I chatted with @kmvanbrunt - for now please leave everything in the one big file |
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
|
@KelvinChung2000 I pushed a change to add 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. |
Adds
@with_annotated, a type-hint-driven alternative to@with_argparserthat builds the parser automatically from a command's signature (positional/option inference, enum/literal/path/collection handling, subcommands, groups, mutex). Marked experimental.cmd2/annotated.pyplusArgument/Optionmetadata classes exported fromcmd2dry_run→--dry-run); opt out viaOption("--my_flag")docs/features/annotated.mdwith an experimental admonition;argument_processing.mdkeeps a short pointerexamples/annotated_example.pyand test suite intests/test_annotated.py