On 10/23/2013 08:36 PM, holger krekel wrote:
> Hi Daniel,
>
> On Wed, Oct 23, 2013 at 19:30 +0200, Daniel Nouri wrote:
>> Dear all
>>
>> I have a function 'somefunc' in module 'a'. Another module 'b' imports
>> that function with a "from import":
>>
>> a.py:
>>
>> def somefunc():
>> return 'hey!'
>>
>> b.py:
>>
>> from a import somefunc
>>
>> def someotherfunc():
>> return somefunc() + 'there'
>>
>>
>> I want to now test 'someotherfunc' while patching away 'somefunc':
>>
>> def test_someotherfunc(monkeypatch):
>> from b import someotherfunc
>> monkeypatch.setattr('a.somefunc', lambda: 'eh?')
>> someotherfunc()
>>
>> But this will fail, since 'b' imported somefunc from 'a' before we
>> monkey patched the module's attribute. Without a "from import", this
>> would work:
>>
>> b.py:
>>
>> import a
>>
>> def someotherfunc():
>> return a.somefunc() + 'there'
>>
>> What I would normally resort to is patch somefunc's func_code, so that
>> even code that used a "from import" before I could patch will use the
>> patched version.
>
> I would probably patch b's somefunc. Note that setattr() will by
> default raise an exception if "b.somefunc" does not exist.
Good point. But this is where my example above actually differs from
where I was trying to do this; namely in a fixture. So ideally all code
paths that called somefunc would get the dummy. (I'm using this fixture
in a functional test.)
> As to patching func_code: good idea, i had forgotten about assigning
> func_code.
> In earlier Python2.X versions assigning to func_code didn't work but i just
> checked that it does on py27 and py33. So it's definitely worthwhile to
> pursue. In your example you could do:
>
> monkeypatch.setattr('a.somefunc.func_code', (lambda: 'eh?').func_code)
>
> and it works (needs to use __code__ on py3).
Nice. I hadn't thought about using setattr in this way!
It's still a little unfortunate that monkeypatch.setattr works fine for
very similar cases (e.g. class methods); just not for this one. At the
same time, it's clearly following Python's own semantics and works just
like setting an attribute.
> I guess we could think about a
>
> monkeypatch.setcode("a.somefunc", lambda: 'eh?')
>
> helper. You could either pass in a function or a code object.
>
> We could even think about allowing non-string targets:
>
> monkeypatch.setcode(a.somefunc, lambda: 'eh?')
>
> Or would "monkeypatch.setfunc" be a better name?
Hmm, I'm not sure how obscure my case is. And I guess even with a
setcode in the API, people would still need to learn the hard way that
the case of patching a module's attributes is special. I would
certainly use setcode if it existed though. :-)
Daniel
_______________________________________________
Pytest-dev mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pytest-dev