buhtz
buhtz

Reputation: 12202

patch_default_args is True but PyFakeFS don't patch pathlib.Path.cwd()

I use pathlib.Path.cwd() as a default argument in a function signature.

def foobar(dir_arg=pathlib.Path.cwd() / 'logs'):
    # ...

When I unittest this function with pyfakefs the argument isn't patched. But the patch_default_args is set to True.

Here is the MWE.

#!/usr/bin/env python3
import pyfakefs.fake_filesystem_unittest as pyfakefs_ut
import pathlib


class Logging(pyfakefs_ut.TestCase):

    def setUp(self):
        print('PyFakeFS activated')
        self.setUpPyfakefs(
            allow_root_user=False,
            patch_default_args=True)

    def test_foobar(self):
        foobar()


def foobar(dir_arg=pathlib.Path.cwd() / 'logs'):
    dir_local = pathlib.Path.cwd() / 'logs'

    print(f'dir_arg: {dir_arg}')
    print(f'dir_local: {dir_local}')


if __name__ == '__main__':
    print('Without PyFakeFS')
    foobar()

Run this as a test (with activated pyfakefs):

python3 -m unittest x.py
PyFakeFS activated
dir_arg: /home/user/tab-cloud/_transfer/logs
dir_local: /logs
.
----------------------------------------------------------------------
Ran 1 test in 0.744s

OK

Run this usual without pyfakefs

./x.py
Without PyFakeFS
dir_arg: /home/user/tab-cloud/_transfer/logs
dir_local: /home/user/tab-cloud/_transfer/logs

The expected output when run as a test would be

PyFakeFS activated
dir_arg: /logs
dir_local: /logs

There is also an open Issue about that problem. But now I think this isn't a bug but more a problem in front of the monitor.

Upvotes: 0

Views: 110

Answers (1)

buhtz
buhtz

Reputation: 12202

My answer is based on the feedback of PyFakeFS's maintainer.

The question fits to an edge case that is not accounted by the patch_default_args argument. It patches filesystem functions but not classes (as in my case).

A solution is to use the modules_to_reload argument.

To demonstrate the solution with code I separated the MWE from the question into two files.

Here is x.py:

#!/usr/bin/env python3
import pathlib


def foobar(dir_arg=pathlib.Path.cwd() / 'logs'):
    dir_local = pathlib.Path.cwd() / 'logs'

    print(f'dir_arg: {dir_arg}')
    print(f'dir_local: {dir_local}')


if __name__ == '__main__':
    print('Without PyFakeFS')
    foobar()

This is test_x.py

#!/usr/bin/env python3
import pyfakefs.fake_filesystem_unittest as pyfakefs_ut
import pathlib
import x

class Logging(pyfakefs_ut.TestCase):

    def setUp(self):
        print('PyFakeFS activated')
        self.setUpPyfakefs(
            allow_root_user=False,
            modules_to_reload=[x])

    def test_foobar(self):
        x.foobar()

Reload order

When you use modules_to_reload the load order is also important in some edge cases. An example and a solution can be found here.

Regarding to the MWE here. When foobar() would be located in a sub-module (sub_x.py) which is imported implicite in the x/__init__.py then the sub-module should be loaded first.

modules_to_reload=[x.sub_x, x]

Upvotes: 1

Related Questions