@@ -654,7 +654,7 @@ class _ClassBuilder(object):
654654 "_on_setattr" ,
655655 "_slots" ,
656656 "_weakref_slot" ,
657- "_has_own_setattr " ,
657+ "_wrote_own_setattr " ,
658658 "_has_custom_setattr" ,
659659 )
660660
@@ -701,15 +701,23 @@ def __init__(
701701 self ._on_setattr = on_setattr
702702
703703 self ._has_custom_setattr = has_custom_setattr
704- self ._has_own_setattr = False
704+ self ._wrote_own_setattr = False
705705
706706 self ._cls_dict ["__attrs_attrs__" ] = self ._attrs
707707
708708 if frozen :
709709 self ._cls_dict ["__setattr__" ] = _frozen_setattrs
710710 self ._cls_dict ["__delattr__" ] = _frozen_delattrs
711711
712- self ._has_own_setattr = True
712+ self ._wrote_own_setattr = True
713+ elif on_setattr == setters .validate :
714+ for a in attrs :
715+ if a .validator is not None :
716+ break
717+ else :
718+ # If class-level on_setattr is set to validating, but there's
719+ # no field to validate, pretend like there's no on_setattr.
720+ self ._on_setattr = None
713721
714722 if getstate_setstate :
715723 (
@@ -759,7 +767,7 @@ def _patch_original_class(self):
759767
760768 # If we've inherited an attrs __setattr__ and don't write our own,
761769 # reset it to object's.
762- if not self ._has_own_setattr and getattr (
770+ if not self ._wrote_own_setattr and getattr (
763771 cls , "__attrs_own_setattr__" , False
764772 ):
765773 cls .__attrs_own_setattr__ = False
@@ -787,7 +795,7 @@ def _create_slots_class(self):
787795 # XXX: a non-attrs class and subclass the resulting class with an attrs
788796 # XXX: class. See `test_slotted_confused` for details. For now that's
789797 # XXX: OK with us.
790- if not self ._has_own_setattr :
798+ if not self ._wrote_own_setattr :
791799 cd ["__attrs_own_setattr__" ] = False
792800
793801 if not self ._has_custom_setattr :
@@ -958,8 +966,7 @@ def add_init(self):
958966 self ._cache_hash ,
959967 self ._base_attr_map ,
960968 self ._is_exc ,
961- self ._on_setattr is not None
962- and self ._on_setattr is not setters .NO_OP ,
969+ self ._on_setattr ,
963970 attrs_init = False ,
964971 )
965972 )
@@ -978,8 +985,7 @@ def add_attrs_init(self):
978985 self ._cache_hash ,
979986 self ._base_attr_map ,
980987 self ._is_exc ,
981- self ._on_setattr is not None
982- and self ._on_setattr is not setters .NO_OP ,
988+ self ._on_setattr ,
983989 attrs_init = True ,
984990 )
985991 )
@@ -1038,7 +1044,7 @@ def __setattr__(self, name, val):
10381044
10391045 self ._cls_dict ["__attrs_own_setattr__" ] = True
10401046 self ._cls_dict ["__setattr__" ] = self ._add_method_dunders (__setattr__ )
1041- self ._has_own_setattr = True
1047+ self ._wrote_own_setattr = True
10421048
10431049 return self
10441050
@@ -2008,10 +2014,14 @@ def _make_init(
20082014 cache_hash ,
20092015 base_attr_map ,
20102016 is_exc ,
2011- has_global_on_setattr ,
2017+ cls_on_setattr ,
20122018 attrs_init ,
20132019):
2014- if frozen and has_global_on_setattr :
2020+ has_cls_on_setattr = (
2021+ cls_on_setattr is not None and cls_on_setattr is not setters .NO_OP
2022+ )
2023+
2024+ if frozen and has_cls_on_setattr :
20152025 raise ValueError ("Frozen classes can't use on_setattr." )
20162026
20172027 needs_cached_setattr = cache_hash or frozen
@@ -2030,7 +2040,7 @@ def _make_init(
20302040
20312041 needs_cached_setattr = True
20322042 elif (
2033- has_global_on_setattr and a .on_setattr is not setters .NO_OP
2043+ has_cls_on_setattr and a .on_setattr is not setters .NO_OP
20342044 ) or _is_slot_attr (a .name , base_attr_map ):
20352045 needs_cached_setattr = True
20362046
@@ -2046,7 +2056,7 @@ def _make_init(
20462056 base_attr_map ,
20472057 is_exc ,
20482058 needs_cached_setattr ,
2049- has_global_on_setattr ,
2059+ has_cls_on_setattr ,
20502060 attrs_init ,
20512061 )
20522062 if cls .__module__ in sys .modules :
@@ -2183,7 +2193,7 @@ def _attrs_to_init_script(
21832193 base_attr_map ,
21842194 is_exc ,
21852195 needs_cached_setattr ,
2186- has_global_on_setattr ,
2196+ has_cls_on_setattr ,
21872197 attrs_init ,
21882198):
21892199 """
@@ -2257,7 +2267,7 @@ def fmt_setter_with_converter(
22572267
22582268 attr_name = a .name
22592269 has_on_setattr = a .on_setattr is not None or (
2260- a .on_setattr is not setters .NO_OP and has_global_on_setattr
2270+ a .on_setattr is not setters .NO_OP and has_cls_on_setattr
22612271 )
22622272 arg_name = a .name .lstrip ("_" )
22632273
0 commit comments