Source code for sherpa.utils.testing

#
#  Copyright (C) 2017, 2020, 2021
#  Smithsonian Astrophysical Observatory
#
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License along
#  with this program; if not, write to the Free Software Foundation, Inc.,
#  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#

import importlib
import os

try:
    import pytest
    HAS_PYTEST = True
except ImportError:
    HAS_PYTEST = False

from sherpa.utils.err import RuntimeErr


def _get_datadir():
    """Return the location of the test data files, if installed.

    Returns
    -------
    path : str or None
        The path to the Sherpa test data directory or None.
    """

    try:
        import sherpatest
        datadir = os.path.dirname(sherpatest.__file__)
    except ImportError:
        try:
            import sherpa
            datadir = os.path.join(os.path.dirname(sherpa.__file__), os.pardir,
                                   'sherpa-test-data', 'sherpatest')
            if not os.path.exists(datadir) or not os.path.isdir(datadir) \
               or not os.listdir(datadir):
                # The dir is empty, maybe the submodule was not initialized
                datadir = None
        except ImportError:
            # neither sherpatest nor sherpa can be found, falling back to None
            datadir = None
    return datadir


DATADIR = _get_datadir()


def set_datadir(datadir):
    """Set the data directory.

    Parameters
    ----------
    datadir : str
        The name to the data directory. It must exist.

    Raises
    ------
    OSError
        If datadir is not a directory or is empty.

    """

    if not os.path.exists(datadir) or not os.path.isdir(datadir) \
       or not os.listdir(datadir):
        raise OSError(f"datadir={datadir} is empty or not a directory")

    global DATADIR
    DATADIR = datadir


def get_datadir():
    """Return the data directory.

    Returns
    -------
    datadir : str or None
        The name to the data directory, if it exists.

    """

    return DATADIR


def has_package_from_list(*packages):
    """
    Returns True if at least one of the ``packages`` args is importable.
    """
    for package in packages:
        try:
            importlib.import_module(package)
            return True
        except (ImportError, RuntimeErr):
            # Apparently we need to also catch RuntimeErr (the Sherpa
            # version) as sherpa.image.DS9 can raise it (e.g. when
            # DS9 is not installed).
            pass
    return False


if HAS_PYTEST:
    #  Pytest cannot be assumed to be installed by the regular user, unlike unittest, which is part of Python's
    #  standard library. The decorator will be defined if pytest is missing, but if the tests are run they throw
    #  and exception prompting users to install pytest, in those cases where pytest is not installed automatically.

[docs] def requires_data(test_function): """ Decorator for functions requiring external data (i.e. data not distributed with Sherpa itself) is missing. This is used to skip tests that require such data. See PR #391 for why this is a function: https://github.com/sherpa/sherpa/pull/391 """ condition = DATADIR is None msg = "required test data missing" return pytest.mark.skipif(condition, reason=msg)(test_function)
def requires_package(msg=None, *packages): """ Decorator for test functions requiring specific packages. """ condition = has_package_from_list(*packages) msg = msg or "required module missing among {}.".format( ", ".join(packages)) def decorator(test_function): return pytest.mark.skipif(not condition, reason=msg)(test_function) return decorator
[docs] def requires_plotting(test_function): """ Decorator for test functions requiring a plotting library. """ packages = ('pylab', ) msg = "plotting backend required" return requires_package(msg, *packages)(test_function)
[docs] def requires_pylab(test_function): """ Returns True if the pylab module is available (pylab). Used to skip tests requiring matplotlib """ packages = ('pylab', ) msg = "matplotlib backend required" return requires_package(msg, *packages)(test_function)
[docs] def requires_fits(test_function): """Returns True if a working backend for FITS I/O is importable. Used to skip tests requiring fits_io. The dummy backend itself is not a "working backend" in the sense that it cannot be used to read or write files. """ packages = ('astropy.io.fits', 'pycrates', ) msg = "FITS backend required" return requires_package(msg, *packages)(test_function)
[docs] def requires_group(test_function): """Decorator for test functions requiring group library""" return requires_package("group library required", 'group')(test_function)
[docs] def requires_stk(test_function): """Decorator for test functions requiring stk library""" return requires_package("stk library required", 'stk')(test_function)
[docs] def requires_ds9(test_function): """Decorator for test functions requiring ds9""" return requires_package('ds9 required', 'sherpa.image.ds9_backend')(test_function)
[docs] def requires_xspec(test_function): return requires_package("xspec required", "sherpa.astro.xspec")(test_function)
else: def wrapped(): raise ImportError(PYTEST_MISSING_MESSAGE) def make_fake(): def wrapper(*args, **kwargs): return wrapped return wrapper requires_data = make_fake() requires_plotting = make_fake() requires_pylab = make_fake() requires_fits = make_fake() requires_group = make_fake() requires_stk = make_fake() requires_ds9 = make_fake() requires_xspec = make_fake()
[docs] def requires_package(*args): return make_fake()
PYTEST_MISSING_MESSAGE = "Package `pytest` is missing. Please install `pytest` before running tests or using the test" \ "decorators"