Skip to content

feat: Annotated based argparse, and auto completer inference#1614

Draft
KelvinChung2000 wants to merge 6 commits intopython-cmd2:mainfrom
KelvinChung2000:feat/annotated-argparse
Draft

feat: Annotated based argparse, and auto completer inference#1614
KelvinChung2000 wants to merge 6 commits intopython-cmd2:mainfrom
KelvinChung2000:feat/annotated-argparse

Conversation

@KelvinChung2000
Copy link

This is a full rework of #1612. Instead of wrapping Typer. We now extract the types and build the argparse parser.

I want to mark it as a draft for now, as some of the stuff will likely need a bit more cleanup. Please have a look at the documentation and example, and let me know if I missed anything obvious.

@codecov
Copy link

codecov bot commented Mar 23, 2026

Codecov Report

❌ Patch coverage is 97.08029% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.40%. Comparing base (ca8495e) to head (1834450).

Files with missing lines Patch % Lines
cmd2/annotated.py 97.68% 5 Missing ⚠️
cmd2/decorators.py 92.85% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1614      +/-   ##
==========================================
- Coverage   99.51%   99.40%   -0.12%     
==========================================
  Files          21       22       +1     
  Lines        4758     5031     +273     
==========================================
+ Hits         4735     5001     +266     
- Misses         23       30       +7     
Flag Coverage Δ
unittests 99.40% <97.08%> (-0.12%) ⬇️

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.

@KelvinChung2000
Copy link
Author

I started working on this before I closed that PR. While this PR is still using LLMs, I have a much better understanding of what it is writing, since it is based on Python type processing, which I am much more familiar with than click. As mentioned, this code needs some more cleanup before it's ready for review; that's why this is a draft. However, I would like some feedback on the documentation and the example to make sure I haven't missed anything obvious. If you'd prefer to defer until it is fully ready, that's fine as well.

@tleonhardt
Copy link
Member

I'm curious to see where this goes. I can't make any promises in advance, but it sounds like a potentially interesting feature. Though, I would prefer for all the tests to pass before I spend any time reviewing it.

If the code isn't too complex so that it appears to integrate with the rest of cmd2 in a way that is easy to maintain I could see it being a valuable addition.

If for some reason it doesn't immediately integrate well, there may be the possibility of creating a new open-source module that generates argparse argument parsers from type annotations.


_convert.__name__ = enum_class.__name__
# Preserve the enum class for downstream consumers like tab completion.
_convert._cmd2_enum_class = enum_class
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to fix fundamental type error causing all tests and mypy to fail:

cmd2/annotated.py:234:5: error: "Callable[[str], Enum]" has no attribute "_cmd2_enum_class"  [attr-defined]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am aware of this. I am working on the code's style right now. I will push once it is at a good enough state.

This page of code contains all the logic required to generate the argparser. While it is not too difficult to understand, I don't like the long if-else chain because it is not intuitive.

Copy link
Member

@tleonhardt tleonhardt Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't look at this particular case, but since cmd2 now only supports Python 3.10 or newer, using the match statement can be a nice option for structural pattern matching with match/case to replace some long if/else chains.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally considered something like a dict dispatch pattern:

def dis(type_to_resolve):
    type_dict = {Enum: _reslove_enum}
    return type_dict[type_to_resolve]

But the things that return for the _resolve_* are too different, and are slowly moving toward more like filling in an "argparse specification," then having a function that takes in this specification and turns it into a parser.

I like this dict dispatch pattern because it is very easy to read and extend, but it might not be the right pattern here.

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