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
 	  - if an attribute's name is in _reset_ok, read-only restrictions are bypassed and only
 	    AttrCtrl checking is performed
 	    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 = ()
 	_set_ok = ()
 	_reset_ok = ()
 	_reset_ok = ()
 
 
 	def __setattr__(self,name,value):
 	def __setattr__(self,name,value):
 		if self._lock and hasattr(self,name):
 		if self._lock and hasattr(self,name):
+			val = getattr(self,name)
 			if name not in (self._set_ok + self._reset_ok):
 			if name not in (self._set_ok + self._reset_ok):
 				raise AttributeError(f'attribute {name!r} of {type(self).__name__} object is read-only')
 				raise AttributeError(f'attribute {name!r} of {type(self).__name__} object is read-only')
 			elif name not in self._reset_ok:
 			elif name not in self._reset_ok:
 				#print(self.__dict__)
 				#print(self.__dict__)
 				if not (
 				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__ ) ):
 					( self._use_class_attr and name not in self.__dict__ ) ):
 					raise AttributeError(
 					raise AttributeError(
 						f'attribute {name!r} of {type(self).__name__} object is already set,'
 						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):
 	def run_test(self,name,ut):
 
 
 		from mmgen.base_obj import AttrCtrl,Lockable
 		from mmgen.base_obj import AttrCtrl,Lockable
+		from decimal import Decimal
 
 
 		qmsg_r('Testing class AttrCtrl...')
 		qmsg_r('Testing class AttrCtrl...')
 
 
@@ -38,7 +39,7 @@ class unit_test(object):
 		qmsg_r('Testing class Lockable...')
 		qmsg_r('Testing class Lockable...')
 
 
 		class MyLockable(Lockable): # class has no attrs, like UserOpts
 		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')
 			_reset_ok = ('bar','baz')
 
 
 		lc = MyLockable()
 		lc = MyLockable()
@@ -46,6 +47,14 @@ class unit_test(object):
 		lc.bar = 'barval'
 		lc.bar = 'barval'
 		lc.baz = 1
 		lc.baz = 1
 		lc.qux = 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.lock()
 
 
 		lc.foo = 'fooval2'
 		lc.foo = 'fooval2'
@@ -54,6 +63,8 @@ class unit_test(object):
 		lc.baz = 2
 		lc.baz = 2
 		lc.baz = 3
 		lc.baz = 3
 
 
+		lc.epsilon = [0]
+
 		class MyLockableClsCheck(Lockable): # class has attrs, like GlobalContext
 		class MyLockableClsCheck(Lockable): # class has attrs, like GlobalContext
 			_use_class_attr = True
 			_use_class_attr = True
 			_set_ok = ('foo','baz')
 			_set_ok = ('foo','baz')
@@ -86,6 +97,12 @@ class unit_test(object):
 		def bad9(): lc.x = 1
 		def bad9(): lc.x = 1
 		def bad10(): lcc.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((
 		ut.process_bad_data((
 			('attr (1)',           'AttributeError', 'has no attr', bad1 ),
 			('attr (1)',           'AttributeError', 'has no attr', bad1 ),
 			('attr (2)',           'AttributeError', 'has no attr', bad9 ),
 			('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 set)",   'AttributeError', 'read-only',   bad7 ),
 			("attr (can't reset)", 'AttributeError', 'reset',       bad3 ),
 			("attr (can't reset)", 'AttributeError', 'reset',       bad3 ),
 			("attr (can't reset)", 'AttributeError', 'reset',       bad8 ),
 			("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')
 		qmsg('OK')