OLD | NEW |
(Empty) | |
| 1 # |
| 2 # Errors |
| 3 # |
| 4 |
| 5 import sys |
| 6 from Cython.Utils import open_new_file |
| 7 import DebugFlags |
| 8 import Options |
| 9 |
| 10 |
| 11 class PyrexError(Exception): |
| 12 pass |
| 13 |
| 14 class PyrexWarning(Exception): |
| 15 pass |
| 16 |
| 17 |
| 18 def context(position): |
| 19 source = position[0] |
| 20 assert not (isinstance(source, unicode) or isinstance(source, str)), ( |
| 21 "Please replace filename strings with Scanning.FileSourceDescriptor inst
ances %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 |
| 33 def 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 |
| 39 def 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 |
| 46 class 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 |
| 63 class 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 |
| 71 class 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 |
| 79 class 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 |
| 86 class 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 |
| 110 class NoElementTreeInstalledException(PyrexError): |
| 111 """raised when the user enabled options.gdb_debug but no ElementTree |
| 112 implementation was found |
| 113 """ |
| 114 |
| 115 listing_file = None |
| 116 num_errors = 0 |
| 117 echo_file = None |
| 118 |
| 119 def 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 |
| 133 def close_listing_file(): |
| 134 global listing_file |
| 135 if listing_file: |
| 136 listing_file.close() |
| 137 listing_file = None |
| 138 |
| 139 def 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 excep
tion 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 |
| 164 def 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 |
| 173 LEVEL=1 # warn about all errors level 1 or higher |
| 174 |
| 175 def 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 |
| 186 def 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 = {} |
| 200 def 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 |
| 215 error_stack = [] |
| 216 |
| 217 def hold_errors(): |
| 218 error_stack.append([]) |
| 219 |
| 220 def 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 |
| 226 def 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 |
| 233 def reset(): |
| 234 _warn_once_seen.clear() |
| 235 del error_stack[:] |
OLD | NEW |