Improve handling of file resources

Follow Python's file object semantics. User code is responsible for
closing resources (usually through a context manager) in a deterministic
way.

To achieve this, remove __del__ functions. These functions used to
closed open file handlers in an attempt to silence Python
ResourceWarnings. However, using __del__ has the following drawbacks:

- __del__ isn't called until the object's reference count reaches 0.
  Therefore, resource handlers remain open or in use longer than
  necessary.

- The __del__ method isn't guaranteed to execute on system exit. See the
  Python documentation:

  https://docs.python.org/3/reference/datamodel.html#object.__del__

  > It is not guaranteed that __del__() methods are called for objects
  > that still exist when the interpreter exits.

- Exceptions that occur inside __del__ are ignored instead of raised.
  This has the potential of hiding bugs. This is also in the Python
  documentation:

  > Warning: Due to the precarious circumstances under which __del__()
  > methods are invoked, exceptions that occur during their execution
  > are ignored, and a warning is printed to sys.stderr instead.

Instead, always close resource handlers when they are no longer in use.
This will close the file handler at a specified point in the user's code
and not wait until the interpreter chooses to. It is always guaranteed
to run. And, if an exception occurs while closing the file handler, the
bug will not be ignored.

Now, when code receives a ResourceWarning, it will highlight an area
that is mishandling resources. It should not simply be silenced, but
fixed by closing resources with a context manager.

All warnings that were emitted during tests have been cleaned up. To
enable warnings, I passed the `-Wa` CLI option to Python. This exposed
some mishandling of resources in ImageFile.__init__() and
SpiderImagePlugin.loadImageSeries(), they too were fixed.
This commit is contained in:
Jon Dufresne 2019-05-25 09:30:58 -07:00
parent 6595ce1609
commit 4cd4adddc3
65 changed files with 1767 additions and 1605 deletions

View File

@ -24,8 +24,8 @@ class TestBmpReference(PillowTestCase):
def open(f):
try:
im = Image.open(f)
im.load()
with Image.open(f) as im:
im.load()
except Exception: # as msg:
pass
@ -48,8 +48,8 @@ class TestBmpReference(PillowTestCase):
]
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:
@ -89,17 +89,17 @@ class TestBmpReference(PillowTestCase):
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:

View File

@ -14,7 +14,8 @@ class TestDecompressionBomb(PillowTestCase):
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
@ -25,21 +26,28 @@ class TestDecompressionBomb(PillowTestCase):
# 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):
@ -53,6 +61,7 @@ class TestDecompressionBomb(PillowTestCase):
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):

View File

@ -5,14 +5,14 @@ from .helper import PillowTestCase
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")

View File

@ -46,13 +46,12 @@ class TestFileBmp(PillowTestCase):
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
@ -72,24 +71,24 @@ class TestFileBmp(PillowTestCase):
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

View File

@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/gfs.t06z.rassda.tm00.bufr_d"
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
@ -28,10 +28,10 @@ class TestFileBufrStub(PillowTestCase):
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

View File

@ -11,8 +11,8 @@ class TestFileContainer(PillowTestCase):
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())

View File

@ -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"
@ -11,19 +13,35 @@ class TestFileDcx(PillowTestCase):
# 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):
@ -32,34 +50,34 @@ class TestFileDcx(PillowTestCase):
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)

View File

@ -25,30 +25,30 @@ class TestFileEps(PillowTestCase):
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_sanity(self):
# Regular scale
image1 = Image.open(file1)
image1.load()
self.assertEqual(image1.mode, "RGB")
self.assertEqual(image1.size, (460, 352))
self.assertEqual(image1.format, "EPS")
with Image.open(file1) as image1:
image1.load()
self.assertEqual(image1.mode, "RGB")
self.assertEqual(image1.size, (460, 352))
self.assertEqual(image1.format, "EPS")
image2 = Image.open(file2)
image2.load()
self.assertEqual(image2.mode, "RGB")
self.assertEqual(image2.size, (360, 252))
self.assertEqual(image2.format, "EPS")
with Image.open(file2) as image2:
image2.load()
self.assertEqual(image2.mode, "RGB")
self.assertEqual(image2.size, (360, 252))
self.assertEqual(image2.format, "EPS")
# Double scale
image1_scale2 = Image.open(file1)
image1_scale2.load(scale=2)
self.assertEqual(image1_scale2.mode, "RGB")
self.assertEqual(image1_scale2.size, (920, 704))
self.assertEqual(image1_scale2.format, "EPS")
with Image.open(file1) as image1_scale2:
image1_scale2.load(scale=2)
self.assertEqual(image1_scale2.mode, "RGB")
self.assertEqual(image1_scale2.size, (920, 704))
self.assertEqual(image1_scale2.format, "EPS")
image2_scale2 = Image.open(file2)
image2_scale2.load(scale=2)
self.assertEqual(image2_scale2.mode, "RGB")
self.assertEqual(image2_scale2.size, (720, 504))
self.assertEqual(image2_scale2.format, "EPS")
with Image.open(file2) as image2_scale2:
image2_scale2.load(scale=2)
self.assertEqual(image2_scale2.mode, "RGB")
self.assertEqual(image2_scale2.size, (720, 504))
self.assertEqual(image2_scale2.format, "EPS")
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
@ -57,43 +57,42 @@ class TestFileEps(PillowTestCase):
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_cmyk(self):
cmyk_image = Image.open("Tests/images/pil_sample_cmyk.eps")
with Image.open("Tests/images/pil_sample_cmyk.eps") as cmyk_image:
self.assertEqual(cmyk_image.mode, "CMYK")
self.assertEqual(cmyk_image.size, (100, 100))
self.assertEqual(cmyk_image.format, "EPS")
self.assertEqual(cmyk_image.mode, "CMYK")
self.assertEqual(cmyk_image.size, (100, 100))
self.assertEqual(cmyk_image.format, "EPS")
cmyk_image.load()
self.assertEqual(cmyk_image.mode, "RGB")
cmyk_image.load()
self.assertEqual(cmyk_image.mode, "RGB")
if "jpeg_decoder" in dir(Image.core):
target = Image.open("Tests/images/pil_sample_rgb.jpg")
self.assert_image_similar(cmyk_image, target, 10)
if "jpeg_decoder" in dir(Image.core):
target = Image.open("Tests/images/pil_sample_rgb.jpg")
self.assert_image_similar(cmyk_image, target, 10)
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_showpage(self):
# See https://github.com/python-pillow/Pillow/issues/2615
plot_image = Image.open("Tests/images/reqd_showpage.eps")
target = Image.open("Tests/images/reqd_showpage.png")
# should not crash/hang
plot_image.load()
# fonts could be slightly different
self.assert_image_similar(plot_image, target, 6)
with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
with Image.open("Tests/images/reqd_showpage.png") as target:
# should not crash/hang
plot_image.load()
# fonts could be slightly different
self.assert_image_similar(plot_image, target, 6)
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_file_object(self):
# issue 479
image1 = Image.open(file1)
with open(self.tempfile("temp_file.eps"), "wb") as fh:
image1.save(fh, "EPS")
with Image.open(file1) as image1:
with open(self.tempfile("temp_file.eps"), "wb") as fh:
image1.save(fh, "EPS")
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_iobase_object(self):
# issue 479
image1 = Image.open(file1)
with io.open(self.tempfile("temp_iobase.eps"), "wb") as fh:
image1.save(fh, "EPS")
with Image.open(file1) as image1:
with io.open(self.tempfile("temp_iobase.eps"), "wb") as fh:
image1.save(fh, "EPS")
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_bytesio_object(self):
@ -120,18 +119,18 @@ class TestFileEps(PillowTestCase):
self.skipTest("zip/deflate support not available")
# Zero bounding box
image1_scale1 = Image.open(file1)
image1_scale1.load()
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
image1_scale1_compare.load()
self.assert_image_similar(image1_scale1, image1_scale1_compare, 5)
with Image.open(file1) as image1_scale1:
image1_scale1.load()
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
image1_scale1_compare.load()
self.assert_image_similar(image1_scale1, image1_scale1_compare, 5)
# Non-Zero bounding box
image2_scale1 = Image.open(file2)
image2_scale1.load()
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
image2_scale1_compare.load()
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
with Image.open(file2) as image2_scale1:
image2_scale1.load()
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
image2_scale1_compare.load()
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_render_scale2(self):
@ -141,57 +140,44 @@ class TestFileEps(PillowTestCase):
self.skipTest("zip/deflate support not available")
# Zero bounding box
image1_scale2 = Image.open(file1)
image1_scale2.load(scale=2)
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
image1_scale2_compare.load()
self.assert_image_similar(image1_scale2, image1_scale2_compare, 5)
with Image.open(file1) as image1_scale2:
image1_scale2.load(scale=2)
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
image1_scale2_compare.load()
self.assert_image_similar(image1_scale2, image1_scale2_compare, 5)
# Non-Zero bounding box
image2_scale2 = Image.open(file2)
image2_scale2.load(scale=2)
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
image2_scale2_compare.load()
self.assert_image_similar(image2_scale2, image2_scale2_compare, 10)
with Image.open(file2) as image2_scale2:
image2_scale2.load(scale=2)
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
image2_scale2_compare.load()
self.assert_image_similar(image2_scale2, image2_scale2_compare, 10)
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_resize(self):
# Arrange
image1 = Image.open(file1)
image2 = Image.open(file2)
image3 = Image.open("Tests/images/illu10_preview.eps")
new_size = (100, 100)
# Act
image1 = image1.resize(new_size)
image2 = image2.resize(new_size)
image3 = image3.resize(new_size)
# Assert
self.assertEqual(image1.size, new_size)
self.assertEqual(image2.size, new_size)
self.assertEqual(image3.size, new_size)
files = [file1, file2, "Tests/images/illu10_preview.eps"]
for fn in files:
with Image.open(fn) as im:
new_size = (100, 100)
im = im.resize(new_size)
self.assertEqual(im.size, new_size)
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_thumbnail(self):
# Issue #619
# Arrange
image1 = Image.open(file1)
image2 = Image.open(file2)
new_size = (100, 100)
# Act
image1.thumbnail(new_size)
image2.thumbnail(new_size)
# Assert
self.assertEqual(max(image1.size), max(new_size))
self.assertEqual(max(image2.size), max(new_size))
files = [file1, file2]
for fn in files:
with Image.open(file1) as im:
new_size = (100, 100)
im.thumbnail(new_size)
self.assertEqual(max(im.size), max(new_size))
def test_read_binary_preview(self):
# Issue 302
# open image with binary preview
Image.open(file3)
with Image.open(file3):
pass
def _test_readline(self, t, ending):
ending = "Failure with line ending: %s" % (
@ -239,16 +225,16 @@ class TestFileEps(PillowTestCase):
# Act / Assert
for filename in FILES:
img = Image.open(filename)
self.assertEqual(img.mode, "RGB")
with Image.open(filename) as img:
self.assertEqual(img.mode, "RGB")
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_emptyline(self):
# Test file includes an empty line in the header data
emptyline_file = "Tests/images/zero_bb_emptyline.eps"
image = Image.open(emptyline_file)
image.load()
with Image.open(emptyline_file) as image:
image.load()
self.assertEqual(image.mode, "RGB")
self.assertEqual(image.size, (460, 352))
self.assertEqual(image.format, "EPS")

View File

@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/hopper.fits"
class TestFileFitsStub(PillowTestCase):
def test_open(self):
# Act
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:
# Assert
self.assertEqual(im.format, "FITS")
# Assert
self.assertEqual(im.format, "FITS")
# 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
@ -28,19 +28,19 @@ class TestFileFitsStub(PillowTestCase):
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
im = Image.open(TEST_FILE)
dummy_fp = None
dummy_filename = "dummy.filename"
with Image.open(TEST_FILE) as im:
dummy_fp = None
dummy_filename = "dummy.filename"
# Act / Assert: stub cannot save without an implemented handler
self.assertRaises(IOError, im.save, dummy_filename)
self.assertRaises(
IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename
)
# Act / Assert: stub cannot save without an implemented handler
self.assertRaises(IOError, im.save, dummy_filename)
self.assertRaises(
IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename
)

View File

@ -1,6 +1,8 @@
import unittest
from PIL import FliImagePlugin, Image
from .helper import PillowTestCase
from .helper import PillowTestCase, is_pypy
# created as an export of a palette image from Gimp2.6
# save as...-> hopper.fli, default options.
@ -12,36 +14,52 @@ animated_test_file = "Tests/images/a.fli"
class TestFileFli(PillowTestCase):
def test_sanity(self):
im = Image.open(static_test_file)
im.load()
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "FLI")
self.assertFalse(im.is_animated)
with Image.open(static_test_file) as im:
im.load()
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "FLI")
self.assertFalse(im.is_animated)
im = Image.open(animated_test_file)
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (320, 200))
self.assertEqual(im.format, "FLI")
self.assertEqual(im.info["duration"], 71)
self.assertTrue(im.is_animated)
with Image.open(animated_test_file) as im:
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (320, 200))
self.assertEqual(im.format, "FLI")
self.assertEqual(im.info["duration"], 71)
self.assertTrue(im.is_animated)
@unittest.skipIf(is_pypy(), "Requires CPython")
def test_unclosed_file(self):
def open():
im = Image.open(static_test_file)
im.load()
self.assert_warning(ResourceWarning, open)
def test_closed_file(self):
def open():
im = Image.open(static_test_file)
im.load()
im.close()
self.assert_warning(None, open)
def test_context_manager(self):
def open():
with Image.open(static_test_file) as im:
im.load()
self.assert_warning(None, open)
def test_tell(self):
# Arrange
im = Image.open(static_test_file)
with Image.open(static_test_file) as im:
# Act
frame = im.tell()
# Act
frame = im.tell()
# Assert
self.assertEqual(frame, 0)
# Assert
self.assertEqual(frame, 0)
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"
@ -49,50 +67,50 @@ class TestFileFli(PillowTestCase):
self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file)
def test_n_frames(self):
im = Image.open(static_test_file)
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
with Image.open(static_test_file) as im:
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
im = Image.open(animated_test_file)
self.assertEqual(im.n_frames, 384)
self.assertTrue(im.is_animated)
with Image.open(animated_test_file) as im:
self.assertEqual(im.n_frames, 384)
self.assertTrue(im.is_animated)
def test_eoferror(self):
im = Image.open(animated_test_file)
n_frames = im.n_frames
with Image.open(animated_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_tell(self):
im = Image.open(animated_test_file)
with Image.open(animated_test_file) as im:
layer_number = im.tell()
self.assertEqual(layer_number, 0)
layer_number = im.tell()
self.assertEqual(layer_number, 0)
im.seek(0)
layer_number = im.tell()
self.assertEqual(layer_number, 0)
im.seek(0)
layer_number = im.tell()
self.assertEqual(layer_number, 0)
im.seek(1)
layer_number = im.tell()
self.assertEqual(layer_number, 1)
im.seek(1)
layer_number = im.tell()
self.assertEqual(layer_number, 1)
im.seek(2)
layer_number = im.tell()
self.assertEqual(layer_number, 2)
im.seek(2)
layer_number = im.tell()
self.assertEqual(layer_number, 2)
im.seek(1)
layer_number = im.tell()
self.assertEqual(layer_number, 1)
im.seek(1)
layer_number = im.tell()
self.assertEqual(layer_number, 1)
def test_seek(self):
im = Image.open(animated_test_file)
im.seek(50)
with Image.open(animated_test_file) as im:
im.seek(50)
expected = Image.open("Tests/images/a_fli.png")
self.assert_image_equal(im, expected)
with Image.open("Tests/images/a_fli.png") as expected:
self.assert_image_equal(im, expected)

View File

@ -10,8 +10,6 @@ class TestFileGbr(PillowTestCase):
self.assertRaises(SyntaxError, GbrImagePlugin.GbrImageFile, invalid_file)
def test_gbr_file(self):
im = Image.open("Tests/images/gbr.gbr")
target = Image.open("Tests/images/gbr.png")
self.assert_image_equal(target, im)
with Image.open("Tests/images/gbr.gbr") as im:
with Image.open("Tests/images/gbr.png") as target:
self.assert_image_equal(target, im)

View File

@ -7,9 +7,9 @@ TEST_GD_FILE = "Tests/images/hopper.gd"
class TestFileGd(PillowTestCase):
def test_sanity(self):
im = GdImageFile.open(TEST_GD_FILE)
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "GD")
with GdImageFile.open(TEST_GD_FILE) as im:
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "GD")
def test_bad_mode(self):
self.assertRaises(ValueError, GdImageFile.open, TEST_GD_FILE, "bad mode")

View File

@ -2,7 +2,7 @@ from io import BytesIO
from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette
from .helper import PillowTestCase, hopper, netpbm_available, unittest
from .helper import PillowTestCase, hopper, is_pypy, netpbm_available, unittest
try:
from PIL import _webp
@ -26,18 +26,34 @@ class TestFileGif(PillowTestCase):
self.skipTest("gif support not available") # can this happen?
def test_sanity(self):
im = Image.open(TEST_GIF)
im.load()
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "GIF")
self.assertEqual(im.info["version"], b"GIF89a")
with Image.open(TEST_GIF) as im:
im.load()
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "GIF")
self.assertEqual(im.info["version"], b"GIF89a")
@unittest.skipIf(is_pypy(), "Requires CPython")
def test_unclosed_file(self):
def open():
im = Image.open(TEST_GIF)
im.load()
self.assert_warning(ResourceWarning, open)
def test_closed_file(self):
def open():
im = Image.open(TEST_GIF)
im.load()
im.close()
self.assert_warning(None, open)
def test_context_manager(self):
def open():
with Image.open(TEST_GIF) as im:
im.load()
self.assert_warning(None, open)
def test_invalid_file(self):
@ -112,67 +128,67 @@ class TestFileGif(PillowTestCase):
out = self.tempfile("temp.gif")
im = hopper()
im.save(out)
reread = Image.open(out)
with Image.open(out) as reread:
self.assert_image_similar(reread.convert("RGB"), im, 50)
self.assert_image_similar(reread.convert("RGB"), im, 50)
def test_roundtrip2(self):
# see https://github.com/python-pillow/Pillow/issues/403
out = self.tempfile("temp.gif")
im = Image.open(TEST_GIF)
im2 = im.copy()
im2.save(out)
reread = Image.open(out)
with Image.open(TEST_GIF) as im:
im2 = im.copy()
im2.save(out)
with Image.open(out) as reread:
self.assert_image_similar(reread.convert("RGB"), hopper(), 50)
self.assert_image_similar(reread.convert("RGB"), hopper(), 50)
def test_roundtrip_save_all(self):
# Single frame image
out = self.tempfile("temp.gif")
im = hopper()
im.save(out, save_all=True)
reread = Image.open(out)
with Image.open(out) as reread:
self.assert_image_similar(reread.convert("RGB"), im, 50)
self.assert_image_similar(reread.convert("RGB"), im, 50)
# Multiframe image
im = Image.open("Tests/images/dispose_bgnd.gif")
with Image.open("Tests/images/dispose_bgnd.gif") as im:
out = self.tempfile("temp.gif")
im.save(out, save_all=True)
reread = Image.open(out)
out = self.tempfile("temp.gif")
im.save(out, save_all=True)
with Image.open(out) as reread:
self.assertEqual(reread.n_frames, 5)
self.assertEqual(reread.n_frames, 5)
def test_headers_saving_for_animated_gifs(self):
important_headers = ["background", "version", "duration", "loop"]
# Multiframe image
im = Image.open("Tests/images/dispose_bgnd.gif")
with Image.open("Tests/images/dispose_bgnd.gif") as im:
info = im.info.copy()
info = im.info.copy()
out = self.tempfile("temp.gif")
im.save(out, save_all=True)
reread = Image.open(out)
out = self.tempfile("temp.gif")
im.save(out, save_all=True)
with Image.open(out) as reread:
for header in important_headers:
self.assertEqual(info[header], reread.info[header])
for header in important_headers:
self.assertEqual(info[header], reread.info[header])
def test_palette_handling(self):
# see https://github.com/python-pillow/Pillow/issues/513
im = Image.open(TEST_GIF)
im = im.convert("RGB")
with Image.open(TEST_GIF) as im:
im = im.convert("RGB")
im = im.resize((100, 100), Image.LANCZOS)
im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256)
im = im.resize((100, 100), Image.LANCZOS)
im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256)
f = self.tempfile("temp.gif")
im2.save(f, optimize=True)
f = self.tempfile("temp.gif")
im2.save(f, optimize=True)
reloaded = Image.open(f)
with Image.open(f) as reloaded:
self.assert_image_similar(im, reloaded.convert("RGB"), 10)
self.assert_image_similar(im, reloaded.convert("RGB"), 10)
def test_palette_434(self):
# see https://github.com/python-pillow/Pillow/issues/434
@ -185,108 +201,115 @@ class TestFileGif(PillowTestCase):
return reloaded
orig = "Tests/images/test.colors.gif"
im = Image.open(orig)
with Image.open(orig) as im:
self.assert_image_similar(im, roundtrip(im), 1)
self.assert_image_similar(im, roundtrip(im, optimize=True), 1)
with roundtrip(im) as reloaded:
self.assert_image_similar(im, reloaded, 1)
with roundtrip(im, optimize=True) as reloaded:
self.assert_image_similar(im, reloaded, 1)
im = im.convert("RGB")
# check automatic P conversion
reloaded = roundtrip(im).convert("RGB")
self.assert_image_equal(im, reloaded)
im = im.convert("RGB")
# check automatic P conversion
with roundtrip(im) as reloaded:
reloaded = reloaded.convert("RGB")
self.assert_image_equal(im, reloaded)
@unittest.skipUnless(netpbm_available(), "netpbm not available")
def test_save_netpbm_bmp_mode(self):
img = Image.open(TEST_GIF).convert("RGB")
with Image.open(TEST_GIF) as img:
img = img.convert("RGB")
tempfile = self.tempfile("temp.gif")
GifImagePlugin._save_netpbm(img, 0, tempfile)
self.assert_image_similar(img, Image.open(tempfile).convert("RGB"), 0)
tempfile = self.tempfile("temp.gif")
GifImagePlugin._save_netpbm(img, 0, tempfile)
with Image.open(tempfile) as reloaded:
self.assert_image_similar(img, reloaded.convert("RGB"), 0)
@unittest.skipUnless(netpbm_available(), "netpbm not available")
def test_save_netpbm_l_mode(self):
img = Image.open(TEST_GIF).convert("L")
with Image.open(TEST_GIF) as img:
img = img.convert("L")
tempfile = self.tempfile("temp.gif")
GifImagePlugin._save_netpbm(img, 0, tempfile)
self.assert_image_similar(img, Image.open(tempfile).convert("L"), 0)
tempfile = self.tempfile("temp.gif")
GifImagePlugin._save_netpbm(img, 0, tempfile)
with Image.open(tempfile) as reloaded:
self.assert_image_similar(img, reloaded.convert("L"), 0)
def test_seek(self):
img = Image.open("Tests/images/dispose_none.gif")
framecount = 0
try:
while True:
framecount += 1
img.seek(img.tell() + 1)
except EOFError:
self.assertEqual(framecount, 5)
with Image.open("Tests/images/dispose_none.gif") as img:
framecount = 0
try:
while True:
framecount += 1
img.seek(img.tell() + 1)
except EOFError:
self.assertEqual(framecount, 5)
def test_seek_info(self):
im = Image.open("Tests/images/iss634.gif")
info = im.info.copy()
with Image.open("Tests/images/iss634.gif") as im:
info = im.info.copy()
im.seek(1)
im.seek(0)
im.seek(1)
im.seek(0)
self.assertEqual(im.info, info)
self.assertEqual(im.info, info)
def test_seek_rewind(self):
im = Image.open("Tests/images/iss634.gif")
im.seek(2)
im.seek(1)
with Image.open("Tests/images/iss634.gif") as im:
im.seek(2)
im.seek(1)
expected = Image.open("Tests/images/iss634.gif")
expected.seek(1)
self.assert_image_equal(im, expected)
with Image.open("Tests/images/iss634.gif") as expected:
expected.seek(1)
self.assert_image_equal(im, expected)
def test_n_frames(self):
for path, n_frames in [[TEST_GIF, 1], ["Tests/images/iss634.gif", 42]]:
# Test is_animated before n_frames
im = Image.open(path)
self.assertEqual(im.is_animated, n_frames != 1)
with Image.open(path) as im:
self.assertEqual(im.is_animated, n_frames != 1)
# Test is_animated after n_frames
im = Image.open(path)
self.assertEqual(im.n_frames, n_frames)
self.assertEqual(im.is_animated, n_frames != 1)
with Image.open(path) as im:
self.assertEqual(im.n_frames, n_frames)
self.assertEqual(im.is_animated, n_frames != 1)
def test_eoferror(self):
im = Image.open(TEST_GIF)
n_frames = im.n_frames
with Image.open(TEST_GIF) 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_dispose_none(self):
img = Image.open("Tests/images/dispose_none.gif")
try:
while True:
img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, 1)
except EOFError:
pass
with Image.open("Tests/images/dispose_none.gif") as img:
try:
while True:
img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, 1)
except EOFError:
pass
def test_dispose_background(self):
img = Image.open("Tests/images/dispose_bgnd.gif")
try:
while True:
img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, 2)
except EOFError:
pass
with Image.open("Tests/images/dispose_bgnd.gif") as img:
try:
while True:
img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, 2)
except EOFError:
pass
def test_dispose_previous(self):
img = Image.open("Tests/images/dispose_prev.gif")
try:
while True:
img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, 3)
except EOFError:
pass
with Image.open("Tests/images/dispose_prev.gif") as img:
try:
while True:
img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, 3)
except EOFError:
pass
def test_save_dispose(self):
out = self.tempfile("temp.gif")
@ -299,10 +322,10 @@ class TestFileGif(PillowTestCase):
im_list[0].save(
out, save_all=True, append_images=im_list[1:], disposal=method
)
img = Image.open(out)
for _ in range(2):
img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, method)
with Image.open(out) as img:
for _ in range(2):
img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, method)
# check per frame disposal
im_list[0].save(
@ -312,11 +335,11 @@ class TestFileGif(PillowTestCase):
disposal=tuple(range(len(im_list))),
)
img = Image.open(out)
with Image.open(out) as img:
for i in range(2):
img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, i + 1)
for i in range(2):
img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, i + 1)
def test_dispose2_palette(self):
out = self.tempfile("temp.gif")
@ -336,17 +359,16 @@ class TestFileGif(PillowTestCase):
im_list[0].save(out, save_all=True, append_images=im_list[1:], disposal=2)
img = Image.open(out)
with Image.open(out) as img:
for i, circle in enumerate(circles):
img.seek(i)
rgb_img = img.convert("RGB")
for i, circle in enumerate(circles):
img.seek(i)
rgb_img = img.convert("RGB")
# Check top left pixel matches background
self.assertEqual(rgb_img.getpixel((0, 0)), (255, 0, 0))
# Check top left pixel matches background
self.assertEqual(rgb_img.getpixel((0, 0)), (255, 0, 0))
# Center remains red every frame
self.assertEqual(rgb_img.getpixel((50, 50)), circle)
# Center remains red every frame
self.assertEqual(rgb_img.getpixel((50, 50)), circle)
def test_dispose2_diff(self):
out = self.tempfile("temp.gif")
@ -375,20 +397,19 @@ class TestFileGif(PillowTestCase):
out, save_all=True, append_images=im_list[1:], disposal=2, transparency=0
)
img = Image.open(out)
with Image.open(out) as img:
for i, colours in enumerate(circles):
img.seek(i)
rgb_img = img.convert("RGBA")
for i, colours in enumerate(circles):
img.seek(i)
rgb_img = img.convert("RGBA")
# Check left circle is correct colour
self.assertEqual(rgb_img.getpixel((20, 50)), colours[0])
# Check left circle is correct colour
self.assertEqual(rgb_img.getpixel((20, 50)), colours[0])
# Check right circle is correct colour
self.assertEqual(rgb_img.getpixel((80, 50)), colours[1])
# Check right circle is correct colour
self.assertEqual(rgb_img.getpixel((80, 50)), colours[1])
# Check BG is correct colour
self.assertEqual(rgb_img.getpixel((1, 1)), (255, 255, 255, 0))
# Check BG is correct colour
self.assertEqual(rgb_img.getpixel((1, 1)), (255, 255, 255, 0))
def test_dispose2_background(self):
out = self.tempfile("temp.gif")
@ -411,17 +432,17 @@ class TestFileGif(PillowTestCase):
out, save_all=True, append_images=im_list[1:], disposal=[0, 2], background=1
)
im = Image.open(out)
im.seek(1)
self.assertEqual(im.getpixel((0, 0)), 0)
with Image.open(out) as im:
im.seek(1)
self.assertEqual(im.getpixel((0, 0)), 0)
def test_iss634(self):
img = Image.open("Tests/images/iss634.gif")
# seek to the second frame
img.seek(img.tell() + 1)
# all transparent pixels should be replaced with the color from the
# first frame
self.assertEqual(img.histogram()[img.info["transparency"]], 0)
with Image.open("Tests/images/iss634.gif") as img:
# seek to the second frame
img.seek(img.tell() + 1)
# all transparent pixels should be replaced with the color from the
# first frame
self.assertEqual(img.histogram()[img.info["transparency"]], 0)
def test_duration(self):
duration = 1000
@ -433,8 +454,8 @@ class TestFileGif(PillowTestCase):
im.info["duration"] = 100
im.save(out, duration=duration)
reread = Image.open(out)
self.assertEqual(reread.info["duration"], duration)
with Image.open(out) as reread:
self.assertEqual(reread.info["duration"], duration)
def test_multiple_duration(self):
duration_list = [1000, 2000, 3000]
@ -450,27 +471,27 @@ class TestFileGif(PillowTestCase):
im_list[0].save(
out, save_all=True, append_images=im_list[1:], duration=duration_list
)
reread = Image.open(out)
with Image.open(out) as reread:
for duration in duration_list:
self.assertEqual(reread.info["duration"], duration)
try:
reread.seek(reread.tell() + 1)
except EOFError:
pass
for duration in duration_list:
self.assertEqual(reread.info["duration"], duration)
try:
reread.seek(reread.tell() + 1)
except EOFError:
pass
# duration as tuple
im_list[0].save(
out, save_all=True, append_images=im_list[1:], duration=tuple(duration_list)
)
reread = Image.open(out)
with Image.open(out) as reread:
for duration in duration_list:
self.assertEqual(reread.info["duration"], duration)
try:
reread.seek(reread.tell() + 1)
except EOFError:
pass
for duration in duration_list:
self.assertEqual(reread.info["duration"], duration)
try:
reread.seek(reread.tell() + 1)
except EOFError:
pass
def test_identical_frames(self):
duration_list = [1000, 1500, 2000, 4000]
@ -487,13 +508,13 @@ class TestFileGif(PillowTestCase):
im_list[0].save(
out, save_all=True, append_images=im_list[1:], duration=duration_list
)
reread = Image.open(out)
with Image.open(out) as reread:
# Assert that the first three frames were combined
self.assertEqual(reread.n_frames, 2)
# Assert that the first three frames were combined
self.assertEqual(reread.n_frames, 2)
# Assert that the new duration is the total of the identical frames
self.assertEqual(reread.info["duration"], 4500)
# Assert that the new duration is the total of the identical frames
self.assertEqual(reread.info["duration"], 4500)
def test_identical_frames_to_single_frame(self):
for duration in ([1000, 1500, 2000, 4000], (1000, 1500, 2000, 4000), 8500):
@ -507,13 +528,12 @@ class TestFileGif(PillowTestCase):
im_list[0].save(
out, save_all=True, append_images=im_list[1:], duration=duration
)
reread = Image.open(out)
with Image.open(out) as reread:
# Assert that all frames were combined
self.assertEqual(reread.n_frames, 1)
# Assert that all frames were combined
self.assertEqual(reread.n_frames, 1)
# Assert that the new duration is the total of the identical frames
self.assertEqual(reread.info["duration"], 8500)
# Assert that the new duration is the total of the identical frames
self.assertEqual(reread.info["duration"], 8500)
def test_number_of_loops(self):
number_of_loops = 2
@ -521,18 +541,18 @@ class TestFileGif(PillowTestCase):
out = self.tempfile("temp.gif")
im = Image.new("L", (100, 100), "#000")
im.save(out, loop=number_of_loops)
reread = Image.open(out)
with Image.open(out) as reread:
self.assertEqual(reread.info["loop"], number_of_loops)
self.assertEqual(reread.info["loop"], number_of_loops)
def test_background(self):
out = self.tempfile("temp.gif")
im = Image.new("L", (100, 100), "#000")
im.info["background"] = 1
im.save(out)
reread = Image.open(out)
with Image.open(out) as reread:
self.assertEqual(reread.info["background"], im.info["background"])
self.assertEqual(reread.info["background"], im.info["background"])
if HAVE_WEBP and _webp.HAVE_WEBPANIM:
im = Image.open("Tests/images/hopper.webp")
@ -540,16 +560,18 @@ class TestFileGif(PillowTestCase):
im.save(out)
def test_comment(self):
im = Image.open(TEST_GIF)
self.assertEqual(im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0")
with Image.open(TEST_GIF) as im:
self.assertEqual(
im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0"
)
out = self.tempfile("temp.gif")
im = Image.new("L", (100, 100), "#000")
im.info["comment"] = b"Test comment text"
im.save(out)
reread = Image.open(out)
out = self.tempfile("temp.gif")
im = Image.new("L", (100, 100), "#000")
im.info["comment"] = b"Test comment text"
im.save(out)
with Image.open(out) as reread:
self.assertEqual(reread.info["comment"], im.info["comment"])
self.assertEqual(reread.info["comment"], im.info["comment"])
def test_comment_over_255(self):
out = self.tempfile("temp.gif")
@ -559,22 +581,22 @@ class TestFileGif(PillowTestCase):
comment += comment
im.info["comment"] = comment
im.save(out)
reread = Image.open(out)
with Image.open(out) as reread:
self.assertEqual(reread.info["comment"], comment)
self.assertEqual(reread.info["comment"], comment)
def test_zero_comment_subblocks(self):
im = Image.open("Tests/images/hopper_zero_comment_subblocks.gif")
expected = Image.open(TEST_GIF)
self.assert_image_equal(im, expected)
with Image.open("Tests/images/hopper_zero_comment_subblocks.gif") as im:
with Image.open(TEST_GIF) as expected:
self.assert_image_equal(im, expected)
def test_version(self):
out = self.tempfile("temp.gif")
def assertVersionAfterSave(im, version):
im.save(out)
reread = Image.open(out)
self.assertEqual(reread.info["version"], version)
with Image.open(out) as reread:
self.assertEqual(reread.info["version"], version)
# Test that GIF87a is used by default
im = Image.new("L", (100, 100), "#000")
@ -590,12 +612,12 @@ class TestFileGif(PillowTestCase):
assertVersionAfterSave(im, b"GIF89a")
# Test that a GIF87a image is also saved in that format
im = Image.open("Tests/images/test.colors.gif")
assertVersionAfterSave(im, b"GIF87a")
with Image.open("Tests/images/test.colors.gif") as im:
assertVersionAfterSave(im, b"GIF87a")
# Test that a GIF89a image is also saved in that format
im.info["version"] = b"GIF89a"
assertVersionAfterSave(im, b"GIF87a")
# Test that a GIF89a image is also saved in that format
im.info["version"] = b"GIF89a"
assertVersionAfterSave(im, b"GIF87a")
def test_append_images(self):
out = self.tempfile("temp.gif")
@ -605,8 +627,8 @@ class TestFileGif(PillowTestCase):
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
im.copy().save(out, save_all=True, append_images=ims)
reread = Image.open(out)
self.assertEqual(reread.n_frames, 3)
with Image.open(out) as reread:
self.assertEqual(reread.n_frames, 3)
# Tests appending using a generator
def imGenerator(ims):
@ -615,16 +637,16 @@ class TestFileGif(PillowTestCase):
im.save(out, save_all=True, append_images=imGenerator(ims))
reread = Image.open(out)
self.assertEqual(reread.n_frames, 3)
with Image.open(out) as reread:
self.assertEqual(reread.n_frames, 3)
# Tests appending single and multiple frame images
im = Image.open("Tests/images/dispose_none.gif")
ims = [Image.open("Tests/images/dispose_prev.gif")]
im.save(out, save_all=True, append_images=ims)
with Image.open("Tests/images/dispose_none.gif") as im:
with Image.open("Tests/images/dispose_prev.gif") as im2:
im.save(out, save_all=True, append_images=[im2])
reread = Image.open(out)
self.assertEqual(reread.n_frames, 10)
with Image.open(out) as reread:
self.assertEqual(reread.n_frames, 10)
def test_transparent_optimize(self):
# from issue #2195, if the transparent color is incorrectly
@ -642,9 +664,9 @@ class TestFileGif(PillowTestCase):
out = self.tempfile("temp.gif")
im.save(out, transparency=253)
reloaded = Image.open(out)
with Image.open(out) as reloaded:
self.assertEqual(reloaded.info["transparency"], 253)
self.assertEqual(reloaded.info["transparency"], 253)
def test_rgb_transparency(self):
out = self.tempfile("temp.gif")
@ -654,8 +676,8 @@ class TestFileGif(PillowTestCase):
im.info["transparency"] = (255, 0, 0)
self.assert_warning(UserWarning, im.save, out)
reloaded = Image.open(out)
self.assertNotIn("transparency", reloaded.info)
with Image.open(out) as reloaded:
self.assertNotIn("transparency", reloaded.info)
# Multiple frames
im = Image.new("RGB", (1, 1))
@ -663,8 +685,8 @@ class TestFileGif(PillowTestCase):
ims = [Image.new("RGB", (1, 1))]
self.assert_warning(UserWarning, im.save, out, save_all=True, append_images=ims)
reloaded = Image.open(out)
self.assertNotIn("transparency", reloaded.info)
with Image.open(out) as reloaded:
self.assertNotIn("transparency", reloaded.info)
def test_bbox(self):
out = self.tempfile("temp.gif")
@ -673,8 +695,8 @@ class TestFileGif(PillowTestCase):
ims = [Image.new("RGB", (100, 100), "#000")]
im.save(out, save_all=True, append_images=ims)
reread = Image.open(out)
self.assertEqual(reread.n_frames, 2)
with Image.open(out) as reread:
self.assertEqual(reread.n_frames, 2)
def test_palette_save_L(self):
# generate an L mode image with a separate palette
@ -686,9 +708,9 @@ class TestFileGif(PillowTestCase):
out = self.tempfile("temp.gif")
im_l.save(out, palette=palette)
reloaded = Image.open(out)
with Image.open(out) as reloaded:
self.assert_image_equal(reloaded.convert("RGB"), im.convert("RGB"))
self.assert_image_equal(reloaded.convert("RGB"), im.convert("RGB"))
def test_palette_save_P(self):
# pass in a different palette, then construct what the image
@ -701,9 +723,9 @@ class TestFileGif(PillowTestCase):
out = self.tempfile("temp.gif")
im.save(out, palette=palette)
reloaded = Image.open(out)
im.putpalette(palette)
self.assert_image_equal(reloaded, im)
with Image.open(out) as reloaded:
im.putpalette(palette)
self.assert_image_equal(reloaded, im)
def test_palette_save_ImagePalette(self):
# pass in a different palette, as an ImagePalette.ImagePalette
@ -715,9 +737,9 @@ class TestFileGif(PillowTestCase):
out = self.tempfile("temp.gif")
im.save(out, palette=palette)
reloaded = Image.open(out)
im.putpalette(palette)
self.assert_image_equal(reloaded, im)
with Image.open(out) as reloaded:
im.putpalette(palette)
self.assert_image_equal(reloaded, im)
def test_save_I(self):
# Test saving something that would trigger the auto-convert to 'L'
@ -727,8 +749,8 @@ class TestFileGif(PillowTestCase):
out = self.tempfile("temp.gif")
im.save(out)
reloaded = Image.open(out)
self.assert_image_equal(reloaded.convert("L"), im.convert("L"))
with Image.open(out) as reloaded:
self.assert_image_equal(reloaded.convert("L"), im.convert("L"))
def test_getdata(self):
# test getheader/getdata against legacy values
@ -759,14 +781,13 @@ class TestFileGif(PillowTestCase):
def test_lzw_bits(self):
# see https://github.com/python-pillow/Pillow/issues/2811
im = Image.open("Tests/images/issue_2811.gif")
self.assertEqual(im.tile[0][3][0], 11) # LZW bits
# codec error prepatch
im.load()
with Image.open("Tests/images/issue_2811.gif") as im:
self.assertEqual(im.tile[0][3][0], 11) # LZW bits
# codec error prepatch
im.load()
def test_extents(self):
im = Image.open("Tests/images/test_extents.gif")
self.assertEqual(im.size, (100, 100))
im.seek(1)
self.assertEqual(im.size, (150, 150))
with Image.open("Tests/images/test_extents.gif") as im:
self.assertEqual(im.size, (100, 100))
im.seek(1)
self.assertEqual(im.size, (150, 150))

View File

@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/WAlaska.wind.7days.grb"
class TestFileGribStub(PillowTestCase):
def test_open(self):
# Act
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:
# Assert
self.assertEqual(im.format, "GRIB")
# Assert
self.assertEqual(im.format, "GRIB")
# 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
@ -28,10 +28,10 @@ class TestFileGribStub(PillowTestCase):
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

View File

@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/hdf5.h5"
class TestFileHdf5Stub(PillowTestCase):
def test_open(self):
# Act
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:
# Assert
self.assertEqual(im.format, "HDF5")
# Assert
self.assertEqual(im.format, "HDF5")
# 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
@ -28,19 +28,19 @@ class TestFileHdf5Stub(PillowTestCase):
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
im = Image.open(TEST_FILE)
dummy_fp = None
dummy_filename = "dummy.filename"
with Image.open(TEST_FILE) as im:
dummy_fp = None
dummy_filename = "dummy.filename"
# Act / Assert: stub cannot save without an implemented handler
self.assertRaises(IOError, im.save, dummy_filename)
self.assertRaises(
IOError, Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename
)
# Act / Assert: stub cannot save without an implemented handler
self.assertRaises(IOError, im.save, dummy_filename)
self.assertRaises(
IOError, Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename
)

View File

@ -15,14 +15,14 @@ class TestFileIcns(PillowTestCase):
def test_sanity(self):
# Loading this icon by default should result in the largest size
# (512x512@2x) being loaded
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:
# Assert that there is no unclosed file warning
self.assert_warning(None, im.load)
# Assert that there is no unclosed file warning
self.assert_warning(None, im.load)
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (1024, 1024))
self.assertEqual(im.format, "ICNS")
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (1024, 1024))
self.assertEqual(im.format, "ICNS")
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
def test_save(self):
@ -56,31 +56,31 @@ class TestFileIcns(PillowTestCase):
def test_sizes(self):
# Check that we can load all of the sizes, and that the final pixel
# dimensions are as expected
im = Image.open(TEST_FILE)
for w, h, r in im.info["sizes"]:
wr = w * r
hr = h * r
im.size = (w, h, r)
im.load()
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (wr, hr))
with Image.open(TEST_FILE) as im:
for w, h, r in im.info["sizes"]:
wr = w * r
hr = h * r
im.size = (w, h, r)
im.load()
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (wr, hr))
# Check that we cannot load an incorrect size
with self.assertRaises(ValueError):
im.size = (1, 1)
# Check that we cannot load an incorrect size
with self.assertRaises(ValueError):
im.size = (1, 1)
def test_older_icon(self):
# This icon was made with Icon Composer rather than iconutil; it still
# uses PNG rather than JP2, however (since it was made on 10.9).
im = Image.open("Tests/images/pillow2.icns")
for w, h, r in im.info["sizes"]:
wr = w * r
hr = h * r
im2 = Image.open("Tests/images/pillow2.icns")
im2.size = (w, h, r)
im2.load()
self.assertEqual(im2.mode, "RGBA")
self.assertEqual(im2.size, (wr, hr))
with Image.open("Tests/images/pillow2.icns") as im:
for w, h, r in im.info["sizes"]:
wr = w * r
hr = h * r
with Image.open("Tests/images/pillow2.icns") as im2:
im2.size = (w, h, r)
im2.load()
self.assertEqual(im2.mode, "RGBA")
self.assertEqual(im2.size, (wr, hr))
def test_jp2_icon(self):
# This icon was made by using Uli Kusterer's oldiconutil to replace
@ -93,15 +93,15 @@ class TestFileIcns(PillowTestCase):
if not enable_jpeg2k:
return
im = Image.open("Tests/images/pillow3.icns")
for w, h, r in im.info["sizes"]:
wr = w * r
hr = h * r
im2 = Image.open("Tests/images/pillow3.icns")
im2.size = (w, h, r)
im2.load()
self.assertEqual(im2.mode, "RGBA")
self.assertEqual(im2.size, (wr, hr))
with Image.open("Tests/images/pillow3.icns") as im:
for w, h, r in im.info["sizes"]:
wr = w * r
hr = h * r
with Image.open("Tests/images/pillow3.icns") as im2:
im2.size = (w, h, r)
im2.load()
self.assertEqual(im2.mode, "RGBA")
self.assertEqual(im2.size, (wr, hr))
def test_getimage(self):
with open(TEST_FILE, "rb") as fp:

View File

@ -9,8 +9,8 @@ TEST_ICO_FILE = "Tests/images/hopper.ico"
class TestFileIco(PillowTestCase):
def test_sanity(self):
im = Image.open(TEST_ICO_FILE)
im.load()
with Image.open(TEST_ICO_FILE) as im:
im.load()
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (16, 16))
self.assertEqual(im.format, "ICO")
@ -46,22 +46,22 @@ class TestFileIco(PillowTestCase):
self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS))
def test_incorrect_size(self):
im = Image.open(TEST_ICO_FILE)
with self.assertRaises(ValueError):
im.size = (1, 1)
with Image.open(TEST_ICO_FILE) as im:
with self.assertRaises(ValueError):
im.size = (1, 1)
def test_save_256x256(self):
"""Issue #2264 https://github.com/python-pillow/Pillow/issues/2264"""
# Arrange
im = Image.open("Tests/images/hopper_256x256.ico")
outfile = self.tempfile("temp_saved_hopper_256x256.ico")
with Image.open("Tests/images/hopper_256x256.ico") as im:
outfile = self.tempfile("temp_saved_hopper_256x256.ico")
# Act
im.save(outfile)
im_saved = Image.open(outfile)
# Act
im.save(outfile)
with Image.open(outfile) as im_saved:
# Assert
self.assertEqual(im_saved.size, (256, 256))
# Assert
self.assertEqual(im_saved.size, (256, 256))
def test_only_save_relevant_sizes(self):
"""Issue #2266 https://github.com/python-pillow/Pillow/issues/2266
@ -69,35 +69,35 @@ class TestFileIco(PillowTestCase):
and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes
"""
# Arrange
im = Image.open("Tests/images/python.ico") # 16x16, 32x32, 48x48
outfile = self.tempfile("temp_saved_python.ico")
with Image.open("Tests/images/python.ico") as im: # 16x16, 32x32, 48x48
outfile = self.tempfile("temp_saved_python.ico")
# Act
im.save(outfile)
# Act
im.save(outfile)
im_saved = Image.open(outfile)
# Assert
self.assertEqual(
im_saved.info["sizes"], {(16, 16), (24, 24), (32, 32), (48, 48)}
)
with Image.open(outfile) as im_saved:
# Assert
self.assertEqual(
im_saved.info["sizes"], {(16, 16), (24, 24), (32, 32), (48, 48)}
)
def test_unexpected_size(self):
# This image has been manually hexedited to state that it is 16x32
# while the image within is still 16x16
im = self.assert_warning(
UserWarning, Image.open, "Tests/images/hopper_unexpected.ico"
)
self.assertEqual(im.size, (16, 16))
def open():
with Image.open("Tests/images/hopper_unexpected.ico") as im:
self.assertEqual(im.size, (16, 16))
self.assert_warning(UserWarning, open)
def test_draw_reloaded(self):
im = Image.open(TEST_ICO_FILE)
outfile = self.tempfile("temp_saved_hopper_draw.ico")
with Image.open(TEST_ICO_FILE) as im:
outfile = self.tempfile("temp_saved_hopper_draw.ico")
draw = ImageDraw.Draw(im)
draw.line((0, 0) + im.size, "#f00")
im.save(outfile)
draw = ImageDraw.Draw(im)
draw.line((0, 0) + im.size, "#f00")
im.save(outfile)
im = Image.open(outfile)
im.save("Tests/images/hopper_draw.ico")
reloaded = Image.open("Tests/images/hopper_draw.ico")
self.assert_image_equal(im, reloaded)
with Image.open(outfile) as im:
im.save("Tests/images/hopper_draw.ico")
with Image.open("Tests/images/hopper_draw.ico") as reloaded:
self.assert_image_equal(im, reloaded)

View File

@ -1,6 +1,8 @@
import unittest
from PIL import Image, ImImagePlugin
from .helper import PillowTestCase, hopper
from .helper import PillowTestCase, hopper, is_pypy
# sample im
TEST_IM = "Tests/images/hopper.im"
@ -8,53 +10,69 @@ TEST_IM = "Tests/images/hopper.im"
class TestFileIm(PillowTestCase):
def test_sanity(self):
im = Image.open(TEST_IM)
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "IM")
with Image.open(TEST_IM) as im:
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "IM")
@unittest.skipIf(is_pypy(), "Requires CPython")
def test_unclosed_file(self):
def open():
im = Image.open(TEST_IM)
im.load()
self.assert_warning(ResourceWarning, open)
def test_closed_file(self):
def open():
im = Image.open(TEST_IM)
im.load()
im.close()
self.assert_warning(None, open)
def test_context_manager(self):
def open():
with Image.open(TEST_IM) as im:
im.load()
self.assert_warning(None, open)
def test_tell(self):
# Arrange
im = Image.open(TEST_IM)
with Image.open(TEST_IM) as im:
# Act
frame = im.tell()
# Act
frame = im.tell()
# Assert
self.assertEqual(frame, 0)
def test_n_frames(self):
im = Image.open(TEST_IM)
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
with Image.open(TEST_IM) as im:
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
def test_eoferror(self):
im = Image.open(TEST_IM)
n_frames = im.n_frames
with Image.open(TEST_IM) 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_roundtrip(self):
for mode in ["RGB", "P", "PA"]:
out = self.tempfile("temp.im")
im = hopper(mode)
im.save(out)
reread = Image.open(out)
with Image.open(out) as reread:
self.assert_image_equal(reread, im)
self.assert_image_equal(reread, im)
def test_save_unsupported_mode(self):
out = self.tempfile("temp.im")

View File

@ -8,20 +8,20 @@ TEST_FILE = "Tests/images/iptc.jpg"
class TestFileIptc(PillowTestCase):
def test_getiptcinfo_jpg_none(self):
# Arrange
im = hopper()
with hopper() as im:
# Act
iptc = IptcImagePlugin.getiptcinfo(im)
# Act
iptc = IptcImagePlugin.getiptcinfo(im)
# Assert
self.assertIsNone(iptc)
def test_getiptcinfo_jpg_found(self):
# Arrange
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:
# Act
iptc = IptcImagePlugin.getiptcinfo(im)
# Act
iptc = IptcImagePlugin.getiptcinfo(im)
# Assert
self.assertIsInstance(iptc, dict)
@ -30,10 +30,10 @@ class TestFileIptc(PillowTestCase):
def test_getiptcinfo_tiff_none(self):
# Arrange
im = Image.open("Tests/images/hopper.tif")
with Image.open("Tests/images/hopper.tif") as im:
# Act
iptc = IptcImagePlugin.getiptcinfo(im)
# Act
iptc = IptcImagePlugin.getiptcinfo(im)
# Assert
self.assertIsNone(iptc)

View File

@ -53,14 +53,14 @@ class TestFileJpeg(PillowTestCase):
def test_app(self):
# Test APP/COM reader (@PIL135)
im = Image.open(TEST_FILE)
self.assertEqual(
im.applist[0], ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00")
)
self.assertEqual(
im.applist[1], ("COM", b"File written by Adobe Photoshop\xa8 4.0\x00")
)
self.assertEqual(len(im.applist), 2)
with Image.open(TEST_FILE) as im:
self.assertEqual(
im.applist[0], ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00")
)
self.assertEqual(
im.applist[1], ("COM", b"File written by Adobe Photoshop\xa8 4.0\x00")
)
self.assertEqual(len(im.applist), 2)
def test_cmyk(self):
# Test CMYK handling. Thanks to Tim and Charlie for test data,
@ -99,20 +99,20 @@ class TestFileJpeg(PillowTestCase):
def test_icc(self):
# Test ICC support
im1 = Image.open("Tests/images/rgb.jpg")
icc_profile = im1.info["icc_profile"]
self.assertEqual(len(icc_profile), 3144)
# Roundtrip via physical file.
f = self.tempfile("temp.jpg")
im1.save(f, icc_profile=icc_profile)
im2 = Image.open(f)
self.assertEqual(im2.info.get("icc_profile"), icc_profile)
# Roundtrip via memory buffer.
im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), icc_profile=icc_profile)
self.assert_image_equal(im1, im2)
self.assertFalse(im1.info.get("icc_profile"))
self.assertTrue(im2.info.get("icc_profile"))
with Image.open("Tests/images/rgb.jpg") as im1:
icc_profile = im1.info["icc_profile"]
self.assertEqual(len(icc_profile), 3144)
# Roundtrip via physical file.
f = self.tempfile("temp.jpg")
im1.save(f, icc_profile=icc_profile)
with Image.open(f) as im2:
self.assertEqual(im2.info.get("icc_profile"), icc_profile)
# Roundtrip via memory buffer.
im1 = self.roundtrip(hopper())
im2 = self.roundtrip(hopper(), icc_profile=icc_profile)
self.assert_image_equal(im1, im2)
self.assertFalse(im1.info.get("icc_profile"))
self.assertTrue(im2.info.get("icc_profile"))
def test_icc_big(self):
# Make sure that the "extra" support handles large blocks
@ -205,24 +205,24 @@ class TestFileJpeg(PillowTestCase):
im.save(f, "JPEG", quality=90, exif=b"1" * 65532)
def test_exif_typeerror(self):
im = Image.open("Tests/images/exif_typeerror.jpg")
# Should not raise a TypeError
im._getexif()
with Image.open("Tests/images/exif_typeerror.jpg") as im:
# Should not raise a TypeError
im._getexif()
def test_exif_gps(self):
# Arrange
im = Image.open("Tests/images/exif_gps.jpg")
gps_index = 34853
expected_exif_gps = {
0: b"\x00\x00\x00\x01",
2: (4294967295, 1),
5: b"\x01",
30: 65535,
29: "1999:99:99 99:99:99",
}
with Image.open("Tests/images/exif_gps.jpg") as im:
gps_index = 34853
expected_exif_gps = {
0: b"\x00\x00\x00\x01",
2: (4294967295, 1),
5: b"\x01",
30: 65535,
29: "1999:99:99 99:99:99",
}
# Act
exif = im._getexif()
# Act
exif = im._getexif()
# Assert
self.assertEqual(exif[gps_index], expected_exif_gps)
@ -256,17 +256,17 @@ class TestFileJpeg(PillowTestCase):
33434: (4294967295, 1),
}
im = Image.open("Tests/images/exif_gps.jpg")
exif = im._getexif()
with Image.open("Tests/images/exif_gps.jpg") as im:
exif = im._getexif()
for tag, value in expected_exif.items():
self.assertEqual(value, exif[tag])
def test_exif_gps_typeerror(self):
im = Image.open("Tests/images/exif_gps_typeerror.jpg")
with Image.open("Tests/images/exif_gps_typeerror.jpg") as im:
# Should not raise a TypeError
im._getexif()
# Should not raise a TypeError
im._getexif()
def test_progressive_compat(self):
im1 = self.roundtrip(hopper())
@ -329,13 +329,13 @@ class TestFileJpeg(PillowTestCase):
self.assertRaises(TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
def test_exif(self):
im = Image.open("Tests/images/pil_sample_rgb.jpg")
info = im._getexif()
self.assertEqual(info[305], "Adobe Photoshop CS Macintosh")
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
info = im._getexif()
self.assertEqual(info[305], "Adobe Photoshop CS Macintosh")
def test_mp(self):
im = Image.open("Tests/images/pil_sample_rgb.jpg")
self.assertIsNone(im._getmp())
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
self.assertIsNone(im._getmp())
def test_quality_keep(self):
# RGB
@ -354,11 +354,13 @@ class TestFileJpeg(PillowTestCase):
def test_junk_jpeg_header(self):
# https://github.com/python-pillow/Pillow/issues/630
filename = "Tests/images/junk_jpeg_header.jpg"
Image.open(filename)
with Image.open(filename):
pass
def test_ff00_jpeg_header(self):
filename = "Tests/images/jpeg_ff00_header.jpg"
Image.open(filename)
with Image.open(filename):
pass
def test_truncated_jpeg_should_read_all_the_data(self):
filename = "Tests/images/truncated_jpeg.jpg"
@ -370,14 +372,13 @@ class TestFileJpeg(PillowTestCase):
def test_truncated_jpeg_throws_IOError(self):
filename = "Tests/images/truncated_jpeg.jpg"
im = Image.open(filename)
with Image.open(filename) as im:
with self.assertRaises(IOError):
im.load()
with self.assertRaises(IOError):
im.load()
# Test that the error is raised if loaded a second time
with self.assertRaises(IOError):
im.load()
# Test that the error is raised if loaded a second time
with self.assertRaises(IOError):
im.load()
def _n_qtables_helper(self, n, test_file):
im = Image.open(test_file)
@ -483,9 +484,9 @@ class TestFileJpeg(PillowTestCase):
@unittest.skipUnless(djpeg_available(), "djpeg not available")
def test_load_djpeg(self):
img = Image.open(TEST_FILE)
img.load_djpeg()
self.assert_image_similar(img, Image.open(TEST_FILE), 0)
with Image.open(TEST_FILE) as img:
img.load_djpeg()
self.assert_image_similar(img, Image.open(TEST_FILE), 0)
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
def test_save_cjpeg(self):
@ -525,10 +526,10 @@ class TestFileJpeg(PillowTestCase):
# Act
# Shouldn't raise error
fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
im = self.assert_warning(UserWarning, Image.open, fn)
with self.assert_warning(UserWarning, Image.open, fn) as im:
# Assert
self.assertEqual(im.format, "JPEG")
# Assert
self.assertEqual(im.format, "JPEG")
def test_save_correct_modes(self):
out = BytesIO()
@ -558,106 +559,106 @@ class TestFileJpeg(PillowTestCase):
def test_load_dpi_rounding(self):
# Round up
im = Image.open("Tests/images/iptc_roundUp.jpg")
self.assertEqual(im.info["dpi"], (44, 44))
with Image.open("Tests/images/iptc_roundUp.jpg") as im:
self.assertEqual(im.info["dpi"], (44, 44))
# Round down
im = Image.open("Tests/images/iptc_roundDown.jpg")
self.assertEqual(im.info["dpi"], (2, 2))
with Image.open("Tests/images/iptc_roundDown.jpg") as im:
self.assertEqual(im.info["dpi"], (2, 2))
def test_save_dpi_rounding(self):
outfile = self.tempfile("temp.jpg")
im = Image.open("Tests/images/hopper.jpg")
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_dpi_tuple_from_exif(self):
# Arrange
# This Photoshop CC 2017 image has DPI in EXIF not metadata
# EXIF XResolution is (2000000, 10000)
im = Image.open("Tests/images/photoshop-200dpi.jpg")
with Image.open("Tests/images/photoshop-200dpi.jpg") as im:
# Act / Assert
self.assertEqual(im.info.get("dpi"), (200, 200))
# Act / Assert
self.assertEqual(im.info.get("dpi"), (200, 200))
def test_dpi_int_from_exif(self):
# Arrange
# This image has DPI in EXIF not metadata
# EXIF XResolution is 72
im = Image.open("Tests/images/exif-72dpi-int.jpg")
with Image.open("Tests/images/exif-72dpi-int.jpg") as im:
# Act / Assert
self.assertEqual(im.info.get("dpi"), (72, 72))
# Act / Assert
self.assertEqual(im.info.get("dpi"), (72, 72))
def test_dpi_from_dpcm_exif(self):
# Arrange
# This is photoshop-200dpi.jpg with EXIF resolution unit set to cm:
# exiftool -exif:ResolutionUnit=cm photoshop-200dpi.jpg
im = Image.open("Tests/images/exif-200dpcm.jpg")
with Image.open("Tests/images/exif-200dpcm.jpg") as im:
# Act / Assert
self.assertEqual(im.info.get("dpi"), (508, 508))
# Act / Assert
self.assertEqual(im.info.get("dpi"), (508, 508))
def test_dpi_exif_zero_division(self):
# Arrange
# This is photoshop-200dpi.jpg with EXIF resolution set to 0/0:
# exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg
im = Image.open("Tests/images/exif-dpi-zerodivision.jpg")
with Image.open("Tests/images/exif-dpi-zerodivision.jpg") as im:
# Act / Assert
# This should return the default, and not raise a ZeroDivisionError
self.assertEqual(im.info.get("dpi"), (72, 72))
# Act / Assert
# This should return the default, and not raise a ZeroDivisionError
self.assertEqual(im.info.get("dpi"), (72, 72))
def test_no_dpi_in_exif(self):
# Arrange
# This is photoshop-200dpi.jpg with resolution removed from EXIF:
# exiftool "-*resolution*"= photoshop-200dpi.jpg
im = Image.open("Tests/images/no-dpi-in-exif.jpg")
with Image.open("Tests/images/no-dpi-in-exif.jpg") as im:
# Act / Assert
# "When the image resolution is unknown, 72 [dpi] is designated."
# http://www.exiv2.org/tags.html
self.assertEqual(im.info.get("dpi"), (72, 72))
# Act / Assert
# "When the image resolution is unknown, 72 [dpi] is designated."
# http://www.exiv2.org/tags.html
self.assertEqual(im.info.get("dpi"), (72, 72))
def test_invalid_exif(self):
# This is no-dpi-in-exif with the tiff header of the exif block
# hexedited from MM * to FF FF FF FF
im = Image.open("Tests/images/invalid-exif.jpg")
with Image.open("Tests/images/invalid-exif.jpg") as im:
# This should return the default, and not a SyntaxError or
# OSError for unidentified image.
self.assertEqual(im.info.get("dpi"), (72, 72))
# This should return the default, and not a SyntaxError or
# OSError for unidentified image.
self.assertEqual(im.info.get("dpi"), (72, 72))
def test_ifd_offset_exif(self):
# Arrange
# This image has been manually hexedited to have an IFD offset of 10,
# in contrast to normal 8
im = Image.open("Tests/images/exif-ifd-offset.jpg")
with Image.open("Tests/images/exif-ifd-offset.jpg") as im:
# Act / Assert
self.assertEqual(im._getexif()[306], "2017:03:13 23:03:09")
# Act / Assert
self.assertEqual(im._getexif()[306], "2017:03:13 23:03:09")
def test_photoshop(self):
im = Image.open("Tests/images/photoshop-200dpi.jpg")
self.assertEqual(
im.info["photoshop"][0x03ED],
{
"XResolution": 200.0,
"DisplayedUnitsX": 1,
"YResolution": 200.0,
"DisplayedUnitsY": 1,
},
)
with Image.open("Tests/images/photoshop-200dpi.jpg") as im:
self.assertEqual(
im.info["photoshop"][0x03ED],
{
"XResolution": 200.0,
"DisplayedUnitsX": 1,
"YResolution": 200.0,
"DisplayedUnitsY": 1,
},
)
# This image does not contain a Photoshop header string
im = Image.open("Tests/images/app13.jpg")
self.assertNotIn("photoshop", im.info)
with Image.open("Tests/images/app13.jpg") as im:
self.assertNotIn("photoshop", im.info)
@unittest.skipUnless(is_win32(), "Windows only")

View File

@ -42,9 +42,9 @@ class TestFileJpeg2k(PillowTestCase):
self.assertEqual(im.get_format_mimetype(), "image/jp2")
def test_jpf(self):
im = Image.open("Tests/images/balloon.jpf")
self.assertEqual(im.format, "JPEG2000")
self.assertEqual(im.get_format_mimetype(), "image/jpx")
with Image.open("Tests/images/balloon.jpf") as im:
self.assertEqual(im.format, "JPEG2000")
self.assertEqual(im.get_format_mimetype(), "image/jpx")
def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg"

View File

@ -144,15 +144,14 @@ class TestFileLibTiff(LibTiffTestCase):
def test_write_metadata(self):
""" Test metadata writing through libtiff """
for legacy_api in [False, True]:
img = Image.open("Tests/images/hopper_g4.tif")
f = self.tempfile("temp.tiff")
with Image.open("Tests/images/hopper_g4.tif") as img:
img.save(f, tiffinfo=img.tag)
img.save(f, tiffinfo=img.tag)
if legacy_api:
original = img.tag.named()
else:
original = img.tag_v2.named()
if legacy_api:
original = img.tag.named()
else:
original = img.tag_v2.named()
# PhotometricInterpretation is set from SAVE_INFO,
# not the original image.
@ -163,11 +162,11 @@ class TestFileLibTiff(LibTiffTestCase):
"PhotometricInterpretation",
]
loaded = Image.open(f)
if legacy_api:
reloaded = loaded.tag.named()
else:
reloaded = loaded.tag_v2.named()
with Image.open(f) as loaded:
if legacy_api:
reloaded = loaded.tag.named()
else:
reloaded = loaded.tag_v2.named()
for tag, value in itertools.chain(reloaded.items(), original.items()):
if tag not in ignored:
@ -302,21 +301,21 @@ class TestFileLibTiff(LibTiffTestCase):
out = self.tempfile("temp.tif")
im.save(out, tiffinfo=tiffinfo)
reloaded = Image.open(out)
for tag, value in tiffinfo.items():
reloaded_value = reloaded.tag_v2[tag]
if (
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
and libtiff
):
# libtiff does not support real RATIONALS
self.assertAlmostEqual(float(reloaded_value), float(value))
continue
with Image.open(out) as reloaded:
for tag, value in tiffinfo.items():
reloaded_value = reloaded.tag_v2[tag]
if (
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
and libtiff
):
# libtiff does not support real RATIONALS
self.assertAlmostEqual(float(reloaded_value), float(value))
continue
if libtiff and isinstance(value, bytes):
value = value.decode()
if libtiff and isinstance(value, bytes):
value = value.decode()
self.assertEqual(reloaded_value, value)
self.assertEqual(reloaded_value, value)
# Test with types
ifd = TiffImagePlugin.ImageFileDirectory_v2()
@ -343,8 +342,8 @@ class TestFileLibTiff(LibTiffTestCase):
TiffImagePlugin.WRITE_LIBTIFF = True
im.save(out, dpi=(72, 72))
TiffImagePlugin.WRITE_LIBTIFF = False
reloaded = Image.open(out)
self.assertEqual(reloaded.info["dpi"], (72.0, 72.0))
with Image.open(out) as reloaded:
self.assertEqual(reloaded.info["dpi"], (72.0, 72.0))
def test_g3_compression(self):
i = Image.open("Tests/images/hopper_g4_500.tif")
@ -412,9 +411,9 @@ class TestFileLibTiff(LibTiffTestCase):
orig.tag[269] = "temp.tif"
orig.save(out)
reread = Image.open(out)
self.assertEqual("temp.tif", reread.tag_v2[269])
self.assertEqual("temp.tif", reread.tag[269][0])
with Image.open(out) as reread:
self.assertEqual("temp.tif", reread.tag_v2[269])
self.assertEqual("temp.tif", reread.tag[269][0])
def test_12bit_rawmode(self):
""" Are we generating the same interpretation
@ -521,36 +520,36 @@ class TestFileLibTiff(LibTiffTestCase):
def test_multipage(self):
# issue #862
TiffImagePlugin.READ_LIBTIFF = True
im = Image.open("Tests/images/multipage.tiff")
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
with Image.open("Tests/images/multipage.tiff") as im:
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
im.seek(0)
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
self.assertTrue(im.tag.next)
im.seek(0)
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
self.assertTrue(im.tag.next)
im.seek(1)
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
self.assertTrue(im.tag.next)
im.seek(1)
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
self.assertTrue(im.tag.next)
im.seek(2)
self.assertFalse(im.tag.next)
self.assertEqual(im.size, (20, 20))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
im.seek(2)
self.assertFalse(im.tag.next)
self.assertEqual(im.size, (20, 20))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
TiffImagePlugin.READ_LIBTIFF = False
def test_multipage_nframes(self):
# issue #862
TiffImagePlugin.READ_LIBTIFF = True
im = Image.open("Tests/images/multipage.tiff")
frames = im.n_frames
self.assertEqual(frames, 3)
for _ in range(frames):
im.seek(0)
# Should not raise ValueError: I/O operation on closed file
im.load()
with Image.open("Tests/images/multipage.tiff") as im:
frames = im.n_frames
self.assertEqual(frames, 3)
for _ in range(frames):
im.seek(0)
# Should not raise ValueError: I/O operation on closed file
im.load()
TiffImagePlugin.READ_LIBTIFF = False
@ -656,9 +655,9 @@ class TestFileLibTiff(LibTiffTestCase):
# /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
# -dNOPAUSE /tmp/test.pdf -c quit
infile = "Tests/images/total-pages-zero.tif"
im = Image.open(infile)
# Should not divide by zero
im.save(outfile)
with Image.open(infile) as im:
# Should not divide by zero
im.save(outfile)
def test_fd_duplication(self):
# https://github.com/python-pillow/Pillow/issues/1651
@ -686,21 +685,21 @@ class TestFileLibTiff(LibTiffTestCase):
self.assertEqual(icc, icc_libtiff)
def test_multipage_compression(self):
im = Image.open("Tests/images/compression.tif")
with Image.open("Tests/images/compression.tif") as im:
im.seek(0)
self.assertEqual(im._compression, "tiff_ccitt")
self.assertEqual(im.size, (10, 10))
im.seek(0)
self.assertEqual(im._compression, "tiff_ccitt")
self.assertEqual(im.size, (10, 10))
im.seek(1)
self.assertEqual(im._compression, "packbits")
self.assertEqual(im.size, (10, 10))
im.load()
im.seek(1)
self.assertEqual(im._compression, "packbits")
self.assertEqual(im.size, (10, 10))
im.load()
im.seek(0)
self.assertEqual(im._compression, "tiff_ccitt")
self.assertEqual(im.size, (10, 10))
im.load()
im.seek(0)
self.assertEqual(im._compression, "tiff_ccitt")
self.assertEqual(im.size, (10, 10))
im.load()
def test_save_tiff_with_jpegtables(self):
# Arrange
@ -836,8 +835,8 @@ class TestFileLibTiff(LibTiffTestCase):
def test_no_rows_per_strip(self):
# This image does not have a RowsPerStrip TIFF tag
infile = "Tests/images/no_rows_per_strip.tif"
im = Image.open(infile)
im.load()
with Image.open(infile) as im:
im.load()
self.assertEqual(im.size, (950, 975))
def test_orientation(self):

View File

@ -16,42 +16,42 @@ TEST_FILE = "Tests/images/hopper.mic"
@unittest.skipUnless(features.check("libtiff"), "libtiff not installed")
class TestFileMic(PillowTestCase):
def test_sanity(self):
im = Image.open(TEST_FILE)
im.load()
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "MIC")
with Image.open(TEST_FILE) as im:
im.load()
self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "MIC")
# Adjust for the gamma of 2.2 encoded into the file
lut = ImagePalette.make_gamma_lut(1 / 2.2)
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
# Adjust for the gamma of 2.2 encoded into the file
lut = ImagePalette.make_gamma_lut(1 / 2.2)
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
im2 = hopper("RGBA")
self.assert_image_similar(im, im2, 10)
im2 = hopper("RGBA")
self.assert_image_similar(im, im2, 10)
def test_n_frames(self):
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:
self.assertEqual(im.n_frames, 1)
self.assertEqual(im.n_frames, 1)
def test_is_animated(self):
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:
self.assertFalse(im.is_animated)
self.assertFalse(im.is_animated)
def test_tell(self):
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:
self.assertEqual(im.tell(), 0)
self.assertEqual(im.tell(), 0)
def test_seek(self):
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:
im.seek(0)
self.assertEqual(im.tell(), 0)
im.seek(0)
self.assertEqual(im.tell(), 0)
self.assertRaises(EOFError, im.seek, 99)
self.assertEqual(im.tell(), 0)
self.assertRaises(EOFError, im.seek, 99)
self.assertEqual(im.tell(), 0)
def test_invalid_file(self):
# Test an invalid OLE file

View File

@ -1,8 +1,9 @@
import unittest
from io import BytesIO
from PIL import Image
from .helper import PillowTestCase
from .helper import PillowTestCase, is_pypy
test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"]
@ -25,78 +26,97 @@ class TestFileMpo(PillowTestCase):
def test_sanity(self):
for test_file in test_files:
im = Image.open(test_file)
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (640, 480))
self.assertEqual(im.format, "MPO")
with Image.open(test_file) as im:
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (640, 480))
self.assertEqual(im.format, "MPO")
@unittest.skipIf(is_pypy(), "Requires CPython")
def test_unclosed_file(self):
def open():
im = Image.open(test_files[0])
im.load()
self.assert_warning(ResourceWarning, open)
def test_closed_file(self):
def open():
im = Image.open(test_files[0])
im.load()
im.close()
self.assert_warning(None, open)
def test_context_manager(self):
def open():
with Image.open(test_files[0]) as im:
im.load()
self.assert_warning(None, open)
def test_app(self):
for test_file in test_files:
# Test APP/COM reader (@PIL135)
im = Image.open(test_file)
self.assertEqual(im.applist[0][0], "APP1")
self.assertEqual(im.applist[1][0], "APP2")
self.assertEqual(
im.applist[1][1][:16], b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00"
)
self.assertEqual(len(im.applist), 2)
with Image.open(test_file) as im:
self.assertEqual(im.applist[0][0], "APP1")
self.assertEqual(im.applist[1][0], "APP2")
self.assertEqual(
im.applist[1][1][:16],
b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00",
)
self.assertEqual(len(im.applist), 2)
def test_exif(self):
for test_file in test_files:
im = Image.open(test_file)
info = im._getexif()
self.assertEqual(info[272], "Nintendo 3DS")
self.assertEqual(info[296], 2)
self.assertEqual(info[34665], 188)
with Image.open(test_file) as im:
info = im._getexif()
self.assertEqual(info[272], "Nintendo 3DS")
self.assertEqual(info[296], 2)
self.assertEqual(info[34665], 188)
def test_frame_size(self):
# This image has been hexedited to contain a different size
# in the EXIF data of the second frame
im = Image.open("Tests/images/sugarshack_frame_size.mpo")
self.assertEqual(im.size, (640, 480))
with Image.open("Tests/images/sugarshack_frame_size.mpo") as im:
self.assertEqual(im.size, (640, 480))
im.seek(1)
self.assertEqual(im.size, (680, 480))
im.seek(1)
self.assertEqual(im.size, (680, 480))
def test_parallax(self):
# Nintendo
im = Image.open("Tests/images/sugarshack.mpo")
exif = im.getexif()
self.assertEqual(exif.get_ifd(0x927C)[0x1101]["Parallax"], -44.798187255859375)
with Image.open("Tests/images/sugarshack.mpo") as im:
exif = im.getexif()
self.assertEqual(
exif.get_ifd(0x927C)[0x1101]["Parallax"], -44.798187255859375
)
# Fujifilm
im = Image.open("Tests/images/fujifilm.mpo")
im.seek(1)
exif = im.getexif()
self.assertEqual(exif.get_ifd(0x927C)[0xB211], -3.125)
with Image.open("Tests/images/fujifilm.mpo") as im:
im.seek(1)
exif = im.getexif()
self.assertEqual(exif.get_ifd(0x927C)[0xB211], -3.125)
def test_mp(self):
for test_file in test_files:
im = Image.open(test_file)
mpinfo = im._getmp()
self.assertEqual(mpinfo[45056], b"0100")
self.assertEqual(mpinfo[45057], 2)
with Image.open(test_file) as im:
mpinfo = im._getmp()
self.assertEqual(mpinfo[45056], b"0100")
self.assertEqual(mpinfo[45057], 2)
def test_mp_offset(self):
# This image has been manually hexedited to have an IFD offset of 10
# in APP2 data, in contrast to normal 8
im = Image.open("Tests/images/sugarshack_ifd_offset.mpo")
mpinfo = im._getmp()
self.assertEqual(mpinfo[45056], b"0100")
self.assertEqual(mpinfo[45057], 2)
with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im:
mpinfo = im._getmp()
self.assertEqual(mpinfo[45056], b"0100")
self.assertEqual(mpinfo[45057], 2)
def test_mp_attribute(self):
for test_file in test_files:
im = Image.open(test_file)
mpinfo = im._getmp()
with Image.open(test_file) as im:
mpinfo = im._getmp()
frameNumber = 0
for mpentry in mpinfo[45058]:
mpattr = mpentry["Attribute"]
@ -113,62 +133,62 @@ class TestFileMpo(PillowTestCase):
def test_seek(self):
for test_file in test_files:
im = Image.open(test_file)
self.assertEqual(im.tell(), 0)
# prior to first image raises an error, both blatant and borderline
self.assertRaises(EOFError, im.seek, -1)
self.assertRaises(EOFError, im.seek, -523)
# after the final image raises an error,
# both blatant and borderline
self.assertRaises(EOFError, im.seek, 2)
self.assertRaises(EOFError, im.seek, 523)
# bad calls shouldn't change the frame
self.assertEqual(im.tell(), 0)
# this one will work
im.seek(1)
self.assertEqual(im.tell(), 1)
# and this one, too
im.seek(0)
self.assertEqual(im.tell(), 0)
with Image.open(test_file) as im:
self.assertEqual(im.tell(), 0)
# prior to first image raises an error, both blatant and borderline
self.assertRaises(EOFError, im.seek, -1)
self.assertRaises(EOFError, im.seek, -523)
# after the final image raises an error,
# both blatant and borderline
self.assertRaises(EOFError, im.seek, 2)
self.assertRaises(EOFError, im.seek, 523)
# bad calls shouldn't change the frame
self.assertEqual(im.tell(), 0)
# this one will work
im.seek(1)
self.assertEqual(im.tell(), 1)
# and this one, too
im.seek(0)
self.assertEqual(im.tell(), 0)
def test_n_frames(self):
im = Image.open("Tests/images/sugarshack.mpo")
self.assertEqual(im.n_frames, 2)
self.assertTrue(im.is_animated)
with Image.open("Tests/images/sugarshack.mpo") as im:
self.assertEqual(im.n_frames, 2)
self.assertTrue(im.is_animated)
def test_eoferror(self):
im = Image.open("Tests/images/sugarshack.mpo")
n_frames = im.n_frames
with Image.open("Tests/images/sugarshack.mpo") 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_image_grab(self):
for test_file in test_files:
im = Image.open(test_file)
self.assertEqual(im.tell(), 0)
im0 = im.tobytes()
im.seek(1)
self.assertEqual(im.tell(), 1)
im1 = im.tobytes()
im.seek(0)
self.assertEqual(im.tell(), 0)
im02 = im.tobytes()
self.assertEqual(im0, im02)
self.assertNotEqual(im0, im1)
with Image.open(test_file) as im:
self.assertEqual(im.tell(), 0)
im0 = im.tobytes()
im.seek(1)
self.assertEqual(im.tell(), 1)
im1 = im.tobytes()
im.seek(0)
self.assertEqual(im.tell(), 0)
im02 = im.tobytes()
self.assertEqual(im0, im02)
self.assertNotEqual(im0, im1)
def test_save(self):
# Note that only individual frames can be saved at present
for test_file in test_files:
im = Image.open(test_file)
self.assertEqual(im.tell(), 0)
jpg0 = self.frame_roundtrip(im)
self.assert_image_similar(im, jpg0, 30)
im.seek(1)
self.assertEqual(im.tell(), 1)
jpg1 = self.frame_roundtrip(im)
self.assert_image_similar(im, jpg1, 30)
with Image.open(test_file) as im:
self.assertEqual(im.tell(), 0)
jpg0 = self.frame_roundtrip(im)
self.assert_image_similar(im, jpg0, 30)
im.seek(1)
self.assertEqual(im.tell(), 1)
jpg1 = self.frame_roundtrip(im)
self.assert_image_similar(im, jpg1, 30)

View File

@ -82,27 +82,27 @@ class TestFilePdf(PillowTestCase):
self.helper_save_as_pdf("RGB", save_all=True)
# Multiframe image
im = Image.open("Tests/images/dispose_bgnd.gif")
with Image.open("Tests/images/dispose_bgnd.gif") as im:
outfile = self.tempfile("temp.pdf")
im.save(outfile, save_all=True)
outfile = self.tempfile("temp.pdf")
im.save(outfile, save_all=True)
self.assertTrue(os.path.isfile(outfile))
self.assertGreater(os.path.getsize(outfile), 0)
self.assertTrue(os.path.isfile(outfile))
self.assertGreater(os.path.getsize(outfile), 0)
# Append images
ims = [hopper()]
im.copy().save(outfile, save_all=True, append_images=ims)
# Append images
ims = [hopper()]
im.copy().save(outfile, save_all=True, append_images=ims)
self.assertTrue(os.path.isfile(outfile))
self.assertGreater(os.path.getsize(outfile), 0)
self.assertTrue(os.path.isfile(outfile))
self.assertGreater(os.path.getsize(outfile), 0)
# Test appending using a generator
def imGenerator(ims):
for im in ims:
yield im
# Test appending using a generator
def imGenerator(ims):
for im in ims:
yield im
im.save(outfile, save_all=True, append_images=imGenerator(ims))
im.save(outfile, save_all=True, append_images=imGenerator(ims))
self.assertTrue(os.path.isfile(outfile))
self.assertGreater(os.path.getsize(outfile), 0)
@ -116,10 +116,10 @@ class TestFilePdf(PillowTestCase):
def test_multiframe_normal_save(self):
# Test saving a multiframe image without save_all
im = Image.open("Tests/images/dispose_bgnd.gif")
with Image.open("Tests/images/dispose_bgnd.gif") as im:
outfile = self.tempfile("temp.pdf")
im.save(outfile)
outfile = self.tempfile("temp.pdf")
im.save(outfile)
self.assertTrue(os.path.isfile(outfile))
self.assertGreater(os.path.getsize(outfile), 0)

View File

@ -81,12 +81,12 @@ class TestFilePng(PillowTestCase):
hopper("RGB").save(test_file)
im = Image.open(test_file)
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "PNG")
self.assertEqual(im.get_format_mimetype(), "image/png")
with Image.open(test_file) as im:
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "PNG")
self.assertEqual(im.get_format_mimetype(), "image/png")
for mode in ["1", "L", "P", "RGB", "I", "I;16"]:
im = hopper(mode)
@ -393,12 +393,12 @@ class TestFilePng(PillowTestCase):
def test_load_dpi_rounding(self):
# Round up
im = Image.open(TEST_PNG_FILE)
self.assertEqual(im.info["dpi"], (96, 96))
with Image.open(TEST_PNG_FILE) as im:
self.assertEqual(im.info["dpi"], (96, 96))
# Round down
im = Image.open("Tests/images/icc_profile_none.png")
self.assertEqual(im.info["dpi"], (72, 72))
with Image.open("Tests/images/icc_profile_none.png") as im:
self.assertEqual(im.info["dpi"], (72, 72))
def test_save_dpi_rounding(self):
im = Image.open(TEST_PNG_FILE)
@ -462,8 +462,13 @@ class TestFilePng(PillowTestCase):
if py3:
rt_text(" Aa" + chr(0xA0) + chr(0xC4) + chr(0xFF)) # Latin1
rt_text(chr(0x400) + chr(0x472) + chr(0x4FF)) # Cyrillic
# CJK:
rt_text(chr(0x4E00) + chr(0x66F0) + chr(0x9FBA) + chr(0x3042) + chr(0xAC00))
rt_text(
chr(0x4E00)
+ chr(0x66F0)
+ chr(0x9FBA) # CJK
+ chr(0x3042)
+ chr(0xAC00)
)
rt_text("A" + chr(0xC4) + chr(0x472) + chr(0x3042)) # Combined
def test_scary(self):
@ -509,19 +514,19 @@ class TestFilePng(PillowTestCase):
def test_trns_null(self):
# Check reading images with null tRNS value, issue #1239
test_file = "Tests/images/tRNS_null_1x1.png"
im = Image.open(test_file)
with Image.open(test_file) as im:
self.assertEqual(im.info["transparency"], 0)
self.assertEqual(im.info["transparency"], 0)
def test_save_icc_profile(self):
im = Image.open("Tests/images/icc_profile_none.png")
self.assertIsNone(im.info["icc_profile"])
with Image.open("Tests/images/icc_profile_none.png") as im:
self.assertIsNone(im.info["icc_profile"])
with_icc = Image.open("Tests/images/icc_profile.png")
expected_icc = with_icc.info["icc_profile"]
with Image.open("Tests/images/icc_profile.png") as with_icc:
expected_icc = with_icc.info["icc_profile"]
im = roundtrip(im, icc_profile=expected_icc)
self.assertEqual(im.info["icc_profile"], expected_icc)
im = roundtrip(im, icc_profile=expected_icc)
self.assertEqual(im.info["icc_profile"], expected_icc)
def test_discard_icc_profile(self):
im = Image.open("Tests/images/icc_profile.png")
@ -614,8 +619,8 @@ class TestFilePng(PillowTestCase):
test_file = self.tempfile("temp.png")
im.save(test_file)
reloaded = Image.open(test_file)
exif = reloaded._getexif()
with Image.open(test_file) as reloaded:
exif = reloaded._getexif()
self.assertEqual(exif[274], 1)
def test_exif_from_jpg(self):
@ -624,8 +629,8 @@ class TestFilePng(PillowTestCase):
test_file = self.tempfile("temp.png")
im.save(test_file)
reloaded = Image.open(test_file)
exif = reloaded._getexif()
with Image.open(test_file) as reloaded:
exif = reloaded._getexif()
self.assertEqual(exif[305], "Adobe Photoshop CS Macintosh")
def test_exif_argument(self):
@ -634,8 +639,8 @@ class TestFilePng(PillowTestCase):
test_file = self.tempfile("temp.png")
im.save(test_file, exif=b"exifstring")
reloaded = Image.open(test_file)
self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring")
with Image.open(test_file) as reloaded:
self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring")
@unittest.skipUnless(
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP support not installed with animation"

View File

@ -66,10 +66,10 @@ class TestFilePpm(PillowTestCase):
with open(path, "w") as f:
f.write("P4\n128 128\n255")
im = Image.open(path)
self.assertEqual(im.get_format_mimetype(), "image/x-portable-bitmap")
with Image.open(path) as im:
self.assertEqual(im.get_format_mimetype(), "image/x-portable-bitmap")
with open(path, "w") as f:
f.write("PyCMYK\n128 128\n255")
im = Image.open(path)
self.assertEqual(im.get_format_mimetype(), "image/x-portable-anymap")
with Image.open(path) as im:
self.assertEqual(im.get_format_mimetype(), "image/x-portable-anymap")

View File

@ -1,26 +1,45 @@
import unittest
from PIL import Image, PsdImagePlugin
from .helper import PillowTestCase, hopper
from .helper import PillowTestCase, hopper, is_pypy
test_file = "Tests/images/hopper.psd"
class TestImagePsd(PillowTestCase):
def test_sanity(self):
im = Image.open(test_file)
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "PSD")
with Image.open(test_file) as im:
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "PSD")
im2 = hopper()
self.assert_image_similar(im, im2, 4.8)
im2 = hopper()
self.assert_image_similar(im, im2, 4.8)
@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():
im = Image.open(test_file)
im.load()
im.close()
self.assert_warning(None, open)
def test_invalid_file(self):
@ -29,64 +48,63 @@ class TestImagePsd(PillowTestCase):
self.assertRaises(SyntaxError, PsdImagePlugin.PsdImageFile, invalid_file)
def test_n_frames(self):
im = Image.open("Tests/images/hopper_merged.psd")
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
with Image.open("Tests/images/hopper_merged.psd") as im:
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
im = Image.open(test_file)
self.assertEqual(im.n_frames, 2)
self.assertTrue(im.is_animated)
with Image.open(test_file) as im:
self.assertEqual(im.n_frames, 2)
self.assertTrue(im.is_animated)
def test_eoferror(self):
im = Image.open(test_file)
# PSD seek index starts at 1 rather than 0
n_frames = im.n_frames + 1
with Image.open(test_file) as im:
# PSD seek index starts at 1 rather than 0
n_frames = im.n_frames + 1
# 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_tell(self):
im = Image.open(test_file)
with Image.open(test_file) as im:
layer_number = im.tell()
self.assertEqual(layer_number, 1)
layer_number = im.tell()
self.assertEqual(layer_number, 1)
self.assertRaises(EOFError, im.seek, 0)
self.assertRaises(EOFError, im.seek, 0)
im.seek(1)
layer_number = im.tell()
self.assertEqual(layer_number, 1)
im.seek(1)
layer_number = im.tell()
self.assertEqual(layer_number, 1)
im.seek(2)
layer_number = im.tell()
self.assertEqual(layer_number, 2)
im.seek(2)
layer_number = im.tell()
self.assertEqual(layer_number, 2)
def test_seek_eoferror(self):
im = Image.open(test_file)
with Image.open(test_file) as im:
self.assertRaises(EOFError, im.seek, -1)
self.assertRaises(EOFError, im.seek, -1)
def test_open_after_exclusive_load(self):
im = Image.open(test_file)
im.load()
im.seek(im.tell() + 1)
im.load()
with Image.open(test_file) as im:
im.load()
im.seek(im.tell() + 1)
im.load()
def test_icc_profile(self):
im = Image.open(test_file)
self.assertIn("icc_profile", im.info)
with Image.open(test_file) as im:
self.assertIn("icc_profile", im.info)
icc_profile = im.info["icc_profile"]
self.assertEqual(len(icc_profile), 3144)
icc_profile = im.info["icc_profile"]
self.assertEqual(len(icc_profile), 3144)
def test_no_icc_profile(self):
im = Image.open("Tests/images/hopper_merged.psd")
self.assertNotIn("icc_profile", im.info)
with Image.open("Tests/images/hopper_merged.psd") as im:
self.assertNotIn("icc_profile", im.info)
def test_combined_larger_than_size(self):
# The 'combined' sizes of the individual parts is larger than the

View File

@ -1,26 +1,43 @@
import tempfile
import unittest
from io import BytesIO
from PIL import Image, ImageSequence, SpiderImagePlugin
from .helper import PillowTestCase, hopper
from .helper import PillowTestCase, hopper, is_pypy
TEST_FILE = "Tests/images/hopper.spider"
class TestImageSpider(PillowTestCase):
def test_sanity(self):
im = Image.open(TEST_FILE)
im.load()
self.assertEqual(im.mode, "F")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "SPIDER")
with Image.open(TEST_FILE) as im:
im.load()
self.assertEqual(im.mode, "F")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "SPIDER")
@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_save(self):
@ -32,10 +49,10 @@ class TestImageSpider(PillowTestCase):
im.save(temp, "SPIDER")
# Assert
im2 = Image.open(temp)
self.assertEqual(im2.mode, "F")
self.assertEqual(im2.size, (128, 128))
self.assertEqual(im2.format, "SPIDER")
with Image.open(temp) as im2:
self.assertEqual(im2.mode, "F")
self.assertEqual(im2.size, (128, 128))
self.assertEqual(im2.format, "SPIDER")
def test_tempfile(self):
# Arrange
@ -57,18 +74,18 @@ class TestImageSpider(PillowTestCase):
def test_tell(self):
# Arrange
im = Image.open(TEST_FILE)
with Image.open(TEST_FILE) as im:
# Act
index = im.tell()
# Act
index = im.tell()
# Assert
self.assertEqual(index, 0)
# Assert
self.assertEqual(index, 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_loadImageSeries(self):
# Arrange
@ -109,15 +126,14 @@ class TestImageSpider(PillowTestCase):
self.assertRaises(IOError, Image.open, invalid_file)
def test_nonstack_file(self):
im = Image.open(TEST_FILE)
self.assertRaises(EOFError, im.seek, 0)
with Image.open(TEST_FILE) as im:
self.assertRaises(EOFError, im.seek, 0)
def test_nonstack_dos(self):
im = Image.open(TEST_FILE)
for i, frame in enumerate(ImageSequence.Iterator(im)):
if i > 1:
self.fail("Non-stack DOS file test failed")
with Image.open(TEST_FILE) as im:
for i, frame in enumerate(ImageSequence.Iterator(im)):
if i > 1:
self.fail("Non-stack DOS file test failed")
# for issue #4093
def test_odd_size(self):

View File

@ -1,6 +1,8 @@
import unittest
from PIL import Image, TarIO
from .helper import PillowTestCase
from .helper import PillowTestCase, is_pypy
codecs = dir(Image.core)
@ -19,17 +21,30 @@ class TestFileTar(PillowTestCase):
["jpeg_decoder", "hopper.jpg", "JPEG"],
]:
if codec in codecs:
tar = TarIO.TarIO(TEST_TAR_FILE, test_path)
im = Image.open(tar)
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, format)
with TarIO.TarIO(TEST_TAR_FILE, test_path) as tar:
im = Image.open(tar)
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, format)
@unittest.skipIf(is_pypy(), "Requires CPython")
def test_unclosed_file(self):
def open():
TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
self.assert_warning(ResourceWarning, open)
def test_close(self):
tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
tar.close()
def open():
tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
tar.close()
self.assert_warning(None, open)
def test_contextmanager(self):
with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"):
pass
def open():
with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"):
pass
self.assert_warning(None, open)

View File

@ -76,20 +76,20 @@ class TestFileTga(PillowTestCase):
test_file = "Tests/images/tga_id_field.tga"
# Act
im = Image.open(test_file)
with Image.open(test_file) as im:
# Assert
self.assertEqual(im.size, (100, 100))
# Assert
self.assertEqual(im.size, (100, 100))
def test_id_field_rle(self):
# tga file with id field
test_file = "Tests/images/rgb32rle.tga"
# Act
im = Image.open(test_file)
with Image.open(test_file) as im:
# Assert
self.assertEqual(im.size, (199, 199))
# Assert
self.assertEqual(im.size, (199, 199))
def test_save(self):
test_file = "Tests/images/tga_id_field.tga"
@ -99,14 +99,14 @@ class TestFileTga(PillowTestCase):
# Save
im.save(out)
test_im = Image.open(out)
self.assertEqual(test_im.size, (100, 100))
self.assertEqual(test_im.info["id_section"], im.info["id_section"])
with Image.open(out) as test_im:
self.assertEqual(test_im.size, (100, 100))
self.assertEqual(test_im.info["id_section"], im.info["id_section"])
# RGBA save
im.convert("RGBA").save(out)
test_im = Image.open(out)
self.assertEqual(test_im.size, (100, 100))
with Image.open(out) as test_im:
self.assertEqual(test_im.size, (100, 100))
def test_save_id_section(self):
test_file = "Tests/images/rgb32rle.tga"
@ -116,27 +116,27 @@ class TestFileTga(PillowTestCase):
# Check there is no id section
im.save(out)
test_im = Image.open(out)
self.assertNotIn("id_section", test_im.info)
with Image.open(out) as test_im:
self.assertNotIn("id_section", test_im.info)
# Save with custom id section
im.save(out, id_section=b"Test content")
test_im = Image.open(out)
self.assertEqual(test_im.info["id_section"], b"Test content")
with Image.open(out) as test_im:
self.assertEqual(test_im.info["id_section"], b"Test content")
# Save with custom id section greater than 255 characters
id_section = b"Test content" * 25
self.assert_warning(UserWarning, lambda: im.save(out, id_section=id_section))
test_im = Image.open(out)
self.assertEqual(test_im.info["id_section"], id_section[:255])
with Image.open(out) as test_im:
self.assertEqual(test_im.info["id_section"], id_section[:255])
test_file = "Tests/images/tga_id_field.tga"
im = Image.open(test_file)
with Image.open(test_file) as im:
# Save with no id section
im.save(out, id_section="")
test_im = Image.open(out)
self.assertNotIn("id_section", test_im.info)
# Save with no id section
im.save(out, id_section="")
with Image.open(out) as test_im:
self.assertNotIn("id_section", test_im.info)
def test_save_orientation(self):
test_file = "Tests/images/rgb32rle.tga"
@ -146,8 +146,8 @@ class TestFileTga(PillowTestCase):
out = self.tempfile("temp.tga")
im.save(out, orientation=1)
test_im = Image.open(out)
self.assertEqual(test_im.info["orientation"], 1)
with Image.open(out) as test_im:
self.assertEqual(test_im.info["orientation"], 1)
def test_save_rle(self):
test_file = "Tests/images/rgb32rle.tga"
@ -158,19 +158,19 @@ class TestFileTga(PillowTestCase):
# Save
im.save(out)
test_im = Image.open(out)
self.assertEqual(test_im.size, (199, 199))
self.assertEqual(test_im.info["compression"], "tga_rle")
with Image.open(out) as test_im:
self.assertEqual(test_im.size, (199, 199))
self.assertEqual(test_im.info["compression"], "tga_rle")
# Save without compression
im.save(out, compression=None)
test_im = Image.open(out)
self.assertNotIn("compression", test_im.info)
with Image.open(out) as test_im:
self.assertNotIn("compression", test_im.info)
# RGBA save
im.convert("RGBA").save(out)
test_im = Image.open(out)
self.assertEqual(test_im.size, (199, 199))
with Image.open(out) as test_im:
self.assertEqual(test_im.size, (199, 199))
test_file = "Tests/images/tga_id_field.tga"
im = Image.open(test_file)
@ -178,8 +178,8 @@ class TestFileTga(PillowTestCase):
# Save with compression
im.save(out, compression="tga_rle")
test_im = Image.open(out)
self.assertEqual(test_im.info["compression"], "tga_rle")
with Image.open(out) as test_im:
self.assertEqual(test_im.info["compression"], "tga_rle")
def test_save_l_transparency(self):
# There are 559 transparent pixels in la.tga.

View File

@ -6,7 +6,7 @@ from PIL import Image, TiffImagePlugin
from PIL._util import py3
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
from .helper import PillowTestCase, hopper, is_win32, unittest
from .helper import PillowTestCase, hopper, is_pypy, is_win32, unittest
logger = logging.getLogger(__name__)
@ -18,32 +18,53 @@ class TestFileTiff(PillowTestCase):
hopper("RGB").save(filename)
im = Image.open(filename)
im.load()
with Image.open(filename) as im:
im.load()
self.assertEqual(im.mode, "RGB")
self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "TIFF")
hopper("1").save(filename)
Image.open(filename)
with Image.open(filename):
pass
hopper("L").save(filename)
Image.open(filename)
with Image.open(filename):
pass
hopper("P").save(filename)
Image.open(filename)
with Image.open(filename):
pass
hopper("RGB").save(filename)
Image.open(filename)
with Image.open(filename):
pass
hopper("I").save(filename)
Image.open(filename)
with Image.open(filename):
pass
@unittest.skipIf(is_pypy(), "Requires CPython")
def test_unclosed_file(self):
def open():
im = Image.open("Tests/images/multipage.tiff")
im.load()
self.assert_warning(ResourceWarning, open)
def test_closed_file(self):
def open():
im = Image.open("Tests/images/multipage.tiff")
im.load()
im.close()
self.assert_warning(None, open)
def test_context_manager(self):
def open():
with Image.open("Tests/images/multipage.tiff") as im:
im.load()
self.assert_warning(None, open)
def test_mac_tiff(self):
@ -75,55 +96,55 @@ class TestFileTiff(PillowTestCase):
def test_xyres_tiff(self):
filename = "Tests/images/pil168.tif"
im = Image.open(filename)
with Image.open(filename) as im:
# legacy api
self.assertIsInstance(im.tag[X_RESOLUTION][0], tuple)
self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple)
# legacy api
self.assertIsInstance(im.tag[X_RESOLUTION][0], tuple)
self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple)
# v2 api
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
# v2 api
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
self.assertEqual(im.info["dpi"], (72.0, 72.0))
self.assertEqual(im.info["dpi"], (72.0, 72.0))
def test_xyres_fallback_tiff(self):
filename = "Tests/images/compression.tif"
im = Image.open(filename)
with Image.open(filename) as im:
# v2 api
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT])
# v2 api
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT])
# Legacy.
self.assertEqual(im.info["resolution"], (100.0, 100.0))
# Fallback "inch".
self.assertEqual(im.info["dpi"], (100.0, 100.0))
# Legacy.
self.assertEqual(im.info["resolution"], (100.0, 100.0))
# Fallback "inch".
self.assertEqual(im.info["dpi"], (100.0, 100.0))
def test_int_resolution(self):
filename = "Tests/images/pil168.tif"
im = Image.open(filename)
with Image.open(filename) as im:
# Try to read a file where X,Y_RESOLUTION are ints
im.tag_v2[X_RESOLUTION] = 71
im.tag_v2[Y_RESOLUTION] = 71
im._setup()
self.assertEqual(im.info["dpi"], (71.0, 71.0))
# Try to read a file where X,Y_RESOLUTION are ints
im.tag_v2[X_RESOLUTION] = 71
im.tag_v2[Y_RESOLUTION] = 71
im._setup()
self.assertEqual(im.info["dpi"], (71.0, 71.0))
def test_load_dpi_rounding(self):
for resolutionUnit, dpi in ((None, (72, 73)), (2, (72, 73)), (3, (183, 185))):
im = Image.open(
with Image.open(
"Tests/images/hopper_roundDown_" + str(resolutionUnit) + ".tif"
)
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
self.assertEqual(im.info["dpi"], (dpi[0], dpi[0]))
) as im:
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
self.assertEqual(im.info["dpi"], (dpi[0], dpi[0]))
im = Image.open(
with Image.open(
"Tests/images/hopper_roundUp_" + str(resolutionUnit) + ".tif"
)
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
self.assertEqual(im.info["dpi"], (dpi[1], dpi[1]))
) as im:
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
self.assertEqual(im.info["dpi"], (dpi[1], dpi[1]))
def test_save_dpi_rounding(self):
outfile = self.tempfile("temp.tif")
@ -155,9 +176,9 @@ class TestFileTiff(PillowTestCase):
TiffImagePlugin.PREFIXES.pop()
def test_bad_exif(self):
i = Image.open("Tests/images/hopper_bad_exif.jpg")
# Should not raise struct.error.
self.assert_warning(UserWarning, i._getexif)
with Image.open("Tests/images/hopper_bad_exif.jpg") as i:
# Should not raise struct.error.
self.assert_warning(UserWarning, i._getexif)
def test_save_rgba(self):
im = hopper("RGBA")
@ -238,44 +259,44 @@ class TestFileTiff(PillowTestCase):
["Tests/images/multipage-lastframe.tif", 1],
["Tests/images/multipage.tiff", 3],
]:
im = Image.open(path)
self.assertEqual(im.n_frames, n_frames)
self.assertEqual(im.is_animated, n_frames != 1)
with Image.open(path) as im:
self.assertEqual(im.n_frames, n_frames)
self.assertEqual(im.is_animated, n_frames != 1)
def test_eoferror(self):
im = Image.open("Tests/images/multipage-lastframe.tif")
n_frames = im.n_frames
with Image.open("Tests/images/multipage-lastframe.tif") 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_multipage(self):
# issue #862
im = Image.open("Tests/images/multipage.tiff")
# file is a multipage tiff: 10x10 green, 10x10 red, 20x20 blue
with Image.open("Tests/images/multipage.tiff") as im:
# file is a multipage tiff: 10x10 green, 10x10 red, 20x20 blue
im.seek(0)
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
im.seek(0)
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
im.seek(1)
im.load()
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
im.seek(1)
im.load()
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
im.seek(0)
im.load()
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
im.seek(0)
im.load()
self.assertEqual(im.size, (10, 10))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
im.seek(2)
im.load()
self.assertEqual(im.size, (20, 20))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
im.seek(2)
im.load()
self.assertEqual(im.size, (20, 20))
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
def test_multipage_last_frame(self):
im = Image.open("Tests/images/multipage-lastframe.tif")
@ -285,62 +306,62 @@ class TestFileTiff(PillowTestCase):
def test___str__(self):
filename = "Tests/images/pil136.tiff"
im = Image.open(filename)
with Image.open(filename) as im:
# Act
ret = str(im.ifd)
# Act
ret = str(im.ifd)
# Assert
self.assertIsInstance(ret, str)
# Assert
self.assertIsInstance(ret, str)
def test_dict(self):
# Arrange
filename = "Tests/images/pil136.tiff"
im = Image.open(filename)
with Image.open(filename) as im:
# v2 interface
v2_tags = {
256: 55,
257: 43,
258: (8, 8, 8, 8),
259: 1,
262: 2,
296: 2,
273: (8,),
338: (1,),
277: 4,
279: (9460,),
282: 72.0,
283: 72.0,
284: 1,
}
self.assertEqual(dict(im.tag_v2), v2_tags)
# v2 interface
v2_tags = {
256: 55,
257: 43,
258: (8, 8, 8, 8),
259: 1,
262: 2,
296: 2,
273: (8,),
338: (1,),
277: 4,
279: (9460,),
282: 72.0,
283: 72.0,
284: 1,
}
self.assertEqual(dict(im.tag_v2), v2_tags)
# legacy interface
legacy_tags = {
256: (55,),
257: (43,),
258: (8, 8, 8, 8),
259: (1,),
262: (2,),
296: (2,),
273: (8,),
338: (1,),
277: (4,),
279: (9460,),
282: ((720000, 10000),),
283: ((720000, 10000),),
284: (1,),
}
self.assertEqual(dict(im.tag), legacy_tags)
# legacy interface
legacy_tags = {
256: (55,),
257: (43,),
258: (8, 8, 8, 8),
259: (1,),
262: (2,),
296: (2,),
273: (8,),
338: (1,),
277: (4,),
279: (9460,),
282: ((720000, 10000),),
283: ((720000, 10000),),
284: (1,),
}
self.assertEqual(dict(im.tag), legacy_tags)
def test__delitem__(self):
filename = "Tests/images/pil136.tiff"
im = Image.open(filename)
len_before = len(dict(im.ifd))
del im.ifd[256]
len_after = len(dict(im.ifd))
self.assertEqual(len_before, len_after + 1)
with Image.open(filename) as im:
len_before = len(dict(im.ifd))
del im.ifd[256]
len_after = len(dict(im.ifd))
self.assertEqual(len_before, len_after + 1)
def test_load_byte(self):
for legacy_api in [False, True]:
@ -369,16 +390,16 @@ class TestFileTiff(PillowTestCase):
def test_seek(self):
filename = "Tests/images/pil136.tiff"
im = Image.open(filename)
im.seek(0)
self.assertEqual(im.tell(), 0)
with Image.open(filename) as im:
im.seek(0)
self.assertEqual(im.tell(), 0)
def test_seek_eof(self):
filename = "Tests/images/pil136.tiff"
im = Image.open(filename)
self.assertEqual(im.tell(), 0)
self.assertRaises(EOFError, im.seek, -1)
self.assertRaises(EOFError, im.seek, 1)
with Image.open(filename) as im:
self.assertEqual(im.tell(), 0)
self.assertRaises(EOFError, im.seek, -1)
self.assertRaises(EOFError, im.seek, 1)
def test__limit_rational_int(self):
from PIL.TiffImagePlugin import _limit_rational
@ -439,15 +460,15 @@ class TestFileTiff(PillowTestCase):
kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36}
filename = self.tempfile("temp.tif")
hopper("RGB").save(filename, **kwargs)
im = Image.open(filename)
with Image.open(filename) as im:
# legacy interface
self.assertEqual(im.tag[X_RESOLUTION][0][0], 72)
self.assertEqual(im.tag[Y_RESOLUTION][0][0], 36)
# legacy interface
self.assertEqual(im.tag[X_RESOLUTION][0][0], 72)
self.assertEqual(im.tag[Y_RESOLUTION][0][0], 36)
# v2 interface
self.assertEqual(im.tag_v2[X_RESOLUTION], 72)
self.assertEqual(im.tag_v2[Y_RESOLUTION], 36)
# v2 interface
self.assertEqual(im.tag_v2[X_RESOLUTION], 72)
self.assertEqual(im.tag_v2[Y_RESOLUTION], 36)
def test_roundtrip_tiff_uint16(self):
# Test an image of all '0' values
@ -480,9 +501,8 @@ class TestFileTiff(PillowTestCase):
def test_strip_planar_raw_with_overviews(self):
# gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16
infile = "Tests/images/tiff_strip_planar_raw_with_overviews.tif"
im = Image.open(infile)
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
with Image.open(infile) as im:
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
def test_tiled_planar_raw(self):
# gdal_translate -of GTiff -co TILED=YES -co BLOCKXSIZE=32 \
@ -545,9 +565,8 @@ class TestFileTiff(PillowTestCase):
# Try save-load round trip to make sure both handle icc_profile.
tmpfile = self.tempfile("temp.tif")
im.save(tmpfile, "TIFF", compression="raw")
reloaded = Image.open(tmpfile)
self.assertEqual(b"Dummy value", reloaded.info["icc_profile"])
with Image.open(tmpfile) as reloaded:
self.assertEqual(b"Dummy value", reloaded.info["icc_profile"])
def test_close_on_load_exclusive(self):
# similar to test_fd_leak, but runs on unixlike os

View File

@ -52,77 +52,81 @@ class TestFileTiffMetadata(PillowTestCase):
img.save(f, tiffinfo=info)
loaded = Image.open(f)
with Image.open(f) as loaded:
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),))
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),))
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),))
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),))
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata,))
self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata)
self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata,))
self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata)
loaded_float = loaded.tag[tag_ids["RollAngle"]][0]
self.assertAlmostEqual(loaded_float, floatdata, places=5)
loaded_double = loaded.tag[tag_ids["YawAngle"]][0]
self.assertAlmostEqual(loaded_double, doubledata)
loaded_float = loaded.tag[tag_ids["RollAngle"]][0]
self.assertAlmostEqual(loaded_float, floatdata, places=5)
loaded_double = loaded.tag[tag_ids["YawAngle"]][0]
self.assertAlmostEqual(loaded_double, doubledata)
# check with 2 element ImageJMetaDataByteCounts, issue #2006
info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8)
img.save(f, tiffinfo=info)
loaded = Image.open(f)
with Image.open(f) as loaded:
self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
self.assertEqual(
loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8)
)
self.assertEqual(
loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8)
)
def test_read_metadata(self):
img = Image.open("Tests/images/hopper_g4.tif")
with Image.open("Tests/images/hopper_g4.tif") as img:
self.assertEqual(
{
"YResolution": IFDRational(4294967295, 113653537),
"PlanarConfiguration": 1,
"BitsPerSample": (1,),
"ImageLength": 128,
"Compression": 4,
"FillOrder": 1,
"RowsPerStrip": 128,
"ResolutionUnit": 3,
"PhotometricInterpretation": 0,
"PageNumber": (0, 1),
"XResolution": IFDRational(4294967295, 113653537),
"ImageWidth": 128,
"Orientation": 1,
"StripByteCounts": (1968,),
"SamplesPerPixel": 1,
"StripOffsets": (8,),
},
img.tag_v2.named(),
)
self.assertEqual(
{
"YResolution": IFDRational(4294967295, 113653537),
"PlanarConfiguration": 1,
"BitsPerSample": (1,),
"ImageLength": 128,
"Compression": 4,
"FillOrder": 1,
"RowsPerStrip": 128,
"ResolutionUnit": 3,
"PhotometricInterpretation": 0,
"PageNumber": (0, 1),
"XResolution": IFDRational(4294967295, 113653537),
"ImageWidth": 128,
"Orientation": 1,
"StripByteCounts": (1968,),
"SamplesPerPixel": 1,
"StripOffsets": (8,),
},
img.tag_v2.named(),
)
self.assertEqual(
{
"YResolution": ((4294967295, 113653537),),
"PlanarConfiguration": (1,),
"BitsPerSample": (1,),
"ImageLength": (128,),
"Compression": (4,),
"FillOrder": (1,),
"RowsPerStrip": (128,),
"ResolutionUnit": (3,),
"PhotometricInterpretation": (0,),
"PageNumber": (0, 1),
"XResolution": ((4294967295, 113653537),),
"ImageWidth": (128,),
"Orientation": (1,),
"StripByteCounts": (1968,),
"SamplesPerPixel": (1,),
"StripOffsets": (8,),
},
img.tag.named(),
)
self.assertEqual(
{
"YResolution": ((4294967295, 113653537),),
"PlanarConfiguration": (1,),
"BitsPerSample": (1,),
"ImageLength": (128,),
"Compression": (4,),
"FillOrder": (1,),
"RowsPerStrip": (128,),
"ResolutionUnit": (3,),
"PhotometricInterpretation": (0,),
"PageNumber": (0, 1),
"XResolution": ((4294967295, 113653537),),
"ImageWidth": (128,),
"Orientation": (1,),
"StripByteCounts": (1968,),
"SamplesPerPixel": (1,),
"StripOffsets": (8,),
},
img.tag.named(),
)
def test_write_metadata(self):
""" Test metadata writing through the python code """
@ -131,10 +135,10 @@ class TestFileTiffMetadata(PillowTestCase):
f = self.tempfile("temp.tiff")
img.save(f, tiffinfo=img.tag)
loaded = Image.open(f)
with Image.open(f) as loaded:
original = img.tag_v2.named()
reloaded = loaded.tag_v2.named()
original = img.tag_v2.named()
reloaded = loaded.tag_v2.named()
for k, v in original.items():
if isinstance(v, IFDRational):
@ -187,18 +191,18 @@ class TestFileTiffMetadata(PillowTestCase):
out = self.tempfile("temp.tiff")
im.save(out)
reloaded = Image.open(out)
self.assertNotIsInstance(im.info["icc_profile"], tuple)
self.assertEqual(im.info["icc_profile"], reloaded.info["icc_profile"])
with Image.open(out) as reloaded:
self.assertNotIsInstance(im.info["icc_profile"], tuple)
self.assertEqual(im.info["icc_profile"], reloaded.info["icc_profile"])
def test_iccprofile_binary(self):
# https://github.com/python-pillow/Pillow/issues/1526
# We should be able to load this,
# but probably won't be able to save it.
im = Image.open("Tests/images/hopper.iccprofile_binary.tif")
self.assertEqual(im.tag_v2.tagtype[34675], 1)
self.assertTrue(im.info["icc_profile"])
with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im:
self.assertEqual(im.tag_v2.tagtype[34675], 1)
self.assertTrue(im.info["icc_profile"])
def test_iccprofile_save_png(self):
im = Image.open("Tests/images/hopper.iccprofile.tif")
@ -218,9 +222,9 @@ class TestFileTiffMetadata(PillowTestCase):
out = self.tempfile("temp.tiff")
im.save(out, tiffinfo=info, compression="raw")
reloaded = Image.open(out)
self.assertEqual(0, reloaded.tag_v2[41988].numerator)
self.assertEqual(0, reloaded.tag_v2[41988].denominator)
with Image.open(out) as reloaded:
self.assertEqual(0, reloaded.tag_v2[41988].numerator)
self.assertEqual(0, reloaded.tag_v2[41988].denominator)
def test_empty_values(self):
data = io.BytesIO(
@ -243,9 +247,9 @@ class TestFileTiffMetadata(PillowTestCase):
self.assertIsInstance(im.tag_v2[34377][0], bytes)
out = self.tempfile("temp.tiff")
im.save(out)
reloaded = Image.open(out)
self.assertEqual(len(reloaded.tag_v2[34377]), 1)
self.assertIsInstance(reloaded.tag_v2[34377][0], bytes)
with Image.open(out) as reloaded:
self.assertEqual(len(reloaded.tag_v2[34377]), 1)
self.assertIsInstance(reloaded.tag_v2[34377][0], bytes)
def test_too_many_entries(self):
ifd = TiffImagePlugin.ImageFileDirectory_v2()

View File

@ -158,19 +158,19 @@ class TestFileWebp(PillowTestCase):
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP save all not available"
)
def test_background_from_gif(self):
im = Image.open("Tests/images/chi.gif")
original_value = im.convert("RGB").getpixel((1, 1))
with Image.open("Tests/images/chi.gif") as im:
original_value = im.convert("RGB").getpixel((1, 1))
# Save as WEBP
out_webp = self.tempfile("temp.webp")
im.save(out_webp, save_all=True)
# Save as WEBP
out_webp = self.tempfile("temp.webp")
im.save(out_webp, save_all=True)
# Save as GIF
out_gif = self.tempfile("temp.gif")
Image.open(out_webp).save(out_gif)
reread = Image.open(out_gif)
reread_value = reread.convert("RGB").getpixel((1, 1))
with Image.open(out_gif) as reread:
reread_value = reread.convert("RGB").getpixel((1, 1))
difference = sum(
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
)

View File

@ -103,7 +103,8 @@ class TestFileWebpAlpha(PillowTestCase):
temp_file = self.tempfile("temp.webp")
file_path = "Tests/images/transparent.gif"
Image.open(file_path).save(temp_file)
with Image.open(file_path) as im:
im.save(temp_file)
image = Image.open(temp_file)
self.assertEqual(image.mode, "RGBA")
@ -112,6 +113,7 @@ class TestFileWebpAlpha(PillowTestCase):
image.load()
image.getdata()
target = Image.open(file_path).convert("RGBA")
with Image.open(file_path) as im:
target = im.convert("RGBA")
self.assert_image_similar(image, target, 25.0)

View File

@ -28,13 +28,13 @@ class TestFileWebpAnimation(PillowTestCase):
attributes correctly.
"""
im = Image.open("Tests/images/hopper.webp")
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
with Image.open("Tests/images/hopper.webp") as im:
self.assertEqual(im.n_frames, 1)
self.assertFalse(im.is_animated)
im = Image.open("Tests/images/iss634.webp")
self.assertEqual(im.n_frames, 42)
self.assertTrue(im.is_animated)
with Image.open("Tests/images/iss634.webp") as im:
self.assertEqual(im.n_frames, 42)
self.assertTrue(im.is_animated)
def test_write_animation_L(self):
"""
@ -43,23 +43,23 @@ class TestFileWebpAnimation(PillowTestCase):
visually similar.
"""
orig = Image.open("Tests/images/iss634.gif")
self.assertGreater(orig.n_frames, 1)
with Image.open("Tests/images/iss634.gif") as orig:
self.assertGreater(orig.n_frames, 1)
temp_file = self.tempfile("temp.webp")
orig.save(temp_file, save_all=True)
im = Image.open(temp_file)
self.assertEqual(im.n_frames, orig.n_frames)
temp_file = self.tempfile("temp.webp")
orig.save(temp_file, save_all=True)
im = Image.open(temp_file)
self.assertEqual(im.n_frames, orig.n_frames)
# Compare first and last frames to the original animated GIF
orig.load()
im.load()
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
orig.seek(orig.n_frames - 1)
im.seek(im.n_frames - 1)
orig.load()
im.load()
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
# Compare first and last frames to the original animated GIF
orig.load()
im.load()
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
orig.seek(orig.n_frames - 1)
im.seek(im.n_frames - 1)
orig.load()
im.load()
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
def test_write_animation_RGB(self):
"""

View File

@ -24,21 +24,21 @@ class TestFileWebpMetadata(PillowTestCase):
def test_read_exif_metadata(self):
file_path = "Tests/images/flower.webp"
image = Image.open(file_path)
with Image.open(file_path) as image:
self.assertEqual(image.format, "WEBP")
exif_data = image.info.get("exif", None)
self.assertTrue(exif_data)
self.assertEqual(image.format, "WEBP")
exif_data = image.info.get("exif", None)
self.assertTrue(exif_data)
exif = image._getexif()
exif = image._getexif()
# camera make
self.assertEqual(exif[271], "Canon")
# camera make
self.assertEqual(exif[271], "Canon")
jpeg_image = Image.open("Tests/images/flower.jpg")
expected_exif = jpeg_image.info["exif"]
with Image.open("Tests/images/flower.jpg") as jpeg_image:
expected_exif = jpeg_image.info["exif"]
self.assertEqual(exif_data, expected_exif)
self.assertEqual(exif_data, expected_exif)
def test_write_exif_metadata(self):
file_path = "Tests/images/flower.jpg"
@ -60,17 +60,17 @@ class TestFileWebpMetadata(PillowTestCase):
def test_read_icc_profile(self):
file_path = "Tests/images/flower2.webp"
image = Image.open(file_path)
with Image.open(file_path) as image:
self.assertEqual(image.format, "WEBP")
self.assertTrue(image.info.get("icc_profile", None))
self.assertEqual(image.format, "WEBP")
self.assertTrue(image.info.get("icc_profile", None))
icc = image.info["icc_profile"]
icc = image.info["icc_profile"]
jpeg_image = Image.open("Tests/images/flower2.jpg")
expected_icc = jpeg_image.info["icc_profile"]
with Image.open("Tests/images/flower2.jpg") as jpeg_image:
expected_icc = jpeg_image.info["icc_profile"]
self.assertEqual(icc, expected_icc)
self.assertEqual(icc, expected_icc)
def test_write_icc_metadata(self):
file_path = "Tests/images/flower2.jpg"
@ -126,10 +126,10 @@ class TestFileWebpMetadata(PillowTestCase):
xmp=xmp_data,
)
image = Image.open(temp_file)
self.assertIn("icc_profile", image.info)
self.assertIn("exif", image.info)
self.assertIn("xmp", image.info)
self.assertEqual(iccp_data, image.info.get("icc_profile", None))
self.assertEqual(exif_data, image.info.get("exif", None))
self.assertEqual(xmp_data, image.info.get("xmp", None))
with Image.open(temp_file) as image:
self.assertIn("icc_profile", image.info)
self.assertIn("exif", image.info)
self.assertIn("xmp", image.info)
self.assertEqual(iccp_data, image.info.get("icc_profile", None))
self.assertEqual(exif_data, image.info.get("exif", None))
self.assertEqual(xmp_data, image.info.get("xmp", None))

View File

@ -7,24 +7,24 @@ class TestFileWmf(PillowTestCase):
def test_load_raw(self):
# Test basic EMF open and rendering
im = Image.open("Tests/images/drawing.emf")
if hasattr(Image.core, "drawwmf"):
# Currently, support for WMF/EMF is Windows-only
im.load()
# Compare to reference rendering
imref = Image.open("Tests/images/drawing_emf_ref.png")
imref.load()
self.assert_image_similar(im, imref, 0)
with Image.open("Tests/images/drawing.emf") as im:
if hasattr(Image.core, "drawwmf"):
# Currently, support for WMF/EMF is Windows-only
im.load()
# Compare to reference rendering
imref = Image.open("Tests/images/drawing_emf_ref.png")
imref.load()
self.assert_image_similar(im, imref, 0)
# Test basic WMF open and rendering
im = Image.open("Tests/images/drawing.wmf")
if hasattr(Image.core, "drawwmf"):
# Currently, support for WMF/EMF is Windows-only
im.load()
# Compare to reference rendering
imref = Image.open("Tests/images/drawing_wmf_ref.png")
imref.load()
self.assert_image_similar(im, imref, 2.0)
with Image.open("Tests/images/drawing.wmf") as im:
if hasattr(Image.core, "drawwmf"):
# Currently, support for WMF/EMF is Windows-only
im.load()
# Compare to reference rendering
imref = Image.open("Tests/images/drawing_wmf_ref.png")
imref.load()
self.assert_image_similar(im, imref, 2.0)
def test_register_handler(self):
class TestHandler:
@ -46,12 +46,12 @@ class TestFileWmf(PillowTestCase):
def test_load_dpi_rounding(self):
# Round up
im = Image.open("Tests/images/drawing.emf")
self.assertEqual(im.info["dpi"], 1424)
with Image.open("Tests/images/drawing.emf") as im:
self.assertEqual(im.info["dpi"], 1424)
# Round down
im = Image.open("Tests/images/drawing_roundDown.emf")
self.assertEqual(im.info["dpi"], 1426)
with Image.open("Tests/images/drawing_roundDown.emf") as im:
self.assertEqual(im.info["dpi"], 1426)
def test_save(self):
im = hopper()

View File

@ -42,11 +42,11 @@ class TestFileXbm(PillowTestCase):
filename = "Tests/images/hopper.xbm"
# Act
im = Image.open(filename)
with Image.open(filename) as im:
# Assert
self.assertEqual(im.mode, "1")
self.assertEqual(im.size, (128, 128))
# Assert
self.assertEqual(im.mode, "1")
self.assertEqual(im.size, (128, 128))
def test_open_filename_with_underscore(self):
# Arrange
@ -54,8 +54,8 @@ class TestFileXbm(PillowTestCase):
filename = "Tests/images/hopper_underscore.xbm"
# Act
im = Image.open(filename)
with Image.open(filename) as im:
# Assert
self.assertEqual(im.mode, "1")
self.assertEqual(im.size, (128, 128))
# Assert
self.assertEqual(im.mode, "1")
self.assertEqual(im.size, (128, 128))

View File

@ -23,11 +23,11 @@ class TestFileXpm(PillowTestCase):
def test_load_read(self):
# Arrange
im = Image.open(TEST_FILE)
dummy_bytes = 1
with Image.open(TEST_FILE) as im:
dummy_bytes = 1
# Act
data = im.load_read(dummy_bytes)
# Act
data = im.load_read(dummy_bytes)
# Assert
self.assertEqual(len(data), 16384)

View File

@ -97,9 +97,9 @@ class TestImage(PillowTestCase):
def test_pathlib(self):
from PIL.Image import Path
im = Image.open(Path("Tests/images/multipage-mmap.tiff"))
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (10, 10))
with Image.open(Path("Tests/images/multipage-mmap.tiff")) as im:
self.assertEqual(im.mode, "P")
self.assertEqual(im.size, (10, 10))
im = Image.open(Path("Tests/images/hopper.jpg"))
self.assertEqual(im.mode, "RGB")
@ -346,7 +346,8 @@ class TestImage(PillowTestCase):
def test_registered_extensions(self):
# Arrange
# Open an image to trigger plugin registration
Image.open("Tests/images/rgb.jpg")
with Image.open("Tests/images/rgb.jpg"):
pass
# Act
extensions = Image.registered_extensions()
@ -448,10 +449,10 @@ class TestImage(PillowTestCase):
def test_offset_not_implemented(self):
# Arrange
im = hopper()
with hopper() as im:
# Act / Assert
self.assertRaises(NotImplementedError, im.offset, None)
# Act / Assert
self.assertRaises(NotImplementedError, im.offset, None)
def test_fromstring(self):
self.assertRaises(NotImplementedError, Image.fromstring)
@ -522,8 +523,8 @@ class TestImage(PillowTestCase):
def test_remap_palette(self):
# Test illegal image mode
im = hopper()
self.assertRaises(ValueError, im.remap_palette, None)
with hopper() as im:
self.assertRaises(ValueError, im.remap_palette, None)
def test__new(self):
from PIL import ImagePalette
@ -587,12 +588,12 @@ class TestImage(PillowTestCase):
def test_overrun(self):
for file in ["fli_overrun.bin", "sgi_overrun.bin", "pcx_overrun.bin"]:
im = Image.open(os.path.join("Tests/images", file))
try:
im.load()
self.assertFail()
except IOError as e:
self.assertEqual(str(e), "buffer overrun when reading image file")
with Image.open(os.path.join("Tests/images", file)) as im:
try:
im.load()
self.assertFail()
except IOError as e:
self.assertEqual(str(e), "buffer overrun when reading image file")
class MockEncoder(object):

View File

@ -144,11 +144,11 @@ class TestImageConvert(PillowTestCase):
def test_gif_with_rgba_palette_to_p(self):
# See https://github.com/python-pillow/Pillow/issues/2433
im = Image.open("Tests/images/hopper.gif")
im.info["transparency"] = 255
im.load()
self.assertEqual(im.palette.mode, "RGBA")
im_p = im.convert("P")
with Image.open("Tests/images/hopper.gif") as im:
im.info["transparency"] = 255
im.load()
self.assertEqual(im.palette.mode, "RGBA")
im_p = im.convert("P")
# Should not raise ValueError: unrecognized raw mode
im_p.load()

View File

@ -5,12 +5,15 @@ from .test_imageqt import PillowQtTestCase
class TestFromQImage(PillowQtTestCase, PillowTestCase):
files_to_test = [
hopper(),
Image.open("Tests/images/transparent.png"),
Image.open("Tests/images/7x13.png"),
]
def setUp(self):
super(TestFromQImage, self).setUp()
self.files_to_test = [
hopper(),
Image.open("Tests/images/transparent.png"),
Image.open("Tests/images/7x13.png"),
]
for im in self.files_to_test:
self.addCleanup(im.close)
def roundtrip(self, expected):
# PIL -> Qt

View File

@ -6,8 +6,8 @@ from .helper import PillowTestCase, hopper
class TestImageMode(PillowTestCase):
def test_sanity(self):
im = hopper()
im.mode
with hopper() as im:
im.mode
from PIL import ImageMode

View File

@ -148,5 +148,5 @@ class TestImageResize(PillowTestCase):
resize(mode, (188, 214))
# Test unknown resampling filter
im = hopper()
self.assertRaises(ValueError, im.resize, (10, 10), "unknown")
with hopper() as im:
self.assertRaises(ValueError, im.resize, (10, 10), "unknown")

View File

@ -39,11 +39,11 @@ class TestImageThumbnail(PillowTestCase):
def test_no_resize(self):
# Check that draft() can resize the image to the destination size
im = Image.open("Tests/images/hopper.jpg")
im.draft(None, (64, 64))
self.assertEqual(im.size, (64, 64))
with Image.open("Tests/images/hopper.jpg") as im:
im.draft(None, (64, 64))
self.assertEqual(im.size, (64, 64))
# Test thumbnail(), where only draft() is necessary to resize the image
im = Image.open("Tests/images/hopper.jpg")
im.thumbnail((64, 64))
self.assert_image(im, im.mode, (64, 64))
with Image.open("Tests/images/hopper.jpg") as im:
im.thumbnail((64, 64))
self.assert_image(im, im.mode, (64, 64))

View File

@ -156,21 +156,21 @@ class TestImageTransform(PillowTestCase):
self.test_mesh()
def test_missing_method_data(self):
im = hopper()
self.assertRaises(ValueError, im.transform, (100, 100), None)
with hopper() as im:
self.assertRaises(ValueError, im.transform, (100, 100), None)
def test_unknown_resampling_filter(self):
im = hopper()
(w, h) = im.size
for resample in (Image.BOX, "unknown"):
self.assertRaises(
ValueError,
im.transform,
(100, 100),
Image.EXTENT,
(0, 0, w, h),
resample,
)
with hopper() as im:
(w, h) = im.size
for resample in (Image.BOX, "unknown"):
self.assertRaises(
ValueError,
im.transform,
(100, 100),
Image.EXTENT,
(0, 0, w, h),
resample,
)
class TestImageTransformAffine(PillowTestCase):

View File

@ -58,10 +58,10 @@ class TestImageCms(PillowTestCase):
i = ImageCms.applyTransform(hopper(), t)
self.assert_image(i, "RGB", (128, 128))
i = hopper()
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
ImageCms.applyTransform(hopper(), t, inPlace=True)
self.assert_image(i, "RGB", (128, 128))
with hopper() as i:
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
ImageCms.applyTransform(hopper(), t, inPlace=True)
self.assert_image(i, "RGB", (128, 128))
p = ImageCms.createProfile("sRGB")
o = ImageCms.getOpenProfile(SRGB)
@ -151,8 +151,8 @@ class TestImageCms(PillowTestCase):
def test_extensions(self):
# extensions
i = Image.open("Tests/images/rgb.jpg")
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
with Image.open("Tests/images/rgb.jpg") as i:
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
self.assertEqual(
ImageCms.getProfileName(p).strip(),
"IEC 61966-2.1 Default RGB colour space - sRGB",
@ -166,9 +166,10 @@ class TestImageCms(PillowTestCase):
self.assertRaises(ValueError, t.apply_in_place, hopper("RGBA"))
# the procedural pyCMS API uses PyCMSError for all sorts of errors
self.assertRaises(
ImageCms.PyCMSError, ImageCms.profileToProfile, hopper(), "foo", "bar"
)
with hopper() as im:
self.assertRaises(
ImageCms.PyCMSError, ImageCms.profileToProfile, im, "foo", "bar"
)
self.assertRaises(
ImageCms.PyCMSError, ImageCms.buildTransform, "foo", "bar", "RGB", "RGB"
)
@ -266,8 +267,8 @@ class TestImageCms(PillowTestCase):
self.assert_image_similar(hopper(), out, 2)
def test_profile_tobytes(self):
i = Image.open("Tests/images/rgb.jpg")
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
with Image.open("Tests/images/rgb.jpg") as i:
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
p2 = ImageCms.getOpenProfile(BytesIO(p.tobytes()))

View File

@ -45,10 +45,10 @@ class TestImageDraw(PillowTestCase):
draw.rectangle(list(range(4)))
def test_valueerror(self):
im = Image.open("Tests/images/chi.gif")
with Image.open("Tests/images/chi.gif") as im:
draw = ImageDraw.Draw(im)
draw.line((0, 0), fill=(0, 0, 0))
draw = ImageDraw.Draw(im)
draw.line((0, 0), fill=(0, 0, 0))
def test_mode_mismatch(self):
im = hopper("RGB").copy()

View File

@ -115,13 +115,13 @@ class TestImageFile(PillowTestCase):
if "zip_encoder" not in codecs:
self.skipTest("PNG (zlib) encoder not available")
im = Image.open("Tests/images/truncated_image.png")
with self.assertRaises(IOError):
im.load()
with Image.open("Tests/images/truncated_image.png") as im:
with self.assertRaises(IOError):
im.load()
# Test that the error is raised if loaded a second time
with self.assertRaises(IOError):
im.load()
# Test that the error is raised if loaded a second time
with self.assertRaises(IOError):
im.load()
def test_truncated_without_errors(self):
if "zip_encoder" not in codecs:
@ -258,12 +258,12 @@ class TestPyDecoder(PillowTestCase):
exif[40963] = 455
exif[11] = "Pillow test"
im.save(out, exif=exif)
reloaded = Image.open(out)
reloaded_exif = reloaded.getexif()
self.assertEqual(reloaded_exif[258], 8)
self.assertNotIn(40960, exif)
self.assertEqual(reloaded_exif[40963], 455)
self.assertEqual(exif[11], "Pillow test")
with Image.open(out) as reloaded:
reloaded_exif = reloaded.getexif()
self.assertEqual(reloaded_exif[258], 8)
self.assertNotIn(40960, exif)
self.assertEqual(reloaded_exif[40963], 455)
self.assertEqual(exif[11], "Pillow test")
im = Image.open("Tests/images/no-dpi-in-exif.jpg") # Big endian
exif = im.getexif()
@ -278,12 +278,12 @@ class TestPyDecoder(PillowTestCase):
exif[40963] = 455
exif[305] = "Pillow test"
im.save(out, exif=exif)
reloaded = Image.open(out)
reloaded_exif = reloaded.getexif()
self.assertEqual(reloaded_exif[258], 8)
self.assertNotIn(40960, exif)
self.assertEqual(reloaded_exif[40963], 455)
self.assertEqual(exif[305], "Pillow test")
with Image.open(out) as reloaded:
reloaded_exif = reloaded.getexif()
self.assertEqual(reloaded_exif[258], 8)
self.assertNotIn(40960, exif)
self.assertEqual(reloaded_exif[40963], 455)
self.assertEqual(exif[305], "Pillow test")
@unittest.skipIf(
not HAVE_WEBP or not _webp.HAVE_WEBPANIM,
@ -300,11 +300,11 @@ class TestPyDecoder(PillowTestCase):
exif[305] = "Pillow test"
def check_exif():
reloaded = Image.open(out)
reloaded_exif = reloaded.getexif()
self.assertEqual(reloaded_exif[258], 8)
self.assertEqual(reloaded_exif[40963], 455)
self.assertEqual(exif[305], "Pillow test")
with Image.open(out) as reloaded:
reloaded_exif = reloaded.getexif()
self.assertEqual(reloaded_exif[258], 8)
self.assertEqual(reloaded_exif[40963], 455)
self.assertEqual(exif[305], "Pillow test")
im.save(out, exif=exif)
check_exif()
@ -323,23 +323,13 @@ class TestPyDecoder(PillowTestCase):
exif[305] = "Pillow test"
im.save(out, exif=exif)
reloaded = Image.open(out)
reloaded_exif = reloaded.getexif()
self.assertEqual(reloaded_exif, {258: 8, 40963: 455, 305: "Pillow test"})
with Image.open(out) as reloaded:
reloaded_exif = reloaded.getexif()
self.assertEqual(reloaded_exif, {258: 8, 40963: 455, 305: "Pillow test"})
def test_exif_interop(self):
im = Image.open("Tests/images/flower.jpg")
exif = im.getexif()
self.assertEqual(
exif.get_ifd(0xA005), {1: "R98", 2: b"0100", 4097: 2272, 4098: 1704}
)
def test_exif_shared(self):
im = Image.open("Tests/images/exif.png")
exif = im.getexif()
self.assertIs(im.getexif(), exif)
def test_exif_str(self):
im = Image.open("Tests/images/exif.png")
exif = im.getexif()
self.assertEqual(str(exif), "{274: 1}")
with Image.open("Tests/images/flower.jpg") as im:
exif = im.getexif()
self.assertEqual(
exif.get_ifd(0xA005), {1: "R98", 2: b"0100", 4097: 2272, 4098: 1704}
)

View File

@ -2,57 +2,61 @@ from PIL import Image, ImageFilter
from .helper import PillowTestCase
im = Image.open("Tests/images/hopper.ppm")
snakes = Image.open("Tests/images/color_snakes.png")
class TestImageOpsUsm(PillowTestCase):
def setUp(self):
super(TestImageOpsUsm, self).setUp()
self.im = Image.open("Tests/images/hopper.ppm")
self.addCleanup(self.im.close)
self.snakes = Image.open("Tests/images/color_snakes.png")
self.addCleanup(self.snakes.close)
def test_filter_api(self):
test_filter = ImageFilter.GaussianBlur(2.0)
i = im.filter(test_filter)
i = self.im.filter(test_filter)
self.assertEqual(i.mode, "RGB")
self.assertEqual(i.size, (128, 128))
test_filter = ImageFilter.UnsharpMask(2.0, 125, 8)
i = im.filter(test_filter)
i = self.im.filter(test_filter)
self.assertEqual(i.mode, "RGB")
self.assertEqual(i.size, (128, 128))
def test_usm_formats(self):
usm = ImageFilter.UnsharpMask
self.assertRaises(ValueError, im.convert("1").filter, usm)
im.convert("L").filter(usm)
self.assertRaises(ValueError, im.convert("I").filter, usm)
self.assertRaises(ValueError, im.convert("F").filter, usm)
im.convert("RGB").filter(usm)
im.convert("RGBA").filter(usm)
im.convert("CMYK").filter(usm)
self.assertRaises(ValueError, im.convert("YCbCr").filter, usm)
self.assertRaises(ValueError, self.im.convert("1").filter, usm)
self.im.convert("L").filter(usm)
self.assertRaises(ValueError, self.im.convert("I").filter, usm)
self.assertRaises(ValueError, self.im.convert("F").filter, usm)
self.im.convert("RGB").filter(usm)
self.im.convert("RGBA").filter(usm)
self.im.convert("CMYK").filter(usm)
self.assertRaises(ValueError, self.im.convert("YCbCr").filter, usm)
def test_blur_formats(self):
blur = ImageFilter.GaussianBlur
self.assertRaises(ValueError, im.convert("1").filter, blur)
blur(im.convert("L"))
self.assertRaises(ValueError, im.convert("I").filter, blur)
self.assertRaises(ValueError, im.convert("F").filter, blur)
im.convert("RGB").filter(blur)
im.convert("RGBA").filter(blur)
im.convert("CMYK").filter(blur)
self.assertRaises(ValueError, im.convert("YCbCr").filter, blur)
self.assertRaises(ValueError, self.im.convert("1").filter, blur)
blur(self.im.convert("L"))
self.assertRaises(ValueError, self.im.convert("I").filter, blur)
self.assertRaises(ValueError, self.im.convert("F").filter, blur)
self.im.convert("RGB").filter(blur)
self.im.convert("RGBA").filter(blur)
self.im.convert("CMYK").filter(blur)
self.assertRaises(ValueError, self.im.convert("YCbCr").filter, blur)
def test_usm_accuracy(self):
src = snakes.convert("RGB")
src = self.snakes.convert("RGB")
i = src.filter(ImageFilter.UnsharpMask(5, 1024, 0))
# Image should not be changed because it have only 0 and 255 levels.
self.assertEqual(i.tobytes(), src.tobytes())
def test_blur_accuracy(self):
i = snakes.filter(ImageFilter.GaussianBlur(0.4))
i = self.snakes.filter(ImageFilter.GaussianBlur(0.4))
# These pixels surrounded with pixels with 255 intensity.
# They must be very close to 255.
for x, y, c in [

View File

@ -24,25 +24,25 @@ class TestImageSequence(PillowTestCase):
self.assertRaises(AttributeError, ImageSequence.Iterator, 0)
def test_iterator(self):
im = Image.open("Tests/images/multipage.tiff")
i = ImageSequence.Iterator(im)
for index in range(0, im.n_frames):
self.assertEqual(i[index], next(i))
self.assertRaises(IndexError, lambda: i[index + 1])
self.assertRaises(StopIteration, next, i)
with Image.open("Tests/images/multipage.tiff") as im:
i = ImageSequence.Iterator(im)
for index in range(0, im.n_frames):
self.assertEqual(i[index], next(i))
self.assertRaises(IndexError, lambda: i[index + 1])
self.assertRaises(StopIteration, next, i)
def test_iterator_min_frame(self):
im = Image.open("Tests/images/hopper.psd")
i = ImageSequence.Iterator(im)
for index in range(1, im.n_frames):
self.assertEqual(i[index], next(i))
with Image.open("Tests/images/hopper.psd") as im:
i = ImageSequence.Iterator(im)
for index in range(1, im.n_frames):
self.assertEqual(i[index], next(i))
def _test_multipage_tiff(self):
im = Image.open("Tests/images/multipage.tiff")
for index, frame in enumerate(ImageSequence.Iterator(im)):
frame.load()
self.assertEqual(index, im.tell())
frame.convert("RGB")
with Image.open("Tests/images/multipage.tiff") as im:
for index, frame in enumerate(ImageSequence.Iterator(im)):
frame.load()
self.assertEqual(index, im.tell())
frame.convert("RGB")
def test_tiff(self):
self._test_multipage_tiff()
@ -58,41 +58,41 @@ class TestImageSequence(PillowTestCase):
TiffImagePlugin.READ_LIBTIFF = False
def test_consecutive(self):
im = Image.open("Tests/images/multipage.tiff")
firstFrame = None
for frame in ImageSequence.Iterator(im):
if firstFrame is None:
firstFrame = frame.copy()
for frame in ImageSequence.Iterator(im):
self.assert_image_equal(frame, firstFrame)
break
with Image.open("Tests/images/multipage.tiff") as im:
firstFrame = None
for frame in ImageSequence.Iterator(im):
if firstFrame is None:
firstFrame = frame.copy()
for frame in ImageSequence.Iterator(im):
self.assert_image_equal(frame, firstFrame)
break
def test_palette_mmap(self):
# Using mmap in ImageFile can require to reload the palette.
im = Image.open("Tests/images/multipage-mmap.tiff")
color1 = im.getpalette()[0:3]
im.seek(0)
color2 = im.getpalette()[0:3]
self.assertEqual(color1, color2)
with Image.open("Tests/images/multipage-mmap.tiff") as im:
color1 = im.getpalette()[0:3]
im.seek(0)
color2 = im.getpalette()[0:3]
self.assertEqual(color1, color2)
def test_all_frames(self):
# Test a single image
im = Image.open("Tests/images/iss634.gif")
ims = ImageSequence.all_frames(im)
with Image.open("Tests/images/iss634.gif") as im:
ims = ImageSequence.all_frames(im)
self.assertEqual(len(ims), 42)
for i, im_frame in enumerate(ims):
self.assertFalse(im_frame is im)
self.assertEqual(len(ims), 42)
for i, im_frame in enumerate(ims):
self.assertFalse(im_frame is im)
im.seek(i)
self.assert_image_equal(im, im_frame)
im.seek(i)
self.assert_image_equal(im, im_frame)
# Test a series of images
ims = ImageSequence.all_frames([im, hopper(), im])
self.assertEqual(len(ims), 85)
# Test a series of images
ims = ImageSequence.all_frames([im, hopper(), im])
self.assertEqual(len(ims), 85)
# Test an operation
ims = ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90))
for i, im_frame in enumerate(ims):
im.seek(i)
self.assert_image_equal(im.rotate(90), im_frame)
# Test an operation
ims = ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90))
for i, im_frame in enumerate(ims):
im.seek(i)
self.assert_image_equal(im.rotate(90), im_frame)

View File

@ -27,8 +27,8 @@ class TestImageShow(PillowTestCase):
ImageShow.register(viewer, -1)
for mode in ("1", "I;16", "LA", "RGB", "RGBA"):
im = hopper(mode)
self.assertTrue(ImageShow.show(im))
with hopper() as im:
self.assertTrue(ImageShow.show(im))
self.assertTrue(viewer.methodCalled)
# Restore original state

View File

@ -26,13 +26,15 @@ path = "Tests/images/hopper.jpg"
class TestLocale(PillowTestCase):
def test_sanity(self):
Image.open(path)
with Image.open(path):
pass
try:
locale.setlocale(locale.LC_ALL, "polish")
except locale.Error:
unittest.skip("Polish locale not available")
try:
Image.open(path)
with Image.open(path):
pass
finally:
locale.setlocale(locale.LC_ALL, (None, None))

View File

@ -23,9 +23,9 @@ class TestMap(PillowTestCase):
Image.MAX_IMAGE_PIXELS = None
# This image hits the offset test.
im = Image.open("Tests/images/l2rgb_read.bmp")
with self.assertRaises((ValueError, MemoryError, IOError)):
im.load()
with Image.open("Tests/images/l2rgb_read.bmp") as im:
with self.assertRaises((ValueError, MemoryError, IOError)):
im.load()
Image.MAX_IMAGE_PIXELS = max_pixels

View File

@ -43,10 +43,10 @@ class TestModeI16(PillowTestCase):
filename = self.tempfile("temp.im")
imIn.save(filename)
imOut = Image.open(filename)
with Image.open(filename) as imOut:
self.verify(imIn)
self.verify(imOut)
self.verify(imIn)
self.verify(imOut)
imOut = imIn.crop((0, 0, w, h))
self.verify(imOut)

View File

@ -24,7 +24,8 @@ class TestShellInjection(PillowTestCase):
dest_file = self.tempfile(filename)
save_func(src_img, 0, dest_file)
# If file can't be opened, shell injection probably occurred
Image.open(dest_file).load()
with Image.open(dest_file) as im:
im.load()
@unittest.skipUnless(djpeg_available(), "djpeg not available")
def test_load_djpeg_filename(self):
@ -32,8 +33,8 @@ class TestShellInjection(PillowTestCase):
src_file = self.tempfile(filename)
shutil.copy(TEST_JPG, src_file)
im = Image.open(src_file)
im.load_djpeg()
with Image.open(src_file) as im:
im.load_djpeg()
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
def test_save_cjpeg_filename(self):
@ -42,10 +43,12 @@ class TestShellInjection(PillowTestCase):
@unittest.skipUnless(netpbm_available(), "netpbm not available")
def test_save_netpbm_filename_bmp_mode(self):
im = Image.open(TEST_GIF).convert("RGB")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
with Image.open(TEST_GIF) as im:
im = im.convert("RGB")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
@unittest.skipUnless(netpbm_available(), "netpbm not available")
def test_save_netpbm_filename_l_mode(self):
im = Image.open(TEST_GIF).convert("L")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
with Image.open(TEST_GIF) as im:
im = im.convert("L")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)

View File

@ -54,5 +54,7 @@ class Test_IFDRational(PillowTestCase):
res = IFDRational(301, 1)
im.save(out, dpi=(res, res), compression="raw")
reloaded = Image.open(out)
self.assertEqual(float(IFDRational(301, 1)), float(reloaded.tag_v2[282]))
with Image.open(out) as reloaded:
self.assertEqual(
float(IFDRational(301, 1)), float(reloaded.tag_v2[282])
)

View File

@ -99,9 +99,9 @@ Create JPEG thumbnails
outfile = os.path.splitext(infile)[0] + ".thumbnail"
if infile != outfile:
try:
im = Image.open(infile)
im.thumbnail(size)
im.save(outfile, "JPEG")
with Image.open(infile) as im:
im.thumbnail(size)
im.save(outfile, "JPEG")
except IOError:
print("cannot create thumbnail for", infile)
@ -267,7 +267,8 @@ Converting between modes
::
from PIL import Image
im = Image.open("hopper.ppm").convert("L")
with Image.open("hopper.ppm") as im:
im = im.convert("L")
The library supports transformations between each supported mode and the “L”
and “RGB” modes. To convert between other modes, you may have to use an
@ -383,15 +384,15 @@ Reading sequences
from PIL import Image
im = Image.open("animation.gif")
im.seek(1) # skip to the second frame
with Image.open("animation.gif") as im:
im.seek(1) # skip to the second frame
try:
while 1:
im.seek(im.tell()+1)
# do something to im
except EOFError:
pass # end of sequence
try:
while 1:
im.seek(im.tell()+1)
# do something to im
except EOFError:
pass # end of sequence
As seen in this example, youll get an :py:exc:`EOFError` exception when the
sequence ends.
@ -422,32 +423,34 @@ Drawing Postscript
from PIL import Image
from PIL import PSDraw
im = Image.open("hopper.ppm")
title = "hopper"
box = (1*72, 2*72, 7*72, 10*72) # in points
with Image.open("hopper.ppm") as im:
title = "hopper"
box = (1*72, 2*72, 7*72, 10*72) # in points
ps = PSDraw.PSDraw() # default is sys.stdout
ps.begin_document(title)
ps = PSDraw.PSDraw() # default is sys.stdout
ps.begin_document(title)
# draw the image (75 dpi)
ps.image(box, im, 75)
ps.rectangle(box)
# draw the image (75 dpi)
ps.image(box, im, 75)
ps.rectangle(box)
# draw title
ps.setfont("HelveticaNarrow-Bold", 36)
ps.text((3*72, 4*72), title)
# draw title
ps.setfont("HelveticaNarrow-Bold", 36)
ps.text((3*72, 4*72), title)
ps.end_document()
ps.end_document()
More on reading images
----------------------
As described earlier, the :py:func:`~PIL.Image.open` function of the
:py:mod:`~PIL.Image` module is used to open an image file. In most cases, you
simply pass it the filename as an argument::
simply pass it the filename as an argument. ``Image.open()`` can be used a
context manager::
from PIL import Image
im = Image.open("hopper.ppm")
with Image.open("hopper.ppm") as im:
...
If everything goes well, the result is an :py:class:`PIL.Image.Image` object.
Otherwise, an :exc:`IOError` exception is raised.
@ -513,12 +516,12 @@ This is only available for JPEG and MPO files.
::
from PIL import Image
from __future__ import print_function
im = Image.open(file)
print("original =", im.mode, im.size)
im.draft("L", (100, 100))
print("draft =", im.mode, im.size)
with Image.open(file) as im:
print("original =", im.mode, im.size)
im.draft("L", (100, 100))
print("draft =", im.mode, im.size)
This prints something like::

View File

@ -8,27 +8,25 @@ object, or a file-like object. Pillow uses the filename or ``Path`` to open a
file, so for the rest of this article, they will all be treated as a file-like
object.
The first four of these items are equivalent, the last is dangerous
and may fail::
The following are all equivalent::
from PIL import Image
import io
import pathlib
im = Image.open('test.jpg')
with Image.open('test.jpg') as im:
...
im2 = Image.open(pathlib.Path('test.jpg'))
with Image.open(pathlib.Path('test.jpg')) as im2:
...
f = open('test.jpg', 'rb')
im3 = Image.open(f)
with open('test.jpg', 'rb') as f:
im3 = Image.open(f)
...
with open('test.jpg', 'rb') as f:
im4 = Image.open(io.BytesIO(f.read()))
# Dangerous FAIL:
with open('test.jpg', 'rb') as f:
im5 = Image.open(f)
im5.load() # FAILS, closed file
...
If a filename or a path-like object is passed to Pillow, then the resulting
file object opened by Pillow may also be closed by Pillow after the
@ -38,13 +36,6 @@ have multiple frames.
Pillow cannot in general close and reopen a file, so any access to
that file needs to be prior to the close.
Issues
------
* Using the file context manager to provide a file-like object to
Pillow is dangerous unless the context of the image is limited to
the context of the file.
Image Lifecycle
---------------
@ -70,9 +61,9 @@ Image Lifecycle
... # image operations here.
The lifecycle of a single-frame image is relatively simple. The file
must remain open until the ``load()`` or ``close()`` function is
called.
The lifecycle of a single-frame image is relatively simple. The file must
remain open until the ``load()`` or ``close()`` function is called or the
context manager exits.
Multi-frame images are more complicated. The ``load()`` method is not
a terminal method, so it should not close the underlying file. In general,
@ -87,14 +78,16 @@ Complications
libtiff (if working on an actual file). Since libtiff closes the file
descriptor internally, it is duplicated prior to passing it into libtiff.
* I don't think that there's any way to make this safe without
changing the lazy loading::
* After a file has been closed, operations that require file access will fail::
# Dangerous FAIL:
with open('test.jpg', 'rb') as f:
im5 = Image.open(f)
im5.load() # FAILS, closed file
with Image.open('test.jpg') as im6:
pass
im6.load() # FAILS, closed file
Proposed File Handling
----------------------
@ -104,5 +97,6 @@ Proposed File Handling
* ``Image.Image.seek()`` should never close the image file.
* Users of the library should call ``Image.Image.close()`` on any
multi-frame image to ensure that the underlying file is closed.
* Users of the library should use a context manager or call
``Image.Image.close()`` on any image opened with a filename or ``Path``
object to ensure that the underlying file is closed.

View File

@ -42,8 +42,8 @@ def testimage():
Or open existing files:
>>> im = Image.open("Tests/images/hopper.gif")
>>> _info(im)
>>> with Image.open("Tests/images/hopper.gif") as im:
... _info(im)
('GIF', 'P', (128, 128))
>>> _info(Image.open("Tests/images/hopper.ppm"))
('PPM', 'RGB', (128, 128))

View File

@ -622,11 +622,6 @@ class Image(object):
# object is gone.
self.im = deferred_error(ValueError("Operation on closed image"))
if sys.version_info.major >= 3:
def __del__(self):
self.__exit__()
def _copy(self):
self.load()
self.im = self.im.copy()

View File

@ -103,21 +103,24 @@ class ImageFile(Image.Image):
self._exclusive_fp = None
try:
self._open()
except (
IndexError, # end of data
TypeError, # end of data (ord)
KeyError, # unsupported mode
EOFError, # got header but not the first frame
struct.error,
) as v:
try:
self._open()
except (
IndexError, # end of data
TypeError, # end of data (ord)
KeyError, # unsupported mode
EOFError, # got header but not the first frame
struct.error,
) as v:
raise SyntaxError(v)
if not self.mode or self.size[0] <= 0:
raise SyntaxError("not identified by this driver")
except BaseException:
# close the file only if we have opened it this constructor
if self._exclusive_fp:
self.fp.close()
raise SyntaxError(v)
if not self.mode or self.size[0] <= 0:
raise SyntaxError("not identified by this driver")
raise
def draft(self, mode, size):
"""Set draft mode"""

View File

@ -219,7 +219,8 @@ def loadImageSeries(filelist=None):
print("unable to find %s" % img)
continue
try:
im = Image.open(img).convert2byte()
with Image.open(img) as im:
im = im.convert2byte()
except Exception:
if not isSpiderImage(img):
print(img + " is not a Spider image file")

View File

@ -15,7 +15,6 @@
#
import io
import sys
from . import ContainerIO
@ -64,10 +63,5 @@ class TarIO(ContainerIO.ContainerIO):
def __exit__(self, *args):
self.close()
if sys.version_info.major >= 3:
def __del__(self):
self.close()
def close(self):
self.fh.close()