def __call__(self):
        assert self._nr_instance != None

        transaction = newrelic.api.transaction.current_transaction()

        # If there is an active transaction then deferred is being
        # called within a context of another deferred so simply call the
        # callback and return.

        if transaction:
            return self._nr_next_object()

        # If there is no transaction recorded against the deferred then
        # don't need to do anything and can simply call the callback and
        # return.

        if not hasattr(self._nr_instance, '_nr_transaction'):
            return self._nr_next_object()

        transaction = self._nr_instance._nr_transaction

        # If we can't find a Twisted.Web request object associated with
        # the transaction or it is no longer valid then simply call the
        # callback and return.

        if not hasattr(transaction, '_nr_current_request'):
            return self._nr_next_object()

        request = transaction._nr_current_request()

        if not request:
            return self._nr_next_object()
        
        try:
            # Save the transaction recorded against the deferred as the
            # active transaction.

            transaction._save_transaction(transaction)

            # Record that are calling a deferred. This changes what we
            # do if the request finish() method is being called.

            request._nr_is_deferred_callback = True

            # We should always be calling into a deferred when we are
            # in the wait state for the request. We need to exit that
            # wait state.

            if request._nr_wait_function_trace:
                request._nr_wait_function_trace.__exit__(None, None, None)
                request._nr_wait_function_trace = None

            else:
                _logger.debug('Called a Twisted.Web deferred when we were '
                        'not in a wait state.')

            # Call the deferred and capture any errors that may come
            # back from it.

            with newrelic.api.error_trace.ErrorTrace(transaction):
                with newrelic.api.function_trace.FunctionTrace(
                        transaction, name='Deferred/Call',
                        group='Python/Twisted'):
                    return self._nr_next_object()

        finally:
            # If the request finish() method was called from the
            # deferred then we need to exit the transaction. Other wise
            # we need to create a new function trace node for a new wait
            # state and pop the transaction.

            if request._nr_is_request_finished:
                transaction.__exit__(None, None, None)
                self._nr_instance._nr_transaction = None

            else:
                # XXX Should we be removing the transaction from the
                # deferred object as well. Can the same deferred be
                # called multiple times for same request. It probably
                # can be reregistered.

                request._nr_wait_function_trace = \
                        newrelic.api.function_trace.FunctionTrace(
                        transaction, name='Deferred/Wait',
                        group='Python/Twisted')

                request._nr_wait_function_trace.__enter__()
                transaction._drop_transaction(transaction)

            request._nr_is_deferred_callback = False
    def __call__(self):
        assert self._nr_instance != None

        # Call finish() method straight away if request is not even
        # associated with a transaction.

        if not hasattr(self._nr_instance, '_nr_transaction'):
            return self._nr_next_object()

        # Technically we should only be able to be called here without
        # an active transaction if we are in the wait state. If we
        # are called in context of original request process() function
        # or a deferred the transaction should already be registered.

        transaction = self._nr_instance._nr_transaction

        if self._nr_instance._nr_wait_function_trace:
            if newrelic.api.transaction.current_transaction():
                _logger.debug('The Twisted.Web request finish() method is '
                        'being called while in wait state but there is '
                        'already a current transaction.')
            else:
                transaction._save_transaction(transaction)

        elif not newrelic.api.transaction.current_transaction():
            _logger.debug('The Twisted.Web request finish() method is '
                    'being called from request process() method or a '
                    'deferred but there is not a current transaction.')

        # Except for case of being called when in wait state, we can't
        # actually exit the transaction at this point as may be called
        # in context of an outer function trace node. We thus flag that
        # are finished and pop back out allowing outer scope to actually
        # exit the transaction.

        self._nr_instance._nr_is_request_finished = True

        # Now call the original finish() function.

        if self._nr_instance._nr_is_deferred_callback:

            # If we are in a deferred callback log any error against the
            # transaction here so we know we will capture it. We
            # possibly don't need to do it here as outer scope may catch
            # it anyway. Duplicate will be ignored so not too important.
            # Most likely the finish() call would never fail anyway.

            try:
                with newrelic.api.function_trace.FunctionTrace(transaction,
                        name='Request/Finish', group='Python/Twisted'):
                    result = self._nr_next_object()

            except:
                transaction.record_exception(*sys.exc_info())
                raise

        elif self._nr_instance._nr_wait_function_trace:

            # Now handle the special case where finish() was called
            # while in the wait state. We might get here through
            # Twisted.Web itself somehow calling finish() when still
            # waiting for a deferred. If this were to occur though then
            # the transaction will not be popped if we simply marked
            # request as finished as no outer scope to see that and
            # clean up. We will thus need to end the function trace and
            # exit the transaction. We end function trace here and then
            # the transaction down below.

            try:
                self._nr_instance._nr_wait_function_trace.__exit__(
                        None, None, None)

                with newrelic.api.function_trace.FunctionTrace(transaction,
                        name='Request/Finish', group='Python/Twisted'):
                    result = self._nr_next_object()

                transaction.__exit__(None, None, None)

            except:
                transaction.__exit__(*sys.exc_info())
                raise

            finally:
                self._nr_instance._nr_wait_function_trace = None
                self._nr_instance._nr_transaction = None
                self._nr_instance = None

        else:
            # This should be the case where finish() is being called in
            # the original render() function.

            try:
                with newrelic.api.function_trace.FunctionTrace(transaction,
                        name='Request/Finish', group='Python/Twisted'):
                    result = self._nr_next_object()

            except:
                raise

        return result