From 2e5ec4c9e89bbe569735594d61988e6f9803d267 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Fri, 26 Sep 2025 10:40:24 +0000 Subject: [PATCH] str.split() + match statement (11 files) --- mmgen/addr.py | 21 +++++++----- mmgen/addrfile.py | 10 +++--- mmgen/bip_hd/chainparams.py | 43 ++++++++++++----------- mmgen/cfg.py | 64 ++++++++++++++++++----------------- mmgen/data/version | 2 +- mmgen/msg.py | 22 +++++++----- mmgen/obj.py | 27 ++++++++------- mmgen/seedsplit.py | 15 ++++---- mmgen/tw/addresses.py | 16 +++++---- mmgen/tw/shared.py | 9 +++-- mmgen/wallet/mmgen.py | 33 ++++++++++-------- mmgen/wallet/mmhex.py | 13 ++++--- test/objtest_d/btc_mainnet.py | 2 +- 13 files changed, 149 insertions(+), 128 deletions(-) diff --git a/mmgen/addr.py b/mmgen/addr.py index babb2e03..6bd97feb 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -129,15 +129,18 @@ class MMGenID(HiliteStr, InitErrors, MMGenObject): trunc_ok = False def __new__(cls, proto, id_str): try: - ss = str(id_str).split(':') - assert len(ss) in (2, 3), 'not 2 or 3 colon-separated items' - t = proto.addr_type((ss[1], proto.dfl_mmtype)[len(ss)==2]) - me = str.__new__(cls, f'{ss[0]}:{t}:{ss[-1]}') - me.sid = SeedID(sid=ss[0]) - me.idx = AddrIdx(ss[-1]) - me.mmtype = t - assert t in proto.mmtypes, f'{t}: invalid address type for {proto.cls_name}' - me.al_id = str.__new__(AddrListID, me.sid+':'+me.mmtype) # checks already done + match id_str.split(':', 2): + case [sid, mmtype, idx]: + assert mmtype in proto.mmtypes, f'{mmtype}: invalid address type for {proto.cls_name}' + case [sid, idx]: + mmtype = proto.dfl_mmtype + case _: + raise ValueError('not 2 or 3 colon-separated items') + me = str.__new__(cls, f'{sid}:{mmtype}:{idx}') + me.sid = SeedID(sid=sid) + me.mmtype = proto.addr_type(mmtype) + me.idx = AddrIdx(idx) + me.al_id = str.__new__(AddrListID, me.sid + ':' + me.mmtype) # checks already done me.sort_key = f'{me.sid}:{me.mmtype}:{me.idx:0{me.idx.max_digits}}' me.proto = proto return me diff --git a/mmgen/addrfile.py b/mmgen/addrfile.py index 77b0f4e3..e7c14060 100755 --- a/mmgen/addrfile.py +++ b/mmgen/addrfile.py @@ -265,10 +265,12 @@ class AddrFile(MMGenObject): match len(ls): case 2 if type(p).__name__ == 'PasswordList': - ss = ls.pop().split(':') - assert len(ss) == 2, f'{ss!r}: invalid password length specifier (must contain colon)' - p.set_pw_fmt(ss[0]) - p.set_pw_len(ss[1]) + match ls.pop().split(':', 1): + case [a, b]: + p.set_pw_fmt(a) + p.set_pw_len(b) + case x: + die(1, f'{x!r}: invalid password length specifier (must contain colon)') p.pw_id_str = MMGenPWIDString(ls.pop()) modname, funcname = p.pw_info[p.pw_fmt].chk_func.split('.') import importlib diff --git a/mmgen/bip_hd/chainparams.py b/mmgen/bip_hd/chainparams.py index 7045ae54..0461e06d 100644 --- a/mmgen/bip_hd/chainparams.py +++ b/mmgen/bip_hd/chainparams.py @@ -17,28 +17,27 @@ def parse_data(): 'idx chain name') def parse_line(line): - l = line.split() - - if l[2] == '-': - return _u( - idx = int(l[0]), - chain = l[1], - name = ' '.join(l[3:]), - ) - else: - return _d( - idx = int(l[0]), - chain = l[1], - curve = defaults.curve if l[2] == 'x' else l[2], - network = 'mainnet' if l[3] == 'm' else 'testnet' if l[3] == 'T' else None, - addr_cls = l[4], - vb_prv = defaults.vb_prv if l[5] == 'x' else l[5], - vb_pub = defaults.vb_pub if l[6] == 'x' else l[6], - vb_wif = l[7], - vb_addr = l[8], - def_path = defaults.def_path if l[9] == 'x' else l[9], - name = ' '.join(l[10:]), - ) + match line.split(): + case [idx, chain, col3, *name] if col3 == '-': + return _u( + idx = int(idx), + chain = chain, + name = ' '.join(name)) + case [idx, chain, curve, net, acls, vprv, vpub, vwif, vaddr, dpath, *name]: + return _d( + idx = int(idx), + chain = chain, + curve = defaults.curve if curve == 'x' else curve, + network = 'mainnet' if net == 'm' else 'testnet' if net == 'T' else None, + addr_cls = acls, + vb_prv = defaults.vb_prv if vprv == 'x' else vprv, + vb_pub = defaults.vb_pub if vpub == 'x' else vpub, + vb_wif = vwif, + vb_addr = vaddr, + def_path = defaults.def_path if dpath == 'x' else dpath, + name = ' '.join(name)) + case _: + raise ValueError(f'{line!r}: invalid line') out = {} for line in _data_in.strip().splitlines(): diff --git a/mmgen/cfg.py b/mmgen/cfg.py index d8d65169..75589cbd 100755 --- a/mmgen/cfg.py +++ b/mmgen/cfg.py @@ -848,28 +848,28 @@ def check_opts(cfg): # Raises exception if any check fails out_fmt = in_fmt def hidden_incog_params(): - a = val.rsplit(',', 1) # permit comma in filename - if len(a) != 2: - display_opt(name, val) - die('UserOptError', 'Option requires two comma-separated arguments') - - fn, offset = a - opt_is_int(offset) + match val.rsplit(',', 1): # permit comma in filename + case [fn, offset]: + opt_is_int(offset) + case _: + display_opt(name, val) + die('UserOptError', 'Option requires two comma-separated arguments') from .fileutil import check_infile, check_outdir, check_outfile - if name == 'hidden_incog_input_params': - check_infile(fn, blkdev_ok=True) - key2 = 'in_fmt' - else: - try: - os.stat(fn) - except: - b = os.path.dirname(fn) - if b: - check_outdir(b) - else: - check_outfile(fn, blkdev_ok=True) - key2 = 'out_fmt' + match name: + case 'hidden_incog_input_params': + check_infile(fn, blkdev_ok=True) + key2 = 'in_fmt' + case 'hidden_incog_output_params': + try: + os.stat(fn) + except: + b = os.path.dirname(fn) + if b: + check_outdir(b) + else: + check_outfile(fn, blkdev_ok=True) + key2 = 'out_fmt' if hasattr(cfg, key2): val2 = getattr(cfg, key2) @@ -895,17 +895,19 @@ def check_opts(cfg): # Raises exception if any check fails opt_is_in_list(val, list(Crypto.hash_presets.keys())) def brain_params(): - a = val.split(',') - if len(a) != 2: - display_opt(name, val) - die('UserOptError', 'Option requires two comma-separated arguments') - - opt_is_int(a[0], desc_pfx='seed length') - from .seed import Seed - opt_is_in_list(int(a[0]), Seed.lens, desc_pfx='seed length') - - from .crypto import Crypto - opt_is_in_list(a[1], list(Crypto.hash_presets.keys()), desc_pfx='hash preset') + match val.split(',', 1): + case [seed_len, hash_preset]: + opt_is_int(seed_len, desc_pfx='seed length') + from .seed import Seed + opt_is_in_list(int(seed_len), Seed.lens, desc_pfx='seed length') + from .crypto import Crypto + opt_is_in_list( + hash_preset, + list(Crypto.hash_presets.keys()), + desc_pfx = 'hash preset') + case _: + display_opt(name, val) + die('UserOptError', 'Option requires two comma-separated arguments') def usr_randchars(): if val != 0: diff --git a/mmgen/data/version b/mmgen/data/version index 79b4409c..a31e19a5 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -16.1.dev0 +16.1.dev1 diff --git a/mmgen/msg.py b/mmgen/msg.py index e9a4963d..55660ef2 100755 --- a/mmgen/msg.py +++ b/mmgen/msg.py @@ -33,15 +33,19 @@ class MMGenIDRange(HiliteStr, InitErrors, MMGenObject): from .addr import AddrListID from .seed import SeedID try: - ss = str(id_str).split(':') - assert len(ss) in (2, 3), 'not 2 or 3 colon-separated items' - t = proto.addr_type((ss[1], proto.dfl_mmtype)[len(ss)==2]) - me = str.__new__(cls, '{}:{}:{}'.format(ss[0], t, ss[-1])) - me.sid = SeedID(sid=ss[0]) - me.idxlist = AddrIdxList(fmt_str=ss[-1]) - me.mmtype = t - assert t in proto.mmtypes, f'{t}: invalid address type for {proto.cls_name}' - me.al_id = str.__new__(AddrListID, me.sid+':'+me.mmtype) # checks already done + match id_str.split(':'): + case [sid, t, fmt_str]: + assert t in proto.mmtypes, f'{t}: invalid address type for {proto.cls_name}' + mmtype = proto.addr_type(t) + case [sid, fmt_str]: + mmtype = proto.addr_type(proto.dfl_mmtype) + case _: + raise ValueError('not 2 or 3 colon-separated items') + me = str.__new__(cls, f'{sid}:{mmtype}:{fmt_str}') + me.sid = SeedID(sid=sid) + me.idxlist = AddrIdxList(fmt_str=fmt_str) + me.mmtype = mmtype + me.al_id = str.__new__(AddrListID, me.sid + ':' + me.mmtype) # checks already done me.proto = proto return me except Exception as e: diff --git a/mmgen/obj.py b/mmgen/obj.py index 362087f1..14754ad9 100755 --- a/mmgen/obj.py +++ b/mmgen/obj.py @@ -223,18 +223,19 @@ class MMGenRange(tuple, InitErrors, MMGenObject): def __new__(cls, *args): try: - if len(args) == 1: - s = args[0] - if isinstance(s, cls): - return s - assert isinstance(s, str), 'not a string or string subclass' - ss = s.split('-', 1) - first = int(ss[0]) - last = int(ss.pop()) - else: - s = repr(args) # needed if exception occurs - assert len(args) == 2, 'one format string arg or two start, stop args required' - first, last = args + match args: + case [str(s)]: + match s.split('-', 1): + case [first]: + last = first + case [first, last]: + pass + first = int(first) + last = int(last) + case [int(first), int(last)]: + pass + case _: + raise ValueError('one format string arg or two integer args (start, stop) required') assert first <= last, 'start of range greater than end of range' if cls.min_idx is not None: assert first >= cls.min_idx, f'start of range < {cls.min_idx:,}' @@ -242,7 +243,7 @@ class MMGenRange(tuple, InitErrors, MMGenObject): assert last <= cls.max_idx, f'end of range > {cls.max_idx:,}' return tuple.__new__(cls, (first, last)) except Exception as e: - return cls.init_fail(e, s) + return cls.init_fail(e, args) @property def first(self): diff --git a/mmgen/seedsplit.py b/mmgen/seedsplit.py index 6e9331a3..4452b3eb 100755 --- a/mmgen/seedsplit.py +++ b/mmgen/seedsplit.py @@ -43,13 +43,16 @@ class SeedSplitSpecifier(HiliteStr, InitErrors, MMGenObject): if isinstance(s, cls): return s try: - arr = s.split(':') - assert len(arr) in (2, 3), 'cannot be parsed' - a, b, c = arr if len(arr) == 3 else ['default'] + arr me = str.__new__(cls, s) - me.id = SeedSplitIDString(a) - me.idx = SeedShareIdx(b) - me.count = SeedShareCount(c) + match s.split(':', 2): + case [id_str, idx, count]: + me.id = SeedSplitIDString(id_str) + case [idx, count]: + me.id = SeedSplitIDString('default') + case _: + raise ValueError('seed split specifier cannot be parsed') + me.idx = SeedShareIdx(idx) + me.count = SeedShareCount(count) assert me.idx <= me.count, 'share index greater than share count' return me except Exception as e: diff --git a/mmgen/tw/addresses.py b/mmgen/tw/addresses.py index 097f9333..02fef30a 100755 --- a/mmgen/tw/addresses.py +++ b/mmgen/tw/addresses.py @@ -111,13 +111,15 @@ class TwAddresses(TwView): self.used_w = 4 if self.has_used else 0 if mmgen_addrs: - a = mmgen_addrs.rsplit(':', 1) - if len(a) != 2: - die(1, - f'{mmgen_addrs}: invalid address list argument ' + - '(must be in form :[:])') - from ..addrlist import AddrIdxList - self.usr_addr_list = [MMGenID(self.proto, f'{a[0]}:{i}') for i in AddrIdxList(fmt_str=a[1])] + match mmgen_addrs.rsplit(':', 1): + case [mmid, fmt_str]: + from ..addrlist import AddrIdxList + self.usr_addr_list = [ + MMGenID(self.proto, f'{mmid}:{i}') for i in AddrIdxList(fmt_str=fmt_str)] + case _: + die(1, + f'{mmgen_addrs}: invalid address list argument ' + + '(must be in form :[:])') else: self.usr_addr_list = [] diff --git a/mmgen/tw/shared.py b/mmgen/tw/shared.py index 8bbadf2c..a5224cec 100755 --- a/mmgen/tw/shared.py +++ b/mmgen/tw/shared.py @@ -56,9 +56,12 @@ class TwLabel(str, InitErrors, MMGenObject): if isinstance(text, cls): return text try: - ts = text.split(None, 1) - mmid = TwMMGenID(proto, ts[0]) - comment = TwComment(ts[1] if len(ts) == 2 else '') + match text.split(None, 1): + case [mmid_in]: + comment = TwComment('') + case [mmid_in, comment]: + comment = TwComment(comment) + mmid = TwMMGenID(proto, mmid_in) me = str.__new__(cls, mmid + (' ' + comment if comment else '')) me.mmid = mmid me.comment = comment diff --git a/mmgen/wallet/mmgen.py b/mmgen/wallet/mmgen.py index cd440d8c..2d64afb1 100755 --- a/mmgen/wallet/mmgen.py +++ b/mmgen/wallet/mmgen.py @@ -125,28 +125,31 @@ class wallet(wallet): self.check_usr_seed_len(bitlen=int(d3)) d.pw_status, d.timestamp = d4, d5 - hpdata = lines[3].split() + match lines[3].split(): + case [hp_lbl, *params] if len(params) == 3: + d.hash_preset = hp_lbl.removesuffix(':') + case _: + raise ValueError(f'{lines[3]}: invalid hash preset line') - d.hash_preset = hp = hpdata[0][:-1] # a string! - self.cfg._util.qmsg(f'Hash preset of wallet: {hp!r}') - if self.cfg.hash_preset and self.cfg.hash_preset != hp: + self.cfg._util.qmsg(f'Hash preset of wallet: {d.hash_preset!r}') + if self.cfg.hash_preset and self.cfg.hash_preset != d.hash_preset: self.cfg._util.qmsg(f'Warning: ignoring user-requested hash preset {self.cfg.hash_preset!r}') - hash_params = tuple(map(int, hpdata[1:])) - - if hash_params != self.crypto.get_hash_params(d.hash_preset): - msg(f'Hash parameters {" ".join(hash_params)!r} don’t match hash preset {d.hash_preset!r}') + if tuple(map(int, params)) != self.crypto.get_hash_params(d.hash_preset): + msg(f'Hash parameters {" ".join(params)!r} don’t match hash preset {d.hash_preset!r}') return False lmin, _, lmax = sorted(baseconv('b58').seedlen_map_rev) # 22, 33, 44 for i, key in (4, 'salt'), (5, 'enc_seed'): - l = lines[i].split(' ') - chksum = l.pop(0) - b58_val = ''.join(l) - - if len(b58_val) < lmin or len(b58_val) > lmax: - msg(f'Invalid format for {key} in {self.desc}: {l}') - return False + match lines[i].split(' '): + case [chksum, *b58_chunks]: + b58_val = ''.join(b58_chunks) + if len(b58_val) < lmin or len(b58_val) > lmax: + msg(f'Invalid format for {key} in {self.desc}: {lines[i]}') + return False + case _: + msg(f'Invalid format for {key} in {self.desc}: {lines[i]}') + return False if not self.cfg._util.compare_chksums( chksum, diff --git a/mmgen/wallet/mmhex.py b/mmgen/wallet/mmhex.py index 2ef66d49..73503c49 100755 --- a/mmgen/wallet/mmhex.py +++ b/mmgen/wallet/mmhex.py @@ -28,13 +28,12 @@ class wallet(wallet): self.fmt_data = f'{self.ssdata.chksum} {split_into_cols(4, seed_hex)}\n' def _deformat(self): - d = self.fmt_data.split() - try: - d[1] - chksum, hex_str = d[0], ''.join(d[1:]) - except: - msg(f'{self.fmt_data.strip()!r}: invalid {self.desc}') - return False + match self.fmt_data.split(): + case [chksum, *hex_chunks] if hex_chunks: + hex_str = ''.join(hex_chunks) + case _: + msg(f'{self.fmt_data!r}: invalid {self.desc}') + return False if not len(hex_str) * 4 in Seed.lens: msg(f'Invalid data length ({len(hex_str)}) in {self.desc}') diff --git a/test/objtest_d/btc_mainnet.py b/test/objtest_d/btc_mainnet.py index f4431be3..06401918 100755 --- a/test/objtest_d/btc_mainnet.py +++ b/test/objtest_d/btc_mainnet.py @@ -227,7 +227,7 @@ tests = { {'text': 'F00BAA12:Z:99', 'proto': proto}, {'text': tw_pfx+' x', 'proto': proto}, {'text': tw_pfx+'я x', 'proto': proto}, - {'text': utf8_ctrl[:40], 'proto': proto}, + {'text': utf8_ctrl[:40], 'proto': proto, 'exc_name': 'BadTwComment'}, {'text': 'F00BAA12:S:1 ' + utf8_ctrl[:40], 'proto': proto, 'exc_name': 'BadTwComment'}, {'text': tw_pfx+'x comment', 'proto': proto}, ),