From f379cde0fc22f1f99d5533a9d2bc8871e8015fea Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Tue, 10 Aug 2021 19:38:20 +0000 Subject: [PATCH] new ClassFlags,ClassOpts classes --- mmgen/exception.py | 1 + mmgen/flags.py | 88 +++++++++++++++++++++++++++++++++++ setup.py | 1 + test/unit_tests_d/ut_flags.py | 79 +++++++++++++++++++++++++++++++ 4 files changed, 169 insertions(+) create mode 100755 mmgen/flags.py create mode 100755 test/unit_tests_d/ut_flags.py diff --git a/mmgen/exception.py b/mmgen/exception.py index dda67d17..5b5f082e 100755 --- a/mmgen/exception.py +++ b/mmgen/exception.py @@ -48,6 +48,7 @@ class BaseConversionError(Exception): mmcode = 2 class BaseConversionPadError(Exception): mmcode = 2 class TransactionChainMismatch(Exception):mmcode = 2 class ObjectInitError(Exception): mmcode = 2 +class ClassFlagsError(Exception): mmcode = 2 # 3: yellow hl, 'MMGen Error' + exception + message class RPCFailure(Exception): mmcode = 3 diff --git a/mmgen/flags.py b/mmgen/flags.py new file mode 100755 index 00000000..0c667524 --- /dev/null +++ b/mmgen/flags.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution +# Copyright (C)2013-2021 The MMGen Project +# +# 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 . + +""" +flags.py: Class flags and opts for the MMGen suite +""" + +from .exception import ClassFlagsError +from .base_obj import AttrCtrl,Lockable +from .util import fmt_list + +class ClassFlags(AttrCtrl): + _name = 'flags' + _desc = 'flag' + reserved_attrs = ('lock',) + + def __init__(self,parent,arg): + self._parent = parent + self._available = getattr(self._parent,'avail_'+self._name) + + for a in self._available: + if a.startswith('_'): + raise ClassFlagsError(f'{a!r}: {self._desc} cannot begin with an underscore') + for b in self.reserved_attrs: + if a == b: + raise ClassFlagsError(f'{a!r}: {b} is a reserved name for {self._desc}') + + if arg: + assert type(arg) in (list,tuple), f"{arg!r}: {self._name!r} must be list or tuple" + else: + arg = [] + + for e in arg: + if e not in self._available: + self.not_available_error(e) + + for e in self._available: + setattr(self,e,e in arg) + + def __dir__(self): + return [k for k in self.__dict__ if not k.startswith('_') and not k in self.reserved_attrs] + + def __str__(self): + return ' '.join(f'{k}={getattr(self,k)}' for k in dir(self)) + + def __setattr__(self,name,val): + + if self._lock: + + if name not in self._available: + self.not_available_error(name) + + if self._name == 'flags': + assert type(val) is bool, f'{val!r} not boolean' + old_val = getattr(self,name) + if val and old_val: + raise ClassFlagsError(f'{self._desc} {name!r} already set') + if not val and not old_val: + raise ClassFlagsError(f'{self._desc} {name!r} not set, so cannot be unset') + + super().__setattr__(name,val) + + def not_available_error(self,name): + raise ClassFlagsError('{!r}: unrecognized {} for {}: (available {}: {})'.format( + name, + self._desc, + type(self._parent).__name__, + self._name, + fmt_list(self._available,fmt='bare') )) + +class ClassOpts(ClassFlags,Lockable): + _name = 'opts' + _desc = 'opt' diff --git a/setup.py b/setup.py index 8b7e068b..6e85468b 100755 --- a/setup.py +++ b/setup.py @@ -127,6 +127,7 @@ setup( 'mmgen.ed25519ll_djbec', 'mmgen.exception', 'mmgen.filename', + 'mmgen.flags', 'mmgen.globalvars', 'mmgen.help', 'mmgen.keccak', diff --git a/test/unit_tests_d/ut_flags.py b/test/unit_tests_d/ut_flags.py new file mode 100755 index 00000000..f36b6463 --- /dev/null +++ b/test/unit_tests_d/ut_flags.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +""" +test/unit_tests_d/ut_flags.py: unit test for the MMGen suite's ClassFlags class +""" + +from mmgen.common import * +from mmgen.exception import * +from mmgen.flags import * + +class unit_test(object): + + def run_test(self,name,ut): + + class cls1: + avail_opts = () + avail_flags = () + def __init__(self,opts=None,flags=None): + self.opt = ClassOpts(self,opts) + self.flag = ClassFlags(self,flags) + + class cls2(cls1): + avail_opts = ('foo','bar') + avail_flags = ('baz',) + + class cls3(cls1): + avail_opts = ('_foo',) + + class cls4(cls1): + avail_opts = ('lock',) + + def test_flags(): + def gen(): + for n,cls in enumerate(( + cls1(), + cls2(), + cls2(opts=['bar']), + cls2(flags=['baz']), + )): + vmsg(f'Cfg {n+1}:') + for k in ('opt','flag'): + vmsg(' {}s: {}'.format( k, getattr(cls,k) )) + yield cls + return list(gen()) + + def test_flags_err(d): + + def bad1(): d[1].flag.foo = False + def bad2(): d[1].opt.baz = False + def bad3(): cls3() + def bad4(): cls4() + def bad5(): cls1(opts='foo') + def bad6(): cls2(opts=['qux']) + def bad7(): d[1].flag.baz = False + def bad8(): d[3].flag.baz = True + def bad9(): d[1].flag.baz = 'x' + + ut.process_bad_data(( + ('flag (1)', 'ClassFlagsError', 'unrecognized flag', bad1 ), + ('opt (1)', 'ClassFlagsError', 'unrecognized opt', bad2 ), + ('avail_opts (1)', 'ClassFlagsError', 'underscore', bad3 ), + ('avail_opts (1)', 'ClassFlagsError', 'reserved name', bad4 ), + ('class invocation (1)', 'AssertionError', 'list or tuple', bad5 ), + ('class invocation (2)', 'ClassFlagsError', 'unrecognized opt', bad6 ), + ('flag (2)', 'ClassFlagsError', 'not set', bad7 ), + ('flag (3)', 'ClassFlagsError', 'already set', bad8 ), + ('flag (4)', 'AssertionError', 'not boolean', bad9 ), + )) + + qmsg_r('Testing flags and opts...') + vmsg('') + classes = test_flags() + qmsg('OK') + + qmsg_r('Testing error handling for flags and opts...') + vmsg('') + test_flags_err(classes) + qmsg('OK') + + return True