Browse Source

`scripts/exec_wrapper.py`: improve traceback handling

- new environment var: EXEC_WRAPPER_TRACEBACK
The MMGen Project 3 years ago
parent
commit
1db6076410
3 changed files with 41 additions and 31 deletions
  1. 34 30
      scripts/exec_wrapper.py
  2. 3 0
      test/test-release.sh
  3. 4 1
      test/test.py

+ 34 - 30
scripts/exec_wrapper.py

@@ -8,10 +8,10 @@ import sys,os,time
 
 
 def exec_wrapper_get_colors():
 def exec_wrapper_get_colors():
 	from collections import namedtuple
 	from collections import namedtuple
-	return namedtuple('colors',['red','green','yellow','blue'])(*[
+	return namedtuple('colors',['red','green','yellow','blue','purple'])(*[
 			(lambda s:s) if os.getenv('MMGEN_DISABLE_COLOR') else
 			(lambda s:s) if os.getenv('MMGEN_DISABLE_COLOR') else
 			(lambda s,n=n:f'\033[{n};1m{s}\033[0m' )
 			(lambda s,n=n:f'\033[{n};1m{s}\033[0m' )
-		for n in (31,32,33,34) ])
+		for n in (31,32,33,34,35) ])
 
 
 def exec_wrapper_init(): # don't change: name is used to test if script is running under exec_wrapper
 def exec_wrapper_init(): # don't change: name is used to test if script is running under exec_wrapper
 
 
@@ -26,42 +26,47 @@ def exec_wrapper_init(): # don't change: name is used to test if script is runni
 	if 'TMUX' in os.environ:
 	if 'TMUX' in os.environ:
 		del os.environ['TMUX']
 		del os.environ['TMUX']
 
 
-	if not os.getenv('EXEC_WRAPPER_NO_TRACEBACK'):
+	if os.getenv('EXEC_WRAPPER_TRACEBACK'):
 		try:
 		try:
 			os.unlink('test.py.err')
 			os.unlink('test.py.err')
 		except:
 		except:
 			pass
 			pass
 
 
-def exec_wrapper_write_traceback(e):
-	import traceback
+def exec_wrapper_write_traceback(e,exit_val):
 
 
-	def gen_output():
-		cwd = os.path.abspath('.')
-		yield 'Traceback (most recent call last):'
-		for e in traceback.extract_tb(sys.exc_info()[2]):
-			yield '  File "{f}", line {l}, in {n}\n    {L}'.format(
-				f = (
-					exec_wrapper_execed_file if e.filename == '<string>' else
-					e.filename.removeprefix(cwd+'/').removeprefix('test/overlay/tree/').replace('_orig.py','.py')
-				),
-				l = '(scrubbed)' if os.getenv('MMGEN_TEST_SUITE_DETERMINISTIC') else e.lineno,
-				n = e.name,
-				L = e.line or 'N/A' )
-
-	tb_lines = list( gen_output() )
 	exc_line = (
 	exc_line = (
 		repr(e) if type(e).__name__ in ('MMGenError','MMGenSystemExit') else
 		repr(e) if type(e).__name__ in ('MMGenError','MMGenSystemExit') else
 		'{}: {}'.format( type(e).__name__, e ))
 		'{}: {}'.format( type(e).__name__, e ))
+	c = exec_wrapper_get_colors()
 
 
-	if 'SystemExit' in exc_line:
-		tb_lines.pop()
+	if os.getenv('EXEC_WRAPPER_TRACEBACK'):
+		import traceback
 
 
-	c = exec_wrapper_get_colors()
-	sys.stdout.write('{}\n{}\n'.format( c.yellow( '\n'.join(tb_lines) ), c.red(exc_line) ))
+		cwd = os.path.abspath('.')
+		def fixup_fn(fn_in):
+			fn = fn_in.removeprefix(cwd+'/').removeprefix('test/overlay/tree/')
+			return (fn.removesuffix('_orig.py') + '.py') if fn.endswith('_orig.py') else fn
+
+		def gen_output():
+			yield 'Traceback (most recent call last):'
+			for e in traceback.extract_tb(sys.exc_info()[2]):
+				yield '  File "{f}", line {l}, in {n}\n    {L}'.format(
+					f = exec_wrapper_execed_file if e.filename == '<string>' else fixup_fn(e.filename),
+					l = '(scrubbed)' if os.getenv('MMGEN_TEST_SUITE_DETERMINISTIC') else e.lineno,
+					n = e.name,
+					L = e.line or 'N/A' )
+
+		tb_lines = list( gen_output() )
+
+		if 'SystemExit' in exc_line:
+			tb_lines.pop()
+
+		sys.stdout.write('{}\n{}\n'.format( c.yellow( '\n'.join(tb_lines) ), c.red(exc_line) ))
 
 
-	if not os.getenv('EXEC_WRAPPER_NO_TRACEBACK'):
 		with open('test.py.err','w') as fp:
 		with open('test.py.err','w') as fp:
 			fp.write('\n'.join(tb_lines + [exc_line]))
 			fp.write('\n'.join(tb_lines + [exc_line]))
+	else:
+		sys.stdout.write( c.purple(('NONZERO_EXIT: ' if exit_val else '') + exc_line) + '\n' )
 
 
 def exec_wrapper_end_msg():
 def exec_wrapper_end_msg():
 	if os.getenv('EXEC_WRAPPER_SPAWN') and not os.getenv('MMGEN_TEST_SUITE_DETERMINISTIC'):
 	if os.getenv('EXEC_WRAPPER_SPAWN') and not os.getenv('MMGEN_TEST_SUITE_DETERMINISTIC'):
@@ -108,17 +113,16 @@ try:
 	with open(exec_wrapper_execed_file) as fp:
 	with open(exec_wrapper_execed_file) as fp:
 		exec(fp.read())
 		exec(fp.read())
 except SystemExit as e:
 except SystemExit as e:
-	if e.code != 0 and not os.getenv('EXEC_WRAPPER_NO_TRACEBACK'):
-		exec_wrapper_write_traceback(e)
+	if e.code != 0:
+		exec_wrapper_write_traceback(e,e.code)
 	else:
 	else:
 		exec_wrapper_tracemalloc_log()
 		exec_wrapper_tracemalloc_log()
 		exec_wrapper_end_msg()
 		exec_wrapper_end_msg()
 	sys.exit(e.code)
 	sys.exit(e.code)
 except Exception as e:
 except Exception as e:
-	if not os.getenv('EXEC_WRAPPER_NO_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)
+	exit_val = e.mmcode if hasattr(e,'mmcode') else e.code if hasattr(e,'code') else 1
+	exec_wrapper_write_traceback(e,exit_val)
+	sys.exit(exit_val)
 
 
 exec_wrapper_tracemalloc_log()
 exec_wrapper_tracemalloc_log()
 exec_wrapper_end_msg()
 exec_wrapper_end_msg()

+ 3 - 0
test/test-release.sh

@@ -155,6 +155,9 @@ do
 		echo   "           -v      Run test/test.py with '--exact-output' and other commands"
 		echo   "           -v      Run test/test.py with '--exact-output' and other commands"
 		echo   "                   with '--verbose' switch"
 		echo   "                   with '--verbose' switch"
 		echo   "           -V      Run test/test.py and other commands with '--verbose' switch"
 		echo   "           -V      Run test/test.py and other commands with '--verbose' switch"
+		echo
+		echo   "  For traceback output and error file support, set the EXEC_WRAPPER_TRACEBACK"
+		echo   "  environment var"
 		exit ;;
 		exit ;;
 	A)  SKIP_ALT_DEP=1
 	A)  SKIP_ALT_DEP=1
 		test_py+=" --no-altcoin"
 		test_py+=" --no-altcoin"

+ 4 - 1
test/test.py

@@ -143,6 +143,9 @@ opts_data = {
 
 
 If no command is given, the whole test suite is run for the currently
 If no command is given, the whole test suite is run for the currently
 specified coin (default BTC).
 specified coin (default BTC).
+
+For traceback output and error file support, set the EXEC_WRAPPER_TRACEBACK
+environment var
 """
 """
 	},
 	},
 	'code': {
 	'code': {
@@ -544,7 +547,7 @@ class TestSuiteRunner(object):
 		env.update(os.environ)
 		env.update(os.environ)
 		if 'exec_wrapper_init' in globals():
 		if 'exec_wrapper_init' in globals():
 			# test.py itself is running under exec_wrapper, so disable traceback file writing for spawned script
 			# test.py itself is running under exec_wrapper, so disable traceback file writing for spawned script
-			env.update({ 'EXEC_WRAPPER_NO_TRACEBACK':'1' }) # Python 3.9: OR the dicts
+			env.update({ 'EXEC_WRAPPER_TRACEBACK':'' }) # Python 3.9: OR the dicts
 
 
 		from test.include.pexpect import MMGenPexpect
 		from test.include.pexpect import MMGenPexpect
 		return MMGenPexpect( args, no_output=no_output, env=env )
 		return MMGenPexpect( args, no_output=no_output, env=env )