diff --git a/HISTORY.md b/HISTORY.md index 33666930..36014063 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,7 @@ # History + ```{currentmodule} cattrs + ``` This project adheres to [Calendar Versioning](https://calver.org/). @@ -9,6 +11,11 @@ The third number is for emergencies when we need to start branches for older rel Our backwards-compatibility policy can be found [here](https://github.com/python-attrs/cattrs/blob/main/.github/SECURITY.md). +## 24.1.3 (UNRELEASED) + +- Fix structuring of keyword-only dataclass fields when not using detailed validation. + ([#637](https://github.com/python-attrs/cattrs/issues/637) [#638](https://github.com/python-attrs/cattrs/pull/638)) + ## 24.1.2 (2024-09-22) - Fix {meth}`BaseConverter.register_structure_hook` and {meth}`BaseConverter.register_unstructure_hook` type hints. diff --git a/src/cattrs/_compat.py b/src/cattrs/_compat.py index 027ef477..717f3d61 100644 --- a/src/cattrs/_compat.py +++ b/src/cattrs/_compat.py @@ -170,6 +170,7 @@ def adapted_fields(cl) -> List[Attribute]: True, type=type_hints.get(attr.name, attr.type), alias=attr.name, + kw_only=getattr(attr, "kw_only", False), ) for attr in attrs ] diff --git a/tests/test_dataclasses.py b/tests/test_dataclasses.py index 0f86c7a0..e6df0f58 100644 --- a/tests/test_dataclasses.py +++ b/tests/test_dataclasses.py @@ -2,9 +2,12 @@ from typing import List import attr +import pytest from cattrs import BaseConverter +from ._compat import is_py310_plus + @dataclasses.dataclass class Foo: @@ -41,3 +44,20 @@ def test_dataclasses(converter: BaseConverter): assert converter.unstructure(struct) == unstruct assert converter.structure(unstruct, Foo) == struct + + +@pytest.mark.skipif(not is_py310_plus, reason="kwonly fields are Python 3.10+") +def test_kw_only_propagation(converter: BaseConverter): + """KW-only args work. + + Reproducer from https://github.com/python-attrs/cattrs/issues/637. + """ + + @dataclasses.dataclass + class PartialKeywords: + a1: str = "Default" + a2: str = dataclasses.field(kw_only=True) + + assert converter.structure({"a2": "Value"}, PartialKeywords) == PartialKeywords( + a1="Default", a2="Value" + )