[pytest-dev] monkeypatch.setattr of functions and from imports

holger krekel holger at merlinux.eu
Wed Oct 23 20:36:14 CEST 2013


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.

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).  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?

best,
holger


More information about the Pytest-dev mailing list