Repository: python-pillow/Pillow

Issue:
AttributeError: 'NoneType' object has no attribute 'read' 
<!--
Thank you for reporting an issue.

Follow these guidelines to ensure your issue is handled properly.

If you have a ...

1.  General question: consider asking the question on Stack Overflow
    with the python-imaging-library tag:

    * https://stackoverflow.com/questions/tagged/python-imaging-library

    Do not ask a question in both places.

    If you think you have found a bug or have an unexplained exception
    then file a bug report here.

2.  Bug report: include a self-contained, copy-pastable example that
    generates the issue if possible. Be concise with code posted.
    Guidelines on how to provide a good bug report:

    * https://stackoverflow.com/help/mcve

    Bug reports which follow these guidelines are easier to diagnose,
    and are often handled much more quickly.

3.  Feature request: do a quick search of existing issues
    to make sure this has not been asked before.

We know asking good questions takes effort, and we appreciate your time.
Thank you.
-->

### What did you do?

Following on from #4509 after the 7.1.0 release, we have a new issue that cropped up opening a png from scikit-image.

### What did you expect to happen?

Not error.

### What actually happened?

It errored.

### What are your OS, Python and Pillow versions?

* OS: Linux
* Python: 3.8.2
* Pillow: 7.1.0

<!--
Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive.

The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as Plone, Django, or Buildout, try to replicate the issue just using Pillow.
-->
The code should just be loading a scikit-image PNG data:

```python
from skimage import data
original = data.camera().astype('float')
```
The error message is:
```python
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'
```

Since this is scikit image loading one of their PNGs, I wonder if it's actually a bug there. 


Discussion:
I'll look into this, I think the issue is `seek(1)` (or anything > 0) not raising EOFError for regular png's

Resolving patch:
diff --git a/src/PIL/PngImagePlugin.py b/src/PIL/PngImagePlugin.py
--- a/src/PIL/PngImagePlugin.py
+++ b/src/PIL/PngImagePlugin.py
@@ -673,7 +673,7 @@ def _open(self):
         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.n_frames = self.png.im_n_frames or 1
         self.default_image = self.info.get("default_image", False)
 
         if self.png.im_palette:
@@ -685,15 +685,16 @@ def _open(self):
         else:
             self.__prepare_idat = length  # used by load_prepare()
 
-        if self._n_frames is not None:
+        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.n_frames += 1
             self._seek(0)
+        self.is_animated = self.n_frames > 1
 
     @property
     def text(self):
@@ -710,16 +711,6 @@ def text(self):
                 self.seek(frame)
         return self._text
 
-    @property
-    def n_frames(self):
-        if self._n_frames is None:
-            return 1
-        return self._n_frames
-
-    @property
-    def is_animated(self):
-        return self._n_frames is not None and self._n_frames > 1
-
     def verify(self):
         """Verify PNG file"""
 


Test patch:
diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py
--- a/Tests/test_file_png.py
+++ b/Tests/test_file_png.py
@@ -637,6 +637,9 @@ def test_seek(self):
         with Image.open(TEST_PNG_FILE) as im:
             im.seek(0)
 
+            with pytest.raises(EOFError):
+                im.seek(1)
+
 
 @pytest.mark.skipif(is_win32(), reason="Requires Unix or macOS")
 @skip_unless_feature("zlib")