diff --git a/CHANGELOG.md b/CHANGELOG.md index a6ab2ec..484358b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 0.32 +### 0.32.1 + +- fix: kw_only dataclass inheritance mis-ordering. + ### 0.32.0 - feat: Add `epilog=` field on commands for text which should go after the argument help. - refactor: All root objects (Command/Arg/Subcommand) to have "Final" variants which represent their post-normalization shape. diff --git a/pyproject.toml b/pyproject.toml index 9f8f6f8..5954a30 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "cappa" -version = "0.32.0" +version = "0.32.1" description = "Declarative CLI argument parser." urls = { repository = "https://github.com/dancardin/cappa" } diff --git a/src/cappa/command.py b/src/cappa/command.py index b011efe..6d19b4a 100644 --- a/src/cappa/command.py +++ b/src/cappa/command.py @@ -217,7 +217,9 @@ def collect( raw_subcommands.append((arg, None, None)) else: - for field, param_view in zip(fields, function_view.parameters): + param_by_name = {p.name: p for p in function_view.parameters} + for field in fields: + param_view = param_by_name[field.name] arg_help = help_text.args.get(param_view.name) maybe_subcommand = Subcommand.detect( diff --git a/tests/test_base.py b/tests/test_base.py index 513ed0f..c6e0adf 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -5,7 +5,7 @@ import cappa from cappa.command import Command -from tests.utils import Backend, backends +from tests.utils import Backend, backends, require_dataclass_kw_only @backends @@ -165,6 +165,27 @@ class Example: ... assert result == Example() +@require_dataclass_kw_only +@backends +def test_kw_only_base_class_field_ordering(backend: Backend): + kwargs = {"kw_only": True} + + @dataclass(**kwargs) + class Base: + name: str = "hello" + + @dataclass + class Child(Base): + number: int = 0 + + result = cappa.parse( + cappa.Command(Child, default_long=True), + argv=["--name", "world"], + backend=backend, + ) + assert result == Child(number=0, name="world") + + @backends def test_collect_skips_non_init_fields(backend: Backend): @dataclass diff --git a/tests/utils.py b/tests/utils.py index 9a9cab8..356a5fc 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -2,6 +2,7 @@ import contextlib import os +import sys from dataclasses import dataclass from typing import Any, Union from unittest.mock import patch @@ -23,9 +24,14 @@ "invoke", "invoke_async", "parse", + "require_dataclass_kw_only", "runner", ] +require_dataclass_kw_only = pytest.mark.skipif( + sys.version_info < (3, 10), reason="kw_only requires Python 3.10+" +) + backends = pytest.mark.parametrize( "backend", [ diff --git a/uv.lock b/uv.lock index f4a67a5..acf0c53 100644 --- a/uv.lock +++ b/uv.lock @@ -84,7 +84,7 @@ wheels = [ [[package]] name = "cappa" -version = "0.32.0" +version = "0.32.1" source = { editable = "." } dependencies = [ { name = "rich" },