Skip to content

Different behavior of io.fits in Python 2.7 and 3.5 #5093

Closed

Description

@mcara

I came across some FITS files created by a third party using their proprietary software. It is clear to me that files are non-standard. However, I have noticed that while I cannot even open most of the files in Python 3.5, they work just file in Python 2.7.

I put the offending files in the following directory https://stsci.box.com/s/y5op9sxhd2vgoyatfc0krap2arovmpuw (Link will expire on Oct. 1st, 2016)

Errors in Python 3.5.1 but not in Python 2.7.5 when opening files:

PIX_002.fits:
WARNING: VerifyWarning: Error validating header for HDU #0 (note: Astropy uses zero-based indexing).
    'ascii' codec can't decode byte 0xaa in position 2081: ordinal not in range(128)
There may be extra bytes after the last HDU or the file is corrupted. [astropy.io.fits.hdu.hdulist]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File ".../lib/python3.5/site-packages/astropy/io/fits/hdu/hdulist.py", line 139, in fitsopen
    return HDUList.fromfile(name, mode, memmap, save_backup, cache, **kwargs)
  File ".../lib/python3.5/site-packages/astropy/io/fits/hdu/hdulist.py", line 281, in fromfile
    save_backup=save_backup, cache=cache, **kwargs)
  File ".../lib/python3.5/site-packages/astropy/io/fits/hdu/hdulist.py", line 871, in _readfrom
    raise IOError('Empty or corrupt FITS file')
OSError: Empty or corrupt FITS file
PIX_006.fits
WARNING: VerifyWarning: Error validating header for HDU #0 (note: Astropy uses zero-based indexing).
    'ascii' codec can't decode byte 0x82 in position 2081: ordinal not in range(128)
There may be extra bytes after the last HDU or the file is corrupted. [astropy.io.fits.hdu.hdulist]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File ".../lib/python3.5/site-packages/astropy/io/fits/hdu/hdulist.py", line 139, in fitsopen
    return HDUList.fromfile(name, mode, memmap, save_backup, cache, **kwargs)
  File ".../lib/python3.5/site-packages/astropy/io/fits/hdu/hdulist.py", line 281, in fromfile
    save_backup=save_backup, cache=cache, **kwargs)
  File ".../lib/python3.5/site-packages/astropy/io/fits/hdu/hdulist.py", line 871, in _readfrom
    raise IOError('Empty or corrupt FITS file')
OSError: Empty or corrupt FITS file
PIX_008.fits
WARNING: VerifyWarning: Error validating header for HDU #0 (note: Astropy uses zero-based indexing).
    'ascii' codec can't decode byte 0x80 in position 2082: ordinal not in range(128)
There may be extra bytes after the last HDU or the file is corrupted. [astropy.io.fits.hdu.hdulist]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File ".../lib/python3.5/site-packages/astropy/io/fits/hdu/hdulist.py", line 139, in fitsopen
    return HDUList.fromfile(name, mode, memmap, save_backup, cache, **kwargs)
  File ".../lib/python3.5/site-packages/astropy/io/fits/hdu/hdulist.py", line 281, in fromfile
    save_backup=save_backup, cache=cache, **kwargs)
  File ".../lib/python3.5/site-packages/astropy/io/fits/hdu/hdulist.py", line 871, in _readfrom
    raise IOError('Empty or corrupt FITS file')
OSError: Empty or corrupt FITS file

All these files can be opened in Python 2.7.5 (and loaded in DS9) without any warning messages. The main concern in this "Issue" is not that files are corrupt (though it would be interesting to know what is the problem) but rather the different behavior of astropy.io.fits in different Python versions regarding file errors.

Activity

  1. pllim commented on Jun 14, 2016

    @pllim
    Member

    The key here is:

    'ascii' codec can't decode byte 0x80 in position 2082: ordinal not in range(128)
    

    I have seen similar error in #4757. Might need some decoding somwhere in io.fits?

  2. mcara commented on Jun 14, 2016

    @mcara
    ContributorAuthor

    Additional info: Opening the files in Python 2.7.5 and then writing them out (with a different name) creates file that can be opened in Python 3.5.1.

  3. astrofrog commented on Jun 14, 2016

    @astrofrog
    Member

    @mcara - not sure if this will help, but could you possibly diff the FITS files before and after to see if it's likely to be a header or data issue?

  4. mcara commented on Jun 15, 2016

    @mcara
    ContributorAuthor

    @astrofrog Screenshot below shows the difference between the user supplied file, PIX_002.fits on the left, and re-written file (using fits.writeto() in Python 2.7) - pix002.fits on the right. The difference seems to indicate that user's image has garbage in the padding between the END card and the beginning of the image block while re-saved image (pix002.fits) has 0x20 filling the end of the block (I think this is how it should have been according to specifications). For unknown reasons fits.open() works fine with garbage in the padding while the same function fails in Python 3.5.

    diff

  5. mwcraig commented on Jun 15, 2016

    @mwcraig
    Member

    For unknown reasons fits.open() works fine with garbage in the padding while the same function fails in Python 3.5.

    Not saying I know which line of code needs to be changed here, but the underlying cause is clear: handling of strings/unicode/byte streams is different in python 3 than in 2, and in this fits file there are characters outside the range of ascii characters, so python 3 fails when it tries to read it using that codec (which is what it will default to if no other codec is supplied).

    Won't realistically have time to dive deeper until next week, but something along the lines of what @pllim suggested will be the fix.

  6. pllim commented on Jun 15, 2016

    @pllim
    Member

    But philosophically, should it even be allowed to succeed? If it has garbage, then failure should not be surprising.

  7. mwcraig commented on Jun 15, 2016

    @mwcraig
    Member

    I think it should, though perhaps that could be controlled by a flag, or emit a warning.

  8. dhomeier commented on Aug 17, 2016

    @dhomeier
    Contributor

    The breach of the standard here actually seems to the header not being a multiple of 2880 bytes long, but instead it is truncated at 2080 (the 80 character 'line' including the END keyword).
    I'd agree that the Python2 version should at the very least give a warning as well, in particular since it looks as if it is now simply reading the following 800 bytes into the header unit, thus truncating the data field by that amount (at least when manually padding the header block, the data image looks notably different from the original file loaded under Python2.7)
    It looks like setting the padding=False keyword should change the way header units of nonstandard length are dealt with, but it does not appear to make any difference here...

  9. mcara commented on Aug 19, 2016

    @mcara
    ContributorAuthor

    @dhomeier I am not sure I agree with your conclusions. Specifically, I will refer to PIX_002.fits file which is compared to the version re-saved in Python 2.7 in #5093 (comment). For this file, if I open both versions (original and re-saved one) in Python 2.7 and compare data I get that the data are identical.

    Screenshot in #5093 (comment) shows that the header block has correct size and that it is not truncated. I think it does not matter where the END card is as long as it is within a block. That is, the END card indicates the end of the header but not of the header block. I believe a fits reader should read 2880 bytes regardless of whether or not the END card was in the block. The problem with PIX_002.fits is that the header contains non ASCII characters (in fact non-space) in the padding between the end card and byte 2880 and Python 3.5 version of astropy.io.fits chokes on it because of this.

  10. dhomeier commented on Aug 20, 2016

    @dhomeier
    Contributor

    @mcara, yes, you are probably right. I had been looking into the issue of latin character treatment in Python 3, and since it was not immediately clear where the real data block begins, a truncated header unit seemed a possibility. But looking at your comparison picture the byte pattern looks obviously regular, so it's more likely as you say that the writer just used something else than blanks for the padding.
    In that case, since there is an unambiguous and non-damaging way to deal with the nonstandard file, I agree that Astropy should handle it better under Python 3 as well.

  11. saimn commented on May 19, 2017

    @saimn
    Contributor

    I think this issue is also fixed with #6084, as I can now print the headers of the 3 files with both Python 2 and 3. The bytes after the END card are ignored so it does not matter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions