Beispiel #1
0
    def showtraceback(self):
        """Display the exception that just occurred.

        We remove the first stack item because it is our own code.

        The output is written by self.write(), below.

        """
        sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
        sys.last_traceback = last_tb
        try:
            lines = []
            for value, tb in traceback._iter_chain(*ei[1:]):
                if isinstance(value, str):
                    lines.append(value)
                    lines.append('\n')
                    continue
                if tb:
                    tblist = traceback.extract_tb(tb)
                    if tb is last_tb:
                        # The last traceback includes the frame we
                        # exec'd in
                        del tblist[:1]
                    tblines = traceback.format_list(tblist)
                    if tblines:
                        lines.append("Traceback (most recent call last):\n")
                        lines.extend(tblines)
                lines.extend(
                    traceback.format_exception_only(type(value), value))
        finally:
            tblist = last_tb = ei = None
        self.write(''.join(lines))
Beispiel #2
0
def print_exception(t, v, tb, limit=None, file=None, chain=None):
    if chain:
        values = traceback._iter_chain(v, tb)
    else:
        values = [(v, tb)]
    if file is None:
        file = sys.stdout
    for v, tb in values:
        file.writelines(format_exception(t, v, tb, limit))
Beispiel #3
0
def format_exception(t, v, tb, limit=None, chain=None):
    if chain:
        values = traceback._iter_chain(v, tb)
    else:
        values = [(v, tb)]
    fmt = zope.exceptions.exceptionformatter.TextExceptionFormatter(
        limit=None, with_filenames=True)
    for v, tb in values:
        return fmt.formatException(t, v, tb)
Beispiel #4
0
    def _iter_chain(exc, custom_tb=None, seen=None):
        '''
        Private :func:`traceback._iter_chain` method forward-ported without
        modification from the most recent stable release of Python 3.4 as of
        this writing (i.e., Python 3.4.3).
        '''

        if seen is None:
            seen = set()
        seen.add(exc)
        its = []
        context = exc.__context__
        cause = exc.__cause__
        if cause is not None and cause not in seen:
            its.append(_iter_chain(cause, False, seen))
            its.append([(_cause_message, None)])
        elif (context is not None and not exc.__suppress_context__
              and context not in seen):
            its.append(_iter_chain(context, None, seen))
            its.append([(_context_message, None)])
        its.append([(exc, custom_tb or exc.__traceback__)])
        # itertools.chain is in an extension module and may be unavailable
        for it in its:
            yield from it
Beispiel #5
0
    def showtraceback(self):
        """Display the exception that just occurred.

        We remove the first stack item because it is our own code.

        The output is written by self.write(), below.

        """
        sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
        sys.last_traceback = last_tb
        try:
            lines = []
            for value, tb in traceback._iter_chain(*ei[1:]):
                if isinstance(value, str):
                    lines.append(value)
                    lines.append('\n')
                    continue
                if tb:
                    tblist = traceback.extract_tb(tb)
                    if tb is last_tb:
                        # The last traceback includes the frame we
                        # exec'd in
                        del tblist[:1]
                    tblines = traceback.format_list(tblist)
                    if tblines:
                        lines.append("Traceback (most recent call last):\n")
                        lines.extend(tblines)
                lines.extend(traceback.format_exception_only(type(value),
                                                             value))
        finally:
            tblist = last_tb = ei = None
        if sys.excepthook is sys.__excepthook__:
            self.write(''.join(lines))
        else:
            # If someone has set sys.excepthook, we let that take precedence
            # over self.write
            sys.excepthook(sys.last_type, sys.last_value, last_tb)
Beispiel #6
0
    def __init__(self, etype, value, tb, **options):
        """
        :param etype: The exception type
        :type etype: type
        :param value: The exception instance
        :type value: Exception
        :param tb: The traceback instance
        :type tb: traceback
        :param options: Options for this instance
        :type options: dict
        """

        #: Options for xtraceback
        self.options = XTracebackOptions(**options)

        #: The exception type
        self.etype = etype

        #: The exception value (instance)
        self.value = value

        #: The list of exceptions and tracebacks
        if self.options.chain and self.value is not None \
                and hasattr(traceback, "_iter_chain"):
            # python 3
            values = list(traceback._iter_chain(value, tb))
            # traceback._iter_chain pulls tb from the exception instance and so
            # does not use the tb argument to this method for the last (root)
            # exception; normally this would not matter as the two are the same
            # but it falls over when tb is None so we replace the last tb in
            # the values list with tb from args
            values[-1] = (values[-1][0], tb)
        else:
            values = [(value, tb)]

        #: Used in XTracebackFrame to determine indent
        self.number_padding = 0

        # build list of exceptions
        self.exceptions = []
        for value, tb in values:
            if not isinstance(self.etype, basestring) \
                    and isinstance(value, basestring):
                exc_type = value
                value = None
            else:
                exc_type = etype if value == self.value else type(value)
            exc = XTracebackExc(exc_type, value, tb, self)
            self.exceptions.append(exc)
            self.number_padding = max(exc.number_padding, self.number_padding)

        # placeholders
        self._lexer = None
        self._formatter = None

        # work out print width
        if self.options.print_width is not None:
            self.print_width = self.options.print_width
        elif fcntl is not None and self.tty_stream:
            self.print_width = struct.unpack(
                'HHHH',
                fcntl.ioctl(self.options.stream,
                            termios.TIOCGWINSZ,
                            struct.pack('HHHH', 0, 0, 0, 0)),
            )[1]
        else:
            self.print_width = DEFAULT_WIDTH
Beispiel #7
0
    def __init__(self, etype, value, tb, **options):
        """
        :param etype: The exception type
        :type etype: type
        :param value: The exception instance
        :type value: Exception
        :param tb: The traceback instance
        :type tb: traceback
        :param options: Options for this instance
        :type options: dict
        """

        #: Options for xtraceback
        self.options = XTracebackOptions(**options)

        #: The exception type
        self.etype = etype

        #: The exception value (instance)
        self.value = value

        #: The list of exceptions and tracebacks
        if self.options.chain and self.value is not None \
                and hasattr(traceback, "_iter_chain"):
            # python 3
            values = list(traceback._iter_chain(value, tb))
            # traceback._iter_chain pulls tb from the exception instance and so
            # does not use the tb argument to this method for the last (root)
            # exception; normally this would not matter as the two are the same
            # but it falls over when tb is None so we replace the last tb in
            # the values list with tb from args
            values[-1] = (values[-1][0], tb)
        else:
            values = [(value, tb)]

        #: Used in XTracebackFrame to determine indent
        self.number_padding = 0

        # build list of exceptions
        self.exceptions = []
        for value, tb in values:
            if not isinstance(self.etype, basestring) \
                    and isinstance(value, basestring):
                exc_type = value
                value = None
            else:
                exc_type = etype if value == self.value else type(value)
            exc = XTracebackExc(exc_type, value, tb, self)
            self.exceptions.append(exc)
            self.number_padding = max(exc.number_padding, self.number_padding)

        # placeholders
        self._lexer = None
        self._formatter = None

        # work out print width
        if self.options.print_width is not None:
            self.print_width = self.options.print_width
        elif fcntl is not None and self.tty_stream:
            self.print_width = struct.unpack(
                'HHHH',
                fcntl.ioctl(self.options.stream, termios.TIOCGWINSZ,
                            struct.pack('HHHH', 0, 0, 0, 0)),
            )[1]
        else:
            self.print_width = DEFAULT_WIDTH
Beispiel #8
0
def get_metadata(exception: Exception) -> tuple:
    '''
    Tuple of various metadata specific to the passed exception, typically
    intended to be logged and/or displayed to end users.

    Parameters
    ----------
    exception : Exception
        Exception to return metadata for.

    Returns
    ----------
    (str, str)
        2-tuple ``(synopsis, traceback)``, where:
        * ``synopsis`` is this exception's human-readable synopsis.
        * ``traceback`` is this exception's non-human-readable traceback.
    '''

    # Avoid circular import dependencies.
    from betse.util.io.error import errhaiku
    from betse.util.py import pyident
    from betse.util.type.text import regexes
    from betse.util.type.text.string import strjoin, strs

    # Generator yielding 2-tuples "(exception, traceback)" for all parent
    # exceptions of this exception *AND* this exception (in that order), where
    # "exception" is each exception and "traceback" is the traceback stored for
    # each exception.
    #
    # Sadly, this list is only gettable via the private traceback._iter_chain()
    # function in older versions of Python. Since this function is unavailable
    # in newer versions of Python, an application-specific compatibility
    # function is called instead.
    exc_parents_generator = _iter_chain(exception, exception.__traceback__)

    # Tuple of 2-tuples "(exception, traceback)" in the reverse order yielded
    # by this generator, preserving readability by ensuring that this exception
    # is logged first, the parent exception of this exception (if any) is
    # logged second, and so forth.
    exc_parents = tuple(reversed(tuple(exc_parents_generator)))

    # 0-based index of the last exception in this list.
    exc_parent_last_index = len(exc_parents) - 1

    # String buffer containing a human-readable synopsis of each exception in
    # this chain, unconditionally output to stderr.
    exc_iota_buffer = StringIO()

    # String buffer containing a non-human-readable traceback of each exception
    # in this chain, conditionally logged to the logfile.
    exc_full_buffer = StringIO()

    # Human-readable header prefixing each such buffer.
    buffer_header = 'Exiting prematurely due to fatal error:\n\n'

    # Initialize these buffers to this header.
    exc_iota_buffer.write(buffer_header)
    exc_full_buffer.write(buffer_header)

    # For each parent exception and that exception's traceback...
    for exc_parent_index, (exc_parent,
                           exc_parent_traceback) in (enumerate(exc_parents)):
        # If this exception is a string, append this string to the synopsis
        # buffer as is and continue to the next parent. This is an edge case
        # that should *NEVER* happen... but could.
        if types.is_str(exc_parent):
            exc_iota_buffer.write(exc_parent + '\n')
            continue

        # List of traceback lines, excluding this exception's message.
        exc_traceback_lines = traceback.format_exception(
            type(exc_parent), exc_parent, exc_parent_traceback)
        exc_traceback_lines.pop()

        # List of exception message lines, excluding traceback and hence
        # consisting only of this exception type and original message.
        exc_message_lines = traceback.format_exception_only(
            type(exc_parent), exc_parent)
        assert types.is_sequence_nonstr_nonempty(exc_message_lines), (
            types.assert_not_sequence_nonstr_nonempty(
                exc_message_lines, 'Exception message lines'))

        # If the exception type prefixing the last line of this message is
        # itself prefixed by the expected and hence ignorable fully-qualified
        # name of the subpackage defining BETSE exceptions, truncate this
        # prefix for brevity.
        #
        # Note that the format_exception_only() function guarantees the last
        # line of this message to *ALWAYS* be "the message indicating which
        # exception occurred."
        if exc_message_lines[-1].startswith('betse.exceptions.'):
            exc_message_lines[-1] = exc_message_lines[-1][
                len('betse.exceptions.'):]

        # Last line of this message. By design, the format_exception_only()
        # function guarantees this line to *ALWAYS* be "the message
        # indicating which exception occurred."
        exc_message_line = exc_message_lines[-1]

        # Append this message to the traceback buffer *BEFORE* appending a
        # truncation of this message to the message buffer.
        exc_full_buffer.write(strjoin.join(exc_message_lines))
        #print('exception string: '+ exc_message_lines[-1])

        # Split the last line of this message into a non-human-readable
        # exception class and ideally human-readable exception message. If this
        # exception is not "None" *AND* is convertable without raising
        # exceptions into a string, both format_exception_only() and
        # _format_final_exc_line() guarantee this line to be formatted as:
        #     "${exc_class}: ${exc_message}"
        exc_message_match_groups = regexes.get_match_groups_numbered(
            exc_message_line, r'^({})(?:\s*|:\s+(.+))$'.format(
                pyident.IDENTIFIER_QUALIFIED_REGEX))

        # This message is guaranteed to be prefixed by a class name.
        exc_class_name = exc_message_match_groups[0]

        # This message is *NOT* guaranteed to be prefixed by a non-empty
        # message (e.g., assert statements passed no message).
        #
        # If a non-empty message matched, prefer that.
        exc_message = None
        if exc_message_match_groups[1] is not None:
            exc_message = exc_message_match_groups[1]
        # Else if a debug assertion failed with no explicit message, prefer the
        # exception context directly detailing this assertion.
        elif exc_class_name == 'AssertionError':
            exc_message = 'Debug assertion failed: {}'.format(
                # A traceback line typically contains an internal newline. The
                # substring preceding this newline details the file and
                # function containing the corresponding call; the substring
                # following this newline is this call. Ignore the former.
                regexes.remove_substrs(exc_traceback_lines[-1], r'^.+\n\s*'))
        # Else, convert this exception's class name into a human-readable
        # message (e.g., from "FileNotFoundError" to "File not found error.").
        # Well, try... at least!
        else:
            exc_message = strs.uppercase_char_first(
                pyident.convert_camelcase_to_whitespaced_lowercase(
                    exc_class_name))
        assert types.is_str_nonempty(exc_message), (
            types.assert_not_str_nonempty(exc_message, 'Exception message'))

        # If this class is "KeyError", this message is the single-quoted name
        # of a non-existent key in a dictionary whose access raised this
        # exception.  Replace this by a human-readable message.
        if exc_class_name == 'KeyError':
            exc_message = 'Dictionary key {} not found.'.format(exc_message)

        # Append this message to the synopsis buffer. For readability, this
        # message is wrapped to the default terminal width and each wrapped
        # line prefixed by indentation.
        exc_iota_buffer.write(strs.wrap(text=exc_message, line_prefix='    '))

        # If this exception has a traceback, append this traceback to the
        # traceback but *NOT* synopsis buffer.
        if exc_parent_traceback:
            # Append a traceback header.
            exc_full_buffer.write('\nTraceback (most recent call last):\n')

            # Append this traceback.
            exc_full_buffer.write(
                strjoin.join(
                    # List of lines formatted from this list.
                    traceback.format_list(
                        # List of stack trace entries from this traceback.
                        traceback.extract_tb(exc_parent_traceback))))

        # If this exception is *NOT* the last, append an explanatory header.
        if exc_parent_index != exc_parent_last_index:
            exc_full_buffer.write('\n'
                                  'The above exception wrapped '
                                  'the following originating exception:'
                                  '\n\n')

    # Append a random error haiku to the traceback buffer... *BECAUSE*!
    exc_full_buffer.write('\n{}'.format(errhaiku.get_random()))

    # Return the string contents of these buffers in the expected order.
    return (exc_iota_buffer.getvalue(), exc_full_buffer.getvalue())