Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 13 additions & 11 deletions invoke/parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,17 +292,9 @@ def handle(self, token: str) -> None:
)
) # noqa
self.see_value(token)
# Positional args (must come above context-name check in case we still
# need a posarg and the user legitimately wants to give it a value that
# just happens to be a valid context name.)
elif self.context and self.context.missing_positional_args:
msg = "Context {!r} requires positional args, eating {!r}"
debug(msg.format(self.context, token))
self.see_positional_arg(token)
# New context
elif token in self.contexts:
self.see_context(token)
# Initial-context flag being given as per-task flag (e.g. --help)
# Core flags after a task name (inv mytask --dry). Must run before the
# positional-arg eater, otherwise unfilled posargs swallow e.g. --dry.
# --help already had this path; other core flags did not.
elif self.initial and token in self.initial.flags:
debug("Saw (initial-context) flag {!r}".format(token))
flag = self.initial.flags[token]
Expand All @@ -318,6 +310,16 @@ def handle(self, token: str) -> None:
# default-False 'dedupe') and it's up to us whether we actually
# put any in place.
self.switch_to_flag(token)
# Positional args (must come above context-name check in case we still
# need a posarg and the user legitimately wants to give it a value that
# just happens to be a valid context name.)
elif self.context and self.context.missing_positional_args:
msg = "Context {!r} requires positional args, eating {!r}"
debug(msg.format(self.context, token))
self.see_positional_arg(token)
# New context
elif token in self.contexts:
self.see_context(token)
# Unknown
else:
if not self.ignore_unknown:
Expand Down
3 changes: 3 additions & 0 deletions sites/www/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
Changelog
=========

- :bug:`1084` Core flags given after a task name are no longer swallowed when
that task still has unfilled positional arguments. ``--help`` already had
this treatment; ``--dry`` and the rest of the core flags now do too.
- :release:`3.0.3 <2026-04-07>`
- :support:`- backported` Reverted the `@task
<invoke.tasks.task>` return value type hint change; it actually just makes
Expand Down
12 changes: 12 additions & 0 deletions tests/parser_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,18 @@ def value_requiring_core_flags_also_work_correctly(self) -> None:
result = parser.parse_argv(["mytask", "--hide", "both"])
assert result[0].args.hide.value == "both"

def core_flags_are_not_eaten_as_unfilled_positionals(self) -> None:
initial = Context(
args=[Argument("dry", kind=bool, default=False)]
)
task1 = Context(
"mytask", args=[Argument("name", positional=True)]
)
parser = Parser(initial=initial, contexts=[task1])
result = parser.parse_argv(["mytask", "--dry", "alice"])
assert result[0].args.dry.value is True
assert result[1].args.name.value == "alice"

class edge_cases:
def core_bool_but_per_task_string(self) -> None:
# Initial parse context with bool --hide, and a task with a
Expand Down