How to mock a function in multiple modules

27 Views Asked by At

I am currently working on extending a third-party code base. This code base unfortunately tightly couples its get_args with every other function. get_args is basically just a getter for a global object _ARGS. Now, I'd like to modify the args for a single function call without actually modifying the global object itself.

To this end, I used unittest.mock.patch to patch the get_args function, and while it succeeds in patching it in my target function f, it does not translate to functions called by f if they are in other modules. The reason is, of course, that I only patch get_args in the module with the called function f.

Is it possible to mock every subsequent call to get_args within my with block?

My current approach might not be the best one to tackle this problem, so I'm also open to alternative solutions to this problem.

Minimum reproducible example:

My code main.py:

from argparse import Namespace
import unittest.mock

from mod0 import get_args
from mod1 import f1


def new_get_args():
    return Namespace(**{
        **get_args().__dict__,
        'a': 'a',
    })


def main():
    with unittest.mock.patch('mod1.get_args', new=new_get_args):
        f1()

main()

Module mod0:

from argparse import Namespace

_ARGS = None


def get_args():
    global _ARGS
    if _ARGS is None:
        _ARGS = Namespace(a=1, b=2)
    return _ARGS

Module mod1:

from mod0 import get_args
from mod2 import f2


def f1():
    args = get_args()
    args.c = 3
    print(f"[f1] Args: {args}")
    f2()

Module mod2:

from mod0 import get_args


def f2():
    args = get_args()
    print(f"[f2] Args: {args}")

Result:

[f1] Args: Namespace(a='a', b=2, c=3)
[f2] Args: Namespace(a=1, b=2)

What I need:

[f1] Args: Namespace(a='a', b=2, c=3)
[f2] Args: Namespace(a='a', b=2, c=3)
0

There are 0 best solutions below