예제 #1
0
    def test_sextractor_md5sum(self):

        # Testing that astromatic.sextractor_md5sum() works as expected means,
        # by extension, checking that the four SExtractor configuration files
        # (.sex, .param, .conv and .nnw) defined in the 'astromatic' module
        # exist and are readable.

        # If the SExtractor configuration files are not modified in between,
        # two different executions of the function must yield the same hash.
        checksum = astromatic.sextractor_md5sum()
        identical = astromatic.sextractor_md5sum()
        self.assertEqual(checksum, identical)

        # The MD5 hash is that of the concatenation of the lines of the four
        # configuration files and the overriding SExtractor options (i.e., the
        # sequence of strings that are optionally passed to the method). If any
        # of the files is modified, or if an overriding option is given, the
        # MD5 hash returned by the function must be different.
        #
        # In order to test this we are not going to temporarily modify the
        # SExtractor configuration files: although we could make a copy of
        # them, something beyond our control (e.g., the SIGKILL signal or a
        # power outage) could prevent the original file from being restored.
        # Instead, what we will temporarily modify are the module-level
        # variables, so that they refer to a modified copy of the files.

        for variable in self.SEXTRACTOR_MODULE_VARS:

            # The variable must exist and refer to an existing file
            path = eval('astromatic.%s' % variable)
            self.assertTrue(os.path.exists(path))

            # Make a temporary copy of the configuration file and modify it,
            # appending a comment to it. Then, mock the module-level variable
            # so that it refers to the modified configuration file. Although
            # such an irrelevant change does not alter how SExtractor works (as
            # the settings are still the same), the MD5 hash must be different.

            ext = os.path.splitext(path)[1]
            copy_path = get_nonexistent_path(ext=ext)

            try:
                shutil.copy2(path, copy_path)
                with open(copy_path, 'at') as fd:
                    fd.write("# useless comment\n")

                with mock.patch.object(astromatic, variable, copy_path):
                    different = astromatic.sextractor_md5sum()
                    self.assertNotEqual(checksum, different)

            finally:
                os.unlink(copy_path)

        # If overriding options are given, they are also used to compute the
        # MD5 hash. Thus, the hash will be different even if the options have
        # the same value as those defined in the configuration file (although
        # that means that we are not actually overriding anything). Test this
        # for all the options defined in the configuration file: in all cases
        # the returned checksum must be different.

        with open(astromatic.SEXTRACTOR_CONFIG) as fd:

            for line in fd:
                stripped = line.strip()
                if stripped and stripped[0] != '#':  # ignore comment lines
                    key, value = stripped.split()[:2]
                    options = {key: value}
                    different = astromatic.sextractor_md5sum(options)
                    self.assertNotEqual(checksum, different)

                    # Try also overriding the option with a different value
                    # (the result of incrementing it by one). Again, this must
                    # result in a different MD5 hash.

                    try:
                        options[key] = str(int(options[key]) + 1)
                        also_different = astromatic.sextractor_md5sum(options)
                        self.assertNotEqual(checksum, also_different)
                        self.assertNotEqual(different, also_different)

                    except ValueError:
                        pass  # non-numeric option

        # IOError is raised if any of the SExtractor configuration files is not
        # readable or does not exist. To test that, mock again the module-level
        # variables so that they refer to temporary copies of the configuration
        # files, but which are unreadable by the user, first, and then removed.

        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_md5sum
                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)

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

        # ... or if any of its elements is not a string
        kwargs['options'] = {'DETECT_MINAREA': 125}
        with self.assertRaises(TypeError):
            astromatic.sextractor_md5sum(**kwargs)
예제 #2
0
    def test_sextractor_md5sum(self):

        # Testing that astromatic.sextractor_md5sum() works as expected means,
        # by extension, checking that the four SExtractor configuration files
        # (.sex, .param, .conv and .nnw) defined in the 'astromatic' module
        # exist and are readable.

        # If the SExtractor configuration files are not modified in between,
        # two different executions of the function must yield the same hash.
        checksum  = astromatic.sextractor_md5sum()
        identical = astromatic.sextractor_md5sum()
        self.assertEqual(checksum, identical)

        # The MD5 hash is that of the concatenation of the lines of the four
        # configuration files and the overriding SExtractor options (i.e., the
        # sequence of strings that are optionally passed to the method). If any
        # of the files is modified, or if an overriding option is given, the
        # MD5 hash returned by the function must be different.
        #
        # In order to test this we are not going to temporarily modify the
        # SExtractor configuration files: although we could make a copy of
        # them, something beyond our control (e.g., the SIGKILL signal or a
        # power outage) could prevent the original file from being restored.
        # Instead, what we will temporarily modify are the module-level
        # variables, so that they refer to a modified copy of the files.

        for variable in self.SEXTRACTOR_MODULE_VARS:

            # The variable must exist and refer to an existing file
            path = eval('astromatic.%s' % variable)
            self.assertTrue(os.path.exists(path))

            # Make a temporary copy of the configuration file and modify it,
            # appending a comment to it. Then, mock the module-level variable
            # so that it refers to the modified configuration file. Although
            # such an irrelevant change does not alter how SExtractor works (as
            # the settings are still the same), the MD5 hash must be different.

            ext = os.path.splitext(path)[1]
            copy_path = get_nonexistent_path(ext = ext)

            try:
                shutil.copy2(path, copy_path)
                with open(copy_path, 'at') as fd:
                    fd.write("# useless comment\n")

                with mock.patch.object(astromatic, variable, copy_path):
                    different = astromatic.sextractor_md5sum()
                    self.assertNotEqual(checksum, different)

            finally:
                os.unlink(copy_path)

        # If overriding options are given, they are also used to compute the
        # MD5 hash. Thus, the hash will be different even if the options have
        # the same value as those defined in the configuration file (although
        # that means that we are not actually overriding anything). Test this
        # for all the options defined in the configuration file: in all cases
        # the returned checksum must be different.

        with open(astromatic.SEXTRACTOR_CONFIG) as fd:

            for line in fd:
                stripped = line.strip()
                if stripped and stripped[0] != '#': # ignore comment lines
                    key, value = stripped.split()[:2]
                    options = {key : value}
                    different = astromatic.sextractor_md5sum(options)
                    self.assertNotEqual(checksum, different)

                    # Try also overriding the option with a different value
                    # (the result of incrementing it by one). Again, this must
                    # result in a different MD5 hash.

                    try:
                        options[key] = str(int(options[key]) + 1)
                        also_different = astromatic.sextractor_md5sum(options)
                        self.assertNotEqual(checksum,  also_different)
                        self.assertNotEqual(different, also_different)

                    except ValueError:
                        pass # non-numeric option

        # IOError is raised if any of the SExtractor configuration files is not
        # readable or does not exist. To test that, mock again the module-level
        # variables so that they refer to temporary copies of the configuration
        # files, but which are unreadable by the user, first, and then removed.

        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_md5sum
                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)

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

        # ... or if any of its elements is not a string
        kwargs['options'] = {'DETECT_MINAREA' : 125}
        with self.assertRaises(TypeError):
            astromatic.sextractor_md5sum(**kwargs)
예제 #3
0
파일: seeing.py 프로젝트: bsipocz/lemon
    def __init__(self, path, maximum, margin, coaddk = keywords.coaddk):
        """ Instantiation method for the FITSeeingImage class.

        The path to the SExtractor catalog is read from the FITS header: if the
        keyword is not found, or if it is present but refers to a non-existent
        file, SExtractor has to be executed again. Even if the catalog exists,
        however, the MD5 of the SExtractor configuration files that were used
        when the catalog was created must be the same.

        The 'maximum' parameter determines the pixel value above which it is
        considered saturated. This value depends not only on the CCD, but also
        on the number of coadded images. If three images were coadded, for
        example, the effective saturation level equals three times the value of
        'saturation'. The number of effective coadds is read from the 'coaddk'
        parameter. If the keyword is missing, a value of one (that is, that the
        image consisted of a single exposure) is assumed.

        The 'margin' argument gives the width, in pixels, of the areas adjacent
        to the edges of the image that are to be ignored when detecting sources
        on the reference image. Stars whose center is fewer than this number of
        pixels from any border of the FITS image are not considered.

        """

        super(FITSeeingImage, self).__init__(path)
        self.margin = margin
        msg = "%s: width of margin: %d pixels" % (self.path, self.margin)
        logging.debug(msg)

        # Compute the MD5 hash of the SExtractor configuration files and this
        # saturation level, which overrides the definition of SATUR_LEVEL.
        satur_level = self.saturation(maximum, coaddk = coaddk)
        options = dict(SATUR_LEVEL = str(satur_level))
        sex_md5sum = astromatic.sextractor_md5sum(options = options)
        msg = "%s: SExtractor MD5 hash: %s" % (self.path, sex_md5sum)
        logging.debug(msg)

        try:

            try:
                self.catalog_path = self.read_keyword(keywords.sex_catalog)
            except KeyError:
                msg = "%s: keyword '%s' not found"
                logging.debug(msg % (self.path, keywords.sex_catalog))
                raise

            # The path not only must be stored: the catalog must also exist
            msg = "%s: on-disk catalog %s" % (self.path, self.catalog_path)
            if not os.path.exists(self.catalog_path):
                logging.debug(msg + " does not exist")
                raise IOError
            else:
                logging.debug(msg)

            try:
                img_sex_md5sum = self.read_keyword(keywords.sex_md5sum)
                msg = "%s: on-disk catalog SExtractor MD5 hash (%s): %s"
                args = self.path, keywords.sex_md5sum, img_sex_md5sum
                logging.debug(msg % args)
            except KeyError:
                msg = "%s: keyword '%s' not found"
                logging.debug(msg % (self.path, keywords.sex_md5sum))
                raise

            if img_sex_md5sum != sex_md5sum:
                msg = "%s: outdated catalog (SExtractor hashes do not match)"
                logging.debug(msg % self.path)
                try:
                    os.unlink(self.catalog_path)
                    msg = "%s: outdated catalog removed from disk" % self.path
                    logging.debug(msg)

                except (IOError, OSError), e:
                    msg = "%s: could not remove outdated catalog (%s)"
                    logging.warning(msg % (self.path, e))

                finally:
                    raise ValueError
예제 #4
0
    def __init__(self, path, maximum, margin, coaddk=keywords.coaddk):
        """ Instantiation method for the FITSeeingImage class.

        The path to the SExtractor catalog is read from the FITS header: if the
        keyword is not found, or if it is present but refers to a non-existent
        file, SExtractor has to be executed again. Even if the catalog exists,
        however, the MD5 of the SExtractor configuration files that were used
        when the catalog was created must be the same.

        The 'maximum' parameter determines the pixel value above which it is
        considered saturated. This value depends not only on the CCD, but also
        on the number of coadded images. If three images were coadded, for
        example, the effective saturation level equals three times the value of
        'saturation'. The number of effective coadds is read from the 'coaddk'
        parameter. If the keyword is missing, a value of one (that is, that the
        image consisted of a single exposure) is assumed.

        The 'margin' argument gives the width, in pixels, of the areas adjacent
        to the edges of the image that are to be ignored when detecting sources
        on the reference image. Stars whose center is fewer than this number of
        pixels from any border of the FITS image are not considered.

        """

        super(FITSeeingImage, self).__init__(path)
        self.margin = margin
        msg = "%s: width of margin: %d pixels" % (self.path, self.margin)
        logging.debug(msg)

        # Compute the MD5 hash of the SExtractor configuration files and this
        # saturation level, which overrides the definition of SATUR_LEVEL.
        satur_level = self.saturation(maximum, coaddk=coaddk)
        options = dict(SATUR_LEVEL=str(satur_level))
        sex_md5sum = astromatic.sextractor_md5sum(options=options)
        msg = "%s: SExtractor MD5 hash: %s" % (self.path, sex_md5sum)
        logging.debug(msg)

        try:

            try:
                self.catalog_path = self.read_keyword(keywords.sex_catalog)
            except KeyError:
                msg = "%s: keyword '%s' not found"
                logging.debug(msg % (self.path, keywords.sex_catalog))
                raise

            # The path not only must be stored: the catalog must also exist
            msg = "%s: on-disk catalog %s" % (self.path, self.catalog_path)
            if not os.path.exists(self.catalog_path):
                logging.debug(msg + " does not exist")
                raise IOError
            else:
                logging.debug(msg)

            try:
                img_sex_md5sum = self.read_keyword(keywords.sex_md5sum)
                msg = "%s: on-disk catalog SExtractor MD5 hash (%s): %s"
                args = self.path, keywords.sex_md5sum, img_sex_md5sum
                logging.debug(msg % args)
            except KeyError:
                msg = "%s: keyword '%s' not found"
                logging.debug(msg % (self.path, keywords.sex_md5sum))
                raise

            if img_sex_md5sum != sex_md5sum:
                msg = "%s: outdated catalog (SExtractor hashes do not match)"
                logging.debug(msg % self.path)
                try:
                    os.unlink(self.catalog_path)
                    msg = "%s: outdated catalog removed from disk" % self.path
                    logging.debug(msg)

                except (IOError, OSError), e:
                    msg = "%s: could not remove outdated catalog (%s)"
                    logging.warning(msg % (self.path, e))

                finally:
                    raise ValueError