Browse Source

[msys2]: enable color output, handle exceptions in main.py

MMGen 6 years ago
parent
commit
823e325c6c

+ 34 - 21
mmgen/color.py

@@ -52,30 +52,42 @@ for c in _colors:
 
 def nocolor(s): return s
 
-def init_color(enable_color=True,num_colors='auto'):
-	if enable_color:
-		assert num_colors in ('auto',8,16,256)
-		globals()['_reset'] = '\033[0m'
-		if num_colors in (8,16):
-			pfx = '_16_'
-		elif num_colors in (256,):
+def init_color(num_colors='auto'):
+	assert num_colors in ('auto',8,16,256)
+	globals()['_reset'] = '\033[0m'
+	if num_colors in (8,16):
+		pfx = '_16_'
+	elif num_colors in (256,):
+		pfx = '_256_'
+	else:
+		try:
+			import os
+			assert os.environ['TERM'][-8:] == '256color'
 			pfx = '_256_'
-		else:
+		except:
 			try:
-				import os
-				assert os.environ['TERM'][-8:] == '256color'
-				pfx = '_256_'
+				import subprocess
+				a = subprocess.check_output(['infocmp','-0']).decode()
+				b = [e.split('#')[1] for e in a.split(',') if e[:6] == 'colors'][0]
+				pfx = ('_16_','_256_')[b=='256']
 			except:
-				try:
-					import subprocess
-					a = subprocess.check_output(['infocmp','-0'])
-					b = [e.split('#')[1] for e in a.split(',') if e[:6] == 'colors'][0]
-					pfx = ('_16_','_256_')[b=='256']
-				except:
-					pfx = '_16_'
+				pfx = '_16_'
 
-		for c in _colors:
-			globals()['_clr_'+c] = globals()[pfx+c]
+	for c in _colors:
+		globals()['_clr_'+c] = globals()[pfx+c]
+
+def start_mscolor():
+	import sys
+	from mmgen.globalvars import g
+	try:
+		import colorama
+		colorama.init(strip=True,convert=True)
+	except:
+		from mmgen.util import msg
+		msg('Import of colorama module failed')
+	else:
+		g.stdout = sys.stdout
+		g.stderr = sys.stderr
 
 def test_color():
 	try:
@@ -83,8 +95,9 @@ def test_color():
 		colorama.init(strip=True,convert=True)
 	except:
 		pass
+
 	for desc,n in (('auto','auto'),('8-color',8),('256-color',256)):
-		if n != 'auto': init_color(num_colors=n)
+		init_color(num_colors=n)
 		print('{:9}: {}'.format(desc,' '.join([globals()[c](c) for c in sorted(_colors)])))
 
 if __name__ == '__main__': test_color()

+ 2 - 2
mmgen/globalvars.py

@@ -45,7 +45,7 @@ class g(object):
 	author    = 'The MMGen Project'
 	email     = '<mmgen@tuta.io>'
 	Cdates    = '2013-2019'
-	keywords  = 'Bitcoin, BTC, cryptocurrency, wallet, cold storage, offline, online, spending, open-source, command-line, Python, Linux, Bitcoin Core, bitcoind, hd, deterministic, hierarchical, secure, anonymous, Electrum, seed, mnemonic, brainwallet, Scrypt, utility, script, scriptable, blockchain, raw, transaction, permissionless, console, terminal, curses, ansi, color, tmux, remote, client, daemon, RPC, json, entropy, xterm, rxvt, PowerShell, MSYS, MinGW, mswin, Armbian, Raspbian, Raspberry Pi, Orange Pi, BCash, BCH, Litecoin, LTC, altcoin, ZEC, Zcash, DASH, Dashpay, ETH, Ethereum, Classic, SHA256Compress, XMR, Monero, monerod, EMC, Emercoin, ERC20, token, deploy, contract, gas, fee, smart contract, solidity, Parity, testnet, devmode, Kovan'
+	keywords  = 'Bitcoin, BTC, Ethereum, ETH, Monero, XMR, ERC20, cryptocurrency, wallet, BIP32, cold storage, offline, online, spending, open-source, command-line, Python, Linux, Bitcoin Core, bitcoind, hd, deterministic, hierarchical, secure, anonymous, Electrum, seed, mnemonic, brainwallet, Scrypt, utility, script, scriptable, blockchain, raw, transaction, permissionless, console, terminal, curses, ansi, color, tmux, remote, client, daemon, RPC, json, entropy, xterm, rxvt, PowerShell, MSYS, MSYS2, MinGW, MinGW64, MSWin, Armbian, Raspbian, Raspberry Pi, Orange Pi, BCash, BCH, Litecoin, LTC, altcoin, ZEC, Zcash, DASH, Dashpay, SHA256Compress, monerod, EMC, Emercoin, token, deploy, contract, gas, fee, smart contract, solidity, Parity, testnet, devmode, Kovan'
 	max_int   = 0xffffffff
 
 	stdin_tty = bool(sys.stdin.isatty() or os.getenv('MMGEN_TEST_SUITE_POPEN_SPAWN'))
@@ -111,7 +111,7 @@ class g(object):
 	else:
 		die(1,"'{}': platform not supported by {}\n".format(sys.platform,proj_name))
 
-	color = sys.stdout.isatty() and platform != 'win'
+	color = sys.stdout.isatty()
 
 	if os.getenv('HOME'):                             # Linux or MSYS
 		home_dir = os.getenv('HOME')

+ 28 - 35
mmgen/main.py

@@ -26,41 +26,34 @@ def launch(what):
 		what = 'wallet'
 	if what == 'keygen': what = 'addrgen'
 
-	import sys
+	import sys,os
+	from mmgen.globalvars import g
 
-	try:
-		import termios
-		platform = 'linux'
-	except:
-		platform = 'win'
+	if g.platform == 'linux' and sys.stdin.isatty():
+		import termios,atexit
+		fd = sys.stdin.fileno()
+		old = termios.tcgetattr(fd)
+		atexit.register(lambda: termios.tcsetattr(fd,termios.TCSADRAIN,old))
 
-	if platform == 'win':
+	try:
 		__import__('mmgen.main_' + what)
-	else:
-		import os,atexit
-		if sys.stdin.isatty():
-			fd = sys.stdin.fileno()
-			old = termios.tcgetattr(fd)
-			def at_exit(): termios.tcsetattr(fd, termios.TCSADRAIN, old)
-			atexit.register(at_exit)
-		try: __import__('mmgen.main_' + what)
-		except KeyboardInterrupt:
-			sys.stderr.write('\nUser interrupt\n')
-		except EOFError:
-			sys.stderr.write('\nEnd of file\n')
-		except Exception as e:
-			if os.getenv('MMGEN_TRACEBACK'):
-				raise
-			else:
-				try: m = '{}'.format(e.args[0])
-				except: m = repr(e.args[0])
-
-				from mmgen.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]
-
-				d[0](d[1],d[2].format(n=type(e).__name__,m=m))
+	except KeyboardInterrupt:
+		sys.stderr.write('\nUser interrupt\n')
+	except EOFError:
+		sys.stderr.write('\nEnd of file\n')
+	except Exception as e:
+		if os.getenv('MMGEN_TRACEBACK'):
+			raise
+		else:
+			try: m = '{}'.format(e.args[0])
+			except: m = repr(e.args[0])
+
+			from mmgen.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]
+
+			d[0](d[1],d[2].format(n=type(e).__name__,m=m))

+ 5 - 4
mmgen/opts.py

@@ -75,10 +75,11 @@ def opt_postproc_initializations():
 	from mmgen.term import set_terminal_vars
 	set_terminal_vars()
 
-	from mmgen.color import init_color
-	init_color(enable_color=g.color,num_colors=('auto',256)[bool(g.force_256_color)])
-
-	if g.color and g.platform == 'win': start_mscolor()
+	if g.color: # MMGEN_DISABLE_COLOR sets this to False
+		from mmgen.color import start_mscolor,init_color
+		if g.platform == 'win':
+			start_mscolor()
+		init_color(num_colors=('auto',256)[bool(g.force_256_color)])
 
 	g.coin = g.coin.upper() # allow user to use lowercase
 	g.dcoin = g.coin

+ 3 - 0
mmgen/term.py

@@ -112,6 +112,7 @@ def _kb_hold_protect_mswin():
 def _get_keypress_mswin(prompt='',immed_chars='',prehold_protect=True,num_chars=None):
 
 	msg_r(prompt)
+	sys.stderr.flush()
 	timeout = float(0.5)
 
 	while True:
@@ -134,12 +135,14 @@ def _get_keypress_mswin(prompt='',immed_chars='',prehold_protect=True,num_chars=
 
 def _get_keypress_mswin_raw(prompt='',immed_chars='',prehold_protect=None,num_chars=None):
 	msg_r(prompt)
+	sys.stderr.flush()
 	ch = msvcrt.getch()
 	if ch == 3: raise KeyboardInterrupt
 	return ch
 
 def _get_keypress_mswin_stub(prompt='',immed_chars='',prehold_protect=None,num_chars=None):
 	msg_r(prompt)
+	sys.stderr.flush()
 	return os.read(0,1)
 
 def _get_terminal_size_linux():

+ 26 - 14
mmgen/util.py

@@ -25,11 +25,26 @@ from hashlib import sha256
 from string import hexdigits,digits
 from mmgen.color import *
 from mmgen.exception import *
+from mmgen.globalvars import g
 
-def msg(s):   g.stderr.write(s + '\n')
-def msg_r(s): g.stderr.write(s)
-def Msg(s):   g.stdout.write(s + '\n')
-def Msg_r(s): g.stdout.write(s)
+if g.platform == 'win':
+	def msg_r(s):
+		try:
+			g.stderr.write(s)
+		except:
+			os.write(2,s.encode())
+	def Msg_r(s):
+		try:
+			g.stdout.write(s)
+		except:
+			os.write(1,s.encode())
+	def msg(s): msg_r(s + '\n')
+	def Msg(s): Msg_r(s + '\n')
+else:
+	def msg_r(s): g.stderr.write(s)
+	def Msg_r(s): g.stdout.write(s)
+	def msg(s):   g.stderr.write(s + '\n')
+	def Msg(s):   g.stdout.write(s + '\n')
 
 def msgred(s): msg(red(s))
 def rmsg(s):   msg(red(s))
@@ -428,15 +443,6 @@ def strip_comments(line):
 def remove_comments(lines):
 	return [m for m in [strip_comments(l) for l in lines] if m != '']
 
-from mmgen.globalvars import g
-
-def start_mscolor():
-	try:
-		import colorama
-		colorama.init(strip=True,convert=True)
-	except:
-		msg('Import of colorama module failed')
-
 def get_hash_params(hash_preset):
 	if hash_preset in g.hash_presets:
 		return g.hash_presets[hash_preset] # N,p,r,buflen
@@ -756,7 +762,13 @@ def my_raw_input(prompt,echo=True,insert_txt='',use_readline=True):
 		reply = input(prompt)
 	else:
 		from getpass import getpass
-		reply = getpass(prompt)
+		if g.platform == 'win':
+			# MSWin hack - getpass('foo') doesn't flush stderr
+			msg_r(prompt.strip()) # getpass('') adds a space
+			sys.stderr.flush()
+			reply = getpass('')
+		else:
+			reply = getpass(prompt)
 
 	kb_hold_protect()
 

+ 1 - 2
scripts/test-release.sh

@@ -1,6 +1,5 @@
 #!/bin/bash
-# Tested on Linux, MinGW-64
-# MinGW's bash 3.1.17 doesn't do ${var^^}
+# Tested on Linux, MSys2
 
 trap 'echo -e "${GREEN}Exiting at user request$RESET"; exit' INT
 

+ 7 - 10
test/test_py_d/common.py

@@ -68,7 +68,7 @@ chksum_pat = r'\b[A-F0-9]{4} [A-F0-9]{4} [A-F0-9]{4} [A-F0-9]{4}\b'
 
 def ok_msg():
 	if opt.profile: return
-	os.write(2,green('\nOK\n').encode() if opt.exact_output or opt.verbose else b' OK\n')
+	sys.stderr.write(green('\nOK\n') if opt.exact_output or opt.verbose else ' OK\n')
 
 def skip(name,reason=None):
 	msg('Skipping {}{}'.format(name,' ({})'.format(reason) if reason else ''))
@@ -81,9 +81,9 @@ def confirm_continue():
 		raise KeyboardInterrupt('Exiting at user request')
 
 def omsg(s):
-	os.write(2,s.encode() + b'\n')
+	sys.stderr.write(s + '\n')
 def omsg_r(s):
-	os.write(2,s.encode())
+	sys.stderr.write(s)
 def imsg(s):
 	if opt.exact_output or opt.verbose: omsg(s)
 def imsg_r(s):
@@ -93,13 +93,10 @@ def iqmsg(s):
 def iqmsg_r(s):
 	if not opt.quiet: omsg_r(s)
 
-if g.platform == 'win':
-	def silence(): pass
-else:
-	devnull_fh = open('/dev/null','w')
-	def silence():
-		if not (opt.verbose or opt.exact_output):
-			g.stdout = g.stderr = devnull_fh
+devnull_fh = open(('/dev/null','null.out')[g.platform == 'win'],'w')
+def silence():
+	if not (opt.verbose or opt.exact_output):
+		g.stdout = g.stderr = devnull_fh
 
 def end_silence():
 	if not (opt.verbose or opt.exact_output):

+ 1 - 1
test/test_py_d/ts_base.py

@@ -67,7 +67,7 @@ class TestSuiteBase(object):
 
 	def skip_for_win(self):
 		if g.platform == 'win':
-			msg("Skipping test '{}': not supported on MinGW platform".format(self.test_name))
+			msg("Skipping test '{}': not supported on MSys2 platform".format(self.test_name))
 			return True
 		else:
 			return False

+ 1 - 1
test/test_py_d/ts_regtest.py

@@ -527,7 +527,7 @@ class TestSuiteRegtest(TestSuiteBase,TestSuiteShared):
 		return t
 
 	def alice_add_label_badaddr1(self):
-		return self.alice_add_label_badaddr(rt_pw,'Invalid coin address for this chain: '+rt_pw)
+		return self.alice_add_label_badaddr(rt_pw,'Invalid coin address for this chain: ')
 
 	def alice_add_label_badaddr2(self):
 		addr = g.proto.pubhash2addr('00'*20,False) # mainnet zero address

+ 1 - 0
test/test_py_d/ts_wallet.py

@@ -147,6 +147,7 @@ class TestSuiteWalletConv(TestSuiteBase,TestSuiteShared):
 									pw        = True )
 
 	def ref_hincog_blkdev_conv_out(self):
+		if self.skip_for_win(): return 'skip'
 		imsg('Creating block device image file')
 		ic_img = joinpath(self.tmpdir,'hincog_blkdev_img')
 		subprocess.check_output(['dd','if=/dev/zero','of='+ic_img,'bs=1K','count=1'],stderr=subprocess.PIPE)