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):
 	def __call__(cls,*args,**kwargs):
 		instance = super().__call__(*args,**kwargs)
 		instance = super().__call__(*args,**kwargs)
 		if instance._autolock:
 		if instance._autolock:
-			instance._lock = True
+			instance.lock()
 		return instance
 		return instance
 
 
 class AttrCtrl(metaclass=AttrCtrlMeta):
 class AttrCtrl(metaclass=AttrCtrlMeta):
@@ -45,7 +45,10 @@ class AttrCtrl(metaclass=AttrCtrlMeta):
 		self._lock = True
 		self._lock = True
 
 
 	def __setattr__(self,name,value):
 	def __setattr__(self,name,value):
+
 		if self._lock:
 		if self._lock:
+			assert name != '_lock', 'lock can be set only once'
+
 			def do_error(name,value,ref_val):
 			def do_error(name,value,ref_val):
 				raise AttributeError(
 				raise AttributeError(
 					f'{value!r}: invalid value for attribute {name!r}'
 					f'{value!r}: invalid value for attribute {name!r}'
@@ -84,6 +87,13 @@ class Lockable(AttrCtrl):
 	_set_ok = ()
 	_set_ok = ()
 	_reset_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):
 	def __setattr__(self,name,value):
 		if self._lock and hasattr(self,name):
 		if self._lock and hasattr(self,name):
 			val = getattr(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...')
 		qmsg_r('Testing class AttrCtrl...')
 
 
 		class MyAttrCtrl(AttrCtrl):
 		class MyAttrCtrl(AttrCtrl):
+			_autolock = False
 			foo = 'fooval'
 			foo = 'fooval'
 		ac = MyAttrCtrl()
 		ac = MyAttrCtrl()
 		ac.lock()
 		ac.lock()
@@ -23,7 +24,11 @@ class unit_test(object):
 		ac.foo = 'new fooval'
 		ac.foo = 'new fooval'
 		ac.foo = 'new fooval2'
 		ac.foo = 'new fooval2'
 
 
+		class MyAttrCtrlAutolock(AttrCtrl): pass
+		aca = MyAttrCtrlAutolock()
+
 		class MyAttrCtrlClsCheck(AttrCtrl):
 		class MyAttrCtrlClsCheck(AttrCtrl):
+			_autolock = False
 			_use_class_attr = True
 			_use_class_attr = True
 			foo = 'fooval'
 			foo = 'fooval'
 			bar = None
 			bar = None
@@ -97,6 +102,10 @@ class unit_test(object):
 		assert lca._lock == True
 		assert lca._lock == True
 		assert lca.foo == True
 		assert lca.foo == True
 
 
+		class MyLockableBad(Lockable):
+			_set_ok = ('foo','bar')
+			foo = 1
+
 		qmsg('OK')
 		qmsg('OK')
 		qmsg_r('Checking error handling...')
 		qmsg_r('Checking error handling...')
 		vmsg('')
 		vmsg('')
@@ -119,6 +128,8 @@ class unit_test(object):
 		def bad15(): lc.epsilon = [0]
 		def bad15(): lc.epsilon = [0]
 
 
 		def bad16(): lca.foo = None
 		def bad16(): lca.foo = None
+		def bad17(): lb = MyLockableBad()
+		def bad18(): aca.lock()
 
 
 		ut.process_bad_data((
 		ut.process_bad_data((
 			('attr (1)',           'AttributeError', 'has no attr', bad1 ),
 			('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',       bad14 ),
 			("attr (can't reset)", 'AttributeError', 'reset',       bad15 ),
 			("attr (can't reset)", 'AttributeError', 'reset',       bad15 ),
 			("attr (can't set)",   'AttributeError', 'read-only',   bad16 ),
 			("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')
 		qmsg('OK')