Skip to content

test_del hangs when running tests on pypy #43

@mgorny

Description

@mgorny

Both cases of test_del hang indefinitely on PyPy. This is probably due to the differences of how PyPy does (not) perform GC.

Here's a traceback from pytest after adding a timeout:

$ pytest -v --timeout=15 test.py
=============================================================== test session starts ===============================================================
platform linux2 -- Python 2.7.13[pypy-6.0.0-final], pytest-3.8.2, py-1.5.4, pluggy-0.7.1 -- /usr/bin/pypy
cachedir: .pytest_cache
hypothesis profile 'default' -> database=DirectoryBasedExampleDatabase('/tmp/py-filelock/.hypothesis/examples')
rootdir: /tmp/py-filelock, inifile:
plugins: virtualenv-1.2.11, timeout-1.2.1, shutil-1.2.11, mock-1.10.0, hypothesis-3.74.3, backports.unittest-mock-1.4
timeout: 15.0s method: signal
collected 23 items                                                                                                                                

test.py::FileLockTest::test_context PASSED                                                                                                  [  4%]
test.py::FileLockTest::test_context1 PASSED                                                                                                 [  8%]
test.py::FileLockTest::test_default_timeout PASSED                                                                                          [ 13%]
test.py::FileLockTest::test_del FAILED                                                                                                      [ 17%]
test.py::FileLockTest::test_nested PASSED                                                                                                   [ 21%]
test.py::FileLockTest::test_nested1 PASSED                                                                                                  [ 26%]
test.py::FileLockTest::test_nested_forced_release PASSED                                                                                    [ 30%]
test.py::FileLockTest::test_simple PASSED                                                                                                   [ 34%]
test.py::FileLockTest::test_threaded PASSED                                                                                                 [ 39%]
test.py::FileLockTest::test_threaded1 PASSED                                                                                                [ 43%]
test.py::FileLockTest::test_timeout PASSED                                                                                                  [ 47%]
test.py::SoftFileLockTest::test_cleanup PASSED                                                                                              [ 52%]
test.py::SoftFileLockTest::test_context PASSED                                                                                              [ 56%]
test.py::SoftFileLockTest::test_context1 PASSED                                                                                             [ 60%]
test.py::SoftFileLockTest::test_default_timeout PASSED                                                                                      [ 65%]
test.py::SoftFileLockTest::test_del FAILED                                                                                                  [ 69%]
test.py::SoftFileLockTest::test_nested PASSED                                                                                               [ 73%]
test.py::SoftFileLockTest::test_nested1 PASSED                                                                                              [ 78%]
test.py::SoftFileLockTest::test_nested_forced_release PASSED                                                                                [ 82%]
test.py::SoftFileLockTest::test_simple PASSED                                                                                               [ 86%]
test.py::SoftFileLockTest::test_threaded PASSED                                                                                             [ 91%]
test.py::SoftFileLockTest::test_threaded1 PASSED                                                                                            [ 95%]
test.py::SoftFileLockTest::test_timeout PASSED                                                                                              [100%]

==================================================================== FAILURES =====================================================================
______________________________________________________________ FileLockTest.test_del ______________________________________________________________

self = <test.FileLockTest testMethod=test_del>

    def test_del(self):
        """
            Tests, if the lock is released, when the object is deleted.
            """
        lock1 = self.LOCK_TYPE(self.LOCK_PATH)
        lock2 = self.LOCK_TYPE(self.LOCK_PATH)
    
        # Acquire lock 1.
        lock1.acquire()
        self.assertTrue(lock1.is_locked)
        self.assertFalse(lock2.is_locked)
    
        # Try to acquire lock 2.
        self.assertRaises(filelock.Timeout, lock2.acquire, timeout = 1) # FIXME (SoftFileLock)
    
        # Delete lock 1 and try to acquire lock 2 again.
        del lock1
    
>       lock2.acquire()

test.py:355: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <filelock.UnixFileLock object at 0x000055eb24d21328>, timeout = -1.0, poll_intervall = 0.05

    def acquire(self, timeout=None, poll_intervall=0.05):
        """
            Acquires the file lock or fails with a :exc:`Timeout` error.
    
            .. code-block:: python
    
                # You can use this method in the context manager (recommended)
                with lock.acquire():
                    pass
    
                # Or use an equivalent try-finally construct:
                lock.acquire()
                try:
                    pass
                finally:
                    lock.release()
    
            :arg float timeout:
                The maximum time waited for the file lock.
                If ``timeout <= 0``, there is no timeout and this method will
                block until the lock could be acquired.
                If ``timeout`` is None, the default :attr:`~timeout` is used.
    
            :arg float poll_intervall:
                We check once in *poll_intervall* seconds if we can acquire the
                file lock.
    
            :raises Timeout:
                if the lock could not be acquired in *timeout* seconds.
    
            .. versionchanged:: 2.0.0
    
                This method returns now a *proxy* object instead of *self*,
                so that it can be used in a with statement without side effects.
            """
        # Use the default timeout, if no timeout is provided.
        if timeout is None:
            timeout = self.timeout
    
        # Increment the number right at the beginning.
        # We can still undo it, if something fails.
        with self._thread_lock:
            self._lock_counter += 1
    
        lock_id = id(self)
        lock_filename = self._lock_file
        start_time = time.time()
        try:
            while True:
                with self._thread_lock:
                    if not self.is_locked:
                        logger().debug('Attempting to acquire lock %s on %s', lock_id, lock_filename)
                        self._acquire()
    
                if self.is_locked:
                    logger().info('Lock %s acquired on %s', lock_id, lock_filename)
                    break
                elif timeout >= 0 and time.time() - start_time > timeout:
                    logger().debug('Timeout on acquiring lock %s on %s', lock_id, lock_filename)
                    raise Timeout(self._lock_file)
                else:
                    logger().debug(
                        'Lock %s not acquired on %s, waiting %s seconds ...',
                        lock_id, lock_filename, poll_intervall
                    )
>                   time.sleep(poll_intervall)
E                   Failed: Timeout >15.0s

filelock.py:284: Failed
____________________________________________________________ SoftFileLockTest.test_del ____________________________________________________________

self = <test.SoftFileLockTest testMethod=test_del>

    def test_del(self):
        """
            Tests, if the lock is released, when the object is deleted.
            """
        lock1 = self.LOCK_TYPE(self.LOCK_PATH)
        lock2 = self.LOCK_TYPE(self.LOCK_PATH)
    
        # Acquire lock 1.
        lock1.acquire()
        self.assertTrue(lock1.is_locked)
        self.assertFalse(lock2.is_locked)
    
        # Try to acquire lock 2.
        self.assertRaises(filelock.Timeout, lock2.acquire, timeout = 1) # FIXME (SoftFileLock)
    
        # Delete lock 1 and try to acquire lock 2 again.
        del lock1
    
>       lock2.acquire()

test.py:355: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <filelock.SoftFileLock object at 0x00007fb06012fbb0>, timeout = -1.0, poll_intervall = 0.05

    def acquire(self, timeout=None, poll_intervall=0.05):
        """
            Acquires the file lock or fails with a :exc:`Timeout` error.
    
            .. code-block:: python
    
                # You can use this method in the context manager (recommended)
                with lock.acquire():
                    pass
    
                # Or use an equivalent try-finally construct:
                lock.acquire()
                try:
                    pass
                finally:
                    lock.release()
    
            :arg float timeout:
                The maximum time waited for the file lock.
                If ``timeout <= 0``, there is no timeout and this method will
                block until the lock could be acquired.
                If ``timeout`` is None, the default :attr:`~timeout` is used.
    
            :arg float poll_intervall:
                We check once in *poll_intervall* seconds if we can acquire the
                file lock.
    
            :raises Timeout:
                if the lock could not be acquired in *timeout* seconds.
    
            .. versionchanged:: 2.0.0
    
                This method returns now a *proxy* object instead of *self*,
                so that it can be used in a with statement without side effects.
            """
        # Use the default timeout, if no timeout is provided.
        if timeout is None:
            timeout = self.timeout
    
        # Increment the number right at the beginning.
        # We can still undo it, if something fails.
        with self._thread_lock:
            self._lock_counter += 1
    
        lock_id = id(self)
        lock_filename = self._lock_file
        start_time = time.time()
        try:
            while True:
                with self._thread_lock:
                    if not self.is_locked:
                        logger().debug('Attempting to acquire lock %s on %s', lock_id, lock_filename)
                        self._acquire()
    
                if self.is_locked:
                    logger().info('Lock %s acquired on %s', lock_id, lock_filename)
                    break
                elif timeout >= 0 and time.time() - start_time > timeout:
                    logger().debug('Timeout on acquiring lock %s on %s', lock_id, lock_filename)
                    raise Timeout(self._lock_file)
                else:
                    logger().debug(
                        'Lock %s not acquired on %s, waiting %s seconds ...',
                        lock_id, lock_filename, poll_intervall
                    )
>                   time.sleep(poll_intervall)
E                   Failed: Timeout >15.0s

filelock.py:284: Failed
====================================================== 2 failed, 21 passed in 38.50 seconds =======================================================

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