def test_deprecation_tolerance(self): """Verify uses of tolerance and rtol. This test should be removed in the next astropy version.""" ha = Header([('B', 1.0), ('C', 0.1)]) hb = ha.copy() hb['B'] = 1.00001 hb['C'] = 0.100001 with catch_warnings(AstropyDeprecationWarning) as warning_lines: diff = HeaderDiff(ha, hb, tolerance=1e-6) assert warning_lines[0].category == AstropyDeprecationWarning assert (str(warning_lines[0].message) == '"tolerance" was ' 'deprecated in version 2.0 and will be removed in a ' 'future version. Use argument "rtol" instead.') assert (diff.diff_keyword_values == {'C': [(0.1, 0.100001)], 'B': [(1.0, 1.00001)]}) assert not diff.identical with catch_warnings(AstropyDeprecationWarning) as warning_lines: # `rtol` is always ignored when `tolerance` is provided diff = HeaderDiff(ha, hb, rtol=1e-6, tolerance=1e-5) assert warning_lines[0].category == AstropyDeprecationWarning assert (str(warning_lines[0].message) == '"tolerance" was ' 'deprecated in version 2.0 and will be removed in a ' 'future version. Use argument "rtol" instead.') assert diff.identical
def test_different_keyword_values(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diff = HeaderDiff(ha, hb) assert not diff.identical assert diff.diff_keyword_values == {'C': [(3, 4)]}
def test_floating_point_atol(self): ha = Header([('A', 1), ('B', 1.0), ('C', 0.0)]) hb = ha.copy() hb['B'] = 1.00001 hb['C'] = 0.000001 diff = HeaderDiff(ha, hb, rtol=1e-6) assert not diff.identical assert (diff.diff_keyword_values == {'B': [(1.0, 1.00001)], 'C': [(0.0, 0.000001)]}) diff = HeaderDiff(ha, hb, rtol=1e-5) assert not diff.identical assert (diff.diff_keyword_values == {'C': [(0.0, 0.000001)]}) diff = HeaderDiff(ha, hb, atol=1e-6) assert not diff.identical assert (diff.diff_keyword_values == {'B': [(1.0, 1.00001)]}) diff = HeaderDiff(ha, hb, atol=1e-5) # strict inequality assert not diff.identical assert (diff.diff_keyword_values == {'B': [(1.0, 1.00001)]}) diff = HeaderDiff(ha, hb, rtol=1e-5, atol=1e-5) assert diff.identical diff = HeaderDiff(ha, hb, atol=1.1e-5) assert diff.identical diff = HeaderDiff(ha, hb, rtol=1e-6, atol=1e-6) assert not diff.identical
def test_ignore_blank_cards(self): """Test for https://aeon.stsci.edu/ssb/trac/pyfits/ticket/152 Ignore blank cards. """ ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = Header([('A', 1), ('', ''), ('B', 2), ('', ''), ('C', 3)]) hc = ha.copy() hc.append() hc.append() # We now have a header with interleaved blanks, and a header with end # blanks, both of which should ignore the blanks assert HeaderDiff(ha, hb).identical assert HeaderDiff(ha, hc).identical assert HeaderDiff(hb, hc).identical assert not HeaderDiff(ha, hb, ignore_blank_cards=False).identical assert not HeaderDiff(ha, hc, ignore_blank_cards=False).identical # Both hb and hc have the same number of blank cards; since order is # currently ignored, these should still be identical even if blank # cards are not ignored assert HeaderDiff(hb, hc, ignore_blank_cards=False).identical hc.append() # But now there are different numbers of blanks, so they should not be # ignored: assert not HeaderDiff(hb, hc, ignore_blank_cards=False).identical
def test_floating_point_atol(self): ha = Header([('A', 1), ('B', 1.0), ('C', 0.0)]) hb = ha.copy() hb['B'] = 1.00001 hb['C'] = 0.000001 diff = HeaderDiff(ha, hb, rtol=1e-6) assert not diff.identical assert (diff.diff_keyword_values == { 'B': [(1.0, 1.00001)], 'C': [(0.0, 0.000001)] }) diff = HeaderDiff(ha, hb, rtol=1e-5) assert not diff.identical assert (diff.diff_keyword_values == {'C': [(0.0, 0.000001)]}) diff = HeaderDiff(ha, hb, atol=1e-6) assert not diff.identical assert (diff.diff_keyword_values == {'B': [(1.0, 1.00001)]}) diff = HeaderDiff(ha, hb, atol=1e-5) # strict inequality assert not diff.identical assert (diff.diff_keyword_values == {'B': [(1.0, 1.00001)]}) diff = HeaderDiff(ha, hb, rtol=1e-5, atol=1e-5) assert diff.identical diff = HeaderDiff(ha, hb, atol=1.1e-5) assert diff.identical diff = HeaderDiff(ha, hb, rtol=1e-6, atol=1e-6) assert not diff.identical
def test_read_zeropoint_magnitude(): # first keyword h = Header() h['MAGZPT'] = 3 assert utils.read_zeropoint_magnitude(h) == 3 # second keyword h = Header() with warnings.catch_warnings(): warnings.simplefilter('ignore', VerifyWarning) h['MAGZEROPOINT'] = 3 assert utils.read_zeropoint_magnitude(h) == 3 # use WFC3 fct h = Header() h['TELESCOP'] = 'HST' h['INSTRUME'] = 'WFC3 ' h['PHOTPLAM'] = 3 h['PHOTFLAM'] = 2 exp_result = -2.5 * np.log10(2) - 21.10 - 5 * np.log10(3) + 18.692 assert np.isclose(utils.WFC3_magnitude_zpt_reader(h), exp_result) # warn if not found h = Header() with pytest.warns(UserWarning): magzpt = utils.read_zeropoint_magnitude(h) assert magzpt is None
def test_different_keyword_values_with_duplicate(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() ha.append(('C', 4)) hb.append(('C', 5)) diff = HeaderDiff(ha, hb) assert not diff.identical assert diff.diff_keyword_values == {'C': [None, (4, 5)]}
def test_different_keyword_comments(self): ha = Header([('A', 1), ('B', 2), ('C', 3, 'comment 1')]) hb = ha.copy() hb.comments['C'] = 'comment 2' diff = HeaderDiff(ha, hb) assert not diff.identical assert (diff.diff_keyword_comments == {'C': [('comment 1', 'comment 2')]})
def test_identical_headers(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() assert HeaderDiff(ha, hb).identical assert HeaderDiff(ha.tostring(), hb.tostring()).identical with pytest.raises(TypeError): HeaderDiff(1, 2)
def test_file_output_overwrite_safety(self): outpath = self.temp('diff_output.txt') ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diffobj = HeaderDiff(ha, hb) diffobj.report(fileobj=outpath) with pytest.raises(OSError): diffobj.report(fileobj=outpath)
def test_file_output_from_path_string(self): outpath = self.temp('diff_output.txt') ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diffobj = HeaderDiff(ha, hb) diffobj.report(fileobj=outpath) report_as_string = diffobj.report() with open(outpath) as fout: assert fout.read() == report_as_string
def test_different_keywords(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 hb['D'] = (5, 'Comment') ha['E'] = (6, 'Comment') ha['F'] = (7, 'Comment') diff = HeaderDiff(ha, hb) assert not diff.identical assert diff.diff_keywords == (['E', 'F'], ['D'])
def test_different_keyword_count(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() del hb['B'] diff = HeaderDiff(ha, hb) assert not diff.identical assert diff.diff_keyword_count == (3, 2) # But make sure the common keywords are at least correct assert diff.common_keywords == ['A', 'C']
def test_file_output_overwrite_success(self): outpath = self.temp('diff_output.txt') ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diffobj = HeaderDiff(ha, hb) diffobj.report(fileobj=outpath) report_as_string = diffobj.report() diffobj.report(fileobj=outpath, overwrite=True) with open(outpath) as fout: assert fout.read() == report_as_string, ( "overwritten output file is not identical to report string")
def test_floating_point_rtol(self): ha = Header([('A', 1), ('B', 2.00001), ('C', 3.000001)]) hb = ha.copy() hb['B'] = 2.00002 hb['C'] = 3.000002 diff = HeaderDiff(ha, hb) assert not diff.identical assert (diff.diff_keyword_values == {'B': [(2.00001, 2.00002)], 'C': [(3.000001, 3.000002)]}) diff = HeaderDiff(ha, hb, rtol=1e-6) assert not diff.identical assert diff.diff_keyword_values == {'B': [(2.00001, 2.00002)]} diff = HeaderDiff(ha, hb, rtol=1e-5) assert diff.identical
def test_file_output_overwrite_vs_clobber(self): """Verify uses of clobber and overwrite.""" outpath = self.temp('diff_output.txt') ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diffobj = HeaderDiff(ha, hb) diffobj.report(fileobj=outpath) with pytest.warns( AstropyDeprecationWarning, match=r'"clobber" was ' r'deprecated in version 2\.0 and will be removed in a ' r'future version\. Use argument "overwrite" instead\.'): diffobj.report(fileobj=outpath, clobber=True)
def test_file_output_overwrite_vs_clobber(self): """Verify uses of clobber and overwrite.""" outpath = self.temp('diff_output.txt') ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 diffobj = HeaderDiff(ha, hb) diffobj.report(fileobj=outpath) with catch_warnings(AstropyDeprecationWarning) as warning_lines: diffobj.report(fileobj=outpath, clobber=True) assert warning_lines[0].category == AstropyDeprecationWarning assert (str(warning_lines[0].message) == '"clobber" was ' 'deprecated in version 2.0 and will be removed in a ' 'future version. Use argument "overwrite" instead.')
def test_asymmetric_duplicate_keywords(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() ha.append(('A', 2, 'comment 1')) ha.append(('A', 3, 'comment 2')) hb.append(('B', 4, 'comment 3')) hb.append(('C', 5, 'comment 4')) diff = HeaderDiff(ha, hb) assert not diff.identical assert diff.diff_keyword_values == {} assert (diff.diff_duplicate_keywords == {'A': (3, 1), 'B': (1, 2), 'C': (1, 2)}) report = diff.report() assert ("Inconsistent duplicates of keyword 'A' :\n" " Occurs 3 time(s) in a, 1 times in (b)") in report
def _density_radiation_field_header(self): '''Common header items in the density and radiation field FITS files''' self._density.header.pop('RATIO') self._radiation_field.header.pop('RATIO') # note: must use to_string() here or astropy.io.fits.Card complains # about the value being a Unit. Oddly it doesn't complain for the # data units. Go figure. utils.setkey("BUNIT",self.density_unit.to_string(),self._density) utils.comment("Best-fit H2 volume density",self._density) utils.setkey("BUNIT",self.radiation_field_unit.to_string(),self._radiation_field) utils.comment("Best-fit interstellar radiation field",self._radiation_field) self._makehistory(self._density) self._makehistory(self._radiation_field) # convert from OrderedDict to astropy.io.fits.header.Header self._density.header = Header(self._density.header) self._radiation_field.header = Header(self._radiation_field.header)
def test_ignore_blanks(self): with fits.conf.set_temp('strip_header_whitespace', False): ha = Header([('A', 1), ('B', 2), ('C', 'A ')]) hb = ha.copy() hb['C'] = 'A' assert ha['C'] != hb['C'] diff = HeaderDiff(ha, hb) # Trailing blanks are ignored by default assert diff.identical assert diff.diff_keyword_values == {} # Don't ignore blanks diff = HeaderDiff(ha, hb, ignore_blanks=False) assert not diff.identical assert diff.diff_keyword_values == {'C': [('A ', 'A')]}
def test_read_value_or_warn(): h = Header() h['a'] = 3 assert utils.read_value_or_warn('a', h) == 3 assert utils.read_value_or_warn(['b', 'a'], h) == 3 with pytest.warns(UserWarning): utils.read_value_or_warn('c', h)
def test_ignore_blank_cards(self, differ): """Test for https://aeon.stsci.edu/ssb/trac/pyfits/ticket/152 Ignore blank cards. """ ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = Header([('A', 1), ('', ''), ('B', 2), ('', ''), ('C', 3)]) hc = ha.copy() if differ is HeaderDiff: hc.append() hc.append() else: # Ensure blanks are not at the end as they are stripped by HDUs hc.add_blank(after=-2) hc.add_blank(after=-2) if differ in (HDUDiff, FITSDiff): # wrap it in a PrimaryHDU ha, hb, hc = (PrimaryHDU(np.arange(10), h) for h in (ha, hb, hc)) hc_header = hc.header if differ is FITSDiff: # wrap it in a HDUList ha, hb, hc = (HDUList([h]) for h in (ha, hb, hc)) hc_header = hc[0].header # We now have a header with interleaved blanks, and a header with end # blanks, both of which should ignore the blanks assert differ(ha, hb).identical assert differ(ha, hc).identical assert differ(hb, hc).identical assert not differ(ha, hb, ignore_blank_cards=False).identical assert not differ(ha, hc, ignore_blank_cards=False).identical # Both hb and hc have the same number of blank cards; since order is # currently ignored, these should still be identical even if blank # cards are not ignored assert differ(hb, hc, ignore_blank_cards=False).identical if differ is HeaderDiff: hc.append() else: # Ensure blanks are not at the end as they are stripped by HDUs hc_header.add_blank(after=-2) # But now there are different numbers of blanks, so they should not be # ignored: assert not differ(hb, hc, ignore_blank_cards=False).identical
def test_ignore_keyword_comments(self): ha = Header([('A', 1, 'A'), ('B', 2, 'B'), ('C', 3, 'C')]) hb = ha.copy() hb.comments['B'] = 'D' hb.comments['C'] = 'E' diff = HeaderDiff(ha, hb, ignore_comments=['*']) assert diff.identical diff = HeaderDiff(ha, hb, ignore_comments=['B']) assert not diff.identical assert diff.diff_keyword_comments == {'C': [('C', 'E')]} report = diff.report() assert 'Keyword B has different comments' not in report assert 'Keyword C has different comments' in report # Test case-insensitivity diff = HeaderDiff(ha, hb, ignore_comments=['b']) assert not diff.identical assert diff.diff_keyword_comments == {'C': [('C', 'E')]}
def test_ignore_keyword_values(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['B'] = 4 hb['C'] = 5 diff = HeaderDiff(ha, hb, ignore_keywords=['*']) assert diff.identical diff = HeaderDiff(ha, hb, ignore_keywords=['B']) assert not diff.identical assert diff.diff_keyword_values == {'C': [(3, 5)]} report = diff.report() assert 'Keyword B has different values' not in report assert 'Keyword C has different values' in report # Test case-insensitivity diff = HeaderDiff(ha, hb, ignore_keywords=['b']) assert not diff.identical assert diff.diff_keyword_values == {'C': [(3, 5)]}
def test_image_store(self): fits_object = fits.open(FITS_FILE) expected_data = cPickle.dumps(fits_object[0].data) expected_header = fits_object[0].header store_fits([self.image], [expected_data], [str(expected_header)]) fetched_image = self.db.session.query(Image).filter(Image.id==self.image.id).first() returned_data = cPickle.loads(fetched_image.data.fits_data) returned_header = Header.fromstring(fetched_image.data.fits_header) self.assertTrue((returned_data, expected_data)) self.assertEqual(returned_header, expected_header)
def test_image_store(self): fits_object = fits.open(FITS_FILE) expected_data = cPickle.dumps(fits_object[0].data) expected_header = fits_object[0].header store_fits([self.image], [expected_data], [str(expected_header)]) fetched_image = self.db.session.query(Image).filter( Image.id == self.image.id).first() returned_data = cPickle.loads(fetched_image.data.fits_data) returned_header = Header.fromstring(fetched_image.data.fits_header) self.assertTrue((returned_data, expected_data)) self.assertEqual(returned_header, expected_header)
def reconstruct_fits(db_image): try: if not db_image.data.fits_header or not db_image.data.fits_data: return None except Imagedata.DoesNotExist as e: return None hdu_header = Header.fromstring(db_image.data.fits_header) data = cPickle.loads(str(db_image.data.fits_data)) hdu = fits.PrimaryHDU(data) hdu.header = hdu_header hdulist = fits.HDUList([hdu]) return hdulist
def fromhdulist(cls, hdulist, compress=False): """ Creates a new FitsHDU from a given HDUList object. Parameters ---------- hdulist : HDUList A valid Headerlet object. compress : bool, optional Gzip compress the FITS file """ fileobj = bs = io.BytesIO() if compress: if hasattr(hdulist, '_file'): name = fileobj_name(hdulist._file) else: name = None fileobj = gzip.GzipFile(name, mode='wb', fileobj=bs) hdulist.writeto(fileobj) if compress: fileobj.close() # A proper HDUList should still be padded out to a multiple of 2880 # technically speaking padding = (_pad_length(bs.tell()) * cls._padding_byte).encode('ascii') bs.write(padding) bs.seek(0) cards = [ ('XTENSION', cls._extension, 'FITS extension'), ('BITPIX', 8, 'array data type'), ('NAXIS', 1, 'number of array dimensions'), ('NAXIS1', len(bs.getvalue()), 'Axis length'), ('PCOUNT', 0, 'number of parameters'), ('GCOUNT', 1, 'number of groups'), ] # Add the XINDn keywords proposed by Perry, though nothing is done with # these at the moment if len(hdulist) > 1: for idx, hdu in enumerate(hdulist[1:]): cards.append(('XIND' + str(idx + 1), hdu._header_offset, f'byte offset of extension {idx + 1}')) cards.append(('COMPRESS', compress, 'Uses gzip compression')) header = Header(cards) return cls._readfrom_internal(_File(bs), header=header)
def test_WFC3WFC3_magnitude_zpt_reader(): h = Header() h['TELESCOP'] = 'HST' h['INSTRUME'] = 'WFC3 ' h['PHOTPLAM'] = 3 h['PHOTFLAM'] = 2 exp_result = -2.5 * np.log10(2) - 21.10 - 5 * np.log10(3) + 18.692 assert np.isclose(utils.WFC3_magnitude_zpt_reader(h), exp_result) h['TELESCOP'] = 'WST' with pytest.raises(AssertionError): utils.WFC3_magnitude_zpt_reader(h) h['TELESCOP'] = 'HST' h['INSTRUME'] = 'WFC4 ' with pytest.raises(AssertionError): utils.WFC3_magnitude_zpt_reader(h) h['INSTRUME'] = 'WFC3 ' h.pop('PHOTPLAM') with pytest.raises(KeyError): utils.WFC3_magnitude_zpt_reader(h)
def _compute_chisq(self): '''Compute the chi-squared values from observed ratios and models''' if self.ratiocount < 2 : raise Exception("Not enough ratios to compute chisq. Need 2, got %d"%self.ratiocount) sumary = sum((self._residual[r]._data**2 for r in self._residual)) self._dof = len(self._residual) - 1 k = utils.firstkey(self._residual) _wcs = deepcopy(self._residual[k].wcs) _meta = deepcopy(self._residual[k].meta) self._chisq = CCDData(sumary,unit='adu',wcs=_wcs,meta=_meta) self._reduced_chisq = self._chisq.divide(self._dof) # must make a copy here otherwise the header is an OrderDict # instead of astropy.io.fits.header.Header self._reduced_chisq.header = Header(deepcopy(self._chisq.header)) self._fixheader(self._chisq) self._fixheader(self._reduced_chisq) utils.comment("Chi-squared",self._chisq) utils.comment(("Reduced Chi-squared (DOF=%d)"%self._dof),self._reduced_chisq) self._makehistory(self._chisq) self._makehistory(self._reduced_chisq)
def test_ignore_hdus(self): a = np.arange(100).reshape(10, 10) b = a.copy() ha = Header([('A', 1), ('B', 2), ('C', 3)]) xa = np.array([(1.0, 1), (3.0, 4)], dtype=[('x', float), ('y', int)]) xb = np.array([(1.0, 2), (3.0, 5)], dtype=[('x', float), ('y', int)]) phdu = PrimaryHDU(header=ha) ihdua = ImageHDU(data=a, name='SCI') ihdub = ImageHDU(data=b, name='SCI') bhdu1 = BinTableHDU(data=xa, name='ASDF') bhdu2 = BinTableHDU(data=xb, name='ASDF') hdula = HDUList([phdu, ihdua, bhdu1]) hdulb = HDUList([phdu, ihdub, bhdu2]) # ASDF extension should be different diff = FITSDiff(hdula, hdulb) assert not diff.identical assert diff.diff_hdus[0][0] == 2 # ASDF extension should be ignored diff = FITSDiff(hdula, hdulb, ignore_hdus=['ASDF']) assert diff.identical, diff.report() diff = FITSDiff(hdula, hdulb, ignore_hdus=['ASD*']) assert diff.identical, diff.report() # SCI extension should be different hdulb['SCI'].data += 1 diff = FITSDiff(hdula, hdulb, ignore_hdus=['ASDF']) assert not diff.identical # SCI and ASDF extensions should be ignored diff = FITSDiff(hdula, hdulb, ignore_hdus=['SCI', 'ASDF']) assert diff.identical, diff.report() # All EXTVER of SCI should be ignored ihduc = ImageHDU(data=a, name='SCI', ver=2) hdulb.append(ihduc) diff = FITSDiff(hdula, hdulb, ignore_hdus=['SCI', 'ASDF']) assert not any(diff.diff_hdus), diff.report() assert any(diff.diff_hdu_count), diff.report()
def from_buff(cls, buff, compress=False, **kwargs): """ Creates a new _AsdfHDU from a given AsdfFile object. Parameters ---------- buff : io.BytesIO A buffer containing an ASDF metadata tree compress : bool, optional Gzip compress the contents of the ASDF HDU """ if compress: buff = gzip.GzipFile(fileobj=buff, mode='wb') # A proper HDU should still be padded out to a multiple of 2880 # technically speaking data_length = buff.tell() padding = (_pad_length(data_length) * cls._padding_byte).encode('ascii') buff.write(padding) buff.seek(0) cards = [ ('XTENSION', cls._extension, 'ASDF extension'), ('BITPIX', 8, 'array data type'), ('NAXIS', 1, 'number of array dimensions'), ('NAXIS1', data_length, 'Axis length'), ('PCOUNT', 0, 'number of parameters'), ('GCOUNT', 1, 'number of groups'), ('COMPRESS', compress, 'Uses gzip compression'), ('EXTNAME', cls._extension, 'Name of ASDF extension'), ] header = Header(cards) return cls._readfrom_internal(_File(buff), header=header)
def __init__(self, data=None, header=None, do_not_scale_image_data=False, uint=True, scale_back=False, ignore_blank=False, **kwargs): from .groups import GroupsHDU super().__init__(data=data, header=header) if data is DELAYED: # Presumably if data is DELAYED then this HDU is coming from an # open file, and was not created in memory if header is None: # this should never happen raise ValueError('No header to setup HDU.') else: # TODO: Some of this card manipulation should go into the # PrimaryHDU and GroupsHDU subclasses # construct a list of cards of minimal header if isinstance(self, ExtensionHDU): c0 = ('XTENSION', 'IMAGE', self.standard_keyword_comments['XTENSION']) else: c0 = ('SIMPLE', True, self.standard_keyword_comments['SIMPLE']) cards = [ c0, ('BITPIX', 8, self.standard_keyword_comments['BITPIX']), ('NAXIS', 0, self.standard_keyword_comments['NAXIS']) ] if isinstance(self, GroupsHDU): cards.append( ('GROUPS', True, self.standard_keyword_comments['GROUPS'])) if isinstance(self, (ExtensionHDU, GroupsHDU)): cards.append( ('PCOUNT', 0, self.standard_keyword_comments['PCOUNT'])) cards.append( ('GCOUNT', 1, self.standard_keyword_comments['GCOUNT'])) if header is not None: orig = header.copy() header = Header(cards) header.extend(orig, strip=True, update=True, end=True) else: header = Header(cards) self._header = header self._do_not_scale_image_data = do_not_scale_image_data self._uint = uint self._scale_back = scale_back # Keep track of whether BZERO/BSCALE were set from the header so that # values for self._orig_bzero and self._orig_bscale can be set # properly, if necessary, once the data has been set. bzero_in_header = 'BZERO' in self._header bscale_in_header = 'BSCALE' in self._header self._bzero = self._header.get('BZERO', 0) self._bscale = self._header.get('BSCALE', 1) # Save off other important values from the header needed to interpret # the image data self._axes = [ self._header.get('NAXIS' + str(axis + 1), 0) for axis in range(self._header.get('NAXIS', 0)) ] # Not supplying a default for BITPIX makes sense because BITPIX # is either in the header or should be determined from the dtype of # the data (which occurs when the data is set). self._bitpix = self._header.get('BITPIX') self._gcount = self._header.get('GCOUNT', 1) self._pcount = self._header.get('PCOUNT', 0) self._blank = None if ignore_blank else self._header.get('BLANK') self._verify_blank() self._orig_bitpix = self._bitpix self._orig_blank = self._header.get('BLANK') # These get set again below, but need to be set to sensible defaults # here. self._orig_bzero = self._bzero self._orig_bscale = self._bscale # Set the name attribute if it was provided (if this is an ImageHDU # this will result in setting the EXTNAME keyword of the header as # well) if 'name' in kwargs and kwargs['name']: self.name = kwargs['name'] if 'ver' in kwargs and kwargs['ver']: self.ver = kwargs['ver'] # Set to True if the data or header is replaced, indicating that # update_header should be called self._modified = False if data is DELAYED: if (not do_not_scale_image_data and (self._bscale != 1 or self._bzero != 0)): # This indicates that when the data is accessed or written out # to a new file it will need to be rescaled self._data_needs_rescale = True return else: # Setting data will update the header and set _bitpix, _bzero, # and _bscale to the appropriate BITPIX for the data, and always # sets _bzero=0 and _bscale=1. self.data = data # Check again for BITPIX/BSCALE/BZERO in case they changed when the # data was assigned. This can happen, for example, if the input # data is an unsigned int numpy array. self._bitpix = self._header.get('BITPIX') # Do not provide default values for BZERO and BSCALE here because # the keywords will have been deleted in the header if appropriate # after scaling. We do not want to put them back in if they # should not be there. self._bzero = self._header.get('BZERO') self._bscale = self._header.get('BSCALE') # Handle case where there was no BZERO/BSCALE in the initial header # but there should be a BSCALE/BZERO now that the data has been set. if not bzero_in_header: self._orig_bzero = self._bzero if not bscale_in_header: self._orig_bscale = self._bscale
def test_slightly_different_headers(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 assert not HeaderDiff(ha, hb).identical
def test_common_keywords(self): ha = Header([('A', 1), ('B', 2), ('C', 3)]) hb = ha.copy() hb['C'] = 4 hb['D'] = (5, 'Comment') assert HeaderDiff(ha, hb).common_keywords == ['A', 'B', 'C']
def __init__(self, data=None, header=None, do_not_scale_image_data=False, uint=True, scale_back=False, ignore_blank=False, **kwargs): from .groups import GroupsHDU super().__init__(data=data, header=header) if header is not None: if not isinstance(header, Header): # TODO: Instead maybe try initializing a new Header object from # whatever is passed in as the header--there are various types # of objects that could work for this... raise ValueError('header must be a Header object') if data is DELAYED: # Presumably if data is DELAYED then this HDU is coming from an # open file, and was not created in memory if header is None: # this should never happen raise ValueError('No header to setup HDU.') # if the file is read the first time, no need to copy, and keep it # unchanged else: self._header = header else: # TODO: Some of this card manipulation should go into the # PrimaryHDU and GroupsHDU subclasses # construct a list of cards of minimal header if isinstance(self, ExtensionHDU): c0 = ('XTENSION', 'IMAGE', self.standard_keyword_comments['XTENSION']) else: c0 = ('SIMPLE', True, self.standard_keyword_comments['SIMPLE']) cards = [ c0, ('BITPIX', 8, self.standard_keyword_comments['BITPIX']), ('NAXIS', 0, self.standard_keyword_comments['NAXIS'])] if isinstance(self, GroupsHDU): cards.append(('GROUPS', True, self.standard_keyword_comments['GROUPS'])) if isinstance(self, (ExtensionHDU, GroupsHDU)): cards.append(('PCOUNT', 0, self.standard_keyword_comments['PCOUNT'])) cards.append(('GCOUNT', 1, self.standard_keyword_comments['GCOUNT'])) if header is not None: orig = header.copy() header = Header(cards) header.extend(orig, strip=True, update=True, end=True) else: header = Header(cards) self._header = header self._do_not_scale_image_data = do_not_scale_image_data self._uint = uint self._scale_back = scale_back # Keep track of whether BZERO/BSCALE were set from the header so that # values for self._orig_bzero and self._orig_bscale can be set # properly, if necessary, once the data has been set. bzero_in_header = 'BZERO' in self._header bscale_in_header = 'BSCALE' in self._header self._bzero = self._header.get('BZERO', 0) self._bscale = self._header.get('BSCALE', 1) # Save off other important values from the header needed to interpret # the image data self._axes = [self._header.get('NAXIS' + str(axis + 1), 0) for axis in range(self._header.get('NAXIS', 0))] # Not supplying a default for BITPIX makes sense because BITPIX # is either in the header or should be determined from the dtype of # the data (which occurs when the data is set). self._bitpix = self._header.get('BITPIX') self._gcount = self._header.get('GCOUNT', 1) self._pcount = self._header.get('PCOUNT', 0) self._blank = None if ignore_blank else self._header.get('BLANK') self._verify_blank() self._orig_bitpix = self._bitpix self._orig_blank = self._header.get('BLANK') # These get set again below, but need to be set to sensible defaults # here. self._orig_bzero = self._bzero self._orig_bscale = self._bscale # Set the name attribute if it was provided (if this is an ImageHDU # this will result in setting the EXTNAME keyword of the header as # well) if 'name' in kwargs and kwargs['name']: self.name = kwargs['name'] if 'ver' in kwargs and kwargs['ver']: self.ver = kwargs['ver'] # Set to True if the data or header is replaced, indicating that # update_header should be called self._modified = False if data is DELAYED: if (not do_not_scale_image_data and (self._bscale != 1 or self._bzero != 0)): # This indicates that when the data is accessed or written out # to a new file it will need to be rescaled self._data_needs_rescale = True return else: # Setting data will set set _bitpix, _bzero, and _bscale to the # appropriate BITPIX for the data, and always sets _bzero=0 and # _bscale=1. self.data = data self.update_header() # Check again for BITPIX/BSCALE/BZERO in case they changed when the # data was assigned. This can happen, for example, if the input # data is an unsigned int numpy array. self._bitpix = self._header.get('BITPIX') # Do not provide default values for BZERO and BSCALE here because # the keywords will have been deleted in the header if appropriate # after scaling. We do not want to put them back in if they # should not be there. self._bzero = self._header.get('BZERO') self._bscale = self._header.get('BSCALE') # Handle case where there was no BZERO/BSCALE in the initial header # but there should be a BSCALE/BZERO now that the data has been set. if not bzero_in_header: self._orig_bzero = self._bzero if not bscale_in_header: self._orig_bscale = self._bscale