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