feat: blocked argument#1698
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. 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
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. |
| # 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} |
There was a problem hiding this comment.
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):
...There was a problem hiding this comment.
I go with blocking instead of accepting. Since this sort of collision might have other side effects.
f433527 to
1b5498b
Compare
close #1689
close #1690