cleanup
[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 = "[unprintable code]\n"
25     else:
26         s =''.join(F[max(0, position[1]-6):position[1]])
27         s = '...\n' + s + ' '*(position[2]-1) + '^\n'
28     s = '-'*60 + '\n' + s + '-'*60 + '\n'
29     return s
30     
31 class CompileError(PyrexError):
32     
33     def __init__(self, position = None, message = ""):
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 = "%s:%d:%d: " % (position[0].get_description(), position[1], position[2])
41             cont = context(position)
42         else:
43             pos_str = ""
44             cont = ''
45         Exception.__init__(self, '\nError converting Pyrex file to C:\n' + cont + '\n' + pos_str + message )
46
47 class CompileWarning(PyrexWarning):
48     
49     def __init__(self, position = None, message = ""):
50         self.position = position
51     # Deprecated and withdrawn in 2.6:
52     #   self.message = message
53         if position:
54             pos_str = "%s:%d:%d: " % (position[0].get_description(), position[1], position[2])
55         else:
56             pos_str = ""
57         Exception.__init__(self, pos_str + message)
58
59
60 class InternalError(Exception):
61     # If this is ever raised, there is a bug in the compiler.
62     
63     def __init__(self, message):
64         Exception.__init__(self, "Internal compiler error: %s"
65             % message)
66
67
68 class CompilerCrash(CompileError):
69     # raised when an unexpected exception occurs in a transform
70     def __init__(self, pos, context, message, cause, stacktrace=None):
71         if message:
72             message = u'\n' + message
73         else:
74             message = u'\n'
75         if context:
76             message = "Compiler crash in " + context + message
77         if stacktrace:
78             import traceback
79             message += (
80                 u'\n\nCompiler crash traceback from this point on:\n' +
81                 u''.join(traceback.format_tb(stacktrace)))
82         if cause:
83             if not stacktrace:
84                 message += u'\n'
85             message += u'%s: %s' % (cause.__class__.__name__, cause)
86         CompileError.__init__(self, pos, message)
87
88
89 listing_file = None
90 num_errors = 0
91 echo_file = None
92
93 def open_listing_file(path, echo_to_stderr = 1):
94     # Begin a new error listing. If path is None, no file
95     # is opened, the error counter is just reset.
96     global listing_file, num_errors, echo_file
97     if path is not None:
98         listing_file = open_new_file(path)
99     else:
100         listing_file = None
101     if echo_to_stderr:
102         echo_file = sys.stderr
103     else:
104         echo_file = None
105     num_errors = 0
106
107 def close_listing_file():
108     global listing_file
109     if listing_file:
110         listing_file.close()
111         listing_file = None
112
113 def report_error(err):
114     if error_stack:
115         error_stack[-1].append(err)
116     else:
117         global num_errors
118         # See Main.py for why dual reporting occurs. Quick fix for now.
119         if err.reported: return
120         err.reported = True
121         line = "%s\n" % err
122         if listing_file:
123             listing_file.write(line)
124         if echo_file:
125             echo_file.write(line)
126         num_errors = num_errors + 1
127
128 def error(position, message):
129     #print "Errors.error:", repr(position), repr(message) ###
130     err = CompileError(position, message)
131     #if position is not None: raise Exception(err) # debug
132     report_error(err)
133     return err
134
135 LEVEL=1 # warn about all errors level 1 or higher
136
137 def warning(position, message, level=0):
138     if level < LEVEL:
139         return
140     warn = CompileWarning(position, message)
141     line = "warning: %s\n" % warn
142     if listing_file:
143         listing_file.write(line)
144     if echo_file:
145         echo_file.write(line)
146     return warn
147
148 # These functions can be used to momentarily suppress errors. 
149
150 error_stack = []
151
152 def hold_errors():
153     error_stack.append([])
154
155 def release_errors(ignore=False):
156     held_errors = error_stack.pop()
157     if not ignore:
158         for err in held_errors:
159             report_error(err)
160
161 def held_errors():
162     return error_stack[-1]