Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent instantiation of PrimaryHDU and ImageHDU with a scalar #10041

Merged
merged 1 commit into from Mar 19, 2020

Conversation

saimn
Copy link
Contributor

@saimn saimn commented Mar 14, 2020

Fix #10022.

@MSeifert04
Copy link
Contributor

I assume the failure is unrelated? #10039

However I would want to wait until the full tests run over this before merging.

@pllim pllim added the Bug label Mar 15, 2020
@bsipocz
Copy link
Member

bsipocz commented Mar 17, 2020

I wanted to go ahead with the merge as the 4.0.x branch is not affected by the pytest issue, but then saw this related error locally.

@saimn - please have a look, and also rebase.

======================================================= FAILURES =======================================================
___________________________________ TestImageFunctions.test_hdu_creation_with_scalar ___________________________________

self = <astropy.io.fits.tests.test_image.TestImageFunctions object at 0x123dda0a0>

    def test_hdu_creation_with_scalar(self):
        msg = r'data object array\(1\) should have at least one dimension'
        with pytest.raises(TypeError, match=msg):
            fits.ImageHDU(data=1)
        with pytest.raises(TypeError, match=msg):
>           fits.PrimaryHDU(data=2.0)

astropy/io/fits/tests/test_image.py:1093: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <astropy.io.fits.hdu.image.PrimaryHDU object at 0x123dda070>, data = 2.0, header = None
do_not_scale_image_data = False, ignore_blank = False, uint = True, scale_back = None

    def __init__(self, data=None, header=None, do_not_scale_image_data=False,
                 ignore_blank=False,
                 uint=True, scale_back=None):
        """
        Construct a primary HDU.
    
        Parameters
        ----------
        data : array or DELAYED, optional
            The data in the HDU.
    
        header : `~astropy.io.fits.Header`, optional
            The header to be used (as a template).  If ``header`` is `None`, a
            minimal header will be provided.
    
        do_not_scale_image_data : bool, optional
            If `True`, image data is not scaled using BSCALE/BZERO values
            when read. (default: False)
    
        ignore_blank : bool, optional
            If `True`, the BLANK header keyword will be ignored if present.
            Otherwise, pixels equal to this value will be replaced with
            NaNs. (default: False)
    
        uint : bool, optional
            Interpret signed integer data where ``BZERO`` is the
            central value and ``BSCALE == 1`` as unsigned integer
            data.  For example, ``int16`` data with ``BZERO = 32768``
            and ``BSCALE = 1`` would be treated as ``uint16`` data.
            (default: True)
    
        scale_back : bool, optional
            If `True`, when saving changes to a file that contained scaled
            image data, restore the data to the original type and reapply the
            original BSCALE/BZERO values.  This could lead to loss of accuracy
            if scaling back to integer values after performing floating point
            operations on the data.  Pseudo-unsigned integers are automatically
            rescaled unless scale_back is explicitly set to `False`.
            (default: None)
        """
    
>       super().__init__(
            data=data, header=header,
            do_not_scale_image_data=do_not_scale_image_data, uint=uint,
            ignore_blank=ignore_blank,
            scale_back=scale_back)

astropy/io/fits/hdu/image.py:989: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <astropy.io.fits.hdu.image.PrimaryHDU object at 0x123dda070>, data = 2.0
header = SIMPLE  =                    T / conforms to FITS standard                      
BITPIX  =                    8 / arra...a type                                
NAXIS   =                    0 / number of array dimensions                     
do_not_scale_image_data = False, uint = True, scale_back = None, ignore_blank = False, kwargs = {}
GroupsHDU = <class 'astropy.io.fits.hdu.groups.GroupsHDU'>, c0 = ('SIMPLE', True, 'conforms to FITS standard')
cards = [('SIMPLE', True, 'conforms to FITS standard'), ('BITPIX', 8, 'array data type'), ('NAXIS', 0, 'number of array dimensions')]
bzero_in_header = False, bscale_in_header = False

    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

astropy/io/fits/hdu/image.py:142: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <astropy.utils.decorators.lazyproperty object at 0x1211e67c0>
obj = <astropy.io.fits.hdu.image.PrimaryHDU object at 0x123dda070>, val = 2.0

    def __set__(self, obj, val):
        obj_dict = obj.__dict__
        if self.fset:
>           ret = self.fset(obj, val)

astropy/utils/decorators.py:759: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <astropy.io.fits.hdu.image.PrimaryHDU object at 0x123dda070>, data = array(2.)

    @data.setter
    def data(self, data):
        if 'data' in self.__dict__ and self.__dict__['data'] is not None:
            if self.__dict__['data'] is data:
                return
            else:
                self._data_replaced = True
            was_unsigned = _is_pseudo_unsigned(self.__dict__['data'].dtype)
        else:
            self._data_replaced = True
            was_unsigned = False
    
        if data is not None and not isinstance(data, np.ndarray):
            # Try to coerce the data into a numpy array--this will work, on
            # some level, for most objects
            try:
                data = np.array(data)
            except Exception:
                raise TypeError('data object {!r} could not be coerced into an '
                                'ndarray'.format(data))
    
            if data.shape == ():
>               raise TypeError('data object {!r} should have at least one '
                                'dimension'.format(data))
E               TypeError: data object array(2.) should have at least one dimension

astropy/io/fits/hdu/image.py:257: TypeError

During handling of the above exception, another exception occurred:

self = <astropy.io.fits.tests.test_image.TestImageFunctions object at 0x123dda0a0>

    def test_hdu_creation_with_scalar(self):
        msg = r'data object array\(1\) should have at least one dimension'
        with pytest.raises(TypeError, match=msg):
            fits.ImageHDU(data=1)
        with pytest.raises(TypeError, match=msg):
>           fits.PrimaryHDU(data=2.0)
E           AssertionError: Pattern 'data object array\\(1\\) should have at least one dimension' not found in 'data object array(2.) should have at least one dimension'

astropy/io/fits/tests/test_image.py:1093: AssertionError

@saimn
Copy link
Contributor Author

saimn commented Mar 18, 2020

Fixed and rebased (but still failing because of the pytest issue).

@bsipocz bsipocz added the 💤 merge-when-ci-passes Do not use: We have auto-merge option now. label Mar 19, 2020
@bsipocz bsipocz merged commit c95d6b8 into astropy:master Mar 19, 2020
@bsipocz
Copy link
Member

bsipocz commented Mar 19, 2020

Thanks @saimn!

@saimn saimn deleted the fits-fix-10022 branch March 19, 2020 18:01
bsipocz added a commit that referenced this pull request Mar 22, 2020
Prevent instantiation of PrimaryHDU and ImageHDU with a scalar
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug io.fits 💤 merge-when-ci-passes Do not use: We have auto-merge option now.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

io.fits fails / has unexpected behavior when writing single float objects
4 participants