PageRenderTime 248ms CodeModel.GetById 80ms app.highlight 107ms RepoModel.GetById 54ms app.codeStats 0ms

/pypy/interpreter/error.py

https://bitbucket.org/dac_io/pypy
Python | 469 lines | 440 code | 13 blank | 16 comment | 19 complexity | dda8ed2747874a74076a2a4767ece226 MD5 | raw file
  1import os, sys
  2from pypy.rlib import jit
  3from pypy.rlib.objectmodel import we_are_translated
  4from errno import EINTR
  5
  6AUTO_DEBUG = os.getenv('PYPY_DEBUG')
  7RECORD_INTERPLEVEL_TRACEBACK = True
  8
  9
 10class OperationError(Exception):
 11    """Interpreter-level exception that signals an exception that should be
 12    sent to the application level.
 13
 14    OperationError instances have three attributes (and no .args),
 15    w_type, _w_value and _application_traceback, which contain the wrapped
 16    type and value describing the exception, and a chained list of
 17    PyTraceback objects making the application-level traceback.
 18    """
 19
 20    _w_value = None
 21    _application_traceback = None
 22
 23    def __init__(self, w_type, w_value, tb=None):
 24        if not we_are_translated() and w_type is None:
 25            from pypy.tool.error import FlowingError
 26            raise FlowingError(w_value)
 27        self.setup(w_type)
 28        self._w_value = w_value
 29        self._application_traceback = tb
 30
 31    def setup(self, w_type):
 32        self.w_type = w_type
 33        if not we_are_translated():
 34            self.debug_excs = []
 35
 36    def clear(self, space):
 37        # for sys.exc_clear()
 38        self.w_type = space.w_None
 39        self._w_value = space.w_None
 40        self._application_traceback = None
 41        if not we_are_translated():
 42            del self.debug_excs[:]
 43
 44    def match(self, space, w_check_class):
 45        "Check if this application-level exception matches 'w_check_class'."
 46        return space.exception_match(self.w_type, w_check_class)
 47
 48    def async(self, space):
 49        "Check if this is an exception that should better not be caught."
 50        if not space.full_exceptions:
 51            # flow objspace does not support such exceptions and more
 52            # importantly, raises KeyboardInterrupt if you try to access
 53            # space.w_KeyboardInterrupt
 54            return False
 55        return (self.match(space, space.w_SystemExit) or
 56                self.match(space, space.w_KeyboardInterrupt))
 57
 58    def __str__(self):
 59        "NOT_RPYTHON: Convenience for tracebacks."
 60        s = self._w_value
 61        if self.__class__ is not OperationError and s is None:
 62            s = self._compute_value()
 63        return '[%s: %s]' % (self.w_type, s)
 64
 65    def errorstr(self, space, use_repr=False):
 66        "The exception class and value, as a string."
 67        w_value = self.get_w_value(space)
 68        if space is None:
 69            # this part NOT_RPYTHON
 70            exc_typename = str(self.w_type)
 71            exc_value    = str(w_value)
 72        else:
 73            w = space.wrap
 74            if space.is_w(space.type(self.w_type), space.w_str):
 75                exc_typename = space.str_w(self.w_type)
 76            else:
 77                exc_typename = space.str_w(
 78                    space.getattr(self.w_type, w('__name__')))
 79            if space.is_w(w_value, space.w_None):
 80                exc_value = ""
 81            else:
 82                try:
 83                    if use_repr:
 84                        exc_value = space.str_w(space.repr(w_value))
 85                    else:
 86                        exc_value = space.str_w(space.str(w_value))
 87                except OperationError:
 88                    # oups, cannot __str__ the exception object
 89                    exc_value = "<oups, exception object itself cannot be str'd>"
 90        if not exc_value:
 91            return exc_typename
 92        else:
 93            return '%s: %s' % (exc_typename, exc_value)
 94
 95    def record_interpreter_traceback(self):
 96        """Records the current traceback inside the interpreter.
 97        This traceback is only useful to debug the interpreter, not the
 98        application."""
 99        if not we_are_translated():
100            if RECORD_INTERPLEVEL_TRACEBACK:
101                self.debug_excs.append(sys.exc_info())
102
103    def print_application_traceback(self, space, file=None):
104        "NOT_RPYTHON: Dump a standard application-level traceback."
105        if file is None: file = sys.stderr
106        self.print_app_tb_only(file)
107        print >> file, self.errorstr(space)
108
109    def print_app_tb_only(self, file):
110        "NOT_RPYTHON"
111        tb = self._application_traceback
112        if tb:
113            import linecache
114            print >> file, "Traceback (application-level):"
115            while tb is not None:
116                co = tb.frame.pycode
117                lineno = tb.get_lineno()
118                fname = co.co_filename
119                if fname.startswith('<inline>\n'):
120                    lines = fname.split('\n')
121                    fname = lines[0].strip()
122                    try:
123                        l = lines[lineno]
124                    except IndexError:
125                        l = ''
126                else:
127                    l = linecache.getline(fname, lineno)
128                print >> file, "  File \"%s\"," % fname,
129                print >> file, "line", lineno, "in", co.co_name
130                if l:
131                    if l.endswith('\n'):
132                        l = l[:-1]
133                    l = "    " + l.lstrip()
134                    print >> file, l
135                tb = tb.next
136
137    def print_detailed_traceback(self, space=None, file=None):
138        """NOT_RPYTHON: Dump a nice detailed interpreter- and
139        application-level traceback, useful to debug the interpreter."""
140        import traceback, cStringIO
141        if file is None: file = sys.stderr
142        f = cStringIO.StringIO()
143        for i in range(len(self.debug_excs)-1, -1, -1):
144            print >> f, "Traceback (interpreter-level):"
145            traceback.print_tb(self.debug_excs[i][2], file=f)
146        f.seek(0)
147        debug_print(''.join(['|| ' + line for line in f.readlines()]), file)
148        if self.debug_excs:
149            from pypy.tool import tb_server
150            tb_server.publish_exc(self.debug_excs[-1])
151        self.print_app_tb_only(file)
152        print >> file, '(application-level)', self.errorstr(space)
153        if AUTO_DEBUG:
154            import debug
155            debug.fire(self)
156
157    @jit.unroll_safe
158    def normalize_exception(self, space):
159        """Normalize the OperationError.  In other words, fix w_type and/or
160        w_value to make sure that the __class__ of w_value is exactly w_type.
161        """
162        #
163        # This method covers all ways in which the Python statement
164        # "raise X, Y" can produce a valid exception type and instance.
165        #
166        # In the following table, 'Class' means a subclass of BaseException
167        # and 'inst' is an instance of either 'Class' or a subclass of it.
168        # Or 'Class' can also be an old-style class and 'inst' an old-style
169        # instance of it.
170        #
171        # Note that 'space.full_exceptions' is set to False by the flow
172        # object space; in this case we must assume that we are in a
173        # non-advanced case, and ignore the advanced cases.  Old-style
174        # classes and instances *are* advanced.
175        #
176        #  input (w_type, w_value)... becomes...                advanced case?
177        # ---------------------------------------------------------------------
178        #  (tuple, w_value)           (tuple[0], w_value)             yes
179        #  (Class, None)              (Class, Class())                no
180        #  (Class, inst)              (inst.__class__, inst)          no
181        #  (Class, tuple)             (Class, Class(*tuple))          yes
182        #  (Class, x)                 (Class, Class(x))               no
183        #  ("string", ...)            ("string", ...)              deprecated
184        #  (inst, None)               (inst.__class__, inst)          no
185        #
186        w_type  = self.w_type
187        w_value = self.get_w_value(space)
188        if space.full_exceptions:
189            while space.is_true(space.isinstance(w_type, space.w_tuple)):
190                w_type = space.getitem(w_type, space.wrap(0))
191
192        if space.exception_is_valid_obj_as_class_w(w_type):
193            # this is for all cases of the form (Class, something)
194            if space.is_w(w_value, space.w_None):
195                # raise Type: we assume we have to instantiate Type
196                w_value = space.call_function(w_type)
197                w_type = self._exception_getclass(space, w_value)
198            else:
199                w_valuetype = space.exception_getclass(w_value)
200                if space.exception_issubclass_w(w_valuetype, w_type):
201                    # raise Type, Instance: let etype be the exact type of value
202                    w_type = w_valuetype
203                else:
204                    if space.full_exceptions and space.is_true(
205                        space.isinstance(w_value, space.w_tuple)):
206                        # raise Type, tuple: assume the tuple contains the
207                        #                    constructor args
208                        w_value = space.call(w_type, w_value)
209                    else:
210                        # raise Type, X: assume X is the constructor argument
211                        w_value = space.call_function(w_type, w_value)
212                    w_type = self._exception_getclass(space, w_value)
213
214        else:
215            # the only case left here is (inst, None), from a 'raise inst'.
216            w_inst = w_type
217            w_instclass = self._exception_getclass(space, w_inst)
218            if not space.is_w(w_value, space.w_None):
219                raise OperationError(space.w_TypeError,
220                                     space.wrap("instance exception may not "
221                                                "have a separate value"))
222            w_value = w_inst
223            w_type = w_instclass
224
225        self.w_type   = w_type
226        self._w_value = w_value
227
228    def _exception_getclass(self, space, w_inst):
229        w_type = space.exception_getclass(w_inst)
230        if not space.exception_is_valid_class_w(w_type):
231            typename = w_type.getname(space)
232            msg = ("exceptions must be old-style classes or derived "
233                   "from BaseException, not %s")
234            raise operationerrfmt(space.w_TypeError, msg, typename)
235        return w_type
236
237    def write_unraisable(self, space, where, w_object=None):
238        if w_object is None:
239            objrepr = ''
240        else:
241            try:
242                objrepr = space.str_w(space.repr(w_object))
243            except OperationError:
244                objrepr = '?'
245        msg = 'Exception %s in %s%s ignored\n' % (
246            self.errorstr(space, use_repr=True), where, objrepr)
247        try:
248            space.call_method(space.sys.get('stderr'), 'write', space.wrap(msg))
249        except OperationError:
250            pass   # ignored
251
252    def get_w_value(self, space):
253        w_value = self._w_value
254        if w_value is None:
255            value = self._compute_value()
256            self._w_value = w_value = space.wrap(value)
257        return w_value
258
259    def _compute_value(self):
260        raise NotImplementedError
261
262    def get_traceback(self):
263        """Calling this marks the PyTraceback as escaped, i.e. it becomes
264        accessible and inspectable by app-level Python code.  For the JIT.
265        Note that this has no effect if there are already several traceback
266        frames recorded, because in this case they are already marked as
267        escaping by executioncontext.leave() being called with
268        got_exception=True.
269        """
270        from pypy.interpreter.pytraceback import PyTraceback
271        tb = self._application_traceback
272        if tb is not None and isinstance(tb, PyTraceback):
273            tb.frame.mark_as_escaped()
274        return tb
275
276    def set_traceback(self, traceback):
277        """Set the current traceback.  It should either be a traceback
278        pointing to some already-escaped frame, or a traceback for the
279        current frame.  To support the latter case we do not mark the
280        frame as escaped.  The idea is that it will be marked as escaping
281        only if the exception really propagates out of this frame, by
282        executioncontext.leave() being called with got_exception=True.
283        """
284        self._application_traceback = traceback
285
286# ____________________________________________________________
287# optimization only: avoid the slowest operation -- the string
288# formatting with '%' -- in the common case were we don't
289# actually need the message.  Only supports %s and %d.
290
291_fmtcache = {}
292_fmtcache2 = {}
293
294def decompose_valuefmt(valuefmt):
295    """Returns a tuple of string parts extracted from valuefmt,
296    and a tuple of format characters."""
297    formats = []
298    parts = valuefmt.split('%')
299    i = 1
300    while i < len(parts):
301        if parts[i].startswith('s') or parts[i].startswith('d'):
302            formats.append(parts[i][0])
303            parts[i] = parts[i][1:]
304            i += 1
305        elif parts[i] == '':    # support for '%%'
306            parts[i-1] += '%' + parts[i+1]
307            del parts[i:i+2]
308        else:
309            raise ValueError("invalid format string (only %s or %d supported)")
310    assert len(formats) > 0, "unsupported: no % command found"
311    return tuple(parts), tuple(formats)
312
313def get_operrcls2(valuefmt):
314    strings, formats = decompose_valuefmt(valuefmt)
315    assert len(strings) == len(formats) + 1
316    try:
317        OpErrFmt = _fmtcache2[formats]
318    except KeyError:
319        from pypy.rlib.unroll import unrolling_iterable
320        attrs = ['x%d' % i for i in range(len(formats))]
321        entries = unrolling_iterable(enumerate(attrs))
322        #
323        class OpErrFmt(OperationError):
324            def __init__(self, w_type, strings, *args):
325                self.setup(w_type)
326                assert len(args) == len(strings) - 1
327                self.xstrings = strings
328                for i, attr in entries:
329                    setattr(self, attr, args[i])
330                if not we_are_translated() and w_type is None:
331                    from pypy.tool.error import FlowingError
332                    raise FlowingError(self._compute_value())
333            def _compute_value(self):
334                lst = [None] * (len(formats) + len(formats) + 1)
335                for i, attr in entries:
336                    string = self.xstrings[i]
337                    value = getattr(self, attr)
338                    lst[i+i] = string
339                    lst[i+i+1] = str(value)
340                lst[-1] = self.xstrings[-1]
341                return ''.join(lst)
342        #
343        _fmtcache2[formats] = OpErrFmt
344    return OpErrFmt, strings
345
346def get_operationerr_class(valuefmt):
347    try:
348        result = _fmtcache[valuefmt]
349    except KeyError:
350        result = _fmtcache[valuefmt] = get_operrcls2(valuefmt)
351    return result
352get_operationerr_class._annspecialcase_ = 'specialize:memo'
353
354def operationerrfmt(w_type, valuefmt, *args):
355    """Equivalent to OperationError(w_type, space.wrap(valuefmt % args)).
356    More efficient in the (common) case where the value is not actually
357    needed."""
358    OpErrFmt, strings = get_operationerr_class(valuefmt)
359    return OpErrFmt(w_type, strings, *args)
360operationerrfmt._annspecialcase_ = 'specialize:arg(1)'
361
362# ____________________________________________________________
363
364# Utilities
365from pypy.tool.ansi_print import ansi_print
366
367def debug_print(text, file=None, newline=True):
368    # 31: ANSI color code "red"
369    ansi_print(text, esc="31", file=file, newline=newline)
370
371try:
372    WindowsError
373except NameError:
374    _WINDOWS = False
375else:
376    _WINDOWS = True
377
378    def wrap_windowserror(space, e, w_filename=None):
379        from pypy.rlib import rwin32
380
381        winerror = e.winerror
382        try:
383            msg = rwin32.FormatError(winerror)
384        except ValueError:
385            msg = 'Windows Error %d' % winerror
386        exc = space.w_WindowsError
387        if w_filename is not None:
388            w_error = space.call_function(exc, space.wrap(winerror),
389                                          space.wrap(msg), w_filename)
390        else:
391            w_error = space.call_function(exc, space.wrap(winerror),
392                                          space.wrap(msg))
393        return OperationError(exc, w_error)
394
395def wrap_oserror2(space, e, w_filename=None, exception_name='w_OSError',
396                  w_exception_class=None): 
397    assert isinstance(e, OSError)
398
399    if _WINDOWS and isinstance(e, WindowsError):
400        return wrap_windowserror(space, e, w_filename)
401
402    errno = e.errno
403
404    if errno == EINTR:
405        space.getexecutioncontext().checksignals()
406
407    try:
408        msg = os.strerror(errno)
409    except ValueError:
410        msg = 'error %d' % errno
411    if w_exception_class is None:
412        exc = getattr(space, exception_name)
413    else:
414        exc = w_exception_class
415    if w_filename is not None:
416        w_error = space.call_function(exc, space.wrap(errno),
417                                      space.wrap(msg), w_filename)
418    else:
419        w_error = space.call_function(exc, space.wrap(errno),
420                                      space.wrap(msg))
421    return OperationError(exc, w_error)
422wrap_oserror2._annspecialcase_ = 'specialize:arg(3)'
423
424def wrap_oserror(space, e, filename=None, exception_name='w_OSError',
425                 w_exception_class=None):
426    if filename is not None:
427        return wrap_oserror2(space, e, space.wrap(filename),
428                             exception_name=exception_name,
429                             w_exception_class=w_exception_class)
430    else:
431        return wrap_oserror2(space, e, None,
432                             exception_name=exception_name,
433                             w_exception_class=w_exception_class)
434wrap_oserror._annspecialcase_ = 'specialize:arg(3)'
435
436def exception_from_errno(space, w_type):
437    from pypy.rlib.rposix import get_errno
438
439    errno = get_errno()
440    msg = os.strerror(errno)
441    w_error = space.call_function(w_type, space.wrap(errno), space.wrap(msg))
442    return OperationError(w_type, w_error)
443
444def new_exception_class(space, name, w_bases=None, w_dict=None):
445    """Create a new exception type.
446    @param name: the name of the type.
447    @param w_bases: Either an exception type, or a wrapped tuple of
448                    exception types.  default is space.w_Exception.
449    @param w_dict: an optional dictionary to populate the class __dict__.
450    """
451    if '.' in name:
452        module, name = name.rsplit('.', 1)
453    else:
454        module = None
455    if w_bases is None:
456        w_bases = space.newtuple([space.w_Exception])
457    elif not space.isinstance_w(w_bases, space.w_tuple):
458        w_bases = space.newtuple([w_bases])
459    if w_dict is None:
460        w_dict = space.newdict()
461    w_exc = space.call_function(
462        space.w_type, space.wrap(name), w_bases, w_dict)
463    if module:
464        space.setattr(w_exc, space.wrap("__module__"), space.wrap(module))
465    return w_exc
466
467def typed_unwrap_error_msg(space, expected, w_obj):
468    type_name = space.type(w_obj).getname(space)
469    return space.wrap("expected %s, got %s object" % (expected, type_name))