From 74ec5efd6394185ca9db20ab613b8d3bff38bb44 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Sat, 15 Jan 2022 14:00:07 +0000 Subject: [PATCH] move Hilite,InitErrors to objmethods.py --- mmgen/addr.py | 1 + mmgen/amt.py | 2 +- mmgen/obj.py | 105 +---------------------------------- mmgen/objmethods.py | 131 ++++++++++++++++++++++++++++++++++++++++++++ mmgen/rpc.py | 1 + mmgen/seed.py | 3 +- mmgen/tw.py | 1 + mmgen/xmrwallet.py | 3 +- 8 files changed, 140 insertions(+), 107 deletions(-) create mode 100755 mmgen/objmethods.py diff --git a/mmgen/addr.py b/mmgen/addr.py index 9d995a44..1a1c5281 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -23,6 +23,7 @@ addr.py: Address generation/display routines for the MMGen suite from hashlib import sha256,sha512 from .common import * from .base_obj import AsyncInit +from .objmethods import Hilite,InitErrors,MMGenObject from .obj import * from .baseconv import * from .protocol import init_proto,hash160 diff --git a/mmgen/amt.py b/mmgen/amt.py index 3acfee77..22b0140b 100755 --- a/mmgen/amt.py +++ b/mmgen/amt.py @@ -21,7 +21,7 @@ amt.py: MMGen CoinAmt and related classes """ from decimal import Decimal -from .obj import Hilite,InitErrors +from .objmethods import Hilite,InitErrors class UnknownCoinAmt(Decimal): pass diff --git a/mmgen/obj.py b/mmgen/obj.py index e1c323d3..76ba89ad 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -27,6 +27,7 @@ from string import hexdigits,ascii_letters,digits from .exception import * from .globalvars import * from .color import * +from .objmethods import * def get_obj(objname,*args,**kwargs): """ @@ -63,17 +64,6 @@ def is_mmgen_id(proto,s): return get_obj(MMGenID, proto=proto, id_str=s, silen def is_coin_addr(proto,s): return get_obj(CoinAddr, proto=proto, addr=s, silent=True,return_bool=True) def is_wif(proto,s): return get_obj(WifKey, proto=proto, wif=s, silent=True,return_bool=True) -def truncate_str(s,width): # width = screen width - wide_count = 0 - for i in range(len(s)): - wide_count += unicodedata.east_asian_width(s[i]) in ('F','W') - if wide_count + i >= width: - return s[:i] + ('',' ')[ - unicodedata.east_asian_width(s[i]) in ('F','W') - and wide_count + i == width] - else: # pad the string to width if necessary - return s + ' '*(width-len(s)-wide_count) - # dict that keeps a list of keys for efficient lookup by index class IndexedDict(dict): @@ -108,99 +98,6 @@ class MMGenList(list,MMGenObject): pass class MMGenDict(dict,MMGenObject): pass class AddrListData(list,MMGenObject): pass -class InitErrors: - - @classmethod - def init_fail(cls,e,m,e2=None,m2=None,objname=None,preformat=False): - - if preformat: - errmsg = m - else: - errmsg = '{!r}: value cannot be converted to {} {}({!s})'.format( - m, - (objname or cls.__name__), - (f'({e2!s}) ' if e2 else ''), - e ) - - if m2: - errmsg = repr(m2) + '\n' + errmsg - - if hasattr(cls,'passthru_excs') and type(e) in cls.passthru_excs: - raise - elif hasattr(cls,'exc'): - raise cls.exc(errmsg) - else: - raise ObjectInitError(errmsg) - - @classmethod - def method_not_implemented(cls): - import traceback - raise NotImplementedError( - 'method {}() not implemented for class {!r}'.format( - traceback.extract_stack()[-2].name, cls.__name__) ) - -class Hilite(object): - - color = 'red' - width = 0 - trunc_ok = True - - @classmethod - # 'width' is screen width (greater than len(s) for CJK strings) - # 'append_chars' and 'encl' must consist of single-width chars only - def fmtc(cls,s,width=None,color=False,encl='',trunc_ok=None, - center=False,nullrepl='',append_chars='',append_color=False): - s_wide_count = len([1 for ch in s if unicodedata.east_asian_width(ch) in ('F','W')]) - if encl: - a,b = list(encl) - add_len = len(append_chars) + 2 - else: - a,b = ('','') - add_len = len(append_chars) - if width == None: - width = cls.width - if trunc_ok == None: - trunc_ok = cls.trunc_ok - if g.test_suite: - assert isinstance(encl,str) and len(encl) in (0,2),"'encl' must be 2-character str" - assert width >= 2 + add_len, f'{s!r}: invalid width ({width}) (must be at least 2)' # CJK: 2 cells - if len(s) + s_wide_count + add_len > width: - assert trunc_ok, "If 'trunc_ok' is false, 'width' must be >= screen width of string" - s = truncate_str(s,width-add_len) - if s == '' and nullrepl: - s = nullrepl.center(width) - else: - s = a+s+b - if center: - s = s.center(width) - if append_chars: - return ( - cls.colorize(s,color=color) - + cls.colorize( - append_chars.ljust(width-len(s)-s_wide_count), - color_override = append_color )) - else: - return cls.colorize(s.ljust(width-s_wide_count),color=color) - - @classmethod - def colorize(cls,s,color=True,color_override=''): - return globals()[color_override or cls.color](s) if color else s - - def fmt(self,*args,**kwargs): - assert args == () # forbid invocation w/o keywords - return self.fmtc(self,*args,**kwargs) - - @classmethod - def hlc(cls,s,color=True,encl=''): - if encl: - assert isinstance(encl,str) and len(encl) == 2, "'encl' must be 2-character str" - s = encl[0] + s + encl[1] - return cls.colorize(s,color=color) - - def hl(self,*args,**kwargs): - assert args == () # forbid invocation w/o keywords - return self.hlc(self,*args,**kwargs) - class Str(str,Hilite): pass class Int(int,Hilite,InitErrors): diff --git a/mmgen/objmethods.py b/mmgen/objmethods.py new file mode 100755 index 00000000..9a81c24f --- /dev/null +++ b/mmgen/objmethods.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# +# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution +# Copyright (C)2013-2022 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 . + +""" +objmethods.py: Mixin classes for MMGen data objects +""" + +import unicodedata +from .color import * +from .globalvars import g +from .devtools import * + +def truncate_str(s,width): # width = screen width + wide_count = 0 + for i in range(len(s)): + wide_count += unicodedata.east_asian_width(s[i]) in ('F','W') + if wide_count + i >= width: + return s[:i] + ('',' ')[ + unicodedata.east_asian_width(s[i]) in ('F','W') + and wide_count + i == width] + else: # pad the string to width if necessary + return s + ' '*(width-len(s)-wide_count) + +class Hilite: + + color = 'red' + width = 0 + trunc_ok = True + + @classmethod + # 'width' is screen width (greater than len(s) for CJK strings) + # 'append_chars' and 'encl' must consist of single-width chars only + def fmtc(cls,s,width=None,color=False,encl='',trunc_ok=None, + center=False,nullrepl='',append_chars='',append_color=False): + s_wide_count = len([1 for ch in s if unicodedata.east_asian_width(ch) in ('F','W')]) + if encl: + a,b = list(encl) + add_len = len(append_chars) + 2 + else: + a,b = ('','') + add_len = len(append_chars) + if width == None: + width = cls.width + if trunc_ok == None: + trunc_ok = cls.trunc_ok + if g.test_suite: + assert isinstance(encl,str) and len(encl) in (0,2),"'encl' must be 2-character str" + assert width >= 2 + add_len, f'{s!r}: invalid width ({width}) (must be at least 2)' # CJK: 2 cells + if len(s) + s_wide_count + add_len > width: + assert trunc_ok, "If 'trunc_ok' is false, 'width' must be >= screen width of string" + s = truncate_str(s,width-add_len) + if s == '' and nullrepl: + s = nullrepl.center(width) + else: + s = a+s+b + if center: + s = s.center(width) + if append_chars: + return ( + cls.colorize(s,color=color) + + cls.colorize( + append_chars.ljust(width-len(s)-s_wide_count), + color_override = append_color )) + else: + return cls.colorize(s.ljust(width-s_wide_count),color=color) + + @classmethod + def colorize(cls,s,color=True,color_override=''): + return globals()[color_override or cls.color](s) if color else s + + def fmt(self,*args,**kwargs): + assert args == () # forbid invocation w/o keywords + return self.fmtc(self,*args,**kwargs) + + @classmethod + def hlc(cls,s,color=True,encl=''): + if encl: + assert isinstance(encl,str) and len(encl) == 2, "'encl' must be 2-character str" + s = encl[0] + s + encl[1] + return cls.colorize(s,color=color) + + def hl(self,*args,**kwargs): + assert args == () # forbid invocation w/o keywords + return self.hlc(self,*args,**kwargs) + +class InitErrors: + + @classmethod + def init_fail(cls,e,m,e2=None,m2=None,objname=None,preformat=False): + + if preformat: + errmsg = m + else: + errmsg = '{!r}: value cannot be converted to {} {}({!s})'.format( + m, + (objname or cls.__name__), + (f'({e2!s}) ' if e2 else ''), + e ) + + if m2: + errmsg = repr(m2) + '\n' + errmsg + + if hasattr(cls,'passthru_excs') and type(e) in cls.passthru_excs: + raise + elif hasattr(cls,'exc'): + raise cls.exc(errmsg) + else: + from .exception import ObjectInitError + raise ObjectInitError(errmsg) + + @classmethod + def method_not_implemented(cls): + import traceback + raise NotImplementedError( + 'method {}() not implemented for class {!r}'.format( + traceback.extract_stack()[-2].name, cls.__name__) ) diff --git a/mmgen/rpc.py b/mmgen/rpc.py index dfa6c7ef..92f4511d 100755 --- a/mmgen/rpc.py +++ b/mmgen/rpc.py @@ -23,6 +23,7 @@ rpc.py: Cryptocoin RPC library for the MMGen suite import base64,json,asyncio from decimal import Decimal from .common import * +from .objmethods import Hilite,InitErrors from .base_obj import AsyncInit rpc_credentials_msg = '\n'+fmt(""" diff --git a/mmgen/seed.py b/mmgen/seed.py index a502ea7a..6e0da86f 100755 --- a/mmgen/seed.py +++ b/mmgen/seed.py @@ -21,7 +21,8 @@ seed.py: Seed-related classes and methods for the MMGen suite """ from .common import * -from .obj import * +from .objmethods import Hilite,InitErrors +from .obj import ImmutableAttr,get_obj from .crypto import get_random,scramble_seed class SeedID(str,Hilite,InitErrors): diff --git a/mmgen/tw.py b/mmgen/tw.py index d74a923b..33e24aef 100755 --- a/mmgen/tw.py +++ b/mmgen/tw.py @@ -25,6 +25,7 @@ from collections import namedtuple from .exception import * from .common import * from .base_obj import AsyncInit +from .objmethods import Hilite,InitErrors,MMGenObject from .obj import * from .tx import is_mmgen_id,is_coin_addr from .rpc import rpc_init diff --git a/mmgen/xmrwallet.py b/mmgen/xmrwallet.py index f411bc00..6184dc70 100755 --- a/mmgen/xmrwallet.py +++ b/mmgen/xmrwallet.py @@ -23,12 +23,13 @@ xmrwallet.py - MoneroWalletOps class import os,re,time,json from collections import namedtuple from .common import * +from .objmethods import Hilite,InitErrors from .addr import KeyAddrList,AddrIdxList from .rpc import MoneroRPCClientRaw,MoneroWalletRPCClient,json_encoder from .seed import SeedID from .daemon import MoneroWalletDaemon from .protocol import _b58a,init_proto -from .obj import CoinAddr,CoinTxID,AddrIdx,Hilite,InitErrors +from .obj import CoinAddr,CoinTxID,AddrIdx xmrwallet_uarg_info = ( lambda e,hp: {