From 6c4ca8ad52b088e9b8dd4a2aa1eed029c46ab46c Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Wed, 4 Aug 2021 10:47:19 +0000 Subject: [PATCH] Lockable: allow all non-int attrs to be considered unset --- mmgen/base_obj.py | 7 ++++--- test/unit_tests_d/ut_lockable.py | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/mmgen/base_obj.py b/mmgen/base_obj.py index a28e0120..177c8d25 100755 --- a/mmgen/base_obj.py +++ b/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,' diff --git a/test/unit_tests_d/ut_lockable.py b/test/unit_tests_d/ut_lockable.py index a87c8114..cfa49128 100755 --- a/test/unit_tests_d/ut_lockable.py +++ b/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')