astropy / astroquery

Functions and classes to access online data resources. Maintainers: @keflavich and @bsipocz and @ceb8
http://astroquery.readthedocs.org/en/latest/
BSD 3-Clause "New" or "Revised" License
703 stars 396 forks source link

Unit tests failed during parallel invocation: FileNotFoundError: [Errno 2] No such file or directory #2968

Open Hellseher opened 7 months ago

Hellseher commented 7 months ago

Hi,

During scheduled update of astroquery in Guix I tried to speed up unit tests with pytest-xdist. It's minimized the wait time especially on multi threaded CI farm. Unfortunately by enabling it I've got 2 tests failed:


python pytest \
    --color=yes \
    --doctest-continue-on-failure \
    --doctest-rst \
    --pyargs astroquery \
    -m not remote_data \
    -n auto \
    -v 
...
____________________ TestXMMNewton.test_get_epic_lightcurve ____________________
[gw7] linux -- Python 3.10.7 /gnu/store/jh59fh48mcffyz5wmsjj0p96xkkflbz0-python-wrapper-3.10.7/bin/python

self = <astroquery.esa.xmm_newton.tests.test_xmm_newton.TestXMMNewton object at 0x7fff62909120>
tmp_path = PosixPath('/tmp/guix-build-python-astroquery-0.4.7.drv-0/pytest-of-nixbld/pytest-0/popen-gw7/test_get_epic_lightcurve0')

    def test_get_epic_lightcurve(self, tmp_path):
        path = Path(tmp_path, "tarfile.tar")
>       self._create_tar(path, self._files)

/gnu/store/8xaif1f5787v7kmvw3xvpng36z2k36y9-python-astroquery-0.4.7/lib/python3.10/site-packages/astroquery/esa/xmm_newton/tests/test_xmm_newton.py:473:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/gnu/store/8xaif1f5787v7kmvw3xvpng36z2k36y9-python-astroquery-0.4.7/lib/python3.10/site-packages/astroquery/esa/xmm_newton/tests/test_xmm_newton.py:265: in _create_tar
    shutil.rmtree(os.path.join(ob_name, ftype))
/gnu/store/4r7k7ipiaqkdf4lmnxwmbz0wx2yzygzc-python-3.10.7/lib/python3.10/shutil.py:724: in rmtree
    _rmtree_safe_fd(fd, path, onerror)
/gnu/store/4r7k7ipiaqkdf4lmnxwmbz0wx2yzygzc-python-3.10.7/lib/python3.10/shutil.py:680: in _rmtree_safe_fd
    onerror(os.unlink, fullname, sys.exc_info())
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

topfd = 14, path = '0405320501/pps'
onerror = <function rmtree.<locals>.onerror at 0x7fff646c36d0>

    def _rmtree_safe_fd(topfd, path, onerror):
        try:
            with os.scandir(topfd) as scandir_it:
                entries = list(scandir_it)
        except OSError as err:
            err.filename = path
            onerror(os.scandir, path, sys.exc_info())
            return
        for entry in entries:
            fullname = os.path.join(path, entry.name)
            try:
                is_dir = entry.is_dir(follow_symlinks=False)
            except OSError:
                is_dir = False
            else:
                if is_dir:
                    try:
                        orig_st = entry.stat(follow_symlinks=False)
                        is_dir = stat.S_ISDIR(orig_st.st_mode)
                    except OSError:
                        onerror(os.lstat, fullname, sys.exc_info())
                        continue
            if is_dir:
                try:
                    dirfd = os.open(entry.name, os.O_RDONLY, dir_fd=topfd)
                    dirfd_closed = False
                except OSError:
                    onerror(os.open, fullname, sys.exc_info())
                else:
                    try:
                        if os.path.samestat(orig_st, os.fstat(dirfd)):
                            _rmtree_safe_fd(dirfd, fullname, onerror)
                            try:
                                os.close(dirfd)
                                dirfd_closed = True
                                os.rmdir(entry.name, dir_fd=topfd)
                            except OSError:
                                onerror(os.rmdir, fullname, sys.exc_info())
                        else:
                            try:
                                # This can only happen if someone replaces
                                # a directory with a symlink after the call to
                                # os.scandir or stat.S_ISDIR above.
                                raise OSError("Cannot call rmtree on a symbolic "
                                              "link")
                            except OSError:
                                onerror(os.path.islink, fullname, sys.exc_info())
                    finally:
                        if not dirfd_closed:
                            os.close(dirfd)
            else:
                try:
>                   os.unlink(entry.name, dir_fd=topfd)
E                   FileNotFoundError: [Errno 2] No such file or directory: 'P0405320501PNS001EXPMAP4000.FTZ'

/gnu/store/4r7k7ipiaqkdf4lmnxwmbz0wx2yzygzc-python-3.10.7/lib/python3.10/shutil.py:678: FileNotFoundError
_______________ TestXMMNewton.test_get_epic_images_invalid_band ________________

[gw5] linux -- Python 3.10.7 /gnu/store/jh59fh48mcffyz5wmsjj0p96xkkflbz0-python-wrapper-3.10.7/bin/python

self = <astroquery.esa.xmm_newton.tests.test_xmm_newton.TestXMMNewton object at 0x7fff6282e4d0>
tmp_path = PosixPath('/tmp/guix-build-python-astroquery-0.4.7.drv-0/pytest-of-nixbld/pytest-0/popen-gw5/test_get_epic_images_invalid_b0')
capsys = <_pytest.capture.CaptureFixture object at 0x7fff646cf3d0>

    def test_get_epic_images_invalid_band(self, tmp_path, capsys):
        path = Path(tmp_path, "tarfile.tar")
        _invalid_band = 10
>       self._create_tar(path, self._files)

/gnu/store/8xaif1f5787v7kmvw3xvpng36z2k36y9-python-astroquery-0.4.7/lib/python3.10/site-packages/astroquery/esa/xmm_newton/tests/test_xmm_newton.py:375:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/gnu/store/8xaif1f5787v7kmvw3xvpng36z2k36y9-python-astroquery-0.4.7/lib/python3.10/site-packages/astroquery/esa/xmm_newton/tests/test_xmm_newton.py:263: in _create_tar
    tar.add(os.path.join(ob_name, ftype, f))
/gnu/store/4r7k7ipiaqkdf4lmnxwmbz0wx2yzygzc-python-3.10.7/lib/python3.10/tarfile.py:1986: in add
    tarinfo = self.gettarinfo(name, arcname)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <tarfile.TarFile object at 0x7fff646cd630>
name = '0405320501/pps/P0405320501M2S003EXPMAP2000.FTZ'
arcname = '0405320501/pps/P0405320501M2S003EXPMAP2000.FTZ', fileobj = None

    def gettarinfo(self, name=None, arcname=None, fileobj=None):
        """Create a TarInfo object from the result of os.stat or equivalent
           on an existing file. The file is either named by `name', or
           specified as a file object `fileobj' with a file descriptor. If
           given, `arcname' specifies an alternative name for the file in the
           archive, otherwise, the name is taken from the 'name' attribute of
           'fileobj', or the 'name' argument. The name should be a text
           string.
        """
        self._check("awx")

        # When fileobj is given, replace name by
        # fileobj's real name.
        if fileobj is not None:
            name = fileobj.name

        # Building the name of the member in the archive.
        # Backward slashes are converted to forward slashes,
        # Absolute paths are turned to relative paths.
        if arcname is None:
            arcname = name
        drv, arcname = os.path.splitdrive(arcname)
        arcname = arcname.replace(os.sep, "/")
        arcname = arcname.lstrip("/")

        # Now, fill the TarInfo object with
        # information specific for the file.
        tarinfo = self.tarinfo()
        tarinfo.tarfile = self  # Not needed

        # Use os.stat or os.lstat, depending on if symlinks shall be resolved.
        if fileobj is None:
            if not self.dereference:
>               statres = os.lstat(name)
E               FileNotFoundError: [Errno 2] No such file or directory: '0405320501/pps/P0405320501M2S003EXPMAP2000.FTZ'

...

/gnu/store/4r7k7ipiaqkdf4lmnxwmbz0wx2yzygzc-python-3.10.7/lib/python3.10/tarfile.py:1865: FileNotFoundError
FAILED esa/xmm_newton/tests/test_xmm_newton.py::TestXMMNewton::test_get_epic_lightcurve
FAILED esa/xmm_newton/tests/test_xmm_newton.py::TestXMMNewton::test_get_epic_images_invalid_band
...
===== 2 failed, 1456 passed, 8 skipped, 2 xfailed, 114 warnings in 13.25s ======
...
``
keflavich commented 7 months ago

I can't reproduce this with a standard test run. Is it possible this was a temporary failure driven by the parallel run?

$ pytest astroquery/esa/xmm_newton --remote-data
============================================================================================================= test session starts =============================================================================================================
platform darwin -- Python 3.10.12, pytest-7.4.2, pluggy-1.3.0

Running tests with astroquery version 0.4.7.dev9202_testrun_testrun.

Running tests with astropy_helpers version 2.0.9.
Running tests in astroquery/esa/xmm_newton.

Date: 2024-03-18T13:39:05

Platform: macOS-13.6-arm64-i386-64bit

Executable: /Users/adam/mambaforge/envs/py310forge/bin/python3.10

Full Python Version:
3.10.12 | packaged by conda-forge | (main, Jun 23 2023, 22:41:52) [Clang 15.0.7 ]

encodings: sys: utf-8, locale: UTF-8, filesystem: utf-8
byteorder: little
float info: dig: 15, mant_dig: 15

Package versions:
Numpy: 1.26.2
Matplotlib: 3.7.2
Astropy: 6.1.dev589+g2390bf8f8f
regions: 0.8
pyVO: 1.5.dev228+ga035576
mocpy: not available
astropy-healpix: 1.0.0
vamdclib: not available

Using Astropy options: remote_data: any.

rootdir: /Users/adam/repos/astroquery
configfile: setup.cfg
plugins: anyio-4.0.0, remotedata-0.4.1, asdf-3.0.0, doctestplus-1.2.1, astropy-header-0.2.2
collected 34 items

astroquery/esa/xmm_newton/tests/test_xmm_newton.py ..........................                                                                                                                                                           [ 76%]
astroquery/esa/xmm_newton/tests/test_xmm_newton_remote.py ........                                                                                                                                                                      [100%]

======================================================================================================== 34 passed in 84.28s (0:01:24) ========================================================================================================
bsipocz commented 7 months ago

It's totally possible to have bugs in these tests e.g. one assumes a file is around that a previous test was creating and that hasn't yet been cleaned up. Any further investigation and PRs to fix it are more than welcome!

Hellseher commented 7 months ago

@keflavich as @bsipocz mentioned it could be a race condition during parallel run of the tests which I tried to enable. It passes just fine in single thread invocation.

bsipocz commented 7 months ago

(fwiw, I do see these failures locally, in fact I see a couple more of the same kind for the esa.xmm_newton module (6 instead of 2))