Browse Source

new MMGenSystemExit exception; remove rdie(), ydie()

The MMGen Project 3 years ago
parent
commit
41376eb515

+ 1 - 1
mmgen/base_proto/bitcoin/tx/status.py

@@ -82,7 +82,7 @@ class Status(TxBase.Status):
 		elif await is_in_wallet():
 			die(0,f'Transaction has {r.confs} confirmation{suf(r.confs)}')
 		elif await is_in_utxos():
-			rdie(2,'ERROR: transaction is in the blockchain (but not in the tracking wallet)!')
+			die(4,'ERROR: transaction is in the blockchain (but not in the tracking wallet)!')
 		elif await is_replaced():
 			msg('Transaction has been replaced')
 			msg('Replacement transaction ' + (

+ 3 - 3
mmgen/devtools.py

@@ -135,7 +135,7 @@ if os.getenv('MMGEN_DEBUG') or os.getenv('MMGEN_TEST_SUITE') or os.getenv('MMGEN
 		def immutable_attr_init_check(self):
 			from .globalvars import g
 			if g.test_suite:
-				from .util import rdie
+				from .util import die
 				cls = type(self)
 				for attrname in sorted({a for a in self.valid_attrs if a[0] != '_'}):
 					for o in (cls,cls.__bases__[0]): # assume there's only one base class
@@ -143,10 +143,10 @@ if os.getenv('MMGEN_DEBUG') or os.getenv('MMGEN_TEST_SUITE') or os.getenv('MMGEN
 							attr = o.__dict__[attrname]
 							break
 					else:
-						rdie(3,f'unable to find descriptor {cls.__name__}.{attrname}')
+						die(4,f'unable to find descriptor {cls.__name__}.{attrname}')
 					if type(attr).__name__ == 'ImmutableAttr':
 						if attrname not in self.__dict__:
-							rdie(3,
+							die(4,
 						f'attribute {attrname!r} of {cls.__name__} has not been initialized in constructor!')
 
 	def print_diff(a,b,from_file='',to_file='',from_json=True):

+ 13 - 0
mmgen/exception.py

@@ -20,6 +20,19 @@
 mmgen.exception: Exception classes for the MMGen suite
 """
 
+class MMGenError(Exception):
+
+	def __init__(self,errno,strerror,stdout):
+		self.mmcode = errno
+		self.stdout = stdout
+		super().__init__(strerror)
+
+	def __repr__(self):
+		return f'{type(self).__name__}({self.mmcode}): {self}'
+
+class MMGenSystemExit(MMGenError):
+	pass
+
 # 1: no hl, message only
 class UserNonConfirmation(Exception):     mmcode = 1
 class BadAgeFormat(Exception):            mmcode = 1

+ 1 - 1
mmgen/fileutil.py

@@ -57,7 +57,7 @@ def check_binary(args):
 	try:
 		run(args,stdout=DEVNULL,stderr=DEVNULL,check=True)
 	except:
-		rdie(2,f'{args[0]!r} binary missing, not in path, or not executable')
+		die(2,f'{args[0]!r} binary missing, not in path, or not executable')
 
 def shred_file(fn,verbose=False):
 	check_binary(['shred','--version'])

+ 1 - 1
mmgen/led.py

@@ -89,7 +89,7 @@ class LEDControl:
 					fp.write(f'{init_val}\n')
 				return True
 			except PermissionError:
-				ydie(1,'\n'+fmt(f"""
+				die(2,'\n'+fmt(f"""
 					You do not have access to the {desc} file
 					To allow access, run the following command:
 

+ 24 - 10
mmgen/main.py

@@ -43,21 +43,35 @@ def launch(mod):
 	except EOFError:
 		sys.stderr.write('\nEnd of file\n')
 	except Exception as e:
+
 		if os.getenv('MMGEN_EXEC_WRAPPER'):
 			raise
 		else:
-			try: m = '{}'.format(e.args[0])
-			except: m = repr(e.args[0])
+			try:
+				errmsg = '{}'.format(e.args[0])
+			except:
+				errmsg = repr(e.args[0])
+
+			from collections import namedtuple
+			from mmgen.color import nocolor,yellow,red
+
+			_o = namedtuple('exit_data',['color','exit_val','fs'])
+			d = {
+				1:   _o(nocolor, 1, '{message}'),
+				2:   _o(yellow,  2, '{message}'),
+				3:   _o(yellow,  3, '\nMMGen Error ({name}): {message}'),
+				4:   _o(red,     4, '\nMMGen Fatal Error ({name}): {message}'),
+				'x': _o(yellow,  5, '\nMMGen Unhandled Exception ({name}): {message}'),
+			}[getattr(e,'mmcode','x')]
+
+			(sys.stdout if getattr(e,'stdout',None) else sys.stderr).write(
+				d.color(d.fs.format(
+					name = type(e).__name__,
+					message = errmsg ))
+				+ '\n' )
 
-			from .util import die,ydie,rdie
-			d = [   (ydie,2,'\nMMGen Unhandled Exception ({n}): {m}'),
-					(die, 1,'{m}'),
-					(ydie,2,'{m}'),
-					(ydie,3,'\nMMGen Error ({n}): {m}'),
-					(rdie,4,'\nMMGen Fatal Error ({n}): {m}')
-				][e.mmcode if hasattr(e,'mmcode') else 0]
+			sys.exit(d.exit_val)
 
-			d[0](d[1],d[2].format(n=type(e).__name__,m=m))
 	except SystemExit as e:
 		if os.getenv('MMGEN_EXEC_WRAPPER') and e.code != 0:
 			from mmgen.color import red

+ 1 - 1
mmgen/main_addrimport.py

@@ -78,7 +78,7 @@ def parse_cmd_args(rpc,cmd_args):
 		al = (AddrList,KeyAddrList)[bool(opt.keyaddr_file)](proto,infile)
 		if al.al_id.mmtype in ('S','B'):
 			if not rpc.info('segwit_is_active'):
-				rdie(2,'Segwit is not active on this chain. Cannot import Segwit addresses')
+				die(2,'Segwit is not active on this chain. Cannot import Segwit addresses')
 		return al
 
 	if len(cmd_args) == 1:

+ 1 - 1
mmgen/main_autosign.py

@@ -164,7 +164,7 @@ async def check_daemons_running():
 			try:
 				await rpc_init(proto)
 			except SocketError as e:
-				ydie(1,f'{coin} daemon not running or not listening on port {proto.rpc_port}')
+				die(2,f'{coin} daemon not running or not listening on port {proto.rpc_port}')
 
 def get_wallet_files():
 	try:

+ 3 - 3
mmgen/main_tool.py

@@ -281,7 +281,7 @@ def process_args(cmd,cmd_args,cls):
 	return ( args, kwargs )
 
 def process_result(ret,pager=False,print_result=False):
-	from .util import Msg,ydie,parse_bytespec
+	from .util import Msg,die,parse_bytespec
 	"""
 	Convert result to something suitable for output to screen and return it.
 	If result is bytes and not convertible to utf8, output as binary using os.write().
@@ -294,7 +294,7 @@ def process_result(ret,pager=False,print_result=False):
 	if ret == True:
 		return True
 	elif ret in (False,None):
-		ydie(1,f'tool command returned {ret!r}')
+		die(2,f'tool command returned {ret!r}')
 	elif isinstance(ret,str):
 		return triage_result(ret)
 	elif isinstance(ret,int):
@@ -312,7 +312,7 @@ def process_result(ret,pager=False,print_result=False):
 			else:
 				return ret
 	else:
-		ydie(1,f'tool.py: can’t handle return value of type {type(ret).__name__!r}')
+		die(2,f'tool.py: can’t handle return value of type {type(ret).__name__!r}')
 
 def get_cmd_cls(cmd):
 	for modname,cmdlist in mods.items():

+ 1 - 1
mmgen/main_txsign.py

@@ -156,6 +156,6 @@ async def main():
 			bad_tx_count += 1
 
 	if bad_tx_count:
-		ydie(2,f'{bad_tx_count} transaction{suf(bad_tx_count)} could not be signed')
+		die(2,f'{bad_tx_count} transaction{suf(bad_tx_count)} could not be signed')
 
 run_session(main())

+ 4 - 5
mmgen/protocol.py

@@ -69,7 +69,7 @@ class CoinProtocol(MMGenObject):
 
 			if 'tx' not in self.mmcaps and g.is_txprog:
 				from .util import die
-				die(1,f'Command {g.prog_name!r} not supported for coin {self.coin}')
+				die(2,f'Command {g.prog_name!r} not supported for coin {self.coin}')
 
 			if hasattr(self,'chain_names'):
 				self.chain_name = self.chain_names[0] # first chain name is default
@@ -171,15 +171,14 @@ class CoinProtocol(MMGenObject):
 			if 0 < int.from_bytes(sec,'big') < self.secp256k1_ge:
 				return sec
 			else: # chance of this is less than 1 in 2^127
-				from .util import ydie
+				from .util import die,ymsg
 				pk = int.from_bytes(sec,'big')
 				if pk == 0: # chance of this is 1 in 2^256
-					ydie(3,'Private key is zero!')
+					die(4,'Private key is zero!')
 				elif pk == self.secp256k1_ge: # ditto
-					ydie(3,'Private key == secp256k1_ge!')
+					die(4,'Private key == secp256k1_ge!')
 				else:
 					if not g.test_suite:
-						from .util import ymsg
 						ymsg(f'Warning: private key is greater than secp256k1 group order!:\n  {hexpriv}')
 					return (pk % self.secp256k1_ge).to_bytes(self.privkey_len,'big')
 

+ 1 - 1
mmgen/regtest.py

@@ -102,7 +102,7 @@ class MMGenRegtest(MMGenObject):
 		out = await self.rpc_call(*cmd_args,wallet='miner')
 
 		if len(out) != blocks:
-			rdie(1,'Error generating blocks')
+			die(4,'Error generating blocks')
 
 		gmsg(f'Mined {blocks} block{suf(blocks)}')
 

+ 3 - 3
mmgen/rpc.py

@@ -537,7 +537,7 @@ class BitcoinRPCClient(RPCClient,metaclass=AsyncInit):
 				await self.icall('createwallet',wallet_name=wname)
 				ymsg(f'Created {self.daemon.coind_name} wallet {wname!r}')
 			elif len(wallets) > 1: # support only one loaded wallet for now
-				rdie(2,f'ERROR: more than one {self.daemon.coind_name} wallet loaded: {wallets}')
+				die(4,f'ERROR: more than one {self.daemon.coind_name} wallet loaded: {wallets}')
 			wallet_checked.append(True)
 
 	def get_daemon_cfg_fn(self):
@@ -665,7 +665,7 @@ class EthereumRPCClient(RPCClient,metaclass=AsyncInit):
 		import re
 		vip = re.match(self.daemon.version_pat,vi,re.ASCII)
 		if not vip:
-			ydie(1,fmt(f"""
+			die(2,fmt(f"""
 			Aborting on daemon mismatch:
 			  Requested daemon: {self.daemon.id}
 			  Running daemon:   {vi}
@@ -821,7 +821,7 @@ def handle_unsupported_daemon_version(rpc,name,warn_only):
 		daemon_warning('version',div=name,fmt_args=[rpc.daemon.coind_name])
 	else:
 		name = rpc.daemon.coind_name
-		rdie(1,'\n'+fmt(f"""
+		die(2,'\n'+fmt(f"""
 			The running {name} daemon has version {rpc.daemon_version_str}.
 			This version of MMGen is tested only on {name} v{rpc.daemon.coind_version_str} and below.
 

+ 4 - 2
mmgen/twaddrs.py

@@ -43,7 +43,8 @@ class TwAddrList(MMGenDict,TwCommon,metaclass=AsyncInit):
 					err = True
 					msg(f'Duplicate MMGen ID ({mmid}) discovered in tracking wallet!\n')
 				mmid_prev = mmid
-			if err: rdie(3,'Tracking wallet is corrupted!')
+			if err:
+				die(4,'Tracking wallet is corrupted!')
 
 		def check_addr_array_lens(acct_pairs):
 			err = False
@@ -55,7 +56,8 @@ class TwAddrList(MMGenDict,TwCommon,metaclass=AsyncInit):
 						msg(f'Label {label!r}: has no associated address!')
 					else:
 						msg(f'{addrs!r}: more than one {proto.coin} address in account!')
-			if err: rdie(3,'Tracking wallet is corrupted!')
+			if err:
+				die(4,'Tracking wallet is corrupted!')
 
 		self.rpc   = await rpc_init(proto)
 		self.total = proto.coin_amt('0')

+ 4 - 4
mmgen/tx/new.py

@@ -17,7 +17,7 @@ from ..opts import opt
 from .base import Base
 from ..color import pink
 from ..obj import get_obj,HexStr
-from ..util import msg,qmsg,fmt,suf,remove_dups,get_extension,keypress_confirm,do_license_msg,line_input
+from ..util import msg,qmsg,fmt,die,suf,remove_dups,get_extension,keypress_confirm,do_license_msg,line_input
 from ..addr import is_mmgen_id,CoinAddr,is_coin_addr
 
 def mmaddr2coinaddr(mmaddr,ad_w,ad_f,proto):
@@ -53,9 +53,9 @@ def mmaddr2coinaddr(mmaddr,ad_w,ad_f,proto):
 				if not (opt.yes or keypress_confirm('Continue anyway?')):
 					sys.exit(1)
 			else:
-				ydie(2,wmsg('addr_not_found'))
+				die(2,wmsg('addr_not_found'))
 		else:
-			ydie(2,wmsg('addr_not_found_no_addrfile'))
+			die(2,wmsg('addr_not_found_no_addrfile'))
 
 	return CoinAddr(proto,coin_addr)
 
@@ -208,7 +208,7 @@ class New(Base):
 				'ERROR: No change output specified' ))
 
 		if self.has_segwit_outputs() and not self.rpc.info('segwit_is_active'):
-			rdie(2,f'{g.proj_name} Segwit address requested on the command line, '
+			die(2,f'{g.proj_name} Segwit address requested on the command line, '
 					+ 'but Segwit is not active on this chain')
 
 		if not self.outputs:

+ 8 - 16
mmgen/util.py

@@ -120,11 +120,13 @@ def mdie(*args):
 	mmsg(*args)
 	sys.exit(0)
 
-def die(ev=0,s=''):
+def die(ev,s='',stdout=False):
 	assert isinstance(ev,int)
-	if s:
-		msg(s)
-	sys.exit(ev)
+	from .exception import MMGenSystemExit,MMGenError
+	if ev <= 2:
+		raise MMGenSystemExit(ev,s,stdout)
+	else:
+		raise MMGenError(ev,s,stdout)
 
 def die_wait(delay,ev=0,s=''):
 	assert isinstance(delay,int)
@@ -142,16 +144,7 @@ def die_pause(ev=0,s=''):
 	sys.exit(ev)
 
 def Die(ev=0,s=''):
-	assert isinstance(ev,int)
-	if s:
-		Msg(s)
-	sys.exit(ev)
-
-def rdie(ev=0,s=''):
-	die(ev,red(s))
-
-def ydie(ev=0,s=''):
-	die(ev,yellow(s))
+	die(ev=ev,s=s,stdout=True)
 
 def pp_fmt(d):
 	import pprint
@@ -206,8 +199,7 @@ def remove_dups(iterable,edesc='element',desc='list',quiet=False,hide=False):
 
 def exit_if_mswin(feature):
 	if g.platform == 'win':
-		m = capfirst(feature) + ' not supported on the MSWin / MSYS2 platform'
-		ydie(1,m)
+		die(2, capfirst(feature) + ' not supported on the MSWin / MSYS2 platform' )
 
 def get_keccak():
 

+ 1 - 1
scripts/create-token.py

@@ -243,7 +243,7 @@ def compile_code(code):
 	if cp.returncode != 0:
 		rmsg('Solidity compiler produced the following error:')
 		msg(err)
-		rdie(2,f'Solidity compiler exited with error (return val: {cp.returncode})')
+		die(4,f'Solidity compiler exited with error (return val: {cp.returncode})')
 	if err:
 		ymsg('Solidity compiler produced the following warning:')
 		msg(err)

+ 8 - 4
scripts/exec_wrapper.py

@@ -32,7 +32,7 @@ def exec_wrapper_init(): # don't change: name is used to test if script is runni
 		except:
 			pass
 
-def exec_wrapper_write_traceback():
+def exec_wrapper_write_traceback(e):
 	import traceback,re
 	lines = traceback.format_exception(*sys.exc_info()) # returns a list
 
@@ -45,7 +45,11 @@ def exec_wrapper_write_traceback():
 		lines.pop()
 
 	c = exec_wrapper_get_colors()
-	sys.stdout.write('{}{}'.format(c.yellow(''.join(lines)),c.red(exc)))
+	message = ( repr(e) if type(e).__name__ in ('MMGenError','MMGenSystemExit') else exc )
+	sys.stdout.write('{}{}'.format(
+		c.yellow( ''.join(lines) ),
+		c.red(message) )
+	+ '\n' )
 
 	with open('my.err','w') as fp:
 		fp.write(''.join(lines+[exc]))
@@ -96,13 +100,13 @@ try:
 		exec(fp.read())
 except SystemExit as e:
 	if e.code != 0 and not os.getenv('EXEC_WRAPPER_NO_TRACEBACK'):
-		exec_wrapper_write_traceback()
+		exec_wrapper_write_traceback(e)
 	else:
 		exec_wrapper_tracemalloc_log()
 		exec_wrapper_end_msg()
 	sys.exit(e.code)
 except Exception as e:
-	exec_wrapper_write_traceback()
+	exec_wrapper_write_traceback(e)
 	retval = e.mmcode if hasattr(e,'mmcode') else e.code if hasattr(e,'code') else 1
 	sys.exit(retval)
 

+ 1 - 1
test/gentest.py

@@ -318,7 +318,7 @@ def do_ab_test(proto,cfg,addr_type,gen1,kg2,ag,tool,cache_data):
 
 	kg1 = KeyGenerator( proto, addr_type.pubkey_type, gen1 )
 	if type(kg1) == type(kg2):
-		rdie(1,'Key generators are the same!')
+		die(4,'Key generators are the same!')
 
 	e = cinfo.get_entry(proto.coin,proto.network)
 	qmsg(green("Comparing address generators '{A}' and '{B}' for {N} {c} ({n}), addrtype {a!r}".format(

+ 6 - 5
test/include/pexpect.py

@@ -23,7 +23,7 @@ test/pexpect.py: pexpect implementation for MMGen test suites
 import sys,os,time
 from mmgen.globalvars import g
 from mmgen.opts import opt
-from mmgen.util import msg,msg_r,vmsg,vmsg_r,rmsg,red,yellow,green,cyan,die,rdie
+from mmgen.util import msg,msg_r,vmsg,vmsg_r,rmsg,red,yellow,green,cyan,die
 from .common import *
 
 try:
@@ -184,11 +184,12 @@ class MMGenPexpect(object):
 				f = (self.p.expect_exact,self.p.expect)[bool(regex)]
 				ret = f(s)
 		except pexpect.TIMEOUT:
-			if opt.debug_pexpect: raise
-			m1 = red(f'\nERROR.  Expect {s!r} timed out.  Exiting\n')
+			if opt.debug_pexpect:
+				raise
+			m1 = f'\nERROR.  Expect {s!r} timed out.  Exiting\n'
 			m2 = f'before: [{self.p.before}]\n'
 			m3 = f'sent value: [{self.sent_value}]' if self.sent_value != None else ''
-			rdie(1,m1+m2+m3)
+			die(2,m1+m2+m3)
 
 		debug_pexpect_msg(self.p)
 
@@ -196,7 +197,7 @@ class MMGenPexpect(object):
 			msg_r(f' ==> {ret} ')
 
 		if ret == -1:
-			rdie(1,f'Error.  Expect returned {ret}')
+			die(4,f'Error.  Expect returned {ret}')
 		else:
 			if t == '':
 				if not nonl and not silent: vmsg('')

+ 6 - 6
test/objattrtest.py

@@ -69,7 +69,7 @@ def get_descriptor_obj(objclass,attrname):
 	for o in (objclass,objclass.__bases__[0]): # assume there's only one base class
 		if attrname in o.__dict__:
 			return o.__dict__[attrname]
-	rdie(3,f'unable to find descriptor {objclass.__name__}.{attrname}')
+	die(4,f'unable to find descriptor {objclass.__name__}.{attrname}')
 
 def test_attr_perm(obj,attrname,perm_name,perm_value,dobj,attrval_type):
 
@@ -93,15 +93,15 @@ def test_attr_perm(obj,attrname,perm_name,perm_value,dobj,attrval_type):
 		elif perm_name == 'delete_ok':
 			delattr(obj,attrname)
 	except SampleObjError as e:
-		rdie(2,f'Test script error ({e})')
+		die(4,f'Test script error ({e})')
 	except Exception as e:
 		if perm_value == True:
 			fs = '{!r}: unable to {} attribute {!r}, though {}ing is allowed ({})'
-			rdie(2,fs.format(type(obj).__name__,pname,attrname,pstem,e))
+			die(4,fs.format(type(obj).__name__,pname,attrname,pstem,e))
 	else:
 		if perm_value == False:
 			fs = '{!r}: attribute {!r} is {n}able, though {n}ing is forbidden'
-			rdie(2,fs.format(type(obj).__name__,attrname,n=pstem))
+			die(4,fs.format(type(obj).__name__,attrname,n=pstem))
 
 def test_attr(data,obj,attrname,dobj,bits,attrval_type):
 	if hasattr(obj,attrname): # TODO
@@ -109,7 +109,7 @@ def test_attr(data,obj,attrname,dobj,bits,attrval_type):
 
 		if attrval_type not in (td_attrval_type,type(None)):
 			fs = '\nattribute {!r} of {!r} instance has incorrect type {!r} (should be {!r})'
-			rdie(2,fs.format(attrname,type(obj).__name__,attrval_type.__name__,td_attrval_type.__name__))
+			die(4,fs.format(attrname,type(obj).__name__,attrval_type.__name__,td_attrval_type.__name__))
 
 	if hasattr(dobj,'__dict__'):
 		d = dobj.__dict__
@@ -118,7 +118,7 @@ def test_attr(data,obj,attrname,dobj,bits,attrval_type):
 			if k in d:
 				if d[k] != bits[k]:
 					fs = 'init value {iv}={a} for attr {n!r} does not match test data ({iv}={b})'
-					rdie(2,fs.format(iv=k,n=attrname,a=d[k],b=bits[k]))
+					die(4,fs.format(iv=k,n=attrname,a=d[k],b=bits[k]))
 				if opt.verbose and d[k] == True:
 					msg_r(f' {k}={d[k]!r}')
 

+ 2 - 2
test/scrambletest.py

@@ -97,7 +97,7 @@ cmd_base = f'python3{cvr_opts} cmds/mmgen-{{}}gen -qS'
 def get_cmd_output(cmd):
 	cp = run(cmd.split(),stdout=PIPE,stderr=PIPE)
 	if cp.returncode != 0:
-		ydie(2,f'\nSpawned program exited with error code {cp.returncode}:\n{cp.stderr.decode()}')
+		die(2,f'\nSpawned program exited with error code {cp.returncode}:\n{cp.stderr.decode()}')
 	return cp.stdout.decode().splitlines()
 
 def do_test(cmd,tdata,msg_str,addr_desc):
@@ -115,7 +115,7 @@ def do_test(cmd,tdata,msg_str,addr_desc):
 			s = k.replace('seed','seed[:8]').replace('addr',addr_desc)
 			vmsg(f'  {s:9}: {cmd_out[k]}')
 		else:
-			rdie(1,f'\nError: sc_{k} value {cmd_out[k]} does not match reference value {ref_data[k]}')
+			die(4,f'\nError: sc_{k} value {cmd_out[k]} does not match reference value {ref_data[k]}')
 	msg('OK')
 
 def do_coin_tests():

+ 4 - 4
test/test.py

@@ -40,7 +40,7 @@ def create_shm_dir(data_dir,trash_dir):
 					try:
 						run(['python3',os.path.join('cmds','mmgen-regtest'),'stop'],check=True)
 					except:
-						rdie(1,f'Unable to remove {tdir!r}!')
+						die(4,f'Unable to remove {tdir!r}!')
 					else:
 						time.sleep(2)
 						shutil.rmtree(tdir)
@@ -977,7 +977,7 @@ class TestSuiteRunner(object):
 			self.skipped_warnings.append(
 				'Test {!r} was skipped:\n  {}'.format(cmd,'\n  '.join(ret[1].split('\n'))))
 		else:
-			rdie(1,f'{cmd!r} returned {ret}')
+			die(2,f'{cmd!r} returned {ret}')
 
 	def check_deps(self,cmds): # TODO: broken
 		if len(cmds) != 1:
@@ -1057,9 +1057,9 @@ except KeyboardInterrupt:
 	tr.warn_skipped()
 	die(1,'\ntest.py exiting at user request')
 except TestSuiteException as e:
-	ydie(1,e.args[0])
+	die(2,e.args[0])
 except TestSuiteFatalException as e:
-	rdie(1,e.args[0])
+	die(4,e.args[0])
 except Exception:
 	if 'exec_wrapper_init' in globals(): # test.py itself is running under exec_wrapper
 		import traceback

+ 3 - 3
test/test_py_d/ts_autosign.py

@@ -244,18 +244,18 @@ class TestSuiteAutosign(TestSuiteBase):
 					run(['mount',mountpoint],check=True)
 					imsg(f'Mounted {mountpoint}')
 				except:
-					ydie(1,f'Could not mount {mountpoint}!  Exiting')
+					die(2,f'Could not mount {mountpoint}!  Exiting')
 
 			txdir = joinpath(mountpoint,'tx')
 			if not os.path.isdir(txdir):
-				ydie(1,f'Directory {txdir} does not exist!  Exiting')
+				die(2,f'Directory {txdir} does not exist!  Exiting')
 
 		def init_led():
 			try:
 				cf = LEDControl(enabled=True,simulate=simulate)
 			except Exception as e:
 				msg(str(e))
-				ydie(2,'LEDControl initialization failed')
+				die(2,'LEDControl initialization failed')
 			for fn in (cf.board.status,cf.board.trigger):
 				if fn:
 					run(['sudo','chmod','0666',fn],check=True)

+ 2 - 2
test/test_py_d/ts_ethdev.py

@@ -59,7 +59,7 @@ def check_solc_ver():
 	try:
 		cp = run(cmd.split(),check=False,stdout=PIPE)
 	except Exception as e:
-		rdie(2,f'Unable to execute {cmd!r}: {e}')
+		die(4,f'Unable to execute {cmd!r}: {e}')
 	res = cp.stdout.decode().strip()
 	if cp.returncode == 0:
 		omsg(
@@ -750,7 +750,7 @@ class TestSuiteEthdev(TestSuiteBase,TestSuiteShared):
 		cp = run(cmd,stdout=DEVNULL,stderr=PIPE)
 		if cp.returncode != 0:
 			rmsg('solc failed with the following output:')
-			ydie(2,cp.stderr.decode())
+			die(2,cp.stderr.decode())
 		imsg('ERC20 token {!r} compiled'.format( token_data['symbol'] ))
 		return 'ok'
 

+ 1 - 1
test/test_py_d/ts_misc.py

@@ -59,7 +59,7 @@ class TestSuiteHelp(TestSuiteBase):
 	def usage(self):
 		t = self.spawn(f'mmgen-walletgen',['foo'])
 		t.expect('USAGE: mmgen-walletgen')
-		t.expect('SystemExit: 1')
+		t.expect('MMGenSystemExit(1)')
 		t.req_exit_val = 1
 		return t
 

+ 3 - 3
test/test_py_d/ts_regtest.py

@@ -741,7 +741,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 	def get_mempool1(self):
 		mp = self._get_mempool()
 		if len(mp) != 1:
-			rdie(2,'Mempool has more or less than one TX!')
+			die(4,'Mempool has more or less than one TX!')
 		self.write_to_tmpfile('rbf_txid',mp[0]+'\n')
 		return 'ok'
 
@@ -762,10 +762,10 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 			return 'skip'
 		mp = self._get_mempool()
 		if len(mp) != 1:
-			rdie(2,'Mempool has more or less than one TX!')
+			die(4,'Mempool has more or less than one TX!')
 		chk = self.read_from_tmpfile('rbf_txid')
 		if chk.strip() == mp[0]:
-			rdie(2,'TX in mempool has not changed!  RBF bump failed')
+			die(4,'TX in mempool has not changed!  RBF bump failed')
 		self.write_to_tmpfile('rbf_txid2',mp[0]+'\n')
 		return 'ok'
 

+ 5 - 5
test/test_py_d/ts_seedsplit.py

@@ -226,11 +226,11 @@ class TestSuiteSeedSplit(TestSuiteBase):
 
 	def ss_bad_invocation1(self):
 		return self.ss_bad_invocation(
-			'mmgen-seedsplit',[],1,'SystemExit: 1')
+			'mmgen-seedsplit',[],1,'MMGenSystemExit(1)')
 
 	def ss_bad_invocation2(self):
 		return self.ss_bad_invocation(
-			'mmgen-seedsplit',['-M1','1:9'],1,'SystemExit: 1')
+			'mmgen-seedsplit',['-M1','1:9'],1,'MMGenSystemExit(1)')
 
 	def ss_bad_invocation3(self):
 		return self.ss_bad_invocation(
@@ -242,11 +242,11 @@ class TestSuiteSeedSplit(TestSuiteBase):
 
 	def ss_bad_invocation5(self):
 		return self.ss_bad_invocation(
-			'mmgen-seedjoin',[],1,'SystemExit: 1')
+			'mmgen-seedjoin',[],1,'MMGenSystemExit(1)')
 
 	def ss_bad_invocation6(self):
 		return self.ss_bad_invocation(
-			'mmgen-seedjoin',[self.tmpdir+'/a'],1,'SystemExit: 1')
+			'mmgen-seedjoin',[self.tmpdir+'/a'],1,'MMGenSystemExit(1)')
 
 	def ss_bad_invocation7(self):
 		return self.ss_bad_invocation(
@@ -258,7 +258,7 @@ class TestSuiteSeedSplit(TestSuiteBase):
 
 	def ss_bad_invocation9(self):
 		return self.ss_bad_invocation(
-			'mmgen-seedsplit',['x'],1,'SystemExit: 1')
+			'mmgen-seedsplit',['x'],1,'MMGenSystemExit(1)')
 
 	def ss_bad_invocation10(self):
 		return self.ss_bad_invocation(

+ 4 - 5
test/tooltest.py

@@ -238,7 +238,7 @@ class MMGenToolTestUtils(object):
 				red('FAILED'),
 				yellow('Command stderr output:'),
 				err.decode() ))
-			rdie(1,f'Called process returned with an error (retcode {cp.returncode})')
+			die(2,f'Called process returned with an error (retcode {cp.returncode})')
 		return (out,out.rstrip())[bool(strip)]
 
 	def run_cmd_chk(self,name,f1,f2,kwargs='',extra_msg='',strip_hex=False,add_opts=[]):
@@ -251,8 +251,7 @@ class MMGenToolTestUtils(object):
 			return (a.lstrip('0') == b.lstrip('0')) if strip_hex else (a == b)
 		if cmp_equal(ret,idata): ok()
 		else:
-			fs = "Error: values don't match:\nIn:  {!r}\nOut: {!r}"
-			rdie(3,fs.format(idata,ret))
+			die(4, "Error: values don't match:\nIn:  {!r}\nOut: {!r}".format(idata,ret))
 		return ret
 
 	def run_cmd_nochk(self,name,f1,kwargs='',add_opts=[]):
@@ -278,7 +277,7 @@ class MMGenToolTestUtils(object):
 			else:
 				if not hush: ok()
 		else:
-			rdie(3,f'Error for command {name!r}')
+			die(4,f'Error for command {name!r}')
 
 	def run_cmd_randinput(self,name,strip=True,add_opts=[]):
 		s = getrand(128)
@@ -298,7 +297,7 @@ def ok_or_die(val,chk_func,s,skip_ok=False):
 	if ret:
 		if not skip_ok: ok()
 	else:
-		rdie(3,f'Returned value {val!r} is not a {s}')
+		die(4,f'Returned value {val!r} is not a {s}')
 
 class MMGenToolTestCmds(object):
 

+ 1 - 1
test/tooltest2.py

@@ -782,7 +782,7 @@ def fork_cmd(cmd_name,args,out,opts,stdin_input):
 		if m:
 			return { b'None': None, b'False': False }[m.group(1)]
 		else:
-			ydie(1,f'Spawned program exited with error: {cp.stderr}')
+			die(2,f'Spawned program exited with error: {cp.stderr}')
 
 	return cmd_out.strip()
 

+ 3 - 3
test/unit_tests.py

@@ -95,7 +95,7 @@ class UnitTestHelpers(object):
 				assert exc == exc_chk, m_exc.format(exc,exc_chk)
 				assert re.search(emsg_chk,emsg), m_err.format(emsg,emsg_chk)
 			else:
-				rdie(3,m_noraise.format(desc,exc_chk))
+				die(4,m_noraise.format(desc,exc_chk))
 
 tests_seen = []
 
@@ -110,7 +110,7 @@ def run_test(test,subtest=None):
 		if type(ret).__name__ == 'coroutine':
 			ret = run_session(ret)
 		if not ret:
-			rdie(1,f'Unit subtest {subtest!r} failed')
+			die(4,f'Unit subtest {subtest!r} failed')
 		pass
 
 	if test not in tests_seen:
@@ -139,7 +139,7 @@ def run_test(test,subtest=None):
 				run_subtest(subtest)
 		else:
 			if not mod.unit_test().run_test(test,UnitTestHelpers):
-				rdie(1,'Unit test {test!r} failed')
+				die(4,'Unit test {test!r} failed')
 
 try:
 	for test in (cmd_args or all_tests):

+ 2 - 2
test/unit_tests_d/ut_daemon.py

@@ -77,9 +77,9 @@ def test_cmds(op):
 						try:
 							cp = run([d.exec_fn,'--help'],stdout=PIPE,stderr=PIPE)
 						except:
-							ydie(1,f'Unable to execute {d.exec_fn}')
+							die(2,f'Unable to execute {d.exec_fn}')
 						if cp.returncode:
-							ydie(1,f'Unable to execute {d.exec_fn}')
+							die(2,f'Unable to execute {d.exec_fn}')
 						else:
 							vmsg('{:16} {}'.format(
 								d.exec_fn+':',

+ 1 - 1
test/unit_tests_d/ut_indexed_dict.py

@@ -21,7 +21,7 @@ class unit_test(object):
 		def bad4(): d.clear()
 		def bad5(): d.update(d)
 
-		def odie(n): rdie(3,f'\nillegal action {bad_msg[n]!r} failed to raise exception')
+		def odie(n): die(4,f'\nillegal action {bad_msg[n]!r} failed to raise exception')
 		def omsg(e): vmsg(' - ' + e.args[0])
 
 		msg_r('Testing class IndexedDict...')