pythonkeyerrorastropyfitspyfits

What can I do for my program not to throw a KeyError for a Header not existing in a .fits file?


I am making a program that searches the computer for .fts and .fits files in which it opens the file and retrieves info that corresponds to a specific keyword in the header and renames the file to that keyword.

I am having a problem where i keep receiving a KeyError becasue a header keyword I am searching for is not found in the file. Is there a way around this? I want to be able to search various keywords and do something even if that key word does not exist in the file.

Here is code:

from astropy.io import fits
import os

for i in os.listdir(os.getcwd()):
if i.endswith(".fits") or i.endswith(".fts"): 

    hdulist = fits.open(i)

    DATEOBS_header = hdulist[0].header['DATE-OBS'] 
    EXPTIME_header = int(round(hdulist[0].header['EXPTIME']))
    CCDTEMP_header = int(round(hdulist[0].header['CCD-TEMP']))
    XBINNING_header = hdulist[0].header['XBINNING']
    FILTER_header = hdulist[0].header['FILTER']
    IMAGETYP_header = hdulist[0].header['IMAGETYP']
    OBJECT_header = hdulist[0].header['OBJECT']

    DATEandTIME = DATEOBS_header[0:]
    YEAR = DATEandTIME[0:4]
    MONTH = DATEandTIME[5:7]
    DAY = DATEandTIME[8:10]

    #TIME = DATEOBS_header[11:] 
    HOUR = DATEandTIME[11:13]
    MINUTE = DATEandTIME[14:16]
    SECONDS = DATEandTIME[17:]

    DATE = str(YEAR) + str(MONTH) + str(DAY) + 'at' + str(HOUR) + str(MINUTE) + str(SECONDS) 

    if IMAGETYP_header == 'Light Frame':
        newname = str(OBJECT_header) + '_' + str(DATE) + '_' + str(CCDTEMP_header) + 'temp_' + str(XBINNING_header) + 'bin_' + str(EXPTIME_header) + 'exptime_' + str(FILTER_header) + '.fits'

    if IMAGETYP_header == 'Dark Frame':
        newname = 'Dark_' + str(DATE) + 'at' + str(TIME) + '_' + str(CCDTEMP_header) + 'temp_' + str(XBINNING_header) + 'bin_' + str(EXPTIME_header) + 'exptime' + '.fits'

    if IMAGETYP_header == 'Flat Field':
        newname = 'Flat_' + str(DATE) + 'at' + str(TIME) + '_' + str(CCDTEMP_header) + 'temp_' + str(XBINNING_header) + 'bin_' + str(EXPTIME_header) + 'exptime_' + str(FILTER_header) + '.fits'

    prevname = i
    os.rename(prevname, newname)

    hdulist.close()

    continue
else:
    continue

This is the Error I get:

Traceback (most recent call last):
  File "glo1.py", line 9, in <module>
    DATEOBS_header = hdulist[0].header['DATE-OBS'] 
  File "/home/luisgeesb/.local/lib/python2.7/site-packages/astropy/io/fits/header.py", line 151, in __getitem__
card = self._cards[self._cardindex(key)]
  File "/home/luisgeesb/.local/lib/python2.7/site-packages/astropy/io/fits/header.py", line 1723, in _cardindex
raise KeyError("Keyword %r not found." % keyword)
KeyError: "Keyword 'DATE-OBS' not found."

Solution

  • To prevent these kinds of exceptions from stopping your program, you can either catch them, like this:

    try:
        DATEOBS_header = hdulist[0].header['DATE-OBS']
    except KeyError:
        DATEOBS_header = None
    

    Or, use the .get() method of dictionaries, which checks if a key exists and if it doesn't returns a default value, instead of raising an exception. The default value returned is None.

    If you do this, you will also need to set some sensible defaults, or catch those cases where you are casting the values (since you cannot cast None).

    Finally, whenever you are reading from files - you should always assume the data is malformed/junk and do a bit of defensive programming. In your code, you are assuming that the values returned for CCDTEMP is a number, but what if the file is corrupted or has a blank? Your application doesn't handle this case.

    Here is some code that attempts to catch as many errors as possible:

    DATEOBS_header = hdulist[0].header.get('DATE-OBS') 
    XBINNING_header = hdulist[0].header.get('XBINNING')
    FILTER_header = hdulist[0].header.get('FILTER')
    IMAGETYP_header = hdulist[0].header.get('IMAGETYP')
    OBJECT_header = hdulist[0].header.get('OBJECT')
    
    # For these two, you need to either set a default
    # Here I am setting the default to 0,           ------------v
    EXPTIME_header = int(round(hdulist[0].header.get('EXPTIME', 0)))
    
    
    # Or you need to check for a value :
    ccdtemp_value = hdulist[0].header.get('CCD-TEMP')
    try:
       ccdtemp_value = int(round(ccdtemp_value))
    except ValueError:
       # This means, the value was either None (header does not exist)
       # or it was something that can't be converted to a number
       # since it cannot be converted to a number, we do not know
       # if the value is None or something like an empty string,
       # so we explicitly set the value to None
       ccdtemp_value = None
    CCDTEMP_header = ccdtemp_value