derwiki-adroll / mock

Automatically exported from code.google.com/p/mock
BSD 2-Clause "Simplified" License
0 stars 0 forks source link

Add a __wrapped__ attribute to functions decorated with patch (etc) #185

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. # test.py
import mock

@mock.patch('sys.exit')
def test_spam(exit_mock, tmpdir):
    assert isinstance(exit_mock. mock.Mock)

2. $ py.test test.py

What is the expected output? What do you see instead?

Excpected: Test passes
Actual:
__________________________________ test_spam ___________________________________

    @wraps(func)
    def patched(*args, **keywargs):
        # don't use a with here (backwards compatability with Python 2.4)
        extra_args = []
        entered_patchers = []

        # can't use try...except...finally because of Python 2.4
        # compatibility
        exc_info = tuple()
        try:
            try:
                for patching in patched.patchings:
                    arg = patching.__enter__()
                    entered_patchers.append(patching)
                    if patching.attribute_name is not None:
                        keywargs.update(arg)
                    elif patching.new is DEFAULT:
                        extra_args.append(arg)

                args += tuple(extra_args)
>               return func(*args, **keywargs)
E               TypeError: test_spam() takes exactly 2 arguments (1 given)

What version of the product are you using? On what operating system?

py.test 2.3, mock 1.0

Please provide any additional information below.

Original issue reported on code.google.com by stefan.s...@gmail.com on 1 Nov 2012 at 2:34

GoogleCodeExporter commented 9 years ago
I’m not sure whether pytest or mock is to blame, so I also posted this here: 
https://bitbucket.org/hpk42/pytest/issue/217/mockpatch-decorator-and-test-fixtur
es-don

Original comment by stefan.s...@gmail.com on 1 Nov 2012 at 2:35

GoogleCodeExporter commented 9 years ago
I'm afraid that py.test funcargs are a bit of a mystery to me. It's possibly 
because py.test does *argument name* introspection for funcargs and decorating 
the test function loses that information.

Original comment by fuzzyman on 1 Nov 2012 at 2:56

GoogleCodeExporter commented 9 years ago
Hi Michael, all,

Yes, pytest introspects function argument names (since 2-3 years).  
@mock.patch() returns a *args/**kwargs signature function which looses that 
information.  One solution is that mock retains the original function 
signature.  That probably requires dynamically creating a new function - i 
guess something you don't want to do.

Another work-around solution might be that mock provides a way recognize a 
mock.patch decorated function and recover the original function. (maybe 
functools.partial could be used for it? It works like this already IISIC). I'd 
see to make this seemlessly work from pytest side subsequently.  Sounds like a 
plan?
best,
holger

Original comment by holger.k...@gmail.com on 1 Nov 2012 at 3:40

GoogleCodeExporter commented 9 years ago
It's not just dynamically generating a new function - but using exec/eval to do 
it (there's no way of programatically creating a function with a specific 
signature in python). I'm not particularly keen to do that.

functools.wraps in Python 3.3 adds a __wrapped__ (?) attribute to decorated 
functions to get to the underlying function. I could add that to the patch 
decorator. py.test would have to walk __wrapped__ attributes to find the 
"bottommost" function.

Original comment by fuzzyman on 1 Nov 2012 at 3:44

GoogleCodeExporter commented 9 years ago
Such a __wrapped__ should help - each @mock.patch() adds exactly one positional 
argument at the beginning, right?  I guess this could help to jointly resolve 
the situation and indeed pytest would get enough information to do its work.

As to dynamic code generation, i am not pushing doing that.  FWIW there is a 
programmatic way by using inspect.formatargspec/getargspec.   Retaining the 
signature (in general) helps to make tools like pydoc/sphinx and pytest work 
nicely.  This is also one of the goals of the 27K times downloaded "venusian" 
project http://docs.pylonsproject.org/projects/venusian/en/latest/

Original comment by holger.k...@gmail.com on 1 Nov 2012 at 3:55

GoogleCodeExporter commented 9 years ago
inspect.formatargspec generates a string that can be exec'd or eval'ed to 
dynamically generate a function. That's the technique I was talking about (and 
in fact is used by the mock autospec functionality). You still can't generate a 
function with a specific signature directly - but you can generate code to 
create one.

venusian looks interesting, but beyond the scope of mock...  

I'll add the __wrapped__ attribute to functions decorated by patch and friends.

Original comment by fuzzyman on 1 Nov 2012 at 4:07

GoogleCodeExporter commented 9 years ago
great. Any chance this could happen within the next 3 days?

Original comment by holger.k...@gmail.com on 3 Nov 2012 at 2:47

GoogleCodeExporter commented 9 years ago
I've just got back from a weekend away. I'll *try* and do it tomorrow, but no 
promises :-/

Original comment by fuzzyman on 4 Nov 2012 at 11:45

GoogleCodeExporter commented 9 years ago
Fixed in 1.0.1

Original comment by fuzzyman on 5 Nov 2012 at 10:25

GoogleCodeExporter commented 9 years ago
Works for me. Thank you very much for the quick resolution. :-)

Original comment by stefan.s...@gmail.com on 6 Nov 2012 at 10:42