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