diff options
-rw-r--r-- | src/CHANGES.txt | 3 | ||||
-rw-r--r-- | src/engine/SCons/Script/SConscript.py | 58 | ||||
-rw-r--r-- | test/SConscript.py | 46 | ||||
-rw-r--r-- | test/errors.py | 13 | ||||
-rw-r--r-- | test/exceptions.py | 7 |
5 files changed, 63 insertions, 64 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 3b63109..4c23967 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -270,6 +270,9 @@ RELEASE 0.97 - XXX - Fix hard-coding of JDK path names in various Java tests. + - Handle Python stack traces consistently (stop at the SConscript stack + frame, by default) even if the Python source code isn't available. + From Levi Stephen: - Allow $JARCHDIR to be expanded to other construction variables. diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 6adb399..4bc8bd3 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -169,6 +169,9 @@ def Return(*vars): else: stack[-1].retval = tuple(retval) + +stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) + def _SConscript(fs, *files, **kw): top = fs.Top sd = fs.SConstruct_dir.rdir() @@ -236,12 +239,14 @@ def _SConscript(fs, *files, **kw): # directory can be easily imported. sys.path = [ f.dir.get_abspath() ] + sys.path - # This is the magic line that actually reads up and - # executes the stuff in the SConscript file. We - # look for the "exec _file_ " from the beginning - # of this line to find the right stack frame (the - # next one) describing the SConscript file and line - # number that creates a node. + # This is the magic line that actually reads up + # and executes the stuff in the SConscript file. + # The locals for this frame contain the special + # bottom-of-the-stack marker so that any + # exceptions that occur when processing this + # SConscript can base the printed frames at this + # level and not show SCons internals as well. + stack[-1].globals.update({stack_bottom:1}) exec _file_ in stack[-1].globals else: SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, @@ -268,51 +273,38 @@ def _SConscript(fs, *files, **kw): else: return tuple(results) -def is_our_exec_statement(line): - return not line is None and line[:12] == "exec _file_ " - def SConscript_exception(file=sys.stderr): """Print an exception stack trace just for the SConscript file(s). This will show users who have Python errors where the problem is, without cluttering the output with all of the internal calls leading up to where we exec the SConscript.""" exc_type, exc_value, exc_tb = sys.exc_info() - stack = traceback.extract_tb(exc_tb) - last_text = "" - found = 0 - i = 0 - for frame in stack: - if is_our_exec_statement(last_text): - found = 1 - break - i = i + 1 - last_text = frame[3] - if not found: + tb = exc_tb + while tb and not tb.tb_frame.f_locals.has_key(stack_bottom): + tb = tb.tb_next + if not tb: # We did not find our exec statement, so this was actually a bug # in SCons itself. Show the whole stack. - i = 0 + tb = exc_tb + stack = traceback.extract_tb(tb) type = str(exc_type) if type[:11] == "exceptions.": type = type[11:] file.write('%s: %s:\n' % (type, exc_value)) - for fname, line, func, text in stack[i:]: + for fname, line, func, text in stack: file.write(' File "%s", line %d:\n' % (fname, line)) file.write(' %s\n' % text) def annotate(node): """Annotate a node with the stack frame describing the SConscript file and line number that created it.""" - stack = traceback.extract_stack() - last_text = "" - for frame in stack: - # If the script text of the previous frame begins with the - # magic "exec _file_ " string, then this frame describes the - # SConscript file and line number that caused this node to be - # created. Record the tuple and carry on. - if is_our_exec_statement(last_text): - node.creator = frame - return - last_text = frame[3] + tb = exc_tb = sys.exc_info()[2] + while tb and not tb.tb_frame.f_locals.has_key(stack_bottom): + tb = tb.tb_next + if not tb: + # We did not find any exec of an SConscript file: what?! + raise InternalError, "could not find SConscript stack frame" + node.creator = traceback.extract_stack(tb)[0] # The following line would cause each Node to be annotated using the # above function. Unfortunately, this is a *huge* performance hit, so diff --git a/test/SConscript.py b/test/SConscript.py index 4aaed3b..08e41a6 100644 --- a/test/SConscript.py +++ b/test/SConscript.py @@ -43,26 +43,26 @@ SConscript('SConscript') x1 = "SConstruct x1" x2 = "SConstruct x2" x3,x4 = SConscript('SConscript1', "x1 x2") -assert x3 == "SConscript1 x3" -assert x4 == "SConscript1 x4" +assert x3 == "SConscript1 x3", x3 +assert x4 == "SConscript1 x4", x4 (x3,x4) = SConscript('SConscript2', ["x1","x2"]) -assert x3 == "SConscript2 x3" -assert x4 == "SConscript2 x4" +assert x3 == "SConscript2 x3", x3 +assert x4 == "SConscript2 x4", x4 Export("x1 x2") SConscript('SConscript3') Import("x1 x2") -assert x1 == "SConscript3 x1" -assert x2 == "SConscript3 x2" +assert x1 == "SConscript3 x1", x1 +assert x2 == "SConscript3 x2", x2 x1 = "SConstruct x1" x2 = "SConstruct x2" Export("x1","x2") SConscript('SConscript4') Import("x1"," x2") -assert x1 == "SConscript4 x1" -assert x2 == "SConscript4 x2" +assert x1 == "SConscript4 x1", x1 +assert x2 == "SConscript4 x2", x2 subdir = Dir('subdir') script = File('SConscript', subdir) @@ -75,7 +75,7 @@ import UserList x7 = "SConstruct x7" x8 = "SConstruct x8" x9 = SConscript('SConscript6', UserList.UserList(["x7", "x8"])) -assert x9 == "SConscript6 x9" +assert x9 == "SConscript6 x9", x9 SConscript('SConscript7') """) @@ -91,8 +91,8 @@ print "SConscript " + os.getcwd() test.write('SConscript1', """ Import("x1 x2") -assert x1 == "SConstruct x1" -assert x2 == "SConstruct x2" +assert x1 == "SConstruct x1", x1 +assert x2 == "SConstruct x2", x2 x3 = "SConscript1 x3" x4 = "SConscript1 x4" @@ -102,8 +102,8 @@ Return("x3 x4") test.write('SConscript2', """\ Import("x1","x2") -assert x1 == "SConstruct x1" -assert x2 == "SConstruct x2" +assert x1 == "SConstruct x1", x1 +assert x2 == "SConstruct x2", x2 x3 = "SConscript2 x3" x4 = "SConscript2 x4" Return("x3","x4") @@ -112,15 +112,15 @@ Return("x3","x4") test.write('SConscript3', """\ Import("x1 x2") -assert x1 == "SConstruct x1" -assert x2 == "SConstruct x2" +assert x1 == "SConstruct x1", x1 +assert x2 == "SConstruct x2", x2 x1 = "SConscript3 x1" x2 = "SConscript3 x2" x5 = SConscript('SConscript31', "x1") Import("x6") -assert x5 == "SConscript31 x5" -assert x6 == "SConscript31 x6" +assert x5 == "SConscript31 x5", x5 +assert x6 == "SConscript31 x6", x6 Export("x1 x2") """) @@ -128,8 +128,8 @@ Export("x1 x2") test.write('SConscript31', """\ Import("x1 x2") -assert x1 == "SConscript3 x1" -assert x2 == "SConstruct x2" +assert x1 == "SConscript3 x1", x1 +assert x2 == "SConstruct x2", x2 x5 = "SConscript31 x5" x6 = "SConscript31 x6" Export("x6") @@ -139,8 +139,8 @@ Return("x5") test.write('SConscript4', """\ Import("x1", "x2") -assert x1 == "SConstruct x1" -assert x2 == "SConstruct x2" +assert x1 == "SConstruct x1", x1 +assert x2 == "SConstruct x2", x2 x1 = "SConscript4 x1" x2 = "SConscript4 x2" Export("x1", "x2") @@ -165,8 +165,8 @@ A = Action("A") test.write('SConscript6', """\ Import("x7 x8") -assert x7 == "SConstruct x7" -assert x8 == "SConstruct x8" +assert x7 == "SConstruct x7", x7 +assert x8 == "SConstruct x8", x8 x9 = "SConscript6 x9" Return("x9") """) diff --git a/test/errors.py b/test/errors.py index 1652f88..571bffc 100644 --- a/test/errors.py +++ b/test/errors.py @@ -56,15 +56,18 @@ env.foo('foo.out', 'foo.in') env.exit('exit.out', 'exit.in') """) +# print_exception doesn't always show a source line if the source file +# no longer exists or that line in the source file no longer exists, +# so make sure the proper variations are supported in the following +# regexp. stderr = """scons: \*\*\* \[exit.out\] Exception Traceback \((most recent call|innermost) last\): - File ".+", line \d+, in \S+ +( File ".+", line \d+, in \S+ [^\n]+ - File ".+", line \d+, in \S+ +)*( File ".+", line \d+, in \S+ +)*( File ".+", line \d+, in \S+ [^\n]+ - File ".+", line \d+, in \S+ - [^\n]+ -\S.+ +)*\S.+ """ test.run(arguments='foo.out exit.out', stderr=stderr, status=2) diff --git a/test/exceptions.py b/test/exceptions.py index 7b45a4b..584d4f1 100644 --- a/test/exceptions.py +++ b/test/exceptions.py @@ -46,11 +46,12 @@ test.write('foo.in', "foo.in\n") expected_stderr = """scons: \*\*\* \[foo.out\] Exception Traceback \((most recent call|innermost) last\): - File ".+", line \d+, in \S+ +( File ".+", line \d+, in \S+ [^\n]+ - File ".+", line \d+, in \S+ +)*( File ".+", line \d+, in \S+ +)*( File ".+", line \d+, in \S+ [^\n]+ - File "SConstruct", line 3, in func +)* File "SConstruct", line 3, in func raise "func exception" func exception """ |