-devel mergeback
[cython.git] / Cython / Compiler / Errors.py
1 #
2 #   Pyrex - Errors
3 #
4
5 import sys
6 from Cython.Utils import open_new_file
7
8
9 class PyrexError(Exception):
10     pass
11
12 class PyrexWarning(Exception):
13     pass
14
15
16 def context(position):
17     source = position[0]
18     assert not (isinstance(source, unicode) or isinstance(source, str)), (
19         "Please replace filename strings with Scanning.FileSourceDescriptor instances %r" % source)
20     try:
21         F = list(source.get_lines())
22     except UnicodeDecodeError:
23         # file has an encoding problem
24         s = u"[unprintable code]\n"
25     else:
26         s = u''.join(F[max(0, position[1]-6):position[1]])
27         s = u'...\n%s%s^\n' % (s, u' '*(position[2]-1))
28     s = u'%s\n%s%s\n' % (u'-'*60, s, u'-'*60)
29     return s
30
31 class CompileError(PyrexError):
32     
33     def __init__(self, position = None, message = u""):
34         self.position = position
35         self.message_only = message
36         self.reported = False
37     # Deprecated and withdrawn in 2.6:
38     #   self.message = message
39         if position:
40             pos_str = u"%s:%d:%d: " % (position[0].get_description(), position[1], position[2])
41             cont = context(position)
42         else:
43             pos_str = u""
44             cont = u''
45         Exception.__init__(self, u'\nError converting Pyrex file to C:\n%s\n%s%s' % (
46             cont, pos_str, message))
47
48 class CompileWarning(PyrexWarning):
49     
50     def __init__(self, position = None, message = ""):
51         self.position = position
52     # Deprecated and withdrawn in 2.6:
53     #   self.message = message
54         if position:
55             pos_str = u"%s:%d:%d: " % (position[0].get_description(), position[1], position[2])
56         else:
57             pos_str = u""
58         Exception.__init__(self, pos_str + message)
59
60
61 class InternalError(Exception):
62     # If this is ever raised, there is a bug in the compiler.
63     
64     def __init__(self, message):
65         Exception.__init__(self, u"Internal compiler error: %s"
66             % message)
67
68
69 class CompilerCrash(CompileError):
70     # raised when an unexpected exception occurs in a transform
71     def __init__(self, pos, context, message, cause, stacktrace=None):
72         if message:
73             message = u'\n' + message
74         else:
75             message = u'\n'
76         if context:
77             message = u"Compiler crash in %s%s" % (context, message)
78         if stacktrace:
79             import traceback
80             message += (
81                 u'\n\nCompiler crash traceback from this point on:\n' +
82                 u''.join(traceback.format_tb(stacktrace)))
83         if cause:
84             if not stacktrace:
85                 message += u'\n'
86             message += u'%s: %s' % (cause.__class__.__name__, cause)
87         CompileError.__init__(self, pos, message)
88
89
90 listing_file = None
91 num_errors = 0
92 echo_file = None
93
94 def open_listing_file(path, echo_to_stderr = 1):
95     # Begin a new error listing. If path is None, no file
96     # is opened, the error counter is just reset.
97     global listing_file, num_errors, echo_file
98     if path is not None:
99         listing_file = open_new_file(path)
100     else:
101         listing_file = None
102     if echo_to_stderr:
103         echo_file = sys.stderr
104     else:
105         echo_file = None
106     num_errors = 0
107
108 def close_listing_file():
109     global listing_file
110     if listing_file:
111         listing_file.close()
112         listing_file = None
113
114 def report_error(err):
115     if error_stack:
116         error_stack[-1].append(err)
117     else:
118         global num_errors
119         # See Main.py for why dual reporting occurs. Quick fix for now.
120         if err.reported: return
121         err.reported = True
122         line = u"%s\n" % err
123         if listing_file:
124             try: listing_file.write(line)
125             except UnicodeEncodeError:
126                 listing_file.write(line.encode('ASCII', 'replace'))
127         if echo_file:
128             try: echo_file.write(line)
129             except UnicodeEncodeError:
130                 echo_file.write(line.encode('ASCII', 'replace'))
131         num_errors = num_errors + 1
132
133 def error(position, message):
134     #print "Errors.error:", repr(position), repr(message) ###
135     err = CompileError(position, message)
136     #if position is not None: raise Exception(err) # debug
137     report_error(err)
138     return err
139
140 LEVEL=1 # warn about all errors level 1 or higher
141
142 def warning(position, message, level=0):
143     if level < LEVEL:
144         return
145     warn = CompileWarning(position, message)
146     line = "warning: %s\n" % warn
147     if listing_file:
148         listing_file.write(line)
149     if echo_file:
150         echo_file.write(line)
151     return warn
152
153 _warn_once_seen = {}
154 def warn_once(position, message, level=0):
155     if level < LEVEL or message in _warn_once_seen:
156         return
157     warn = CompileWarning(position, message)
158     line = "warning: %s\n" % warn
159     if listing_file:
160         listing_file.write(line)
161     if echo_file:
162         echo_file.write(line)
163     _warn_once_seen[message] = True
164     return warn
165
166
167 # These functions can be used to momentarily suppress errors. 
168
169 error_stack = []
170
171 def hold_errors():
172     error_stack.append([])
173
174 def release_errors(ignore=False):
175     held_errors = error_stack.pop()
176     if not ignore:
177         for err in held_errors:
178             report_error(err)
179
180 def held_errors():
181     return error_stack[-1]