def wrap_spy(function, owner=None):
    """
    Surrounds the given 'function' with a spy wrapper that tracks usage data, such as:
      * call count
      * arguments it was called with
      * keyword argument it was called with
      * return values
      * etc.

    Parameters:
        function: function, could be one of 3 things:
                    1. the original function the user wants to spy on
                    2. the custom function the user specified to replace the original
                    3. a default function configurable via returns/throws
        owner: object, the owner of the original function. It is necessary in certain cases
               to specify this, such as when the user stubs a class. Otherwise, the SpyCall
               arguments will erroneously include the 'owner' as the first parameter of every call.
    Returns:
        function, the spy wrapper that is replacing the inputted function
    """
    def __set__(value, new_list):
        """
        For python 2.x compatibility
        """
        setattr(wrapped, value, new_list)

    def wrapped(*args, **kwargs):
        """
        Fully manipulatable inspector function
        """
        if owner:
            if len(args) > 0:
                if owner == args[0].__class__:
                    args = args[1:]
        
        wrapped.callCount += 1
        wrapped.args_list.append(args)
        wrapped.kwargs_list.append(kwargs)

        call = SpyCall()
        call.args = args
        call.kwargs = kwargs
        call.stack = traceback.format_stack()
        wrapped.call_list.append(call)

        try:
            ret = function(*args, **kwargs)
            wrapped.ret_list.append(ret)
            call.returnValue = ret
            return ret
        except BaseException as excpt:
            # Todo: make sure e.__class__ is enough for all purpose or not
            wrapped.error_list.append(excpt.__class__)
            call.exception = excpt
            raise excpt

    wrapped.__set__ = __set__
    wrapped.callCount = 0
    wrapped.args_list = []
    wrapped.call_list = []
    wrapped.kwargs_list = []
    wrapped.error_list = []
    wrapped.ret_list = []
    wrapped.LOCK = True
    return wrapped
def wrap_spy ( function , owner = None ) : def __set__ ( value , new_list ) : setattr ( wrapped , value , new_list ) def wrapped ( * args , ** kwargs ) : if owner : if len ( args ) > 0 : if owner == args [ 0 ] . __class__ : args = args [ 1 : ] wrapped . callCount += 1 wrapped . args_list . append ( args ) wrapped . kwargs_list . append ( kwargs ) call = SpyCall ( ) call . args = args call . kwargs = kwargs call . stack = traceback . format_stack ( ) wrapped . call_list . append ( call ) try : ret = function ( * args , ** kwargs ) wrapped . ret_list . append ( ret ) call . returnValue = ret return ret except BaseException as excpt : wrapped . error_list . append ( excpt . __class__ ) call . exception = excpt raise excpt wrapped . __set__ = __set__ wrapped . callCount = 0 wrapped . args_list = [ ] wrapped . call_list = [ ] wrapped . kwargs_list = [ ] wrapped . error_list = [ ] wrapped . ret_list = [ ] wrapped . LOCK = True return wrapped
Surrounds the given 'function' with a spy wrapper that tracks usage data, such as:
      * call count
      * arguments it was called with
      * keyword argument it was called with
      * return values
      * etc.

    Parameters:
        function: function, could be one of 3 things:
                    1. the original function the user wants to spy on
                    2. the custom function the user specified to replace the original
                    3. a default function configurable via returns/throws
        owner: object, the owner of the original function. It is necessary in certain cases
               to specify this, such as when the user stubs a class. Otherwise, the SpyCall
               arguments will erroneously include the 'owner' as the first parameter of every call.
    Returns:
        function, the spy wrapper that is replacing the inputted function