Browse Source

str.split() + match statement (11 files)

The MMGen Project 2 months ago
parent
commit
2e5ec4c9e8

+ 12 - 9
mmgen/addr.py

@@ -129,15 +129,18 @@ class MMGenID(HiliteStr, InitErrors, MMGenObject):
 	trunc_ok = False
 	trunc_ok = False
 	def __new__(cls, proto, id_str):
 	def __new__(cls, proto, id_str):
 		try:
 		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.sort_key = f'{me.sid}:{me.mmtype}:{me.idx:0{me.idx.max_digits}}'
 			me.proto = proto
 			me.proto = proto
 			return me
 			return me

+ 6 - 4
mmgen/addrfile.py

@@ -265,10 +265,12 @@ class AddrFile(MMGenObject):
 
 
 			match len(ls):
 			match len(ls):
 				case 2 if type(p).__name__ == 'PasswordList':
 				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())
 					p.pw_id_str = MMGenPWIDString(ls.pop())
 					modname, funcname = p.pw_info[p.pw_fmt].chk_func.split('.')
 					modname, funcname = p.pw_info[p.pw_fmt].chk_func.split('.')
 					import importlib
 					import importlib

+ 21 - 22
mmgen/bip_hd/chainparams.py

@@ -17,28 +17,27 @@ def parse_data():
 		'idx chain name')
 		'idx chain name')
 
 
 	def parse_line(line):
 	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 = {}
 	out = {}
 	for line in _data_in.strip().splitlines():
 	for line in _data_in.strip().splitlines():

+ 33 - 31
mmgen/cfg.py

@@ -848,28 +848,28 @@ def check_opts(cfg): # Raises exception if any check fails
 		out_fmt = in_fmt
 		out_fmt = in_fmt
 
 
 		def hidden_incog_params():
 		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
 			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):
 			if hasattr(cfg, key2):
 				val2 = getattr(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()))
 			opt_is_in_list(val, list(Crypto.hash_presets.keys()))
 
 
 		def brain_params():
 		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():
 		def usr_randchars():
 			if val != 0:
 			if val != 0:

+ 1 - 1
mmgen/data/version

@@ -1 +1 @@
-16.1.dev0
+16.1.dev1

+ 13 - 9
mmgen/msg.py

@@ -33,15 +33,19 @@ class MMGenIDRange(HiliteStr, InitErrors, MMGenObject):
 		from .addr import AddrListID
 		from .addr import AddrListID
 		from .seed import SeedID
 		from .seed import SeedID
 		try:
 		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
 			me.proto = proto
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:

+ 14 - 13
mmgen/obj.py

@@ -223,18 +223,19 @@ class MMGenRange(tuple, InitErrors, MMGenObject):
 
 
 	def __new__(cls, *args):
 	def __new__(cls, *args):
 		try:
 		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'
 			assert first <= last, 'start of range greater than end of range'
 			if cls.min_idx is not None:
 			if cls.min_idx is not None:
 				assert first >= cls.min_idx, f'start of range < {cls.min_idx:,}'
 				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:,}'
 				assert last <= cls.max_idx, f'end of range > {cls.max_idx:,}'
 			return tuple.__new__(cls, (first, last))
 			return tuple.__new__(cls, (first, last))
 		except Exception as e:
 		except Exception as e:
-			return cls.init_fail(e, s)
+			return cls.init_fail(e, args)
 
 
 	@property
 	@property
 	def first(self):
 	def first(self):

+ 9 - 6
mmgen/seedsplit.py

@@ -43,13 +43,16 @@ class SeedSplitSpecifier(HiliteStr, InitErrors, MMGenObject):
 		if isinstance(s, cls):
 		if isinstance(s, cls):
 			return s
 			return s
 		try:
 		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 = 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'
 			assert me.idx <= me.count, 'share index greater than share count'
 			return me
 			return me
 		except Exception as e:
 		except Exception as e:

+ 9 - 7
mmgen/tw/addresses.py

@@ -111,13 +111,15 @@ class TwAddresses(TwView):
 		self.used_w = 4 if self.has_used else 0
 		self.used_w = 4 if self.has_used else 0
 
 
 		if mmgen_addrs:
 		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 <seed ID>:[<type>:]<idx list>)')
-			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 <seed ID>:[<type>:]<idx list>)')
 		else:
 		else:
 			self.usr_addr_list = []
 			self.usr_addr_list = []
 
 

+ 6 - 3
mmgen/tw/shared.py

@@ -56,9 +56,12 @@ class TwLabel(str, InitErrors, MMGenObject):
 		if isinstance(text, cls):
 		if isinstance(text, cls):
 			return text
 			return text
 		try:
 		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 = str.__new__(cls, mmid + (' ' + comment if comment else ''))
 			me.mmid = mmid
 			me.mmid = mmid
 			me.comment = comment
 			me.comment = comment

+ 19 - 16
mmgen/wallet/mmgen.py

@@ -125,28 +125,31 @@ class wallet(wallet):
 		self.check_usr_seed_len(bitlen=int(d3))
 		self.check_usr_seed_len(bitlen=int(d3))
 		d.pw_status, d.timestamp = d4, d5
 		d.pw_status, d.timestamp = d4, d5
 
 
-		hpdata = lines[3].split()
-
-		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:
+		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')
+
+		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}')
 			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
 			return False
 
 
 		lmin, _, lmax = sorted(baseconv('b58').seedlen_map_rev) # 22, 33, 44
 		lmin, _, lmax = sorted(baseconv('b58').seedlen_map_rev) # 22, 33, 44
 		for i, key in (4, 'salt'), (5, 'enc_seed'):
 		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(
 			if not self.cfg._util.compare_chksums(
 					chksum,
 					chksum,

+ 6 - 7
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'
 		self.fmt_data = f'{self.ssdata.chksum} {split_into_cols(4, seed_hex)}\n'
 
 
 	def _deformat(self):
 	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:
 		if not len(hex_str) * 4 in Seed.lens:
 			msg(f'Invalid data length ({len(hex_str)}) in {self.desc}')
 			msg(f'Invalid data length ({len(hex_str)}) in {self.desc}')

+ 1 - 1
test/objtest_d/btc_mainnet.py

@@ -227,7 +227,7 @@ tests = {
 			{'text': 'F00BAA12:Z:99', 'proto': proto},
 			{'text': 'F00BAA12:Z:99', 'proto': proto},
 			{'text': tw_pfx+' x',     'proto': proto},
 			{'text': tw_pfx+' x',     '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': 'F00BAA12:S:1 ' + utf8_ctrl[:40], 'proto': proto, 'exc_name': 'BadTwComment'},
 			{'text': tw_pfx+'x comment', 'proto': proto},
 			{'text': tw_pfx+'x comment', 'proto': proto},
 		),
 		),