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