Errors.py 5.58 KB
Newer Older
William Stein's avatar
William Stein committed
1 2 3 4 5
#
#   Pyrex - Errors
#

import sys
William Stein's avatar
William Stein committed
6
from Cython.Utils import open_new_file
William Stein's avatar
William Stein committed
7 8 9 10 11


class PyrexError(Exception):
    pass

12 13
class PyrexWarning(Exception):
    pass
William Stein's avatar
William Stein committed
14

15

16
def context(position):
17 18 19
    source = position[0]
    assert not (isinstance(source, unicode) or isinstance(source, str)), (
        "Please replace filename strings with Scanning.FileSourceDescriptor instances %r" % source)
20 21 22 23
    try:
        F = list(source.get_lines())
    except UnicodeDecodeError:
        # file has an encoding problem
24
        s = u"[unprintable code]\n"
25
    else:
26 27 28
        s = u''.join(F[max(0, position[1]-6):position[1]])
        s = u'...\n%s%s^\n' % (s, u' '*(position[2]-1))
    s = u'%s\n%s%s\n' % (u'-'*60, s, u'-'*60)
29
    return s
30

William Stein's avatar
William Stein committed
31 32
class CompileError(PyrexError):
    
33
    def __init__(self, position = None, message = u""):
William Stein's avatar
William Stein committed
34
        self.position = position
35
        self.message_only = message
36
        self.reported = False
37 38
    # Deprecated and withdrawn in 2.6:
    #   self.message = message
William Stein's avatar
William Stein committed
39
        if position:
Craig Citro's avatar
Craig Citro committed
40 41
            pos_str = u"%s:%d:%d: " % (position[0].get_description(),
                                       position[1], position[2])
42
            cont = context(position)
William Stein's avatar
William Stein committed
43
        else:
44 45
            pos_str = u""
            cont = u''
46 47 48 49 50 51
        if position is None:
            Exception.__init__(self, message)
        else:
            Exception.__init__(
                self, u'\nError converting Pyrex file to C:\n%s\n%s%s' % (
                cont, pos_str, message))
William Stein's avatar
William Stein committed
52

53 54 55 56
class CompileWarning(PyrexWarning):
    
    def __init__(self, position = None, message = ""):
        self.position = position
57 58
    # Deprecated and withdrawn in 2.6:
    #   self.message = message
59
        if position:
60
            pos_str = u"%s:%d:%d: " % (position[0].get_description(), position[1], position[2])
61
        else:
62
            pos_str = u""
63 64
        Exception.__init__(self, pos_str + message)

William Stein's avatar
William Stein committed
65 66 67 68 69

class InternalError(Exception):
    # If this is ever raised, there is a bug in the compiler.
    
    def __init__(self, message):
70
        Exception.__init__(self, u"Internal compiler error: %s"
William Stein's avatar
William Stein committed
71
            % message)
72 73 74 75 76 77 78 79 80 81


class CompilerCrash(CompileError):
    # raised when an unexpected exception occurs in a transform
    def __init__(self, pos, context, message, cause, stacktrace=None):
        if message:
            message = u'\n' + message
        else:
            message = u'\n'
        if context:
82
            message = u"Compiler crash in %s%s" % (context, message)
83
        if stacktrace:
Stefan Behnel's avatar
Stefan Behnel committed
84
            import traceback
85
            message += (
Stefan Behnel's avatar
Stefan Behnel committed
86
                u'\n\nCompiler crash traceback from this point on:\n' +
87 88 89 90 91 92 93
                u''.join(traceback.format_tb(stacktrace)))
        if cause:
            if not stacktrace:
                message += u'\n'
            message += u'%s: %s' % (cause.__class__.__name__, cause)
        CompileError.__init__(self, pos, message)

William Stein's avatar
William Stein committed
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

listing_file = None
num_errors = 0
echo_file = None

def open_listing_file(path, echo_to_stderr = 1):
    # Begin a new error listing. If path is None, no file
    # is opened, the error counter is just reset.
    global listing_file, num_errors, echo_file
    if path is not None:
        listing_file = open_new_file(path)
    else:
        listing_file = None
    if echo_to_stderr:
        echo_file = sys.stderr
    else:
        echo_file = None
    num_errors = 0

def close_listing_file():
    global listing_file
    if listing_file:
        listing_file.close()
        listing_file = None

119
def report_error(err):
120 121 122 123 124 125 126
    if error_stack:
        error_stack[-1].append(err)
    else:
        global num_errors
        # See Main.py for why dual reporting occurs. Quick fix for now.
        if err.reported: return
        err.reported = True
127
        line = u"%s\n" % err
128
        if listing_file:
129 130 131
            try: listing_file.write(line)
            except UnicodeEncodeError:
                listing_file.write(line.encode('ASCII', 'replace'))
132
        if echo_file:
133 134 135
            try: echo_file.write(line)
            except UnicodeEncodeError:
                echo_file.write(line.encode('ASCII', 'replace'))
136
        num_errors = num_errors + 1
137 138 139

def error(position, message):
    #print "Errors.error:", repr(position), repr(message) ###
140 141 142
    if position is None:
        raise InternalError(message)
    err = CompileError(position, message)    
143
    #if position is not None: raise Exception(err) # debug
144
    report_error(err)
William Stein's avatar
William Stein committed
145
    return err
146

147 148
LEVEL=1 # warn about all errors level 1 or higher

149 150 151 152 153 154 155 156 157 158 159
def message(position, message, level=1):
    if level < LEVEL:
        return
    warn = CompileWarning(position, message)
    line = "note: %s\n" % warn
    if listing_file:
        listing_file.write(line)
    if echo_file:
        echo_file.write(line)
    return warn

160
def warning(position, message, level=0):
161 162
    if level < LEVEL:
        return
163
    warn = CompileWarning(position, message)
164
    line = "warning: %s\n" % warn
165 166 167 168 169
    if listing_file:
        listing_file.write(line)
    if echo_file:
        echo_file.write(line)
    return warn
170

Robert Bradshaw's avatar
Robert Bradshaw committed
171 172 173 174 175 176 177 178 179 180 181 182 183 184
_warn_once_seen = {}
def warn_once(position, message, level=0):
    if level < LEVEL or message in _warn_once_seen:
        return
    warn = CompileWarning(position, message)
    line = "warning: %s\n" % warn
    if listing_file:
        listing_file.write(line)
    if echo_file:
        echo_file.write(line)
    _warn_once_seen[message] = True
    return warn


185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
# These functions can be used to momentarily suppress errors. 

error_stack = []

def hold_errors():
    error_stack.append([])

def release_errors(ignore=False):
    held_errors = error_stack.pop()
    if not ignore:
        for err in held_errors:
            report_error(err)

def held_errors():
    return error_stack[-1]