Ejemplo n.º 1
0
 def test_sextractor_not_installed(self, which_mock):
     which_mock.return_value = []
     with self.assertRaises(astromatic.SExtractorNotInstalled):
         astromatic.sextractor_version()
     which_mock.assert_has_calls(
         [mock.call('sextractor'),
          mock.call('sex')])
Ejemplo n.º 2
0
    def test_sextractor_version(self):

        # We have no other way of knowing the SExtractor version that is
        # installed on the system than running it ourselves with the --version
        # option and examine its output: if sextractor_version() returns the
        # tuple (2, 8, 6), for example, that means that the SExtractor version
        # number must contain the string '2.8.6'. What we are doing, basically,
        # is to test the functionality the other way around: we take the tuple
        # that sextractror_version() outputs, transform it back to a string and
        # verify that it corresponds to what the --version option prints.

        try:
            executable = methods.which(*astromatic.SEXTRACTOR_COMMANDS)[0]
        except IndexError:
            msg = "SExtractor not found in the current environment"
            raise astromatic.SExtractorNotInstalled(msg)

        with tempfile.TemporaryFile() as fd:
            args = [executable, '--version']
            subprocess.check_call(args, stdout=fd)
            fd.seek(0)
            # For example: "SExtractor version 2.5.0 (2006-07-14)"
            stdout = '\n'.join(fd.readlines())

        version = astromatic.sextractor_version()
        # From, for example, (2, 5, 0) to '2.5.0'
        version_str = '.'.join(str(x) for x in version)
        self.assertTrue(version_str in stdout)

        # SExtractorNotInstalled must be raised if no SExtractor executable is
        # detected. In order to simulate this, mock os.environ and clear the
        # 'PATH' environment variable. In this manner, as the list of paths to
        # directories where executables may be found is empty, SExtractor (and
        # any other command) will appear as not installed on the system.

        environment_copy = copy.deepcopy(os.environ)
        with mock.patch.object(os, 'environ', environment_copy) as mocked:
            mocked['PATH'] = ''
            with self.assertRaises(astromatic.SExtractorNotInstalled):
                astromatic.sextractor_version()
Ejemplo n.º 3
0
    def test_sextractor(self):

        # Note that, being a third-party program, we cannot write a unit test
        # to check that SExtractor works as it should (i.e., that it correctly
        # detects astronomical sources on FITS images): that is something that
        # Emmanuel Bertin, its developer, certainly takes care of. What we must
        # test for is whether astromatic.sextractor(), our Python wrapper to
        # call SExtractor, works as we expect and raises the appropriate errors
        # when something goes wrong. Nothing less, nothing more.

        # First, the most probable scenario: our wrapper should run SExtractor
        # on any FITS image that it receives (here we use some downloaded from
        # the STScI Digitized Sky Survey) and return the path to the output
        # catalog, which astromatic.Catalog should be always able to parse.

        kwargs = dict(stdout=open(os.devnull), stderr=open(os.devnull))
        for img_path in dss_images.TEST_IMAGES:
            catalog_path = astromatic.sextractor(img_path, **kwargs)
            try:
                Catalog(catalog_path)
            finally:
                os.unlink(catalog_path)

        # SExtractorNotInstalled must be raised if no SExtractor executable is
        # detected. In order to simulate this, mock os.environ and clear the
        # 'PATH' environment variable. In this manner, as the list of paths to
        # directories where executables may be found is empty, SExtractor (and
        # any other command) will appear as not installed on the system.

        environment_copy = copy.deepcopy(os.environ)
        with mock.patch.object(os, 'environ', environment_copy) as mocked:
            mocked['PATH'] = ''
            with self.assertRaises(astromatic.SExtractorNotInstalled):
                astromatic.sextractor(img_path)

        # IOError is raised if any of the four SExtractor configuration files
        # is not readable or does no exist. To test that, mock the module-level
        # variables so that they first refer to an unreadable file and later to
        # a nonexistent one.

        for variable in self.SEXTRACTOR_MODULE_VARS:

            path = eval('astromatic.%s' % variable)
            ext = os.path.splitext(path)[1]
            copy_path = get_nonexistent_path(ext=ext)
            shutil.copy2(path, copy_path)

            with mock.patch.object(astromatic, variable, copy_path):

                # chmod u-r
                mode = stat.S_IMODE(os.stat(copy_path)[stat.ST_MODE])
                mode ^= stat.S_IRUSR
                os.chmod(copy_path, mode)

                args = IOError, astromatic.sextractor, img_path
                self.assertFalse(os.access(copy_path, os.R_OK))
                self.assertRaises(*args)

                os.unlink(copy_path)
                self.assertFalse(os.path.exists(copy_path))
                self.assertRaises(*args)

        # SExtractorUpgradeRequired must be raised if the version of SExtractor
        # that is installed on the system is older than that defined in the
        # SEXTRACTOR_REQUIRED_VERSION module-level variable. We cannot (easily,
        # at least) change the version that is installed, but we can test for
        # this by changing the value of the required version, setting it to a
        # tuple that is greater than the installed version of SExtractor.

        # From, for example, (2, 8, 6) to (2, 8, 7)
        version = list(astromatic.sextractor_version())
        version[-1] += 1
        version = tuple(version)

        with mock.patch.object(astromatic, 'SEXTRACTOR_REQUIRED_VERSION',
                               version):
            with self.assertRaises(astromatic.SExtractorUpgradeRequired):
                astromatic.sextractor(img_path)

        # The SExtractorError exception is raised if anything goes wrong during
        # the execution of SExtractor (in more technical terms, if its return
        # code is other than zero). For example, the FITS image may not exist,
        # or a non-numerical value may be assigned to a parameter that expects
        # one, or we could try to run SExtractor on a FITS extension that does
        # not exist. These three are just examples: SExtractor may fail for
        # many different reasons that we cannot even foresee.

        # (1) Try to run SExtractor on a non-existent image
        kwargs = dict(stdout=open(os.devnull), stderr=open(os.devnull))
        nonexistent_path = get_nonexistent_path(ext='.fits')
        with self.assertRaises(astromatic.SExtractorError):
            astromatic.sextractor(nonexistent_path, **kwargs)

        # (2) The DETECT_MINAREA parameter (minimum number of pixels above the
        # threshold needed to trigger detection) expects an integer. Assigning
        # to it a string will cause SExtractor to complain ("keyword out of
        # range") and abort its execution.

        kwargs['options'] = dict(DETECT_MINAREA='Y')
        with self.assertRaises(astromatic.SExtractorError):
            astromatic.sextractor(img_path, **kwargs)
        del kwargs['options']

        # (3) Try to run SExtractor on a nonexistent FITS extension.
        hdulist = pyfits.open(img_path, mode='readonly')
        nextensions = len(hdulist)
        hdulist.close()

        kwargs['ext'] = nextensions + 1
        with self.assertRaises(astromatic.SExtractorError):
            astromatic.sextractor(img_path, **kwargs)

        # TypeError raised if 'options' is not a dictionary
        kwargs = dict(options=['DETECT_MINAREA', '5'])
        with self.assertRaises(TypeError):
            astromatic.sextractor(img_path, **kwargs)

        # ... or if any of its elements is not a string.
        kwargs['options'] = {'DETECT_MINAREA': 125}
        with self.assertRaises(TypeError):
            astromatic.sextractor(img_path, **kwargs)

        # TypeError also raised if 'ext' is not an integer...
        kwargs = dict(ext=0.56)
        with self.assertRaises(TypeError):
            astromatic.sextractor(img_path, **kwargs)

        # ... even if it is a float but has nothing after the decimal point
        # (for which the built-in is_integer() function would return True).
        kwargs = dict(ext=0.0)
        with self.assertRaises(TypeError):
            astromatic.sextractor(img_path, **kwargs)
Ejemplo n.º 4
0
 def test_sextractor_version(self, check_output_mock):
     check_output_mock.return_value = "SExtractor version 2.19.5 (2014-03-19)\n"
     self.assertEqual((2, 19, 5), astromatic.sextractor_version())
     check_output_mock.assert_called_once()
Ejemplo n.º 5
0
    match = re.match(regexp, version)
    if match is None:
        msg = "cannot extract version from '%s' (%s)" % (version, module)
        raise Exception(msg)
    else:
        version = match.group(0)
        return str_to_version(version)


# For each module whose minimum version has been defined in requirements.txt,
# create an import hook and add it to sys.meta_path, which is searched before
# any implicit default finders or sys.path.

for module, version in [
    ("numpy", (1, 7, 1)),
    ("aplpy", (0, 9, 9)),
    ("scipy", (0, 12, 0)),
    ("matplotlib", (1, 2, 1)),
    ("mock", (1, 0, 1)),
    ("pyfits", (3, 1, 2)),
    ("pyraf", (2, 1, 1)),
    ("uncertainties", (2, 4, 1)),
]:
    hook = RequireModuleVersionHook(module, version, get__version__)
    sys.meta_path.append(hook)

# If the minimum version is not met, raise SExtractorUpgradeRequired as
# soon as possible, instead of waiting until we attempt to run SExtractor.
if astromatic.sextractor_version() < astromatic.SEXTRACTOR_REQUIRED_VERSION:
    raise astromatic.SExtractorUpgradeRequired()
Ejemplo n.º 6
0
    regexp = "\d+(\.\d+)+"
    match = re.match(regexp, version)
    if match is None:
        msg = "cannot extract version from '%s' (%s)" % (version, module)
        raise Exception(msg)
    else:
        version = match.group(0)
        return str_to_version(version)


# For each module whose minimum version has been defined in requirements.txt,
# create an import hook and add it to sys.meta_path, which is searched before
# any implicit default finders or sys.path.

for module, version in [('numpy', (1, 7, 1)), ('aplpy', (0, 9, 9)),
                        ('scipy', (0, 12, 0)), ('matplotlib', (1, 2, 1)),
                        ('mock', (1, 0, 1)), ('pyfits', (3, 1, 2)),
                        ('pyraf', (2, 1, 1)), ('uncertainties', (2, 4, 1))]:
    hook = RequireModuleVersionHook(module, version, get__version__)
    sys.meta_path.append(hook)

# If the minimum version is not met, raise SExtractorUpgradeRequired as
# soon as possible, instead of waiting until we attempt to run SExtractor.
sextractor_version = astromatic.sextractor_version()
sextractor_minimum = astromatic.SEXTRACTOR_REQUIRED_VERSION
if not sextractor_version >= sextractor_minimum:
    msg = "SExtractor >= %s is required, found %s"
    args = (version_to_str(sextractor_minimum),
            version_to_str(sextractor_version))
    raise astromatic.SExtractorUpgradeRequired(msg % args)