Skip to content

Commit 80a7c95

Browse files
satya-waylitpre-commit-ci[bot]johanneswilm
authored
Close files in create thumbnail (#252)
* Close the orig file in `create_thumbnail` method * Skip python-magic tests on windows * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Johannes Wilm <mail@johanneswilm.org>
1 parent 7cb5533 commit 80a7c95

File tree

3 files changed

+37
-29
lines changed

3 files changed

+37
-29
lines changed

CHANGELOG.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
Changelog
22
=========
33
* 9.0.0 (in progress)
4+
* Fix files not closed in `create_thumbnail`
45
* Add Django 5.2 and 6.0 support
56
* Add Python 3.13, 3.14 support
67
* Drop Python 3.8, 3.9 support

avatar/models.py

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import binascii
22
import hashlib
33
import os
4+
from contextlib import closing
45
from io import BytesIO
56

67
from django.core.files import File
@@ -142,38 +143,38 @@ def create_thumbnail(self, width, height=None, quality=None):
142143
orig = self.avatar.storage.open(self.avatar.name, "rb")
143144
except IOError:
144145
return # What should we do here? Render a "sorry, didn't work" img?
145-
try:
146-
image = Image.open(orig)
147-
image = self.transpose_image(image)
148-
quality = quality or settings.AVATAR_THUMB_QUALITY
149-
w, h = image.size
150-
if w != width or h != height:
151-
ratioReal = 1.0 * w / h
152-
ratioWant = 1.0 * width / height
153-
if ratioReal > ratioWant:
154-
diff = int((w - (h * ratioWant)) / 2)
155-
image = image.crop((diff, 0, w - diff, h))
156-
elif ratioReal < ratioWant:
157-
diff = int((h - (w / ratioWant)) / 2)
158-
image = image.crop((0, diff, w, h - diff))
159-
if settings.AVATAR_THUMB_FORMAT == "JPEG" and image.mode == "RGBA":
160-
image = image.convert("RGB")
161-
elif image.mode not in (settings.AVATAR_THUMB_MODES):
162-
image = image.convert(settings.AVATAR_THUMB_MODES[0])
163-
image = image.resize((width, height), settings.AVATAR_RESIZE_METHOD)
164-
thumb = BytesIO()
165-
image.save(thumb, settings.AVATAR_THUMB_FORMAT, quality=quality)
166-
thumb_file = ContentFile(thumb.getvalue())
167-
else:
146+
147+
with closing(orig):
148+
try:
149+
image = Image.open(orig)
150+
except IOError:
168151
thumb_file = File(orig)
152+
else:
153+
image = self.transpose_image(image)
154+
quality = quality or settings.AVATAR_THUMB_QUALITY
155+
w, h = image.size
156+
if w != width or h != height:
157+
ratioReal = 1.0 * w / h
158+
ratioWant = 1.0 * width / height
159+
if ratioReal > ratioWant:
160+
diff = int((w - (h * ratioWant)) / 2)
161+
image = image.crop((diff, 0, w - diff, h))
162+
elif ratioReal < ratioWant:
163+
diff = int((h - (w / ratioWant)) / 2)
164+
image = image.crop((0, diff, w, h - diff))
165+
if settings.AVATAR_THUMB_FORMAT == "JPEG" and image.mode == "RGBA":
166+
image = image.convert("RGB")
167+
elif image.mode not in (settings.AVATAR_THUMB_MODES):
168+
image = image.convert(settings.AVATAR_THUMB_MODES[0])
169+
image = image.resize((width, height), settings.AVATAR_RESIZE_METHOD)
170+
thumb = BytesIO()
171+
image.save(thumb, settings.AVATAR_THUMB_FORMAT, quality=quality)
172+
thumb_file = ContentFile(thumb.getvalue())
173+
else:
174+
thumb_file = File(orig)
169175
thumb_name = self.avatar_name(width, height)
170176
thumb = self.avatar.storage.save(thumb_name, thumb_file)
171-
except IOError:
172-
thumb_file = File(orig)
173-
thumb = self.avatar.storage.save(
174-
self.avatar_name(width, height), thumb_file
175-
)
176-
invalidate_cache(self.user, width, height)
177+
invalidate_cache(self.user, width, height)
177178

178179
def avatar_url(self, width, height=None):
179180
return self.avatar.storage.url(self.avatar_name(width, height))

tests/tests.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import math
22
import os.path
3+
import sys
34
from pathlib import Path
45
from shutil import rmtree
6+
from unittest import skipIf
57

68
from django.contrib.admin.sites import AdminSite
79
from django.core import management
@@ -118,6 +120,7 @@ def test_normal_image_upload(self):
118120
self.assertTrue(avatar.primary)
119121

120122
# We allow the .tiff file extension but not the mime type
123+
@skipIf(sys.platform == "win32", "Skipping test on Windows platform")
121124
@override_settings(AVATAR_ALLOWED_FILE_EXTS=(".png", ".gif", ".jpg", ".tiff"))
122125
@override_settings(
123126
AVATAR_ALLOWED_MIMETYPES=("image/png", "image/gif", "image/jpeg")
@@ -130,6 +133,7 @@ def test_unsupported_image_format_upload(self):
130133
self.assertNotEqual(response.context["upload_avatar_form"].errors, {})
131134

132135
# We allow the .tiff file extension and the mime type
136+
@skipIf(sys.platform == "win32", "Skipping test on Windows platform")
133137
@override_settings(AVATAR_ALLOWED_FILE_EXTS=(".png", ".gif", ".jpg", ".tiff"))
134138
@override_settings(
135139
AVATAR_ALLOWED_MIMETYPES=("image/png", "image/gif", "image/jpeg", "image/tiff")
@@ -141,13 +145,15 @@ def test_supported_image_format_upload(self):
141145
self.assertEqual(len(response.redirect_chain), 1) # Redirect only if it worked
142146
self.assertEqual(response.context["upload_avatar_form"].errors, {})
143147

148+
@skipIf(sys.platform == "win32", "Skipping test on Windows platform")
144149
@override_settings(AVATAR_ALLOWED_FILE_EXTS=(".jpg", ".png"))
145150
def test_image_without_wrong_extension(self):
146151
response = upload_helper(self, "imagefilewithoutext")
147152
self.assertEqual(response.status_code, 200)
148153
self.assertEqual(len(response.redirect_chain), 0) # Redirect only if it worked
149154
self.assertNotEqual(response.context["upload_avatar_form"].errors, {})
150155

156+
@skipIf(sys.platform == "win32", "Skipping test on Windows platform")
151157
@override_settings(AVATAR_ALLOWED_FILE_EXTS=(".jpg", ".png"))
152158
def test_image_with_wrong_extension(self):
153159
response = upload_helper(self, "imagefilewithwrongext.ogg")

0 commit comments

Comments
 (0)