• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Status: being written afresh by Vladimir Prus
2
3# Copyright 2007 Vladimir Prus
4# Distributed under the Boost Software License, Version 1.0.
5# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
6
7# This file is supposed to implement error reporting for Boost.Build.
8# Experience with jam version has shown that printing full backtrace
9# on each error is buffling. Further, for errors printed after parsing --
10# during target building, the stacktrace does not even mention what
11# target is being built.
12
13# This module implements explicit contexts -- where other code can
14# communicate which projects/targets are being built, and error
15# messages will show those contexts. For programming errors,
16# Python assertions are to be used.
17
18import bjam
19import traceback
20import sys
21
22def format(message, prefix=""):
23    parts = str(message).split("\n")
24    return "\n".join(prefix+p for p in parts)
25
26
27class Context:
28
29    def __init__(self, message, nested=None):
30        self.message_ = message
31        self.nested_ = nested
32
33    def report(self, indent=""):
34        print indent + "    -", self.message_
35        if self.nested_:
36            print indent + "        declared at:"
37            for n in self.nested_:
38                n.report(indent + "    ")
39
40class JamfileContext:
41
42    def __init__(self):
43        raw = bjam.backtrace()
44        self.raw_ = raw
45
46    def report(self, indent=""):
47        for r in self.raw_:
48            print indent + "    - %s:%s" % (r[0], r[1])
49
50class ExceptionWithUserContext(Exception):
51
52    def __init__(self, message, context,
53                 original_exception=None, original_tb=None, stack=None):
54        Exception.__init__(self, message)
55        self.context_ = context
56        self.original_exception_ = original_exception
57        self.original_tb_ = original_tb
58        self.stack_ = stack
59
60    def report(self):
61        print "error:", self.args[0]
62        if self.original_exception_:
63            print format(str(self.original_exception_), "    ")
64        print
65        print "    error context (most recent first):"
66        for c in self.context_[::-1]:
67            c.report()
68        print
69        if "--stacktrace" in bjam.variable("ARGV"):
70            if self.original_tb_:
71                traceback.print_tb(self.original_tb_)
72            elif self.stack_:
73                for l in traceback.format_list(self.stack_):
74                    print l,
75        else:
76            print "    use the '--stacktrace' option to get Python stacktrace"
77        print
78
79def user_error_checkpoint(callable):
80    def wrapper(self, *args):
81        errors = self.manager().errors()
82        try:
83            return callable(self, *args)
84        except ExceptionWithUserContext, e:
85            raise
86        except Exception, e:
87            errors.handle_stray_exception(e)
88        finally:
89            errors.pop_user_context()
90
91    return wrapper
92
93class Errors:
94
95    def __init__(self):
96        self.contexts_ = []
97        self._count = 0
98
99    def count(self):
100        return self._count
101
102    def push_user_context(self, message, nested=None):
103        self.contexts_.append(Context(message, nested))
104
105    def pop_user_context(self):
106        del self.contexts_[-1]
107
108    def push_jamfile_context(self):
109        self.contexts_.append(JamfileContext())
110
111    def pop_jamfile_context(self):
112        del self.contexts_[-1]
113
114    def capture_user_context(self):
115        return self.contexts_[:]
116
117    def handle_stray_exception(self, e):
118        raise ExceptionWithUserContext("unexpected exception", self.contexts_[:],
119                                       e, sys.exc_info()[2])
120    def __call__(self, message):
121        self._count = self._count + 1
122        raise ExceptionWithUserContext(message, self.contexts_[:],
123                                       stack=traceback.extract_stack())
124
125
126def nearest_user_location():
127    """
128    Returns:
129        tuple: the filename and line number of the nearest user location
130    """
131    bt = bjam.backtrace()
132    if not bt:
133        return None
134    last = bt[-1]
135    return last[0], last[1]
136