Repository: python-pillow/Pillow

Issue:
Pillow 9.2: 'NoneType' object has no attribute 'copy'
Testcase:
````python
import base64, io
import PIL.Image

gif = base64.b64decode('''
R0lGODlhSQCCAPflAP39/f39/f39/f79/f79/f79/f79/f79/f79/f7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v3+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+
/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/v7+/P7++P398fz87Pz75vz84f393f392v392fv72Pj41fPw0ezoy+Xgxt7ZwNTPuM/Ks8zDqsy/odC7kdy5e+O5bOa2Yea0XOayWeWtVuWtU+SsT+WtS+KqSuCnRN2jQNuhP9icPdaZO9OWPtCTPNCQOc6MNMuJMsmHMcWFMsKFNrOGSaCFXo6DboJ/c3d0bW5rZnRoWoVnSptqO6lrMbFqKrZpKLRkJq9fJKpbI6VYIp5TIZpSIJlPIJdMIJVHIJBEHopDHYVDHYJCHH4+Gno8GXk4GHY2F3UzFXEvFGwqDmUkDF0cB1cYBFETA0kNA0MKAz4HAzkGBTQEBygDCBgDCwgCCgYDEwYDGQUDHgkCJAsDKAoGLAcPMQUWNgMdOgMgPQEiPgIkQAMmQwMpRQQrSAksSREsShUqRxosSR4wSyMyTSo0
Szc9TT4+Pz84Nz85MyH5BAgAAOUALAAAAABJAIIAAAj+AFEIHEiwoMGDCBMqXMiwocOHECNKnEixosWLGDNq3Mixo8ePIEOKHEmypMmTKFOqXMmypcuXMGPKnEmzps2bOHPq3Mmzp8+fQIMKHUq0qNGjSJMqXcq0qdOnBSeNykX12LFiuG7dqjUrl7Fly4J5eoTCCMsMRgSM0lWsbS1XtGDVukXrFi5bsFCxwkXrVKlRo0iRCtYLZQZNw6jSetXqVjFbuHAVU1at2bVs2KhNUyYtWS6upVCVUlWL6r9HGUQWI/a5lqzXt5xhuwytGbRs17R1AxdOHLhv4Lht45bNmbK7t2a5siXsn9mNCB7FimVX8rHb0KZ105ZtWjJjxLD+FmtWbRo0aNq8ceMGrpv7bdCYFaPVqtUpWP82yrqL67q1bel1cw012FTzTDLh4fLKKqvY8spctdRSzCys5IKKMNh4A6A20BQjy4Kp6JIfRQKgQEpdxSTDjDTarMfdNcok
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''')

with PIL.Image.open(io.BytesIO(gif)) as img:
    assert not getattr(img,'is_animated',False)
    PIL.ImageOps.exif_transpose( img )
````

Console:
```` 
Traceback (most recent call last):
  File "err.py", line 17, in <module>
    PIL.ImageOps.exif_transpose( img )
  File "/home/max/.local/lib/python3.8/site-packages/PIL/ImageOps.py", line 610, in exif_transpose
    return image.copy()
  File "/home/max/.local/lib/python3.8/site-packages/PIL/Image.py", line 1150, in copy
    return self._new(self.im.copy())
AttributeError: 'NoneType' object has no attribute 'copy'
````



Discussion:

Resolving patch:
diff --git a/src/PIL/GifImagePlugin.py b/src/PIL/GifImagePlugin.py
--- a/src/PIL/GifImagePlugin.py
+++ b/src/PIL/GifImagePlugin.py
@@ -185,8 +185,6 @@ def _seek(self, frame, update_image=True):
         if not s or s == b";":
             raise EOFError
 
-        self.tile = []
-
         palette = None
 
         info = {}
@@ -295,6 +293,8 @@ def _seek(self, frame, update_image=True):
         if not update_image:
             return
 
+        self.tile = []
+
         if self.dispose:
             self.im.paste(self.dispose, self.dispose_extent)
 


Test patch:
diff --git a/Tests/images/comment_after_only_frame.gif b/Tests/images/comment_after_only_frame.gif
new file mode 100644
Binary files /dev/null and b/Tests/images/comment_after_only_frame.gif differ
diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py
--- a/Tests/test_file_gif.py
+++ b/Tests/test_file_gif.py
@@ -399,6 +399,11 @@ def test_no_change():
         assert im.is_animated
         assert_image_equal(im, expected)
 
+    with Image.open("Tests/images/comment_after_only_frame.gif") as im:
+        expected = Image.new("P", (1, 1))
+        assert not im.is_animated
+        assert_image_equal(im, expected)
+
 
 def test_eoferror():
     with Image.open(TEST_GIF) as im: