@@ -103,6 +103,44 @@ def __reduce__(self, _none_constructor=type(None), _args=()):  # noqa: B008
103103        return  _none_constructor , _args 
104104
105105
106+ class  _Hashability (enum .Enum ):
107+     """ 
108+     The hashability of a class. 
109+     """ 
110+ 
111+     HASHABLE  =  "hashable"   # write a __hash__ 
112+     UNHASHABLE  =  "unhashable"   # set __hash__ to None 
113+     LEAVE_ALONE  =  "leave_alone"   # don't touch __hash__ 
114+ 
115+ 
116+ class  _AttrsParams (NamedTuple ):
117+     """ 
118+     Effective parameters to attrs() or define() decorators. 
119+     """ 
120+ 
121+     exception : bool 
122+     slots : bool 
123+     frozen : bool 
124+     init : bool 
125+     repr : bool 
126+     eq : bool 
127+     order : bool 
128+     hash : _Hashability 
129+     match_args : bool 
130+     kw_only : bool 
131+     weakref_slot : bool 
132+     auto_attribs : bool 
133+     collect_by_mro : bool 
134+     auto_detect : bool 
135+     auto_exc : bool 
136+     cache_hash : bool 
137+     str : bool 
138+     getstate_setstate : bool 
139+     has_custom_setattr : bool 
140+     on_setattr : Callable [[str , Any ], Any ]
141+     field_transformer : Callable [[Attribute ], Attribute ]
142+ 
143+ 
106144def  attrib (
107145    default = NOTHING ,
108146    validator = None ,
@@ -663,38 +701,27 @@ def __init__(
663701        self ,
664702        cls : type ,
665703        these ,
666-         slots ,
667-         frozen ,
668-         weakref_slot ,
669-         getstate_setstate ,
670-         auto_attribs ,
671-         kw_only ,
672-         cache_hash ,
673-         is_exc ,
674-         collect_by_mro ,
675-         on_setattr ,
676-         has_custom_setattr ,
677-         field_transformer ,
704+         attrs_params : _AttrsParams ,
678705    ):
679706        attrs , base_attrs , base_map  =  _transform_attrs (
680707            cls ,
681708            these ,
682-             auto_attribs ,
683-             kw_only ,
684-             collect_by_mro ,
685-             field_transformer ,
709+             attrs_params . auto_attribs ,
710+             attrs_params . kw_only ,
711+             attrs_params . collect_by_mro ,
712+             attrs_params . field_transformer ,
686713        )
687714
688715        self ._cls  =  cls 
689-         self ._cls_dict  =  dict (cls .__dict__ ) if  slots  else  {}
716+         self ._cls_dict  =  dict (cls .__dict__ ) if  attrs_params . slots  else  {}
690717        self ._attrs  =  attrs 
691718        self ._base_names  =  {a .name  for  a  in  base_attrs }
692719        self ._base_attr_map  =  base_map 
693720        self ._attr_names  =  tuple (a .name  for  a  in  attrs )
694-         self ._slots  =  slots 
695-         self ._frozen  =  frozen 
696-         self ._weakref_slot  =  weakref_slot 
697-         self ._cache_hash  =  cache_hash 
721+         self ._slots  =  attrs_params . slots 
722+         self ._frozen  =  attrs_params . frozen 
723+         self ._weakref_slot  =  attrs_params . weakref_slot 
724+         self ._cache_hash  =  attrs_params . cache_hash 
698725        self ._has_pre_init  =  bool (getattr (cls , "__attrs_pre_init__" , False ))
699726        self ._pre_init_has_args  =  False 
700727        if  self ._has_pre_init :
@@ -705,20 +732,21 @@ def __init__(
705732            self ._pre_init_has_args  =  len (pre_init_signature .parameters ) >  1 
706733        self ._has_post_init  =  bool (getattr (cls , "__attrs_post_init__" , False ))
707734        self ._delete_attribs  =  not  bool (these )
708-         self ._is_exc  =  is_exc 
709-         self ._on_setattr  =  on_setattr 
735+         self ._is_exc  =  attrs_params . exception 
736+         self ._on_setattr  =  attrs_params . on_setattr 
710737
711-         self ._has_custom_setattr  =  has_custom_setattr 
738+         self ._has_custom_setattr  =  attrs_params . has_custom_setattr 
712739        self ._wrote_own_setattr  =  False 
713740
714741        self ._cls_dict ["__attrs_attrs__" ] =  self ._attrs 
742+         self ._cls_dict ["__attrs_params__" ] =  attrs_params 
715743
716-         if  frozen :
744+         if  attrs_params . frozen :
717745            self ._cls_dict ["__setattr__" ] =  _frozen_setattrs 
718746            self ._cls_dict ["__delattr__" ] =  _frozen_delattrs 
719747
720748            self ._wrote_own_setattr  =  True 
721-         elif  on_setattr  in  (
749+         elif  self . _on_setattr  in  (
722750            _DEFAULT_ON_SETATTR ,
723751            setters .validate ,
724752            setters .convert ,
@@ -734,18 +762,18 @@ def __init__(
734762                    break 
735763            if  (
736764                (
737-                     on_setattr  ==  _DEFAULT_ON_SETATTR 
765+                     self . _on_setattr  ==  _DEFAULT_ON_SETATTR 
738766                    and  not  (has_validator  or  has_converter )
739767                )
740-                 or  (on_setattr  ==  setters .validate  and  not  has_validator )
741-                 or  (on_setattr  ==  setters .convert  and  not  has_converter )
768+                 or  (self . _on_setattr  ==  setters .validate  and  not  has_validator )
769+                 or  (self . _on_setattr  ==  setters .convert  and  not  has_converter )
742770            ):
743771                # If class-level on_setattr is set to convert + validate, but 
744772                # there's no field to convert or validate, pretend like there's 
745773                # no on_setattr. 
746774                self ._on_setattr  =  None 
747775
748-         if  getstate_setstate :
776+         if  attrs_params . getstate_setstate :
749777            (
750778                self ._cls_dict ["__getstate__" ],
751779                self ._cls_dict ["__setstate__" ],
@@ -1439,6 +1467,7 @@ def attrs(
14391467        on_setattr  =  setters .pipe (* on_setattr )
14401468
14411469    def  wrap (cls ):
1470+         nonlocal  hash 
14421471        is_frozen  =  frozen  or  _has_frozen_base_class (cls )
14431472        is_exc  =  auto_exc  is  True  and  issubclass (cls , BaseException )
14441473        has_own_setattr  =  auto_detect  and  _has_own_attribute (
@@ -1449,84 +1478,95 @@ def wrap(cls):
14491478            msg  =  "Can't freeze a class with a custom __setattr__." 
14501479            raise  ValueError (msg )
14511480
1452-         builder  =  _ClassBuilder (
1453-             cls ,
1454-             these ,
1455-             slots ,
1456-             is_frozen ,
1457-             weakref_slot ,
1458-             _determine_whether_to_implement (
1481+         eq  =  not  is_exc  and  _determine_whether_to_implement (
1482+             cls , eq_ , auto_detect , ("__eq__" , "__ne__" )
1483+         )
1484+ 
1485+         if  is_exc :
1486+             hashability  =  _Hashability .LEAVE_ALONE 
1487+         elif  hash  is  True :
1488+             hashability  =  _Hashability .HASHABLE 
1489+         elif  hash  is  False :
1490+             hashability  =  _Hashability .LEAVE_ALONE 
1491+         elif  hash  is  None :
1492+             if  auto_detect  is  True  and  _has_own_attribute (cls , "__hash__" ):
1493+                 hashability  =  _Hashability .LEAVE_ALONE 
1494+             elif  eq  is  True  and  is_frozen  is  True :
1495+                 hashability  =  _Hashability .HASHABLE 
1496+             elif  eq  is  False :
1497+                 hashability  =  _Hashability .LEAVE_ALONE 
1498+             else :
1499+                 hashability  =  _Hashability .UNHASHABLE 
1500+         else :
1501+             msg  =  "Invalid value for hash.  Must be True, False, or None." 
1502+             raise  TypeError (msg )
1503+ 
1504+         if  hashability  is  not   _Hashability .HASHABLE  and  cache_hash :
1505+             msg  =  "Invalid value for cache_hash.  To use hash caching, hashing must be either explicitly or implicitly enabled." 
1506+             raise  TypeError (msg )
1507+ 
1508+         attrs_params  =  _AttrsParams (
1509+             exception = is_exc ,
1510+             frozen = is_frozen ,
1511+             slots = slots ,
1512+             init = _determine_whether_to_implement (
1513+                 cls , init , auto_detect , ("__init__" ,)
1514+             ),
1515+             repr = _determine_whether_to_implement (
1516+                 cls , repr , auto_detect , ("__repr__" ,)
1517+             ),
1518+             eq = eq ,
1519+             order = not  is_exc 
1520+             and  _determine_whether_to_implement (
1521+                 cls ,
1522+                 order_ ,
1523+                 auto_detect ,
1524+                 ("__lt__" , "__le__" , "__gt__" , "__ge__" ),
1525+             ),
1526+             hash = hashability ,
1527+             match_args = match_args ,
1528+             kw_only = kw_only ,
1529+             weakref_slot = weakref_slot ,
1530+             auto_attribs = auto_attribs  if  auto_attribs  is  not   None  else  False ,
1531+             collect_by_mro = collect_by_mro ,
1532+             auto_detect = auto_detect ,
1533+             auto_exc = is_exc ,
1534+             cache_hash = cache_hash ,
1535+             str = str ,
1536+             getstate_setstate = _determine_whether_to_implement (
14591537                cls ,
14601538                getstate_setstate ,
14611539                auto_detect ,
14621540                ("__getstate__" , "__setstate__" ),
14631541                default = slots ,
14641542            ),
1465-             auto_attribs ,
1466-             kw_only ,
1467-             cache_hash ,
1468-             is_exc ,
1469-             collect_by_mro ,
1470-             on_setattr ,
1471-             has_own_setattr ,
1472-             field_transformer ,
1543+             has_custom_setattr = has_own_setattr ,
1544+             on_setattr = on_setattr ,
1545+             field_transformer = field_transformer ,
14731546        )
14741547
1475-         if   _determine_whether_to_implement ( 
1476-              cls ,  repr ,  auto_detect , ( "__repr__" ,) 
1477-         ) :
1548+         builder   =   _ClassBuilder ( cls ,  these ,  attrs_params ) 
1549+ 
1550+         if   attrs_params . repr   is   True :
14781551            builder .add_repr (repr_ns )
14791552
1480-         if  str  is  True :
1553+         if  attrs_params . str  is  True :
14811554            builder .add_str ()
14821555
1483-         eq  =  _determine_whether_to_implement (
1484-             cls , eq_ , auto_detect , ("__eq__" , "__ne__" )
1485-         )
1486-         if  not  is_exc  and  eq  is  True :
1556+         if  attrs_params .eq  is  True :
14871557            builder .add_eq ()
1488-         if  not  is_exc  and  _determine_whether_to_implement (
1489-             cls , order_ , auto_detect , ("__lt__" , "__le__" , "__gt__" , "__ge__" )
1490-         ):
1558+         if  attrs_params .order  is  True :
14911559            builder .add_order ()
14921560
14931561        if  not  frozen :
14941562            builder .add_setattr ()
14951563
1496-         nonlocal  hash 
1497-         if  (
1498-             hash  is  None 
1499-             and  auto_detect  is  True 
1500-             and  _has_own_attribute (cls , "__hash__" )
1501-         ):
1502-             hash  =  False 
1503- 
1504-         if  hash  is  not   True  and  hash  is  not   False  and  hash  is  not   None :
1505-             # Can't use `hash in` because 1 == True for example. 
1506-             msg  =  "Invalid value for hash.  Must be True, False, or None." 
1507-             raise  TypeError (msg )
1508- 
1509-         if  hash  is  False  or  (hash  is  None  and  eq  is  False ) or  is_exc :
1510-             # Don't do anything. Should fall back to __object__'s __hash__ 
1511-             # which is by id. 
1512-             if  cache_hash :
1513-                 msg  =  "Invalid value for cache_hash.  To use hash caching, hashing must be either explicitly or implicitly enabled." 
1514-                 raise  TypeError (msg )
1515-         elif  hash  is  True  or  (
1516-             hash  is  None  and  eq  is  True  and  is_frozen  is  True 
1517-         ):
1518-             # Build a __hash__ if told so, or if it's safe. 
1564+         if  attrs_params .hash  is  _Hashability .HASHABLE :
15191565            builder .add_hash ()
1520-         else :
1521-             # Raise TypeError on attempts to hash. 
1522-             if  cache_hash :
1523-                 msg  =  "Invalid value for cache_hash.  To use hash caching, hashing must be either explicitly or implicitly enabled." 
1524-                 raise  TypeError (msg )
1566+         elif  attrs_params .hash  is  _Hashability .UNHASHABLE :
15251567            builder .make_unhashable ()
15261568
1527-         if  _determine_whether_to_implement (
1528-             cls , init , auto_detect , ("__init__" ,)
1529-         ):
1569+         if  attrs_params .init :
15301570            builder .add_init ()
15311571        else :
15321572            builder .add_attrs_init ()
0 commit comments