22from typing import Dict , Type
33
44import pytest
5- from attrs import NOTHING , Factory , define , field
5+ from attrs import NOTHING , Factory , define , field , frozen
66from hypothesis import assume , given
77from hypothesis .strategies import data , just , one_of , sampled_from
88
@@ -450,12 +450,18 @@ class A:
450450def test_init_false_overridden (converter : BaseConverter ) -> None :
451451 """init=False handling can be overriden."""
452452
453+ @frozen
454+ class Inner :
455+ a : int
456+
453457 @define
454458 class A :
455459 a : int
456460 b : int = field (init = False )
457461 _c : int = field (init = False )
458- d : int = field (init = False , default = 4 )
462+ d : Inner = field (init = False )
463+ e : int = field (init = False , default = 4 )
464+ f : Inner = field (init = False , default = Inner (1 ))
459465
460466 converter .register_unstructure_hook (
461467 A , make_dict_unstructure_fn (A , converter , _cattrs_include_init_false = True )
@@ -464,28 +470,36 @@ class A:
464470 a = A (1 )
465471 a .b = 2
466472 a ._c = 3
473+ a .d = Inner (4 )
467474
468- assert converter .unstructure (a ) == {"a" : 1 , "b" : 2 , "_c" : 3 , "d" : 4 }
475+ assert converter .unstructure (a ) == {
476+ "a" : 1 ,
477+ "b" : 2 ,
478+ "_c" : 3 ,
479+ "d" : {"a" : 4 },
480+ "e" : 4 ,
481+ "f" : {"a" : 1 },
482+ }
469483
470484 converter .register_structure_hook (
471- A ,
472- make_dict_structure_fn (
473- A ,
474- converter ,
475- _cattrs_include_init_false = True ,
476- _cattrs_detailed_validation = converter .detailed_validation ,
477- ),
485+ A , make_dict_structure_fn (A , converter , _cattrs_include_init_false = True )
478486 )
479487
480- structured = converter .structure ({"a" : 1 , "b" : 2 , "_c" : 3 }, A )
488+ structured = converter .structure ({"a" : 1 , "b" : 2 , "_c" : 3 , "d" : { "a" : 1 } }, A )
481489 assert structured .b == 2
482490 assert structured ._c == 3
483- assert structured .d == 4
491+ assert structured .d == Inner (1 )
492+ assert structured .e == 4
493+ assert structured .f == Inner (1 )
484494
485- structured = converter .structure ({"a" : 1 , "b" : 2 , "_c" : 3 , "d" : - 4 }, A )
495+ structured = converter .structure (
496+ {"a" : 1 , "b" : 2 , "_c" : 3 , "d" : {"a" : 5 }, "e" : - 4 , "f" : {"a" : 2 }}, A
497+ )
486498 assert structured .b == 2
487499 assert structured ._c == 3
488- assert structured .d == - 4
500+ assert structured .d == Inner (5 )
501+ assert structured .e == - 4
502+ assert structured .f == Inner (2 )
489503
490504
491505def test_init_false_field_override (converter : BaseConverter ) -> None :
@@ -538,6 +552,29 @@ class A:
538552 assert structured .d == - 4
539553
540554
555+ def test_init_false_no_structure_hook (converter : BaseConverter ):
556+ """init=False attributes with converters and `prefer_attrs_converters` work."""
557+
558+ @define
559+ class A :
560+ a : int = field (converter = int , init = False )
561+
562+ converter .register_structure_hook (
563+ A ,
564+ make_dict_structure_fn (
565+ A ,
566+ converter ,
567+ _cattrs_prefer_attrib_converters = True ,
568+ _cattrs_include_init_false = True ,
569+ ),
570+ )
571+
572+ res = A ()
573+ res .a = 5
574+
575+ assert converter .structure ({"a" : "5" }, A ) == res
576+
577+
541578@given (forbid_extra_keys = ..., detailed_validation = ...)
542579def test_forbid_extra_keys_from_converter (
543580 forbid_extra_keys : bool , detailed_validation : bool
0 commit comments