# Buggy code
def _open(self):
    if self.fp.read(8) != _MAGIC:
        raise SyntaxError("not a PNG file")
    self.__fp = self.fp
    self.__frame = 0

    #
    # Parse headers up to the first IDAT or fDAT chunk

    self.png = PngStream(self.fp)

    while True:
        #
        # get next chunk

        cid, pos, length = self.png.read()

        try:
            s = self.png.call(cid, pos, length)
        except EOFError:
            break
        except AttributeError:
            logger.debug("%r %s %s (unknown)", cid, pos, length)
            s = ImageFile._safe_read(self.fp, length)

        self.png.crc(cid, s)

    #
    # Copy relevant attributes from the PngStream.  An alternative
    # would be to let the PngStream class modify these attributes
    # directly, but that introduces circular references which are
    # difficult to break if things go wrong in the decoder...
    # (believe me, I've tried ;-)

    self.mode = self.png.im_mode
    self._size = self.png.im_size
    self.info = self.png.im_info
    self._text = None
    self.tile = self.png.im_tile
    self.custom_mimetype = self.png.im_custom_mimetype
    self._n_frames = self.png.im_n_frames
    self.default_image = self.info.get("default_image", False)

    if self.png.im_palette:
        rawmode, data = self.png.im_palette
        self.palette = ImagePalette.raw(rawmode, data)

    if cid == b"fdAT":
        self.__prepare_idat = length - 4
    else:
        self.__prepare_idat = length  # used by load_prepare()

    if self._n_frames is not None:
        self._close_exclusive_fp_after_loading = False
        self.png.save_rewind()
        self.__rewind_idat = self.__prepare_idat
        self.__rewind = self.__fp.tell()
        if self.default_image:
            # IDAT chunk contains default image and not first animation frame
            self._n_frames += 1
        self._seek(0)


# Traceback (AttributeError)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/miniconda/envs/sunpy/lib/python3.8/site-packages/skimage/data/__init__.py", line 109, in camera
return _load("camera.png")
File "/opt/miniconda/envs/sunpy/lib/python3.8/site-packages/skimage/data/__init__.py", line 96, in _load
return imread(_os.path.join(data_dir, f), plugin='pil', as_gray=as_gray)
File "/opt/miniconda/envs/sunpy/lib/python3.8/site-packages/skimage/io/_io.py", line 48, in imread
img = call_plugin('imread', fname, plugin=plugin, **plugin_args)
File "/opt/miniconda/envs/sunpy/lib/python3.8/site-packages/skimage/io/manage_plugins.py", line 210, in call_plugin
return func(*args, **kwargs)
File "/opt/miniconda/envs/sunpy/lib/python3.8/site-packages/skimage/io/_plugins/pil_plugin.py", line 36, in imread
return pil_to_ndarray(im, dtype=dtype, img_num=img_num)
File "/opt/miniconda/envs/sunpy/lib/python3.8/site-packages/skimage/io/_plugins/pil_plugin.py", line 66, in pil_to_ndarray
image.seek(i)
File "/opt/miniconda/envs/sunpy/lib/python3.8/site-packages/PIL/PngImagePlugin.py", line 748, in seek
self._seek(f)
File "/opt/miniconda/envs/sunpy/lib/python3.8/site-packages/PIL/PngImagePlugin.py", line 791, in _seek
cid, pos, length = self.png.read()
AttributeError: 'NoneType' object has no attribute 'read'

# Fixed code
def _open(self):
    if self.fp.read(8) != _MAGIC:
        raise SyntaxError("not a PNG file")
    self.__fp = self.fp
    self.__frame = 0

    #
    # Parse headers up to the first IDAT or fDAT chunk

    self.png = PngStream(self.fp)

    while True:
        #
        # get next chunk

        cid, pos, length = self.png.read()

        try:
            s = self.png.call(cid, pos, length)
        except EOFError:
            break
        except AttributeError:
            logger.debug("%r %s %s (unknown)", cid, pos, length)
            s = ImageFile._safe_read(self.fp, length)

        self.png.crc(cid, s)

    #
    # Copy relevant attributes from the PngStream.  An alternative
    # would be to let the PngStream class modify these attributes
    # directly, but that introduces circular references which are
    # difficult to break if things go wrong in the decoder...
    # (believe me, I've tried ;-)

    self.mode = self.png.im_mode
    self._size = self.png.im_size
    self.info = self.png.im_info
    self._text = None
    self.tile = self.png.im_tile
    self.custom_mimetype = self.png.im_custom_mimetype
    self.n_frames = self.png.im_n_frames or 1
    self.default_image = self.info.get("default_image", False)

    if self.png.im_palette:
        rawmode, data = self.png.im_palette
        self.palette = ImagePalette.raw(rawmode, data)

    if cid == b"fdAT":
        self.__prepare_idat = length - 4
    else:
        self.__prepare_idat = length  # used by load_prepare()

    if self.png.im_n_frames is not None:
        self._close_exclusive_fp_after_loading = False
        self.png.save_rewind()
        self.__rewind_idat = self.__prepare_idat
        self.__rewind = self.__fp.tell()
        if self.default_image:
            # IDAT chunk contains default image and not first animation frame
            self.n_frames += 1
        self._seek(0)
    self.is_animated = self.n_frames > 1