Skip to content

Commit 005cd38

Browse files
committed
Add docs
1 parent 0a8358b commit 005cd38

File tree

3 files changed

+19
-6
lines changed

3 files changed

+19
-6
lines changed

HISTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Our backwards-compatibility policy can be found [here](https://github.com/python
1717
([#688](https://github.com/python-attrs/cattrs/pull/688))
1818
- Python 3.9 is no longer supported, as it is end-of-life. Use previous versions on this Python version.
1919
([#698](https://github.com/python-attrs/cattrs/pull/698))
20+
- Apply the attrs converter to the default value before checking if it is equal to the attribute's value, when `omit_if_default` is true and an attrs converter is specified.
21+
([#696](https://github.com/python-attrs/cattrs/pull/696))
2022

2123
## 25.3.0 (2025-10-07)
2224

src/cattrs/gen/__init__.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ def make_dict_unstructure_fn_from_attrs(
9999
.. versionchanged:: 25.2.0
100100
The `_cattrs_use_alias` parameter takes its value from the given converter
101101
by default.
102+
.. versionchanged:: NEXT
103+
When `_cattrs_omit_if_default` is true and the attribute has an attrs converter
104+
specified, the converter is applied to the default value before checking if it
105+
is equal to the attribute's value.
102106
"""
103107

104108
fn_name = "unstructure_" + cl.__name__
@@ -186,15 +190,20 @@ def make_dict_unstructure_fn_from_attrs(
186190
c = a.converter
187191
if c is not None:
188192
conv_name = f"__c_conv_{attr_name}"
189-
globs[conv_name] = c
190-
internal_arg_parts[conv_name] = c
191193
if isinstance(c, Converter):
194+
globs[conv_name] = c
195+
internal_arg_parts[conv_name] = c
192196
field_name = f"__c_field_{attr_name}"
193197
globs[field_name] = a
194198
internal_arg_parts[field_name] = a
195199
def_str = f"{conv_name}({def_str}, instance, {field_name})"
196-
else:
200+
elif isinstance(d, Factory):
201+
globs[conv_name] = c
202+
internal_arg_parts[conv_name] = c
197203
def_str = f"{conv_name}({def_str})"
204+
else:
205+
globs[def_name] = c(d)
206+
internal_arg_parts[def_name] = c(d)
198207

199208
lines.append(f" if instance.{attr_name} != {def_str}:")
200209
lines.append(f" res['{kn}'] = {invoke}")

tests/test_converter.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ class C:
343343
@given(simple_typed_classes(defaults="always", allow_nan=False))
344344
def test_omit_default_with_attrs_converter_roundtrip(cl_and_vals):
345345
"""
346-
Omit default with attrs' converter on the converter works.
346+
Omit default with an attrs converter works.
347347
"""
348348
converter = Converter(omit_if_default=True)
349349
cl, vals, kwargs = cl_and_vals
@@ -352,16 +352,18 @@ def test_omit_default_with_attrs_converter_roundtrip(cl_and_vals):
352352
class C:
353353
a1: int = field(default="1", converter=int)
354354
a2: int = field(default="1", converter=attrs.Converter(int))
355+
a3: int = field(factory=lambda: "1", converter=int)
356+
a4: int = field(factory=lambda: "1", converter=attrs.Converter(int))
355357
c: cl = Factory(lambda: cl(*vals, **kwargs))
356358

357359
inst = C()
358360
unstructured = converter.unstructure(inst)
359361
assert unstructured == {}
360362
assert inst == converter.structure(unstructured, C)
361363

362-
inst = C(0, 0)
364+
inst = C(0, 0, 0, 0)
363365
unstructured = converter.unstructure(inst)
364-
assert unstructured == {"a1": 0, "a2": 0}
366+
assert unstructured == {"a1": 0, "a2": 0, "a3": 0, "a4": 0}
365367
assert inst == converter.structure(unstructured, C)
366368

367369

0 commit comments

Comments
 (0)