Mini Shell

Direktori : /opt/imh-python/lib/python3.9/site-packages/twisted/python/
Upload File :
Current File : //opt/imh-python/lib/python3.9/site-packages/twisted/python/failure.py

# -*- test-case-name: twisted.test.test_failure -*-
# See also test suite twisted.test.test_pbfailure

# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.


"""
Asynchronous-friendly error mechanism.

See L{Failure}.
"""


# System Imports
import builtins
import copy
import inspect
import linecache
import sys
from inspect import getmro
from io import StringIO

import opcode

from twisted.python import reflect

count = 0
traceupLength = 4


class DefaultException(Exception):
    pass


def format_frames(frames, write, detail="default"):
    """
    Format and write frames.

    @param frames: is a list of frames as used by Failure.frames, with
        each frame being a list of
        (funcName, fileName, lineNumber, locals.items(), globals.items())
    @type frames: list
    @param write: this will be called with formatted strings.
    @type write: callable
    @param detail: Four detail levels are available:
        default, brief, verbose, and verbose-vars-not-captured.
        C{Failure.printDetailedTraceback} uses the latter when the caller asks
        for verbose, but no vars were captured, so that an explicit warning
        about the missing data is shown.
    @type detail: string
    """
    if detail not in ("default", "brief", "verbose", "verbose-vars-not-captured"):
        raise ValueError(
            "Detail must be default, brief, verbose, or "
            "verbose-vars-not-captured. (not %r)" % (detail,)
        )
    w = write
    if detail == "brief":
        for method, filename, lineno, localVars, globalVars in frames:
            w(f"{filename}:{lineno}:{method}\n")
    elif detail == "default":
        for method, filename, lineno, localVars, globalVars in frames:
            w(f'  File "{filename}", line {lineno}, in {method}\n')
            w("    %s\n" % linecache.getline(filename, lineno).strip())
    elif detail == "verbose-vars-not-captured":
        for method, filename, lineno, localVars, globalVars in frames:
            w("%s:%d: %s(...)\n" % (filename, lineno, method))
        w(" [Capture of Locals and Globals disabled (use captureVars=True)]\n")
    elif detail == "verbose":
        for method, filename, lineno, localVars, globalVars in frames:
            w("%s:%d: %s(...)\n" % (filename, lineno, method))
            w(" [ Locals ]\n")
            # Note: the repr(val) was (self.pickled and val) or repr(val)))
            for name, val in localVars:
                w(f"  {name} : {repr(val)}\n")
            w(" ( Globals )\n")
            for name, val in globalVars:
                w(f"  {name} : {repr(val)}\n")


# slyphon: i have a need to check for this value in trial
#          so I made it a module-level constant
EXCEPTION_CAUGHT_HERE = "--- <exception caught here> ---"


class NoCurrentExceptionError(Exception):
    """
    Raised when trying to create a Failure from the current interpreter
    exception state and there is no current exception state.
    """


def _Traceback(stackFrames, tbFrames):
    """
    Construct a fake traceback object using a list of frames.

    It should have the same API as stdlib to allow interaction with
    other tools.

    @param stackFrames: [(methodname, filename, lineno, locals, globals), ...]
    @param tbFrames: [(methodname, filename, lineno, locals, globals), ...]
    """
    assert len(tbFrames) > 0, "Must pass some frames"
    # We deliberately avoid using recursion here, as the frames list may be
    # long.

    # 'stackFrames' is a list of frames above (ie, older than) the point the
    # exception was caught, with oldest at the start. Start by building these
    # into a linked list of _Frame objects (with the f_back links pointing back
    # towards the oldest frame).
    stack = None
    for sf in stackFrames:
        stack = _Frame(sf, stack)

    # 'tbFrames' is a list of frames from the point the exception was caught,
    # down to where it was thrown, with the oldest at the start. Add these to
    # the linked list of _Frames, but also wrap each one with a _Traceback
    # frame which is linked in the opposite direction (towards the newest
    # frame).
    stack = _Frame(tbFrames[0], stack)
    firstTb = tb = _TracebackFrame(stack)
    for sf in tbFrames[1:]:
        stack = _Frame(sf, stack)
        tb.tb_next = _TracebackFrame(stack)
        tb = tb.tb_next

    # Return the first _TracebackFrame.
    return firstTb


# The set of attributes for _TracebackFrame, _Frame and _Code were taken from
# https://docs.python.org/3.10/library/inspect.html Other Pythons may have a
# few more attributes that should be added if needed.
class _TracebackFrame:
    """
    Fake traceback object which can be passed to functions in the standard
    library L{traceback} module.
    """

    def __init__(self, frame):
        """
        @param frame: _Frame object
        """
        self.tb_frame = frame
        self.tb_lineno = frame.f_lineno
        self.tb_lasti = frame.f_lasti
        self.tb_next = None


class _Frame:
    """
    A fake frame object, used by L{_Traceback}.

    @ivar f_code: fake L{code<types.CodeType>} object
    @ivar f_lineno: line number
    @ivar f_globals: fake f_globals dictionary (usually empty)
    @ivar f_locals: fake f_locals dictionary (usually empty)
    @ivar f_back: previous stack frame (towards the caller)
    """

    def __init__(self, frameinfo, back):
        """
        @param frameinfo: (methodname, filename, lineno, locals, globals)
        @param back: previous (older) stack frame
        @type back: C{frame}
        """
        name, filename, lineno, localz, globalz = frameinfo
        self.f_code = _Code(name, filename)
        self.f_lineno = lineno
        self.f_globals = dict(globalz or {})
        self.f_locals = dict(localz or {})
        self.f_back = back
        self.f_lasti = 0
        self.f_builtins = vars(builtins).copy()
        self.f_trace = None


class _Code:
    """
    A fake code object, used by L{_Traceback} via L{_Frame}.

    It is intended to have the same API as the stdlib code type to allow
    interoperation with other tools based on that interface.
    """

    def __init__(self, name, filename):
        self.co_name = name
        self.co_filename = filename
        self.co_lnotab = b""
        self.co_firstlineno = 0
        self.co_argcount = 0
        self.co_varnames = []
        self.co_code = b""
        self.co_cellvars = ()
        self.co_consts = ()
        self.co_flags = 0
        self.co_freevars = ()
        self.co_posonlyargcount = 0
        self.co_kwonlyargcount = 0
        self.co_names = ()
        self.co_nlocals = 0
        self.co_stacksize = 0


_inlineCallbacksExtraneous = []


def _extraneous(f):
    """
    Mark the given callable as extraneous to inlineCallbacks exception
    reporting; don't show these functions.

    @param f: a function that you NEVER WANT TO SEE AGAIN in ANY TRACEBACK
        reported by Failure.

    @type f: function

    @return: f
    """
    _inlineCallbacksExtraneous.append(f.__code__)
    return f


class Failure(BaseException):
    """
    A basic abstraction for an error that has occurred.

    This is necessary because Python's built-in error mechanisms are
    inconvenient for asynchronous communication.

    The C{stack} and C{frame} attributes contain frames.  Each frame is a tuple
    of (funcName, fileName, lineNumber, localsItems, globalsItems), where
    localsItems and globalsItems are the contents of
    C{locals().items()}/C{globals().items()} for that frame, or an empty tuple
    if those details were not captured.

    @ivar value: The exception instance responsible for this failure.
    @ivar type: The exception's class.
    @ivar stack: list of frames, innermost last, excluding C{Failure.__init__}.
    @ivar frames: list of frames, innermost first.
    """

    pickled = 0
    stack = None

    # The opcode of "yield" in Python bytecode. We need this in
    # _findFailure in order to identify whether an exception was
    # thrown by a throwExceptionIntoGenerator.
    # on PY3, b'a'[0] == 97 while in py2 b'a'[0] == b'a' opcodes
    # are stored in bytes so we need to properly account for this
    # difference.
    _yieldOpcode = opcode.opmap["YIELD_VALUE"]

    def __init__(self, exc_value=None, exc_type=None, exc_tb=None, captureVars=False):
        """
        Initialize me with an explanation of the error.

        By default, this will use the current C{exception}
        (L{sys.exc_info}()).  However, if you want to specify a
        particular kind of failure, you can pass an exception as an
        argument.

        If no C{exc_value} is passed, then an "original" C{Failure} will
        be searched for. If the current exception handler that this
        C{Failure} is being constructed in is handling an exception
        raised by L{raiseException}, then this C{Failure} will act like
        the original C{Failure}.

        For C{exc_tb} only L{traceback} instances or L{None} are allowed.
        If L{None} is supplied for C{exc_value}, the value of C{exc_tb} is
        ignored, otherwise if C{exc_tb} is L{None}, it will be found from
        execution context (ie, L{sys.exc_info}).

        @param captureVars: if set, capture locals and globals of stack
            frames.  This is pretty slow, and makes no difference unless you
            are going to use L{printDetailedTraceback}.
        """
        global count
        count = count + 1
        self.count = count
        self.type = self.value = tb = None
        self.captureVars = captureVars

        if isinstance(exc_value, str) and exc_type is None:
            raise TypeError("Strings are not supported by Failure")

        stackOffset = 0

        if exc_value is None:
            exc_value = self._findFailure()

        if exc_value is None:
            self.type, self.value, tb = sys.exc_info()
            if self.type is None:
                raise NoCurrentExceptionError()
            stackOffset = 1
        elif exc_type is None:
            if isinstance(exc_value, Exception):
                self.type = exc_value.__class__
            else:
                # Allow arbitrary objects.
                self.type = type(exc_value)
            self.value = exc_value
        else:
            self.type = exc_type
            self.value = exc_value

        if isinstance(self.value, Failure):
            self._extrapolate(self.value)
            return

        if hasattr(self.value, "__failure__"):

            # For exceptions propagated through coroutine-awaiting (see
            # Deferred.send, AKA Deferred.__next__), which can't be raised as
            # Failure because that would mess up the ability to except: them:
            self._extrapolate(self.value.__failure__)

            # Clean up the inherently circular reference established by storing
            # the failure there.  This should make the common case of a Twisted
            # / Deferred-returning coroutine somewhat less hard on the garbage
            # collector.
            del self.value.__failure__
            return

        if tb is None:
            if exc_tb:
                tb = exc_tb
            elif getattr(self.value, "__traceback__", None):
                # Python 3
                tb = self.value.__traceback__

        frames = self.frames = []
        stack = self.stack = []

        # Added 2003-06-23 by Chris Armstrong. Yes, I actually have a
        # use case where I need this traceback object, and I've made
        # sure that it'll be cleaned up.
        self.tb = tb

        if tb:
            f = tb.tb_frame
        elif not isinstance(self.value, Failure):
            # We don't do frame introspection since it's expensive,
            # and if we were passed a plain exception with no
            # traceback, it's not useful anyway
            f = stackOffset = None

        while stackOffset and f:
            # This excludes this Failure.__init__ frame from the
            # stack, leaving it to start with our caller instead.
            f = f.f_back
            stackOffset -= 1

        # Keeps the *full* stack.  Formerly in spread.pb.print_excFullStack:
        #
        #   The need for this function arises from the fact that several
        #   PB classes have the peculiar habit of discarding exceptions
        #   with bareword "except:"s.  This premature exception
        #   catching means tracebacks generated here don't tend to show
        #   what called upon the PB object.

        while f:
            if captureVars:
                localz = f.f_locals.copy()
                if f.f_locals is f.f_globals:
                    globalz = {}
                else:
                    globalz = f.f_globals.copy()
                for d in globalz, localz:
                    if "__builtins__" in d:
                        del d["__builtins__"]
                localz = localz.items()
                globalz = globalz.items()
            else:
                localz = globalz = ()
            stack.insert(
                0,
                (
                    f.f_code.co_name,
                    f.f_code.co_filename,
                    f.f_lineno,
                    localz,
                    globalz,
                ),
            )
            f = f.f_back

        while tb is not None:
            f = tb.tb_frame
            if captureVars:
                localz = f.f_locals.copy()
                if f.f_locals is f.f_globals:
                    globalz = {}
                else:
                    globalz = f.f_globals.copy()
                for d in globalz, localz:
                    if "__builtins__" in d:
                        del d["__builtins__"]
                localz = list(localz.items())
                globalz = list(globalz.items())
            else:
                localz = globalz = ()
            frames.append(
                (
                    f.f_code.co_name,
                    f.f_code.co_filename,
                    tb.tb_lineno,
                    localz,
                    globalz,
                )
            )
            tb = tb.tb_next
        if inspect.isclass(self.type) and issubclass(self.type, Exception):
            parentCs = getmro(self.type)
            self.parents = list(map(reflect.qual, parentCs))
        else:
            self.parents = [self.type]

    def _extrapolate(self, otherFailure):
        """
        Extrapolate from one failure into another, copying its stack frames.

        @param otherFailure: Another L{Failure}, whose traceback information,
            if any, should be preserved as part of the stack presented by this
            one.
        @type otherFailure: L{Failure}
        """
        # Copy all infos from that failure (including self.frames).
        self.__dict__ = copy.copy(otherFailure.__dict__)

        # If we are re-throwing a Failure, we merge the stack-trace stored in
        # the failure with the current exception's stack.  This integrated with
        # throwExceptionIntoGenerator and allows to provide full stack trace,
        # even if we go through several layers of inlineCallbacks.
        _, _, tb = sys.exc_info()
        frames = []
        while tb is not None:
            f = tb.tb_frame
            if f.f_code not in _inlineCallbacksExtraneous:
                frames.append(
                    (f.f_code.co_name, f.f_code.co_filename, tb.tb_lineno, (), ())
                )
            tb = tb.tb_next
        # Merging current stack with stack stored in the Failure.
        frames.extend(self.frames)
        self.frames = frames

    def trap(self, *errorTypes):
        """
        Trap this failure if its type is in a predetermined list.

        This allows you to trap a Failure in an error callback.  It will be
        automatically re-raised if it is not a type that you expect.

        The reason for having this particular API is because it's very useful
        in Deferred errback chains::

            def _ebFoo(self, failure):
                r = failure.trap(Spam, Eggs)
                print('The Failure is due to either Spam or Eggs!')
                if r == Spam:
                    print('Spam did it!')
                elif r == Eggs:
                    print('Eggs did it!')

        If the failure is not a Spam or an Eggs, then the Failure will be
        'passed on' to the next errback. In Python 2 the Failure will be
        raised; in Python 3 the underlying exception will be re-raised.

        @type errorTypes: L{Exception}
        """
        error = self.check(*errorTypes)
        if not error:
            self.raiseException()
        return error

    def check(self, *errorTypes):
        """
        Check if this failure's type is in a predetermined list.

        @type errorTypes: list of L{Exception} classes or
                          fully-qualified class names.
        @returns: the matching L{Exception} type, or None if no match.
        """
        for error in errorTypes:
            err = error
            if inspect.isclass(error) and issubclass(error, Exception):
                err = reflect.qual(error)
            if err in self.parents:
                return error
        return None

    def raiseException(self):
        """
        raise the original exception, preserving traceback
        information if available.
        """
        raise self.value.with_traceback(self.tb)

    @_extraneous
    def throwExceptionIntoGenerator(self, g):
        """
        Throw the original exception into the given generator,
        preserving traceback information if available.

        @return: The next value yielded from the generator.
        @raise StopIteration: If there are no more values in the generator.
        @raise anything else: Anything that the generator raises.
        """
        # Note that the actual magic to find the traceback information
        # is done in _findFailure.
        return g.throw(self.type, self.value, self.tb)

    @classmethod
    def _findFailure(cls):
        """
        Find the failure that represents the exception currently in context.
        """
        tb = sys.exc_info()[-1]
        if not tb:
            return

        secondLastTb = None
        lastTb = tb
        while lastTb.tb_next:
            secondLastTb = lastTb
            lastTb = lastTb.tb_next

        lastFrame = lastTb.tb_frame

        # NOTE: f_locals.get('self') is used rather than
        # f_locals['self'] because psyco frames do not contain
        # anything in their locals() dicts.  psyco makes debugging
        # difficult anyhow, so losing the Failure objects (and thus
        # the tracebacks) here when it is used is not that big a deal.

        # Handle raiseException-originated exceptions
        if lastFrame.f_code is cls.raiseException.__code__:
            return lastFrame.f_locals.get("self")

        # Handle throwExceptionIntoGenerator-originated exceptions
        # this is tricky, and differs if the exception was caught
        # inside the generator, or above it:

        # It is only really originating from
        # throwExceptionIntoGenerator if the bottom of the traceback
        # is a yield.
        # Pyrex and Cython extensions create traceback frames
        # with no co_code, but they can't yield so we know it's okay to
        # just return here.
        if (not lastFrame.f_code.co_code) or lastFrame.f_code.co_code[
            lastTb.tb_lasti
        ] != cls._yieldOpcode:
            return

        # If the exception was caught above the generator.throw
        # (outside the generator), it will appear in the tb (as the
        # second last item):
        if secondLastTb:
            frame = secondLastTb.tb_frame
            if frame.f_code is cls.throwExceptionIntoGenerator.__code__:
                return frame.f_locals.get("self")

        # If the exception was caught below the generator.throw
        # (inside the generator), it will appear in the frames' linked
        # list, above the top-level traceback item (which must be the
        # generator frame itself, thus its caller is
        # throwExceptionIntoGenerator).
        frame = tb.tb_frame.f_back
        if frame and frame.f_code is cls.throwExceptionIntoGenerator.__code__:
            return frame.f_locals.get("self")

    def __repr__(self) -> str:
        return "<{} {}: {}>".format(
            reflect.qual(self.__class__),
            reflect.qual(self.type),
            self.getErrorMessage(),
        )

    def __str__(self) -> str:
        return "[Failure instance: %s]" % self.getBriefTraceback()

    def __getstate__(self):
        """Avoid pickling objects in the traceback."""
        if self.pickled:
            return self.__dict__
        c = self.__dict__.copy()

        c["frames"] = [
            [
                v[0],
                v[1],
                v[2],
                _safeReprVars(v[3]),
                _safeReprVars(v[4]),
            ]
            for v in self.frames
        ]

        # Added 2003-06-23. See comment above in __init__
        c["tb"] = None

        if self.stack is not None:
            # XXX: This is a band-aid.  I can't figure out where these
            # (failure.stack is None) instances are coming from.
            c["stack"] = [
                [
                    v[0],
                    v[1],
                    v[2],
                    _safeReprVars(v[3]),
                    _safeReprVars(v[4]),
                ]
                for v in self.stack
            ]

        c["pickled"] = 1
        return c

    def cleanFailure(self):
        """
        Remove references to other objects, replacing them with strings.

        On Python 3, this will also set the C{__traceback__} attribute of the
        exception instance to L{None}.
        """
        self.__dict__ = self.__getstate__()
        if getattr(self.value, "__traceback__", None):
            # Python 3
            self.value.__traceback__ = None

    def getTracebackObject(self):
        """
        Get an object that represents this Failure's stack that can be passed
        to traceback.extract_tb.

        If the original traceback object is still present, return that. If this
        traceback object has been lost but we still have the information,
        return a fake traceback object (see L{_Traceback}). If there is no
        traceback information at all, return None.
        """
        if self.tb is not None:
            return self.tb
        elif len(self.frames) > 0:
            return _Traceback(self.stack, self.frames)
        else:
            return None

    def getErrorMessage(self) -> str:
        """
        Get a string of the exception which caused this Failure.
        """
        if isinstance(self.value, Failure):
            return self.value.getErrorMessage()
        return reflect.safe_str(self.value)

    def getBriefTraceback(self) -> str:
        io = StringIO()
        self.printBriefTraceback(file=io)
        return io.getvalue()

    def getTraceback(self, elideFrameworkCode: int = 0, detail: str = "default") -> str:
        io = StringIO()
        self.printTraceback(
            file=io, elideFrameworkCode=elideFrameworkCode, detail=detail
        )
        return io.getvalue()

    def printTraceback(self, file=None, elideFrameworkCode=False, detail="default"):
        """
        Emulate Python's standard error reporting mechanism.

        @param file: If specified, a file-like object to which to write the
            traceback.

        @param elideFrameworkCode: A flag indicating whether to attempt to
            remove uninteresting frames from within Twisted itself from the
            output.

        @param detail: A string indicating how much information to include
            in the traceback.  Must be one of C{'brief'}, C{'default'}, or
            C{'verbose'}.
        """
        if file is None:
            from twisted.python import log

            file = log.logerr
        w = file.write

        if detail == "verbose" and not self.captureVars:
            # We don't have any locals or globals, so rather than show them as
            # empty make the output explicitly say that we don't have them at
            # all.
            formatDetail = "verbose-vars-not-captured"
        else:
            formatDetail = detail

        # Preamble
        if detail == "verbose":
            w(
                "*--- Failure #%d%s---\n"
                % (self.count, (self.pickled and " (pickled) ") or " ")
            )
        elif detail == "brief":
            if self.frames:
                hasFrames = "Traceback"
            else:
                hasFrames = "Traceback (failure with no frames)"
            w(
                "%s: %s: %s\n"
                % (hasFrames, reflect.safe_str(self.type), reflect.safe_str(self.value))
            )
        else:
            w("Traceback (most recent call last):\n")

        # Frames, formatted in appropriate style
        if self.frames:
            if not elideFrameworkCode:
                format_frames(self.stack[-traceupLength:], w, formatDetail)
                w(f"{EXCEPTION_CAUGHT_HERE}\n")
            format_frames(self.frames, w, formatDetail)
        elif not detail == "brief":
            # Yeah, it's not really a traceback, despite looking like one...
            w("Failure: ")

        # Postamble, if any
        if not detail == "brief":
            w(f"{reflect.qual(self.type)}: {reflect.safe_str(self.value)}\n")

        # Chaining
        if isinstance(self.value, Failure):
            # TODO: indentation for chained failures?
            file.write(" (chained Failure)\n")
            self.value.printTraceback(file, elideFrameworkCode, detail)
        if detail == "verbose":
            w("*--- End of Failure #%d ---\n" % self.count)

    def printBriefTraceback(self, file=None, elideFrameworkCode=0):
        """
        Print a traceback as densely as possible.
        """
        self.printTraceback(file, elideFrameworkCode, detail="brief")

    def printDetailedTraceback(self, file=None, elideFrameworkCode=0):
        """
        Print a traceback with detailed locals and globals information.
        """
        self.printTraceback(file, elideFrameworkCode, detail="verbose")


def _safeReprVars(varsDictItems):
    """
    Convert a list of (name, object) pairs into (name, repr) pairs.

    L{twisted.python.reflect.safe_repr} is used to generate the repr, so no
    exceptions will be raised by faulty C{__repr__} methods.

    @param varsDictItems: a sequence of (name, value) pairs as returned by e.g.
        C{locals().items()}.
    @returns: a sequence of (name, repr) pairs.
    """
    return [(name, reflect.safe_repr(obj)) for (name, obj) in varsDictItems]


# slyphon: make post-morteming exceptions tweakable

DO_POST_MORTEM = True


def _debuginit(
    self,
    exc_value=None,
    exc_type=None,
    exc_tb=None,
    captureVars=False,
    Failure__init__=Failure.__init__,
):
    """
    Initialize failure object, possibly spawning pdb.
    """
    if (exc_value, exc_type, exc_tb) == (None, None, None):
        exc = sys.exc_info()
        if not exc[0] == self.__class__ and DO_POST_MORTEM:
            try:
                strrepr = str(exc[1])
            except BaseException:
                strrepr = "broken str"
            print(
                "Jumping into debugger for post-mortem of exception '{}':".format(
                    strrepr
                )
            )
            import pdb

            pdb.post_mortem(exc[2])
    Failure__init__(self, exc_value, exc_type, exc_tb, captureVars)


def startDebugMode():
    """
    Enable debug hooks for Failures.
    """
    Failure.__init__ = _debuginit

Zerion Mini Shell 1.0