python mock: @wraps(f) problems
Question: I want to test a simple decorator I wrote:

It looks like this:

    
    
    #utilities.py
    import other_module
    def decor(f):
        @wraps(f)
        def wrapper(*args, **kwds):
                other_module.startdoingsomething()
            try:
                return f(*args, **kwds)
            finally:
                other_module.enddoingsomething()
        return wrapper
    

Then I test it using python-mock:

    
    
    #test_utilities.py
    def test_decor(self):
        mock_func = Mock()
        decorated_func = self.utilities.decor(mock_func)
        decorated_func(1,2,3)
        self.assertTrue(self.other_module.startdoingsomething.called)
        self.assertTrue(self.other_module.enddoingsomething.called)
        mock_func.assert_called_with(1,2,3)
    

But it kicks back with:

    
    
    Traceback (most recent call last):
      File "test_utilities.py", line 25, in test_decor
        decorated_func = Mock(wraps=self.utilities.decor(mock_func))
      File "utilities.py", line 35, in decor
        @wraps(f)
      File "/usr/lib/python2.7/functools.py", line 33, in update_wrapper
        setattr(wrapper, attr, getattr(wrapped, attr))
      File "/usr/local/lib/python2.7/dist-packages/mock.py", line 660, in __getattr__
        raise AttributeError(name)
    AttributeError: __name__
    

I know functools.wraps() is just a helper wrapper. So if I take it out the
test works.

Can I get Mock to play nice with functools.wraps()?

Python 2.7.3


Answer: Just give your mock that attribute:

    
    
    mock_func.__name__ = 'foo'
    

That's it really.

Demo:

    
    
    >>> from functools import wraps
    >>> from mock import Mock
    >>> def decor(f):
    ...     @wraps(f)
    ...     def wrapper(*args, **kwds):
    ...         return f(*args, **kwds)
    ...     return wrapper
    ... 
    >>> mock_func = Mock()
    >>> decor(mock_func)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 2, in decor
      File ".../opt/lib/python2.7/functools.py", line 33, in update_wrapper
        setattr(wrapper, attr, getattr(wrapped, attr))
      File ".../lib/python2.7/site-packages/mock.py", line 660, in __getattr__
        raise AttributeError(name)
    AttributeError: __name__
    >>> mock_func.__name__ = 'foo'
    >>> decor(mock_func)
    <function foo at 0x10c4321b8>
    

Setting `__name__` is perfectly fine; the `@wraps` decorator simply copies
over the `__name__` attribute to the wrapper, and on function objects that
attribute is normally set to a string value. It's a writable attribute on
functions, in any case, and as long as you use strings `function.__name__` can
be set to any value.

