Skip to content

Commit

Permalink
Merge pull request #198 from DanCardin/dc/propagate-context
Browse files Browse the repository at this point in the history
fix: Ensure  args supply the correct parent context to the arg's action handler.
  • Loading branch information
DanCardin authored Jan 17, 2025
2 parents 1524a8a + 21568be commit fc0d8eb
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 12 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## 0.26

### 0.26.4
- fix: Ensure `propagate=True` args supply the correct parent context to the arg's action handler.

### 0.26.3
- fix: Reexport Empty/EmptyType types at the root.

Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "cappa"
version = "0.26.3"
version = "0.26.4"
description = "Declarative CLI argument parser."

urls = { repository = "https://github.com/dancardin/cappa" }
Expand Down
26 changes: 16 additions & 10 deletions src/cappa/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,23 +247,24 @@ def propagated_context(self) -> dict[str, ParseContext]:
def next_argument(self):
return self.arguments.popleft()

def resolve_context(self, field_name: str, option: RawOption | None = None):
context = self
if option and field_name in self.propagated_options:
context = self.propagated_context[field_name]
return context

def set_result(
self,
field_name: str,
value: typing.Any,
option: RawOption | None = None,
has_value: bool = True,
):
context = self
if option:
if field_name in self.propagated_options:
context = self.propagated_context[field_name]

if field_name in context.missing_options:
context.missing_options.remove(field_name)
if option and field_name in self.missing_options:
self.missing_options.remove(field_name)

if has_value:
context.result[field_name] = value
self.result[field_name] = value

def push(self, command: Command, name: str) -> ParseContext:
nested_context = ParseContext.from_command(command, parent_context=self)
Expand Down Expand Up @@ -639,10 +640,13 @@ def consume_arg(

action_handler = determine_action_handler(arg.action)

# Propagated arguments need to supply the correct parent context to their action handler.
resolved_context = context.resolve_context(field_name, option)

fulfilled_deps: dict = {
Command: parse_state.current_command,
Output: parse_state.output,
ParseContext: context,
ParseContext: resolved_context,
ParseState: parse_state,
Arg: arg,
Value: Value(result),
Expand All @@ -654,7 +658,9 @@ def consume_arg(
kwargs = fulfill_deps(action_handler, fulfilled_deps).kwargs
result = action_handler(**kwargs)

context.set_result(field_name, result, option, assert_type(arg.has_value, bool))
resolved_context.set_result(
field_name, result, option, assert_type(arg.has_value, bool)
)

check_deprecated(parse_state, arg, option)

Expand Down
22 changes: 22 additions & 0 deletions tests/arg/test_propagate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing_extensions import Annotated

import cappa
from cappa.subcommand import Subcommands
from tests.utils import CapsysOutput, parse


Expand Down Expand Up @@ -133,3 +134,24 @@ class Command:
assert "foo" in output.stdout
assert "Everywhere" in output.stdout
assert "Nowhere" not in output.stdout


def test_count():
"""Test that actions requiring accumulated prior state (e.g. count) **get** values from the correct context."""

@dataclass
class Command:
foo: Annotated[int, cappa.Arg(short=True, propagate=True, count=True)] = 1
subcommand: Subcommands[One | None] = None

result = parse(Command, "-f")
assert result == Command(1, None)

result = parse(Command, "-fff")
assert result == Command(3, None)

result = parse(Command, "one", "-f")
assert result == Command(1, One())

result = parse(Command, "one", "-fff")
assert result == Command(3, One())
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit fc0d8eb

Please sign in to comment.