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

        transaction = newrelic.api.transaction.current_transaction()

        # Check to see if we are being called within the context of any
        # sort of transaction. If we are, then we don't bother doing
        # anything and just call the wrapped function. This should not
        # really ever occur with Twisted.Web wrapper but check anyway.

        if transaction:
            return self._nr_next_object()

        # Always use the default application specified in the agent
        # configuration.

        application = newrelic.api.application.application_instance()

        # We need to fake up a WSGI like environ dictionary with the key
        # bits of information we need.

        environ = {}

        environ['REQUEST_URI'] = self._nr_instance.path

        # Now start recording the actual web transaction.
 
        transaction = newrelic.api.web_transaction.WebTransaction(
                application, environ)

        if not transaction.enabled:
            return self._nr_next_object()

        transaction.__enter__()

        self._nr_instance._nr_transaction = transaction

        self._nr_instance._nr_is_deferred_callback = False
        self._nr_instance._nr_is_request_finished = False
        self._nr_instance._nr_wait_function_trace = None

        # We need to add a reference to the Twisted.Web request object
        # in the transaction as only able to stash the transaction in a
        # deferred. Need to use a weakref to avoid an object cycle which
        # may prevent cleanup of transaction.

        transaction._nr_current_request = weakref.ref(self._nr_instance)

        try:
            # Call the original method in a trace object to give better
            # context in transaction traces. Three things can happen
            # within this call. The render() function which is in turn
            # called can return a result immediately which means user
            # code should have called finish() on the request, it can
            # raise an exception which is caught in process() function
            # where error handling calls finish(), or it can return that
            # it is not done yet and register deferred callbacks to
            # complete the request.

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

            # In the case of a result having being returned or an
            # exception occuring, then finish() will have been called.
            # We can't just exit the transaction in the finish call
            # however as need to still pop back up through the above
            # function trace. So if flagged that have finished, then we
            # exit the transaction here. Otherwise we setup a function
            # trace to track wait time for deferred and manually pop the
            # transaction as being the current one for this thread.

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

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

                self._nr_instance._nr_wait_function_trace.__enter__()
                transaction._drop_transaction(transaction)

        except:
            # If an error occurs assume that transaction should be
            # exited. Technically don't believe this should ever occur
            # unless our code here has an error or Twisted.Web is
            # broken.

            _logger.exception('Unexpected exception raised by Twisted.Web '
                    'Request.process() exception.')

            transaction.__exit__(*sys.exc_info())
            self._nr_instance._nr_transaction = None
            self._nr_instance = None

            raise

        return result