Browse Source

AttrCtrl,Lockable: forbid relocking, perform additional check

The MMGen Project 3 years ago
parent
commit
d7b0133bf4
2 changed files with 24 additions and 1 deletions
  1. 11 1
      mmgen/base_obj.py
  2. 13 0
      test/unit_tests_d/ut_lockable.py

+ 11 - 1
mmgen/base_obj.py

@@ -24,7 +24,7 @@ class AttrCtrlMeta(type):
 	def __call__(cls,*args,**kwargs):
 		instance = super().__call__(*args,**kwargs)
 		if instance._autolock:
-			instance._lock = True
+			instance.lock()
 		return instance
 
 class AttrCtrl(metaclass=AttrCtrlMeta):
@@ -45,7 +45,10 @@ class AttrCtrl(metaclass=AttrCtrlMeta):
 		self._lock = True
 
 	def __setattr__(self,name,value):
+
 		if self._lock:
+			assert name != '_lock', 'lock can be set only once'
+
 			def do_error(name,value,ref_val):
 				raise AttributeError(
 					f'{value!r}: invalid value for attribute {name!r}'
@@ -84,6 +87,13 @@ class Lockable(AttrCtrl):
 	_set_ok = ()
 	_reset_ok = ()
 
+	def lock(self):
+		for name in ('_set_ok','_reset_ok'):
+			for attr in getattr(self,name):
+				assert hasattr(self,attr), (
+					f'attribute {attr!r} in {name!r} not found in {type(self).__name__} object {id(self)}' )
+		super().lock()
+
 	def __setattr__(self,name,value):
 		if self._lock and hasattr(self,name):
 			val = getattr(self,name)

+ 13 - 0
test/unit_tests_d/ut_lockable.py

@@ -16,6 +16,7 @@ class unit_test(object):
 		qmsg_r('Testing class AttrCtrl...')
 
 		class MyAttrCtrl(AttrCtrl):
+			_autolock = False
 			foo = 'fooval'
 		ac = MyAttrCtrl()
 		ac.lock()
@@ -23,7 +24,11 @@ class unit_test(object):
 		ac.foo = 'new fooval'
 		ac.foo = 'new fooval2'
 
+		class MyAttrCtrlAutolock(AttrCtrl): pass
+		aca = MyAttrCtrlAutolock()
+
 		class MyAttrCtrlClsCheck(AttrCtrl):
+			_autolock = False
 			_use_class_attr = True
 			foo = 'fooval'
 			bar = None
@@ -97,6 +102,10 @@ class unit_test(object):
 		assert lca._lock == True
 		assert lca.foo == True
 
+		class MyLockableBad(Lockable):
+			_set_ok = ('foo','bar')
+			foo = 1
+
 		qmsg('OK')
 		qmsg_r('Checking error handling...')
 		vmsg('')
@@ -119,6 +128,8 @@ class unit_test(object):
 		def bad15(): lc.epsilon = [0]
 
 		def bad16(): lca.foo = None
+		def bad17(): lb = MyLockableBad()
+		def bad18(): aca.lock()
 
 		ut.process_bad_data((
 			('attr (1)',           'AttributeError', 'has no attr', bad1 ),
@@ -137,6 +148,8 @@ class unit_test(object):
 			("attr (can't reset)", 'AttributeError', 'reset',       bad14 ),
 			("attr (can't reset)", 'AttributeError', 'reset',       bad15 ),
 			("attr (can't set)",   'AttributeError', 'read-only',   bad16 ),
+			("attr (bad _set_ok)", 'AssertionError', 'not found in',bad17 ),
+			("call to lock()",     'AssertionError', 'only once',   bad18 ),
 		))
 
 		qmsg('OK')