Skip to content

Commit

Permalink
Improve handling of file resources (#3577)
Browse files Browse the repository at this point in the history
Improve handling of file resources
  • Loading branch information
hugovk committed Nov 1, 2019
2 parents 97dae4f + 4cd4add commit 2cf510e
Show file tree
Hide file tree
Showing 65 changed files with 1,779 additions and 1,617 deletions.
30 changes: 15 additions & 15 deletions Tests/test_bmp_reference.py
Expand Up @@ -24,8 +24,8 @@ def test_bad(self):

def open(f):
try:
im = Image.open(f)
im.load()
with Image.open(f) as im:
im.load()
except Exception: # as msg:
pass

Expand All @@ -48,8 +48,8 @@ def test_questionable(self):
]
for f in self.get_files("q"):
try:
im = Image.open(f)
im.load()
with Image.open(f) as im:
im.load()
if os.path.basename(f) not in supported:
print("Please add %s to the partially supported bmp specs." % f)
except Exception: # as msg:
Expand Down Expand Up @@ -89,17 +89,17 @@ def get_compare(f):

for f in self.get_files("g"):
try:
im = Image.open(f)
im.load()
compare = Image.open(get_compare(f))
compare.load()
if im.mode == "P":
# assert image similar doesn't really work
# with paletized image, since the palette might
# be differently ordered for an equivalent image.
im = im.convert("RGBA")
compare = im.convert("RGBA")
self.assert_image_similar(im, compare, 5)
with Image.open(f) as im:
im.load()
with Image.open(get_compare(f)) as compare:
compare.load()
if im.mode == "P":
# assert image similar doesn't really work
# with paletized image, since the palette might
# be differently ordered for an equivalent image.
im = im.convert("RGBA")
compare = im.convert("RGBA")
self.assert_image_similar(im, compare, 5)

except Exception as msg:
# there are three here that are unsupported:
Expand Down
17 changes: 13 additions & 4 deletions Tests/test_decompression_bomb.py
Expand Up @@ -14,7 +14,8 @@ def tearDown(self):
def test_no_warning_small_file(self):
# Implicit assert: no warning.
# A warning would cause a failure.
Image.open(TEST_FILE)
with Image.open(TEST_FILE):
pass

def test_no_warning_no_limit(self):
# Arrange
Expand All @@ -25,21 +26,28 @@ def test_no_warning_no_limit(self):
# Act / Assert
# Implicit assert: no warning.
# A warning would cause a failure.
Image.open(TEST_FILE)
with Image.open(TEST_FILE):
pass

def test_warning(self):
# Set limit to trigger warning on the test file
Image.MAX_IMAGE_PIXELS = 128 * 128 - 1
self.assertEqual(Image.MAX_IMAGE_PIXELS, 128 * 128 - 1)

self.assert_warning(Image.DecompressionBombWarning, Image.open, TEST_FILE)
def open():
with Image.open(TEST_FILE):
pass

self.assert_warning(Image.DecompressionBombWarning, open)

def test_exception(self):
# Set limit to trigger exception on the test file
Image.MAX_IMAGE_PIXELS = 64 * 128 - 1
self.assertEqual(Image.MAX_IMAGE_PIXELS, 64 * 128 - 1)

self.assertRaises(Image.DecompressionBombError, lambda: Image.open(TEST_FILE))
with self.assertRaises(Image.DecompressionBombError):
with Image.open(TEST_FILE):
pass

def test_exception_ico(self):
with self.assertRaises(Image.DecompressionBombError):
Expand All @@ -53,6 +61,7 @@ def test_exception_gif(self):
class TestDecompressionCrop(PillowTestCase):
def setUp(self):
self.src = hopper()
self.addCleanup(self.src.close)
Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width * 4 - 1

def tearDown(self):
Expand Down
12 changes: 6 additions & 6 deletions Tests/test_file_blp.py
Expand Up @@ -5,14 +5,14 @@

class TestFileBlp(PillowTestCase):
def test_load_blp2_raw(self):
im = Image.open("Tests/images/blp/blp2_raw.blp")
target = Image.open("Tests/images/blp/blp2_raw.png")
self.assert_image_equal(im, target)
with Image.open("Tests/images/blp/blp2_raw.blp") as im:
with Image.open("Tests/images/blp/blp2_raw.png") as target:
self.assert_image_equal(im, target)

def test_load_blp2_dxt1(self):
im = Image.open("Tests/images/blp/blp2_dxt1.blp")
target = Image.open("Tests/images/blp/blp2_dxt1.png")
self.assert_image_equal(im, target)
with Image.open("Tests/images/blp/blp2_dxt1.blp") as im:
with Image.open("Tests/images/blp/blp2_dxt1.png") as target:
self.assert_image_equal(im, target)

def test_load_blp2_dxt1a(self):
im = Image.open("Tests/images/blp/blp2_dxt1a.blp")
Expand Down
27 changes: 13 additions & 14 deletions Tests/test_file_bmp.py
Expand Up @@ -46,13 +46,12 @@ def test_dpi(self):
dpi = (72, 72)

output = io.BytesIO()
im = hopper()
im.save(output, "BMP", dpi=dpi)
with hopper() as im:
im.save(output, "BMP", dpi=dpi)

output.seek(0)
reloaded = Image.open(output)

self.assertEqual(reloaded.info["dpi"], dpi)
with Image.open(output) as reloaded:
self.assertEqual(reloaded.info["dpi"], dpi)

def test_save_bmp_with_dpi(self):
# Test for #1301
Expand All @@ -72,24 +71,24 @@ def test_save_bmp_with_dpi(self):

def test_load_dpi_rounding(self):
# Round up
im = Image.open("Tests/images/hopper.bmp")
self.assertEqual(im.info["dpi"], (96, 96))
with Image.open("Tests/images/hopper.bmp") as im:
self.assertEqual(im.info["dpi"], (96, 96))

# Round down
im = Image.open("Tests/images/hopper_roundDown.bmp")
self.assertEqual(im.info["dpi"], (72, 72))
with Image.open("Tests/images/hopper_roundDown.bmp") as im:
self.assertEqual(im.info["dpi"], (72, 72))

def test_save_dpi_rounding(self):
outfile = self.tempfile("temp.bmp")
im = Image.open("Tests/images/hopper.bmp")

im.save(outfile, dpi=(72.2, 72.2))
reloaded = Image.open(outfile)
self.assertEqual(reloaded.info["dpi"], (72, 72))
with Image.open(outfile) as reloaded:
self.assertEqual(reloaded.info["dpi"], (72, 72))

im.save(outfile, dpi=(72.8, 72.8))
reloaded = Image.open(outfile)
self.assertEqual(reloaded.info["dpi"], (73, 73))
im.save(outfile, dpi=(72.8, 72.8))
with Image.open(outfile) as reloaded:
self.assertEqual(reloaded.info["dpi"], (73, 73))

def test_load_dib(self):
# test for #1293, Imagegrab returning Unsupported Bitfields Format
Expand Down
18 changes: 9 additions & 9 deletions Tests/test_file_bufrstub.py
Expand Up @@ -8,14 +8,14 @@
class TestFileBufrStub(PillowTestCase):
def test_open(self):
# Act
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:

# Assert
self.assertEqual(im.format, "BUFR")
# Assert
self.assertEqual(im.format, "BUFR")

# Dummy data from the stub
self.assertEqual(im.mode, "F")
self.assertEqual(im.size, (1, 1))
# Dummy data from the stub
self.assertEqual(im.mode, "F")
self.assertEqual(im.size, (1, 1))

def test_invalid_file(self):
# Arrange
Expand All @@ -28,10 +28,10 @@ def test_invalid_file(self):

def test_load(self):
# Arrange
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:

# Act / Assert: stub cannot load without an implemented handler
self.assertRaises(IOError, im.load)
# Act / Assert: stub cannot load without an implemented handler
self.assertRaises(IOError, im.load)

def test_save(self):
# Arrange
Expand Down
4 changes: 2 additions & 2 deletions Tests/test_file_container.py
Expand Up @@ -11,8 +11,8 @@ def test_sanity(self):
dir(ContainerIO)

def test_isatty(self):
im = hopper()
container = ContainerIO.ContainerIO(im, 0, 0)
with hopper() as im:
container = ContainerIO.ContainerIO(im, 0, 0)

self.assertFalse(container.isatty())

Expand Down
66 changes: 42 additions & 24 deletions Tests/test_file_dcx.py
@@ -1,6 +1,8 @@
import unittest

from PIL import DcxImagePlugin, Image

from .helper import PillowTestCase, hopper
from .helper import PillowTestCase, hopper, is_pypy

# Created with ImageMagick: convert hopper.ppm hopper.dcx
TEST_FILE = "Tests/images/hopper.dcx"
Expand All @@ -11,19 +13,35 @@ def test_sanity(self):
# Arrange

# Act
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:

# Assert
self.assertEqual(im.size, (128, 128))
self.assertIsInstance(im, DcxImagePlugin.DcxImageFile)
orig = hopper()
self.assert_image_equal(im, orig)
# Assert
self.assertEqual(im.size, (128, 128))
self.assertIsInstance(im, DcxImagePlugin.DcxImageFile)
orig = hopper()
self.assert_image_equal(im, orig)

@unittest.skipIf(is_pypy(), "Requires CPython")
def test_unclosed_file(self):
def open():
im = Image.open(TEST_FILE)
im.load()

self.assert_warning(ResourceWarning, open)

def test_closed_file(self):
def open():
im = Image.open(TEST_FILE)
im.load()
im.close()

self.assert_warning(None, open)

def test_context_manager(self):
def open():
with Image.open(TEST_FILE) as im:
im.load()

self.assert_warning(None, open)

def test_invalid_file(self):
Expand All @@ -32,34 +50,34 @@ def test_invalid_file(self):

def test_tell(self):
# Arrange
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:

# Act
frame = im.tell()
# Act
frame = im.tell()

# Assert
self.assertEqual(frame, 0)
# Assert
self.assertEqual(frame, 0)

def test_n_frames(self):
im = Image.open(TEST_FILE)
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
with Image.open(TEST_FILE) as im:
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)

def test_eoferror(self):
im = Image.open(TEST_FILE)
n_frames = im.n_frames
with Image.open(TEST_FILE) as im:
n_frames = im.n_frames

# Test seeking past the last frame
self.assertRaises(EOFError, im.seek, n_frames)
self.assertLess(im.tell(), n_frames)
# Test seeking past the last frame
self.assertRaises(EOFError, im.seek, n_frames)
self.assertLess(im.tell(), n_frames)

# Test that seeking to the last frame does not raise an error
im.seek(n_frames - 1)
# Test that seeking to the last frame does not raise an error
im.seek(n_frames - 1)

def test_seek_too_far(self):
# Arrange
im = Image.open(TEST_FILE)
frame = 999 # too big on purpose
with Image.open(TEST_FILE) as im:
frame = 999 # too big on purpose

# Act / Assert
self.assertRaises(EOFError, im.seek, frame)

0 comments on commit 2cf510e

Please sign in to comment.