Keep getting tripped up by the "non-defaulted function args do not become valid flags" implication of #23. Even if/when #27 is implemented, I can see it tripping users up (if it trips up the author, it's gonna trip up the users!)
I am not 100% sure we can't have + eat our cake; we still cannot have arbitrary numbers of positional args, but within the "we know whether or not the next non-flag thing is a posarg or a task name" framework, it should be possible to "chip away" at positional args by giving them as flags.
For example, imagine this task:
@task(foo, bar, biz='baz'):
pass
Currently, as of #23, the Context created for this task looks as follows:
foo
is a positional argument, and is in .args
but not .flags
- Ditto for
bar
biz
is a nonpositional argument and is in both .args
and .flags
When parsing occurs, the parser knows it cannot skip to the next task boundary until both foo
and bar
have been filled in, by virtue of Context.needs_positional_arg
. That's the only actual test.
Right now we explicitly test to make sure positionals are not available as flags (i.e. they're not added to the flags
data structure and thus --a-positional-arg value
is actually invalid). I do not remember why this is, but hopefully tweaking it and running the test suite will remind me.
Assuming it was just tripping up something dumb in lazy tests, I'll probably just remove that blockade and ensure that positional args given as flags are removed from the positional arg "queue". Thus, in the above example, if we parsed invoke mytask --foo=foovalue barvalue
it would remove foo
from the positionals list, then when it saw barvalue
it would correctly associate it with bar
.
Finally: this does add conceptual complexity to the parser, but I think it's worth it to avoid the more irritating pitfall of "I didn't give this arg a default value, why can't I give it as a flag now?"