fix generators after raise-from merge
[cython.git] / Cython / Compiler / Errors.py
1 #
2 #   Pyrex - Errors
3 #
4
5 import sys
6 from Cython.Utils import open_new_file
7 import DebugFlags
8 import Options
9
10
11 class PyrexError(Exception):
12     pass
13
14 class PyrexWarning(Exception):
15     pass
16
17
18 def context(position):
19     source = position[0]
20     assert not (isinstance(source, unicode) or isinstance(source, str)), (
21         "Please replace filename strings with Scanning.FileSourceDescriptor instances %r" % source)
22     try:
23         F = list(source.get_lines())
24     except UnicodeDecodeError:
25         # file has an encoding problem
26         s = u"[unprintable code]\n"
27     else:
28         s = u''.join(F[max(0, position[1]-6):position[1]])
29         s = u'...\n%s%s^\n' % (s, u' '*(position[2]-1))
30     s = u'%s\n%s%s\n' % (u'-'*60, s, u'-'*60)
31     return s
32
33 def format_position(position):
34     if position:
35         return u"%s:%d:%d: " % (position[0].get_description(),
36                                 position[1], position[2])
37     return u''
38
39 def format_error(message, position):
40     if position:
41         pos_str = format_position(position)
42         cont = context(position)
43         message = u'\nError compiling Cython file:\n%s\n%s%s' % (cont, pos_str, message or u'')
44     return message
45
46 class CompileError(PyrexError):
47
48     def __init__(self, position = None, message = u""):
49         self.position = position
50         self.message_only = message
51         self.reported = False
52     # Deprecated and withdrawn in 2.6:
53     #   self.message = message
54         Exception.__init__(self, format_error(message, position))
55
56 class CompileWarning(PyrexWarning):
57
58     def __init__(self, position = None, message = ""):
59         self.position = position
60     # Deprecated and withdrawn in 2.6:
61     #   self.message = message
62         Exception.__init__(self, format_position(position) + message)
63
64 class InternalError(Exception):
65     # If this is ever raised, there is a bug in the compiler.
66
67     def __init__(self, message):
68         self.message_only = message
69         Exception.__init__(self, u"Internal compiler error: %s"
70             % message)
71
72 class AbortError(Exception):
73     # Throw this to stop the compilation immediately.
74
75     def __init__(self, message):
76         self.message_only = message
77         Exception.__init__(self, u"Abort error: %s" % message)
78
79 class CompilerCrash(CompileError):
80     # raised when an unexpected exception occurs in a transform
81     def __init__(self, pos, context, message, cause, stacktrace=None):
82         if message:
83             message = u'\n' + message
84         else:
85             message = u'\n'
86         self.message_only = message
87         if context:
88             message = u"Compiler crash in %s%s" % (context, message)
89         if stacktrace:
90             import traceback
91             message += (
92                 u'\n\nCompiler crash traceback from this point on:\n' +
93                 u''.join(traceback.format_tb(stacktrace)))
94         if cause:
95             if not stacktrace:
96                 message += u'\n'
97             message += u'%s: %s' % (cause.__class__.__name__, cause)
98         CompileError.__init__(self, pos, message)
99
100 class NoElementTreeInstalledException(PyrexError):
101     """raised when the user enabled options.gdb_debug but no ElementTree
102     implementation was found
103     """
104
105 listing_file = None
106 num_errors = 0
107 echo_file = None
108
109 def open_listing_file(path, echo_to_stderr = 1):
110     # Begin a new error listing. If path is None, no file
111     # is opened, the error counter is just reset.
112     global listing_file, num_errors, echo_file
113     if path is not None:
114         listing_file = open_new_file(path)
115     else:
116         listing_file = None
117     if echo_to_stderr:
118         echo_file = sys.stderr
119     else:
120         echo_file = None
121     num_errors = 0
122
123 def close_listing_file():
124     global listing_file
125     if listing_file:
126         listing_file.close()
127         listing_file = None
128
129 def report_error(err):
130     if error_stack:
131         error_stack[-1].append(err)
132     else:
133         global num_errors
134         # See Main.py for why dual reporting occurs. Quick fix for now.
135         if err.reported: return
136         err.reported = True
137         try: line = u"%s\n" % err
138         except UnicodeEncodeError:
139             # Python <= 2.5 does this for non-ASCII Unicode exceptions
140             line = format_error(getattr(err, 'message_only', "[unprintable exception message]"),
141                                 getattr(err, 'position', None)) + u'\n'
142         if listing_file:
143             try: listing_file.write(line)
144             except UnicodeEncodeError:
145                 listing_file.write(line.encode('ASCII', 'replace'))
146         if echo_file:
147             try: echo_file.write(line)
148             except UnicodeEncodeError:
149                 echo_file.write(line.encode('ASCII', 'replace'))
150         num_errors = num_errors + 1
151         if Options.fast_fail:
152             raise AbortError, "fatal errors"
153
154 def error(position, message):
155     #print "Errors.error:", repr(position), repr(message) ###
156     if position is None:
157         raise InternalError(message)
158     err = CompileError(position, message)
159     if DebugFlags.debug_exception_on_error: raise Exception(err) # debug
160     report_error(err)
161     return err
162
163 LEVEL=1 # warn about all errors level 1 or higher
164
165 def message(position, message, level=1):
166     if level < LEVEL:
167         return
168     warn = CompileWarning(position, message)
169     line = "note: %s\n" % warn
170     if listing_file:
171         listing_file.write(line)
172     if echo_file:
173         echo_file.write(line)
174     return warn
175
176 def warning(position, message, level=0):
177     if level < LEVEL:
178         return
179     if Options.warning_errors and position:
180         return error(position, message)
181     warn = CompileWarning(position, message)
182     line = "warning: %s\n" % warn
183     if listing_file:
184         listing_file.write(line)
185     if echo_file:
186         echo_file.write(line)
187     return warn
188
189 _warn_once_seen = {}
190 def warn_once(position, message, level=0):
191     if level < LEVEL or message in _warn_once_seen:
192         return
193     warn = CompileWarning(position, message)
194     line = "warning: %s\n" % warn
195     if listing_file:
196         listing_file.write(line)
197     if echo_file:
198         echo_file.write(line)
199     _warn_once_seen[message] = True
200     return warn
201
202
203 # These functions can be used to momentarily suppress errors.
204
205 error_stack = []
206
207 def hold_errors():
208     error_stack.append([])
209
210 def release_errors(ignore=False):
211     held_errors = error_stack.pop()
212     if not ignore:
213         for err in held_errors:
214             report_error(err)
215
216 def held_errors():
217     return error_stack[-1]
218
219
220 # this module needs a redesign to support parallel cythonisation, but
221 # for now, the following works at least in sequential compiler runs
222
223 def reset():
224     _warn_once_seen.clear()
225     del error_stack[:]