Skip to content

feat: blocked argument#1698

Open
KelvinChung2000 wants to merge 4 commits into
mainfrom
feat/annotated-dataclass-blocks
Open

feat: blocked argument#1698
KelvinChung2000 wants to merge 4 commits into
mainfrom
feat/annotated-dataclass-blocks

Conversation

@KelvinChung2000

Copy link
Copy Markdown
Contributor

close #1689
close #1690

@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 99.61%. Comparing base (b17b1bd) to head (1b5498b).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1698      +/-   ##
==========================================
+ Coverage   99.58%   99.61%   +0.02%     
==========================================
  Files          23       23              
  Lines        5773     5909     +136     
==========================================
+ Hits         5749     5886     +137     
+ Misses         24       23       -1     
Flag Coverage Δ
unittests 99.61% <100.00%> (+0.02%) ⬆️

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

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

Comment thread cmd2/annotated.py
Comment thread cmd2/annotated.py
Comment thread cmd2/annotated.py Outdated
# function (the block instance is), so leaving them in would crash the call with an unexpected keyword.
# Pop them up front, before the already-supplied/inherited checks, so a directly-supplied instance does
# not strand command-line field values in func_kwargs.
field_values = {name: func_kwargs.pop(name) for name in spec.field_names if name in func_kwargs}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Programmatically supplied block instances can be accidentally passed to the dataclass constructor if a field shares the block's parameter name.

In _reconstruct_dataclass_blocks, if an ArgumentBlock has a field with the exact same name as the function parameter that accepts the block (e.g., def do_x(self, opts: Opts) where Opts has a field named opts), and a caller passes the block instance directly via kwargs (e.g., do_x("cmd", opts=Opts(...))), the block instance will be mistakenly popped as a field value and passed into the dataclass constructor, resulting in a TypeError. You must safely discard stray command-line field values without popping the provided block instance itself.

A change might look something like ...

for block_name, spec in blocks.items():
    if block_name in func_kwargs:
        # A block instance is already present (e.g. a programmatic call passing it directly); use it as-is.
        # Pop any stray command-line field values so they don't crash the call with an unexpected keyword.
        for name in spec.field_names:
            if name != block_name:
                func_kwargs.pop(name, None)
        continue

    # The expanded field values always come out of func_kwargs -- they are not parameters of the command
    # function (the block instance is), so leaving them in would crash the call with an unexpected keyword.
    field_values = {name: func_kwargs.pop(name) for name in spec.field_names if name in func_kwargs}
    if spec.inherited and not getattr(ns, _base_args_marker_dest(spec.dc_type), False):
...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I go with blocking instead of accepting. Since this sort of collision might have other side effects.

@KelvinChung2000 KelvinChung2000 force-pushed the feat/annotated-dataclass-blocks branch from f433527 to 1b5498b Compare June 26, 2026 23:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants