From d7b0133bf46e6aec6cb2e4a801e204f09065578a Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 10 Aug 2021 19:38:20 +0000 Subject: [PATCH] AttrCtrl,Lockable: forbid relocking, perform additional check --- mmgen/base_obj.py | 12 +++++++++++- test/unit_tests_d/ut_lockable.py | 13 +++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mmgen/base_obj.py b/mmgen/base_obj.py index 8a5327b7..5ffe6a78 100755 --- a/mmgen/base_obj.py +++ b/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) diff --git a/test/unit_tests_d/ut_lockable.py b/test/unit_tests_d/ut_lockable.py index 34d6ba83..0d67f858 100755 --- a/test/unit_tests_d/ut_lockable.py +++ b/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')