Skip to content

pyfakefs conflicts with fasteners on file locking: bad file descriptor #645

@Wenzel

Description

@Wenzel

Describe the bug
While working on my library based on apache-libcloud, I used the local provider to use the filesystem as an object storage.

While I wanted to mix this with pyfakefs in my unit test, this resulted in a bad file descriptor error in the test teadown, with a potential conflict between the fasteners library and pyfakefs logic.

How To Reproduce
Surprisingly, I found this bug only when running my test suite on Github Actions.
Running it on my laptop works fine, and I have no clue at this point with the Python environment would be different in Github Actions.

I build a small repository for you to test this issue and have a 100% repro:
https://github.com/Wenzel/test_bug_pyfaefs_github_actions/tree/test

(Please note the test branch where from which I have opened a pull request to trigger Github Actions and have a repro)

I created 2 tests:

  • test_fake_fs
  • test_no_fake_fs

Each one will use a fixture to create a libcloud object storage driver based on the local provider (filesystem), but one of them also uses pyfakefs, instead of the real filesystem.

When the fake filesystem is used, the teardown fails, as you can see below. (But only on Github Actions ❓ ❗ )

This is the stacktrace that I get on Github Actions:

==================================== ERRORS ====================================
______________________ ERROR at teardown of test_fake_fs _______________________

self = <fasteners.process_lock._FcntlLock object at 0x7fd702951dc0>
blocking = True, watch = <fasteners._utils.StopWatch object at 0x7fd702951f40>

    def _try_acquire(self, blocking, watch):
        try:
>           self.trylock()

../../../.cache/pypoetry/virtualenvs/test-bad-file-descriptor-gyJ7bpKD-py3.8/lib/python3.8/site-packages/fasteners/process_lock.py:78: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <fasteners.process_lock._FcntlLock object at 0x7fd702951dc0>

    def trylock(self):
>       self._trylock(self.lockfile)

../../../.cache/pypoetry/virtualenvs/test-bad-file-descriptor-gyJ7bpKD-py3.8/lib/python3.8/site-packages/fasteners/process_lock.py:196: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

lockfile = <pyfakefs.fake_filesystem.FakeFileWrapper object at 0x7fd7029670a0>

    @staticmethod
    def _trylock(lockfile):
>       fcntl.lockf(lockfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
E       OSError: [Errno 9] Bad file descriptor

../../../.cache/pypoetry/virtualenvs/test-bad-file-descriptor-gyJ7bpKD-py3.8/lib/python3.8/site-packages/fasteners/process_lock.py:432: OSError

During handling of the above exception, another exception occurred:

fs = <pyfakefs.fake_filesystem.FakeFilesystem object at 0x7fd701d1d610>
tmp_dir = PosixPath('/tmp/tmprt34tghc')

    @pytest.fixture
    def libcloud_driver_fake_fs(fs, tmp_dir):
        """Initializes libcloud based on the fake filesystem PyFakeFS"""
        with libcloud_driver(tmp_dir) as driver:
>           yield driver

tests/test_01.py:32: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
/opt/hostedtoolcache/Python/3.8.12/x64/lib/python3.8/contextlib.py:120: in __exit__
    next(self.gen)
tests/test_01.py:25: in libcloud_driver
    driver.delete_container(cont)
../../../.cache/pypoetry/virtualenvs/test-bad-file-descriptor-gyJ7bpKD-py3.8/lib/python3.8/site-packages/libcloud/storage/drivers/local.py:664: in delete_container
    with LockLocalStorage(path):
../../../.cache/pypoetry/virtualenvs/test-bad-file-descriptor-gyJ7bpKD-py3.8/lib/python3.8/site-packages/libcloud/storage/drivers/local.py:89: in __enter__
    success = self.ipc_lock.acquire(blocking=True,
../../../.cache/pypoetry/virtualenvs/test-bad-file-descriptor-gyJ7bpKD-py3.8/lib/python3.8/site-packages/fasteners/process_lock.py:140: in acquire
    gotten = r(self._try_acquire, blocking, watch)
../../../.cache/pypoetry/virtualenvs/test-bad-file-descriptor-gyJ7bpKD-py3.8/lib/python3.8/site-packages/fasteners/_utils.py:124: in __call__
    return fn(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <fasteners.process_lock._FcntlLock object at 0x7fd702951dc0>
blocking = True, watch = <fasteners._utils.StopWatch object at 0x7fd702951f40>

    def _try_acquire(self, blocking, watch):
        try:
            self.trylock()
        except IOError as e:
            if e.errno in (errno.EACCES, errno.EAGAIN):
                if not blocking or watch.expired():
                    return False
                else:
                    raise _utils.RetryAgain()
            else:
>               raise threading.ThreadError("Unable to acquire lock on"
                                            " `%(path)s` due to"
                                            " %(exception)s" %
                                            {
                                                'path': self.path,
                                                'exception': e,
                                            })
E               RuntimeError: Unable to acquire lock on `b'/tmp/3abf221e48c97e8e7239ce5aceb53aeb2296dd948939cb89e08856bede3265c6.lock'` due to [Errno 9] Bad file descriptor

../../../.cache/pypoetry/virtualenvs/test-bad-file-descriptor-gyJ7bpKD-py3.8/lib/python3.8/site-packages/fasteners/process_lock.py:86: RuntimeError
=========================== short test summary info ============================
ERROR tests/test_01.py::test_fake_fs - RuntimeError: Unable to acquire lock o...
========================== 2 passed, 1 error in 0.28s ==========================

Your environment

My local environment

Linux-5.4.0-88-generic-x86_64-with-Ubuntu-20.04-focal
('Python', '2.7.18 (default, Mar  8 2021, 13:02:45) \n[GCC 9.3.0]')
pyfakefs 4.5.1

I hope you have enough information at this point to better understand / investigate the bug.

Feel free to ask for more context !

Thank you for maintaining pyfakefs 🙂

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions