123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- #!/usr/bin/env python3
- #
- # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
- # Copyright (C)2013-2024 The MMGen Project <mmgen@tuta.io>
- #
- # This program is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # This program is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <http://www.gnu.org/licenses/>.
- """
- base_obj: base objects with no internal imports for the MMGen suite
- """
- class AsyncInit(type):
- async def __call__(cls,*args,**kwargs):
- instance = cls.__new__(cls,*args,**kwargs)
- await type(instance).__init__(instance,*args,**kwargs)
- return instance
- class AttrCtrlMeta(type):
- def __call__(cls,*args,**kwargs):
- instance = super().__call__(*args,**kwargs)
- if instance._autolock:
- instance._lock()
- return instance
- class AttrCtrl(metaclass=AttrCtrlMeta):
- """
- After instance is locked, forbid setting any attribute if the attribute is not present
- in either the class or instance dict.
- Ensure that attribute's type matches that of the instance attribute, or the class
- attribute, if _use_class_attr is True. If the instance or class attribute is set
- to None, no type checking is performed.
- If _default_to_none is True, return None when accessing non-existent attributes
- instead of raising AttributeError.
- """
- _autolock = True
- _locked = False
- _use_class_attr = False
- _default_to_none = False
- _skip_type_check = ()
- def _lock(self):
- self._locked = True
- def __getattr__(self,name):
- if self._locked and self._default_to_none:
- return None
- else:
- raise AttributeError(f'{type(self).__name__} object has no attribute {name!r}')
- def __setattr__(self,name,value):
- if self._locked:
- assert name != '_locked', 'lock can be set only once'
- def do_error(name,value,ref_val):
- raise AttributeError(
- f'{value!r}: invalid value for attribute {name!r}'
- + ' of {} object (must be of type {}, not {})'.format(
- type(self).__name__,
- type(ref_val).__name__,
- type(value).__name__ ) )
- if not (name in self.__dict__ or hasattr(type(self),name)):
- raise AttributeError(f'{type(self).__name__} object has no attribute {name!r}')
- ref_val = getattr(type(self),name) if self._use_class_attr else getattr(self,name)
- if (
- (name not in self._skip_type_check)
- and (ref_val is not None)
- and not isinstance(value,type(ref_val))
- ):
- do_error(name,value,ref_val)
- return object.__setattr__(self,name,value)
- def __delattr__(self,name):
- if self._locked:
- raise AttributeError('attribute cannot be deleted')
- return object.__delattr__(self,name)
- class Lockable(AttrCtrl):
- """
- After instance is locked, its attributes become read-only, with the following exceptions:
- - if an attribute's name is in _set_ok, it can be set once after locking, if unset
- - 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 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 _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._locked and (name in self.__dict__ or hasattr(type(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 (
- (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,'
- + ' and resetting is forbidden' )
- # else name is in (_set_ok + _reset_ok) -- allow name to be in both lists
- return AttrCtrl.__setattr__(self,name,value)
|