cc5e2392aa6599bac95c195225253b2fa577e2de
[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 def format_position(position):
32     if position:
33         return u"%s:%d:%d: " % (position[0].get_description(),
34                                 position[1], position[2])
35     return u''
36
37 def format_error(message, position):
38     if position:
39         pos_str = format_position(position)
40         cont = context(position)
41         message = u'\nError converting Pyrex file to C:\n%s\n%s%s' % (cont, pos_str, message or u'')
42     return message
43
44 class CompileError(PyrexError):
45     
46     def __init__(self, position = None, message = u""):
47         self.position = position
48         self.message_only = message
49         self.reported = False
50     # Deprecated and withdrawn in 2.6:
51     #   self.message = message
52         Exception.__init__(self, format_error(message, position))
53
54 class CompileWarning(PyrexWarning):
55     
56     def __init__(self, position = None, message = ""):
57         self.position = position
58     # Deprecated and withdrawn in 2.6:
59     #   self.message = message
60         Exception.__init__(self, format_position(position) + message)
61
62
63 class InternalError(Exception):
64     # If this is ever raised, there is a bug in the compiler.
65     
66     def __init__(self, message):
67         self.message_only = message
68         Exception.__init__(self, u"Internal compiler error: %s"
69             % message)
70
71
72 class CompilerCrash(CompileError):
73     # raised when an unexpected exception occurs in a transform
74     def __init__(self, pos, context, message, cause, stacktrace=None):
75         if message:
76             message = u'\n' + message
77         else:
78             message = u'\n'
79         self.message_only = message
80         if context:
81             message = u"Compiler crash in %s%s" % (context, message)
82         if stacktrace:
83             import traceback
84             message += (
85                 u'\n\nCompiler crash traceback from this point on:\n' +
86                 u''.join(traceback.format_tb(stacktrace)))
87         if cause:
88             if not stacktrace:
89                 message += u'\n'
90             message += u'%s: %s' % (cause.__class__.__name__, cause)
91         CompileError.__init__(self, pos, message)
92
93
94 listing_file = None
95 num_errors = 0
96 echo_file = None
97
98 def open_listing_file(path, echo_to_stderr = 1):
99     # Begin a new error listing. If path is None, no file
100     # is opened, the error counter is just reset.
101     global listing_file, num_errors, echo_file
102     if path is not None:
103         listing_file = open_new_file(path)
104     else:
105         listing_file = None
106     if echo_to_stderr:
107         echo_file = sys.stderr
108     else:
109         echo_file = None
110     num_errors = 0
111
112 def close_listing_file():
113     global listing_file
114     if listing_file:
115         listing_file.close()
116         listing_file = None
117
118 def report_error(err):
119     if error_stack:
120         error_stack[-1].append(err)
121     else:
122         global num_errors
123         # See Main.py for why dual reporting occurs. Quick fix for now.
124         if err.reported: return
125         err.reported = True
126         try: line = u"%s\n" % err
127         except UnicodeEncodeError:
128             # Python <= 2.5 does this for non-ASCII Unicode exceptions
129             line = format_error(getattr(err, 'message_only', "[unprintable exception message]"),
130                                 getattr(err, 'position', None)) + u'\n'
131         if listing_file:
132             try: listing_file.write(line)
133             except UnicodeEncodeError:
134                 listing_file.write(line.encode('ASCII', 'replace'))
135         if echo_file:
136             try: echo_file.write(line)
137             except UnicodeEncodeError:
138                 echo_file.write(line.encode('ASCII', 'replace'))
139         num_errors = num_errors + 1
140
141 def error(position, message):
142     #print "Errors.error:", repr(position), repr(message) ###
143     if position is None:
144         raise InternalError(message)
145     err = CompileError(position, message)    
146     #if position is not None: raise Exception(err) # debug
147     report_error(err)
148     return err
149
150 LEVEL=1 # warn about all errors level 1 or higher
151
152 def message(position, message, level=1):
153     if level < LEVEL:
154         return
155     warn = CompileWarning(position, message)
156     line = "note: %s\n" % warn
157     if listing_file:
158         listing_file.write(line)
159     if echo_file:
160         echo_file.write(line)
161     return warn
162
163 def warning(position, message, level=0):
164     if level < LEVEL:
165         return
166     warn = CompileWarning(position, message)
167     line = "warning: %s\n" % warn
168     if listing_file:
169         listing_file.write(line)
170     if echo_file:
171         echo_file.write(line)
172     return warn
173
174 _warn_once_seen = {}
175 def warn_once(position, message, level=0):
176     if level < LEVEL or message in _warn_once_seen:
177         return
178     warn = CompileWarning(position, message)
179     line = "warning: %s\n" % warn
180     if listing_file:
181         listing_file.write(line)
182     if echo_file:
183         echo_file.write(line)
184     _warn_once_seen[message] = True
185     return warn
186
187
188 # These functions can be used to momentarily suppress errors. 
189
190 error_stack = []
191
192 def hold_errors():
193     error_stack.append([])
194
195 def release_errors(ignore=False):
196     held_errors = error_stack.pop()
197     if not ignore:
198         for err in held_errors:
199             report_error(err)
200
201 def held_errors():
202     return error_stack[-1]