Browse Source

Lockable: allow all non-int attrs to be considered unset

The MMGen Project 3 years ago
parent
commit
6c4ca8ad52
2 changed files with 27 additions and 4 deletions
  1. 4 3
      mmgen/base_obj.py
  2. 23 1
      test/unit_tests_d/ut_lockable.py

+ 4 - 3
mmgen/base_obj.py

@@ -70,20 +70,21 @@ class Lockable(AttrCtrl):
 	  - if an attribute's name is in _reset_ok, read-only restrictions are bypassed and only
 	    AttrCtrl checking is performed
 
-	An attribute is considered unset if its value is None, or if it is present in the instance
-	__dict__, if _use_class_attr is True.
+	An attribute is considered unset if its value is None, or if it evaluates to False but is
+	not zero; or if it is not present in the instance __dict__ when _use_class_attr is True.
 	"""
 	_set_ok = ()
 	_reset_ok = ()
 
 	def __setattr__(self,name,value):
 		if self._lock and hasattr(self,name):
+			val = getattr(self,name)
 			if name not in (self._set_ok + self._reset_ok):
 				raise AttributeError(f'attribute {name!r} of {type(self).__name__} object is read-only')
 			elif name not in self._reset_ok:
 				#print(self.__dict__)
 				if not (
-					getattr(self,name) is None or
+					val is None or (not (val == 0) and not val) or
 					( self._use_class_attr and name not in self.__dict__ ) ):
 					raise AttributeError(
 						f'attribute {name!r} of {type(self).__name__} object is already set,'

+ 23 - 1
test/unit_tests_d/ut_lockable.py

@@ -11,6 +11,7 @@ class unit_test(object):
 	def run_test(self,name,ut):
 
 		from mmgen.base_obj import AttrCtrl,Lockable
+		from decimal import Decimal
 
 		qmsg_r('Testing class AttrCtrl...')
 
@@ -38,7 +39,7 @@ class unit_test(object):
 		qmsg_r('Testing class Lockable...')
 
 		class MyLockable(Lockable): # class has no attrs, like UserOpts
-			_set_ok = ('foo','baz')
+			_set_ok = ('foo','baz','alpha','beta','gamma','delta','epsilon')
 			_reset_ok = ('bar','baz')
 
 		lc = MyLockable()
@@ -46,6 +47,14 @@ class unit_test(object):
 		lc.bar = 'barval'
 		lc.baz = 1
 		lc.qux = 1
+
+		# are these considered set?
+		lc.alpha = 0             # yes
+		lc.beta = False          # yes
+		lc.gamma = Decimal('0')  # yes
+		lc.delta = 0.0           # yes
+		lc.epsilon = []          # no
+
 		lc.lock()
 
 		lc.foo = 'fooval2'
@@ -54,6 +63,8 @@ class unit_test(object):
 		lc.baz = 2
 		lc.baz = 3
 
+		lc.epsilon = [0]
+
 		class MyLockableClsCheck(Lockable): # class has attrs, like GlobalContext
 			_use_class_attr = True
 			_set_ok = ('foo','baz')
@@ -86,6 +97,12 @@ class unit_test(object):
 		def bad9(): lc.x = 1
 		def bad10(): lcc.x = 1
 
+		def bad11(): lc.alpha = 0
+		def bad12(): lc.beta = False
+		def bad13(): lc.gamma = Decimal('0')
+		def bad14(): lc.delta = float(0)
+		def bad15(): lc.epsilon = [0]
+
 		ut.process_bad_data((
 			('attr (1)',           'AttributeError', 'has no attr', bad1 ),
 			('attr (2)',           'AttributeError', 'has no attr', bad9 ),
@@ -97,6 +114,11 @@ class unit_test(object):
 			("attr (can't set)",   'AttributeError', 'read-only',   bad7 ),
 			("attr (can't reset)", 'AttributeError', 'reset',       bad3 ),
 			("attr (can't reset)", 'AttributeError', 'reset',       bad8 ),
+			("attr (can't reset)", 'AttributeError', 'reset',       bad11 ),
+			("attr (can't reset)", 'AttributeError', 'reset',       bad12 ),
+			("attr (can't reset)", 'AttributeError', 'reset',       bad13 ),
+			("attr (can't reset)", 'AttributeError', 'reset',       bad14 ),
+			("attr (can't reset)", 'AttributeError', 'reset',       bad15 ),
 		))
 
 		qmsg('OK')