mirror of
https://github.com/python-pillow/Pillow.git
synced 2024-12-24 17:06:16 +03:00
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:
parent
6595ce1609
commit
4cd4adddc3
|
@ -24,8 +24,8 @@ class TestBmpReference(PillowTestCase):
|
||||||
|
|
||||||
def open(f):
|
def open(f):
|
||||||
try:
|
try:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
except Exception: # as msg:
|
except Exception: # as msg:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -48,8 +48,8 @@ class TestBmpReference(PillowTestCase):
|
||||||
]
|
]
|
||||||
for f in self.get_files("q"):
|
for f in self.get_files("q"):
|
||||||
try:
|
try:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
if os.path.basename(f) not in supported:
|
if os.path.basename(f) not in supported:
|
||||||
print("Please add %s to the partially supported bmp specs." % f)
|
print("Please add %s to the partially supported bmp specs." % f)
|
||||||
except Exception: # as msg:
|
except Exception: # as msg:
|
||||||
|
@ -89,17 +89,17 @@ class TestBmpReference(PillowTestCase):
|
||||||
|
|
||||||
for f in self.get_files("g"):
|
for f in self.get_files("g"):
|
||||||
try:
|
try:
|
||||||
im = Image.open(f)
|
with Image.open(f) as im:
|
||||||
im.load()
|
im.load()
|
||||||
compare = Image.open(get_compare(f))
|
with Image.open(get_compare(f)) as compare:
|
||||||
compare.load()
|
compare.load()
|
||||||
if im.mode == "P":
|
if im.mode == "P":
|
||||||
# assert image similar doesn't really work
|
# assert image similar doesn't really work
|
||||||
# with paletized image, since the palette might
|
# with paletized image, since the palette might
|
||||||
# be differently ordered for an equivalent image.
|
# be differently ordered for an equivalent image.
|
||||||
im = im.convert("RGBA")
|
im = im.convert("RGBA")
|
||||||
compare = im.convert("RGBA")
|
compare = im.convert("RGBA")
|
||||||
self.assert_image_similar(im, compare, 5)
|
self.assert_image_similar(im, compare, 5)
|
||||||
|
|
||||||
except Exception as msg:
|
except Exception as msg:
|
||||||
# there are three here that are unsupported:
|
# there are three here that are unsupported:
|
||||||
|
|
|
@ -14,7 +14,8 @@ class TestDecompressionBomb(PillowTestCase):
|
||||||
def test_no_warning_small_file(self):
|
def test_no_warning_small_file(self):
|
||||||
# Implicit assert: no warning.
|
# Implicit assert: no warning.
|
||||||
# A warning would cause a failure.
|
# A warning would cause a failure.
|
||||||
Image.open(TEST_FILE)
|
with Image.open(TEST_FILE):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_no_warning_no_limit(self):
|
def test_no_warning_no_limit(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -25,21 +26,28 @@ class TestDecompressionBomb(PillowTestCase):
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
# Implicit assert: no warning.
|
# Implicit assert: no warning.
|
||||||
# A warning would cause a failure.
|
# A warning would cause a failure.
|
||||||
Image.open(TEST_FILE)
|
with Image.open(TEST_FILE):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_warning(self):
|
def test_warning(self):
|
||||||
# Set limit to trigger warning on the test file
|
# Set limit to trigger warning on the test file
|
||||||
Image.MAX_IMAGE_PIXELS = 128 * 128 - 1
|
Image.MAX_IMAGE_PIXELS = 128 * 128 - 1
|
||||||
self.assertEqual(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):
|
def test_exception(self):
|
||||||
# Set limit to trigger exception on the test file
|
# Set limit to trigger exception on the test file
|
||||||
Image.MAX_IMAGE_PIXELS = 64 * 128 - 1
|
Image.MAX_IMAGE_PIXELS = 64 * 128 - 1
|
||||||
self.assertEqual(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):
|
def test_exception_ico(self):
|
||||||
with self.assertRaises(Image.DecompressionBombError):
|
with self.assertRaises(Image.DecompressionBombError):
|
||||||
|
@ -53,6 +61,7 @@ class TestDecompressionBomb(PillowTestCase):
|
||||||
class TestDecompressionCrop(PillowTestCase):
|
class TestDecompressionCrop(PillowTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.src = hopper()
|
self.src = hopper()
|
||||||
|
self.addCleanup(self.src.close)
|
||||||
Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width * 4 - 1
|
Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width * 4 - 1
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
|
|
@ -5,14 +5,14 @@ from .helper import PillowTestCase
|
||||||
|
|
||||||
class TestFileBlp(PillowTestCase):
|
class TestFileBlp(PillowTestCase):
|
||||||
def test_load_blp2_raw(self):
|
def test_load_blp2_raw(self):
|
||||||
im = Image.open("Tests/images/blp/blp2_raw.blp")
|
with Image.open("Tests/images/blp/blp2_raw.blp") as im:
|
||||||
target = Image.open("Tests/images/blp/blp2_raw.png")
|
with Image.open("Tests/images/blp/blp2_raw.png") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_load_blp2_dxt1(self):
|
def test_load_blp2_dxt1(self):
|
||||||
im = Image.open("Tests/images/blp/blp2_dxt1.blp")
|
with Image.open("Tests/images/blp/blp2_dxt1.blp") as im:
|
||||||
target = Image.open("Tests/images/blp/blp2_dxt1.png")
|
with Image.open("Tests/images/blp/blp2_dxt1.png") as target:
|
||||||
self.assert_image_equal(im, target)
|
self.assert_image_equal(im, target)
|
||||||
|
|
||||||
def test_load_blp2_dxt1a(self):
|
def test_load_blp2_dxt1a(self):
|
||||||
im = Image.open("Tests/images/blp/blp2_dxt1a.blp")
|
im = Image.open("Tests/images/blp/blp2_dxt1a.blp")
|
||||||
|
|
|
@ -46,13 +46,12 @@ class TestFileBmp(PillowTestCase):
|
||||||
dpi = (72, 72)
|
dpi = (72, 72)
|
||||||
|
|
||||||
output = io.BytesIO()
|
output = io.BytesIO()
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
im.save(output, "BMP", dpi=dpi)
|
im.save(output, "BMP", dpi=dpi)
|
||||||
|
|
||||||
output.seek(0)
|
output.seek(0)
|
||||||
reloaded = Image.open(output)
|
with Image.open(output) as reloaded:
|
||||||
|
self.assertEqual(reloaded.info["dpi"], dpi)
|
||||||
self.assertEqual(reloaded.info["dpi"], dpi)
|
|
||||||
|
|
||||||
def test_save_bmp_with_dpi(self):
|
def test_save_bmp_with_dpi(self):
|
||||||
# Test for #1301
|
# Test for #1301
|
||||||
|
@ -72,24 +71,24 @@ class TestFileBmp(PillowTestCase):
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open("Tests/images/hopper.bmp")
|
with Image.open("Tests/images/hopper.bmp") as im:
|
||||||
self.assertEqual(im.info["dpi"], (96, 96))
|
self.assertEqual(im.info["dpi"], (96, 96))
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/hopper_roundDown.bmp")
|
with Image.open("Tests/images/hopper_roundDown.bmp") as im:
|
||||||
self.assertEqual(im.info["dpi"], (72, 72))
|
self.assertEqual(im.info["dpi"], (72, 72))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
outfile = self.tempfile("temp.bmp")
|
outfile = self.tempfile("temp.bmp")
|
||||||
im = Image.open("Tests/images/hopper.bmp")
|
im = Image.open("Tests/images/hopper.bmp")
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.2, 72.2))
|
im.save(outfile, dpi=(72.2, 72.2))
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.8, 72.8))
|
im.save(outfile, dpi=(72.8, 72.8))
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
||||||
|
|
||||||
def test_load_dib(self):
|
def test_load_dib(self):
|
||||||
# test for #1293, Imagegrab returning Unsupported Bitfields Format
|
# test for #1293, Imagegrab returning Unsupported Bitfields Format
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/gfs.t06z.rassda.tm00.bufr_d"
|
||||||
class TestFileBufrStub(PillowTestCase):
|
class TestFileBufrStub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "BUFR")
|
self.assertEqual(im.format, "BUFR")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,10 +28,10 @@ class TestFileBufrStub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -11,8 +11,8 @@ class TestFileContainer(PillowTestCase):
|
||||||
dir(ContainerIO)
|
dir(ContainerIO)
|
||||||
|
|
||||||
def test_isatty(self):
|
def test_isatty(self):
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
container = ContainerIO.ContainerIO(im, 0, 0)
|
container = ContainerIO.ContainerIO(im, 0, 0)
|
||||||
|
|
||||||
self.assertFalse(container.isatty())
|
self.assertFalse(container.isatty())
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import DcxImagePlugin, Image
|
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
|
# Created with ImageMagick: convert hopper.ppm hopper.dcx
|
||||||
TEST_FILE = "Tests/images/hopper.dcx"
|
TEST_FILE = "Tests/images/hopper.dcx"
|
||||||
|
@ -11,19 +13,35 @@ class TestFileDcx(PillowTestCase):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertIsInstance(im, DcxImagePlugin.DcxImageFile)
|
self.assertIsInstance(im, DcxImagePlugin.DcxImageFile)
|
||||||
orig = hopper()
|
orig = hopper()
|
||||||
self.assert_image_equal(im, orig)
|
self.assert_image_equal(im, orig)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
im.load()
|
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)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
|
@ -32,34 +50,34 @@ class TestFileDcx(PillowTestCase):
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
frame = im.tell()
|
frame = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(frame, 0)
|
self.assertEqual(frame, 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
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)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_seek_too_far(self):
|
def test_seek_too_far(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
frame = 999 # too big on purpose
|
frame = 999 # too big on purpose
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(EOFError, im.seek, frame)
|
self.assertRaises(EOFError, im.seek, frame)
|
||||||
|
|
|
@ -25,30 +25,30 @@ class TestFileEps(PillowTestCase):
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
# Regular scale
|
# Regular scale
|
||||||
image1 = Image.open(file1)
|
with Image.open(file1) as image1:
|
||||||
image1.load()
|
image1.load()
|
||||||
self.assertEqual(image1.mode, "RGB")
|
self.assertEqual(image1.mode, "RGB")
|
||||||
self.assertEqual(image1.size, (460, 352))
|
self.assertEqual(image1.size, (460, 352))
|
||||||
self.assertEqual(image1.format, "EPS")
|
self.assertEqual(image1.format, "EPS")
|
||||||
|
|
||||||
image2 = Image.open(file2)
|
with Image.open(file2) as image2:
|
||||||
image2.load()
|
image2.load()
|
||||||
self.assertEqual(image2.mode, "RGB")
|
self.assertEqual(image2.mode, "RGB")
|
||||||
self.assertEqual(image2.size, (360, 252))
|
self.assertEqual(image2.size, (360, 252))
|
||||||
self.assertEqual(image2.format, "EPS")
|
self.assertEqual(image2.format, "EPS")
|
||||||
|
|
||||||
# Double scale
|
# Double scale
|
||||||
image1_scale2 = Image.open(file1)
|
with Image.open(file1) as image1_scale2:
|
||||||
image1_scale2.load(scale=2)
|
image1_scale2.load(scale=2)
|
||||||
self.assertEqual(image1_scale2.mode, "RGB")
|
self.assertEqual(image1_scale2.mode, "RGB")
|
||||||
self.assertEqual(image1_scale2.size, (920, 704))
|
self.assertEqual(image1_scale2.size, (920, 704))
|
||||||
self.assertEqual(image1_scale2.format, "EPS")
|
self.assertEqual(image1_scale2.format, "EPS")
|
||||||
|
|
||||||
image2_scale2 = Image.open(file2)
|
with Image.open(file2) as image2_scale2:
|
||||||
image2_scale2.load(scale=2)
|
image2_scale2.load(scale=2)
|
||||||
self.assertEqual(image2_scale2.mode, "RGB")
|
self.assertEqual(image2_scale2.mode, "RGB")
|
||||||
self.assertEqual(image2_scale2.size, (720, 504))
|
self.assertEqual(image2_scale2.size, (720, 504))
|
||||||
self.assertEqual(image2_scale2.format, "EPS")
|
self.assertEqual(image2_scale2.format, "EPS")
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -57,43 +57,42 @@ class TestFileEps(PillowTestCase):
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_cmyk(self):
|
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.mode, "CMYK")
|
||||||
self.assertEqual(cmyk_image.size, (100, 100))
|
self.assertEqual(cmyk_image.size, (100, 100))
|
||||||
self.assertEqual(cmyk_image.format, "EPS")
|
self.assertEqual(cmyk_image.format, "EPS")
|
||||||
|
|
||||||
cmyk_image.load()
|
cmyk_image.load()
|
||||||
self.assertEqual(cmyk_image.mode, "RGB")
|
self.assertEqual(cmyk_image.mode, "RGB")
|
||||||
|
|
||||||
if "jpeg_decoder" in dir(Image.core):
|
if "jpeg_decoder" in dir(Image.core):
|
||||||
target = Image.open("Tests/images/pil_sample_rgb.jpg")
|
target = Image.open("Tests/images/pil_sample_rgb.jpg")
|
||||||
self.assert_image_similar(cmyk_image, target, 10)
|
self.assert_image_similar(cmyk_image, target, 10)
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_showpage(self):
|
def test_showpage(self):
|
||||||
# See https://github.com/python-pillow/Pillow/issues/2615
|
# See https://github.com/python-pillow/Pillow/issues/2615
|
||||||
plot_image = Image.open("Tests/images/reqd_showpage.eps")
|
with Image.open("Tests/images/reqd_showpage.eps") as plot_image:
|
||||||
target = Image.open("Tests/images/reqd_showpage.png")
|
with Image.open("Tests/images/reqd_showpage.png") as target:
|
||||||
|
# should not crash/hang
|
||||||
# should not crash/hang
|
plot_image.load()
|
||||||
plot_image.load()
|
# fonts could be slightly different
|
||||||
# fonts could be slightly different
|
self.assert_image_similar(plot_image, target, 6)
|
||||||
self.assert_image_similar(plot_image, target, 6)
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_file_object(self):
|
def test_file_object(self):
|
||||||
# issue 479
|
# issue 479
|
||||||
image1 = Image.open(file1)
|
with Image.open(file1) as image1:
|
||||||
with open(self.tempfile("temp_file.eps"), "wb") as fh:
|
with open(self.tempfile("temp_file.eps"), "wb") as fh:
|
||||||
image1.save(fh, "EPS")
|
image1.save(fh, "EPS")
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_iobase_object(self):
|
def test_iobase_object(self):
|
||||||
# issue 479
|
# issue 479
|
||||||
image1 = Image.open(file1)
|
with Image.open(file1) as image1:
|
||||||
with io.open(self.tempfile("temp_iobase.eps"), "wb") as fh:
|
with io.open(self.tempfile("temp_iobase.eps"), "wb") as fh:
|
||||||
image1.save(fh, "EPS")
|
image1.save(fh, "EPS")
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_bytesio_object(self):
|
def test_bytesio_object(self):
|
||||||
|
@ -120,18 +119,18 @@ class TestFileEps(PillowTestCase):
|
||||||
self.skipTest("zip/deflate support not available")
|
self.skipTest("zip/deflate support not available")
|
||||||
|
|
||||||
# Zero bounding box
|
# Zero bounding box
|
||||||
image1_scale1 = Image.open(file1)
|
with Image.open(file1) as image1_scale1:
|
||||||
image1_scale1.load()
|
image1_scale1.load()
|
||||||
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
|
image1_scale1_compare = Image.open(file1_compare).convert("RGB")
|
||||||
image1_scale1_compare.load()
|
image1_scale1_compare.load()
|
||||||
self.assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
self.assert_image_similar(image1_scale1, image1_scale1_compare, 5)
|
||||||
|
|
||||||
# Non-Zero bounding box
|
# Non-Zero bounding box
|
||||||
image2_scale1 = Image.open(file2)
|
with Image.open(file2) as image2_scale1:
|
||||||
image2_scale1.load()
|
image2_scale1.load()
|
||||||
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
|
image2_scale1_compare = Image.open(file2_compare).convert("RGB")
|
||||||
image2_scale1_compare.load()
|
image2_scale1_compare.load()
|
||||||
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
self.assert_image_similar(image2_scale1, image2_scale1_compare, 10)
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_render_scale2(self):
|
def test_render_scale2(self):
|
||||||
|
@ -141,57 +140,44 @@ class TestFileEps(PillowTestCase):
|
||||||
self.skipTest("zip/deflate support not available")
|
self.skipTest("zip/deflate support not available")
|
||||||
|
|
||||||
# Zero bounding box
|
# Zero bounding box
|
||||||
image1_scale2 = Image.open(file1)
|
with Image.open(file1) as image1_scale2:
|
||||||
image1_scale2.load(scale=2)
|
image1_scale2.load(scale=2)
|
||||||
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
|
image1_scale2_compare = Image.open(file1_compare_scale2).convert("RGB")
|
||||||
image1_scale2_compare.load()
|
image1_scale2_compare.load()
|
||||||
self.assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
self.assert_image_similar(image1_scale2, image1_scale2_compare, 5)
|
||||||
|
|
||||||
# Non-Zero bounding box
|
# Non-Zero bounding box
|
||||||
image2_scale2 = Image.open(file2)
|
with Image.open(file2) as image2_scale2:
|
||||||
image2_scale2.load(scale=2)
|
image2_scale2.load(scale=2)
|
||||||
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
|
image2_scale2_compare = Image.open(file2_compare_scale2).convert("RGB")
|
||||||
image2_scale2_compare.load()
|
image2_scale2_compare.load()
|
||||||
self.assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
self.assert_image_similar(image2_scale2, image2_scale2_compare, 10)
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_resize(self):
|
def test_resize(self):
|
||||||
# Arrange
|
files = [file1, file2, "Tests/images/illu10_preview.eps"]
|
||||||
image1 = Image.open(file1)
|
for fn in files:
|
||||||
image2 = Image.open(file2)
|
with Image.open(fn) as im:
|
||||||
image3 = Image.open("Tests/images/illu10_preview.eps")
|
new_size = (100, 100)
|
||||||
new_size = (100, 100)
|
im = im.resize(new_size)
|
||||||
|
self.assertEqual(im.size, new_size)
|
||||||
# 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)
|
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_thumbnail(self):
|
def test_thumbnail(self):
|
||||||
# Issue #619
|
# Issue #619
|
||||||
# Arrange
|
# Arrange
|
||||||
image1 = Image.open(file1)
|
files = [file1, file2]
|
||||||
image2 = Image.open(file2)
|
for fn in files:
|
||||||
new_size = (100, 100)
|
with Image.open(file1) as im:
|
||||||
|
new_size = (100, 100)
|
||||||
# Act
|
im.thumbnail(new_size)
|
||||||
image1.thumbnail(new_size)
|
self.assertEqual(max(im.size), max(new_size))
|
||||||
image2.thumbnail(new_size)
|
|
||||||
|
|
||||||
# Assert
|
|
||||||
self.assertEqual(max(image1.size), max(new_size))
|
|
||||||
self.assertEqual(max(image2.size), max(new_size))
|
|
||||||
|
|
||||||
def test_read_binary_preview(self):
|
def test_read_binary_preview(self):
|
||||||
# Issue 302
|
# Issue 302
|
||||||
# open image with binary preview
|
# open image with binary preview
|
||||||
Image.open(file3)
|
with Image.open(file3):
|
||||||
|
pass
|
||||||
|
|
||||||
def _test_readline(self, t, ending):
|
def _test_readline(self, t, ending):
|
||||||
ending = "Failure with line ending: %s" % (
|
ending = "Failure with line ending: %s" % (
|
||||||
|
@ -239,16 +225,16 @@ class TestFileEps(PillowTestCase):
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
for filename in FILES:
|
for filename in FILES:
|
||||||
img = Image.open(filename)
|
with Image.open(filename) as img:
|
||||||
self.assertEqual(img.mode, "RGB")
|
self.assertEqual(img.mode, "RGB")
|
||||||
|
|
||||||
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
|
||||||
def test_emptyline(self):
|
def test_emptyline(self):
|
||||||
# Test file includes an empty line in the header data
|
# Test file includes an empty line in the header data
|
||||||
emptyline_file = "Tests/images/zero_bb_emptyline.eps"
|
emptyline_file = "Tests/images/zero_bb_emptyline.eps"
|
||||||
|
|
||||||
image = Image.open(emptyline_file)
|
with Image.open(emptyline_file) as image:
|
||||||
image.load()
|
image.load()
|
||||||
self.assertEqual(image.mode, "RGB")
|
self.assertEqual(image.mode, "RGB")
|
||||||
self.assertEqual(image.size, (460, 352))
|
self.assertEqual(image.size, (460, 352))
|
||||||
self.assertEqual(image.format, "EPS")
|
self.assertEqual(image.format, "EPS")
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/hopper.fits"
|
||||||
class TestFileFitsStub(PillowTestCase):
|
class TestFileFitsStub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "FITS")
|
self.assertEqual(im.format, "FITS")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,19 +28,19 @@ class TestFileFitsStub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
dummy_fp = None
|
dummy_fp = None
|
||||||
dummy_filename = "dummy.filename"
|
dummy_filename = "dummy.filename"
|
||||||
|
|
||||||
# Act / Assert: stub cannot save without an implemented handler
|
# Act / Assert: stub cannot save without an implemented handler
|
||||||
self.assertRaises(IOError, im.save, dummy_filename)
|
self.assertRaises(IOError, im.save, dummy_filename)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename
|
IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import FliImagePlugin, Image
|
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
|
# created as an export of a palette image from Gimp2.6
|
||||||
# save as...-> hopper.fli, default options.
|
# save as...-> hopper.fli, default options.
|
||||||
|
@ -12,36 +14,52 @@ animated_test_file = "Tests/images/a.fli"
|
||||||
|
|
||||||
class TestFileFli(PillowTestCase):
|
class TestFileFli(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(static_test_file)
|
with Image.open(static_test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "FLI")
|
self.assertEqual(im.format, "FLI")
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (320, 200))
|
self.assertEqual(im.size, (320, 200))
|
||||||
self.assertEqual(im.format, "FLI")
|
self.assertEqual(im.format, "FLI")
|
||||||
self.assertEqual(im.info["duration"], 71)
|
self.assertEqual(im.info["duration"], 71)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(static_test_file)
|
im = Image.open(static_test_file)
|
||||||
im.load()
|
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)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(static_test_file)
|
with Image.open(static_test_file) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
frame = im.tell()
|
frame = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(frame, 0)
|
self.assertEqual(frame, 0)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
@ -49,50 +67,50 @@ class TestFileFli(PillowTestCase):
|
||||||
self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file)
|
self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(static_test_file)
|
with Image.open(static_test_file) as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
self.assertEqual(im.n_frames, 384)
|
self.assertEqual(im.n_frames, 384)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_seek_tell(self):
|
def test_seek_tell(self):
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
|
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 0)
|
self.assertEqual(layer_number, 0)
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 0)
|
self.assertEqual(layer_number, 0)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 2)
|
self.assertEqual(layer_number, 2)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
im = Image.open(animated_test_file)
|
with Image.open(animated_test_file) as im:
|
||||||
im.seek(50)
|
im.seek(50)
|
||||||
|
|
||||||
expected = Image.open("Tests/images/a_fli.png")
|
with Image.open("Tests/images/a_fli.png") as expected:
|
||||||
self.assert_image_equal(im, expected)
|
self.assert_image_equal(im, expected)
|
||||||
|
|
|
@ -10,8 +10,6 @@ class TestFileGbr(PillowTestCase):
|
||||||
self.assertRaises(SyntaxError, GbrImagePlugin.GbrImageFile, invalid_file)
|
self.assertRaises(SyntaxError, GbrImagePlugin.GbrImageFile, invalid_file)
|
||||||
|
|
||||||
def test_gbr_file(self):
|
def test_gbr_file(self):
|
||||||
im = Image.open("Tests/images/gbr.gbr")
|
with Image.open("Tests/images/gbr.gbr") as im:
|
||||||
|
with Image.open("Tests/images/gbr.png") as target:
|
||||||
target = Image.open("Tests/images/gbr.png")
|
self.assert_image_equal(target, im)
|
||||||
|
|
||||||
self.assert_image_equal(target, im)
|
|
||||||
|
|
|
@ -7,9 +7,9 @@ TEST_GD_FILE = "Tests/images/hopper.gd"
|
||||||
|
|
||||||
class TestFileGd(PillowTestCase):
|
class TestFileGd(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = GdImageFile.open(TEST_GD_FILE)
|
with GdImageFile.open(TEST_GD_FILE) as im:
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "GD")
|
self.assertEqual(im.format, "GD")
|
||||||
|
|
||||||
def test_bad_mode(self):
|
def test_bad_mode(self):
|
||||||
self.assertRaises(ValueError, GdImageFile.open, TEST_GD_FILE, "bad mode")
|
self.assertRaises(ValueError, GdImageFile.open, TEST_GD_FILE, "bad mode")
|
||||||
|
|
|
@ -2,7 +2,7 @@ from io import BytesIO
|
||||||
|
|
||||||
from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette
|
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:
|
try:
|
||||||
from PIL import _webp
|
from PIL import _webp
|
||||||
|
@ -26,18 +26,34 @@ class TestFileGif(PillowTestCase):
|
||||||
self.skipTest("gif support not available") # can this happen?
|
self.skipTest("gif support not available") # can this happen?
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "GIF")
|
self.assertEqual(im.format, "GIF")
|
||||||
self.assertEqual(im.info["version"], b"GIF89a")
|
self.assertEqual(im.info["version"], b"GIF89a")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_GIF)
|
im = Image.open(TEST_GIF)
|
||||||
im.load()
|
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)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
|
@ -112,67 +128,67 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = hopper()
|
im = hopper()
|
||||||
im.save(out)
|
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):
|
def test_roundtrip2(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/403
|
# see https://github.com/python-pillow/Pillow/issues/403
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
im2 = im.copy()
|
im2 = im.copy()
|
||||||
im2.save(out)
|
im2.save(out)
|
||||||
reread = Image.open(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):
|
def test_roundtrip_save_all(self):
|
||||||
# Single frame image
|
# Single frame image
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = hopper()
|
im = hopper()
|
||||||
im.save(out, save_all=True)
|
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
|
# 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")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, save_all=True)
|
im.save(out, save_all=True)
|
||||||
reread = Image.open(out)
|
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):
|
def test_headers_saving_for_animated_gifs(self):
|
||||||
important_headers = ["background", "version", "duration", "loop"]
|
important_headers = ["background", "version", "duration", "loop"]
|
||||||
# Multiframe image
|
# 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")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, save_all=True)
|
im.save(out, save_all=True)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
|
|
||||||
for header in important_headers:
|
for header in important_headers:
|
||||||
self.assertEqual(info[header], reread.info[header])
|
self.assertEqual(info[header], reread.info[header])
|
||||||
|
|
||||||
def test_palette_handling(self):
|
def test_palette_handling(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/513
|
# see https://github.com/python-pillow/Pillow/issues/513
|
||||||
|
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
im = im.convert("RGB")
|
im = im.convert("RGB")
|
||||||
|
|
||||||
im = im.resize((100, 100), Image.LANCZOS)
|
im = im.resize((100, 100), Image.LANCZOS)
|
||||||
im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256)
|
im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256)
|
||||||
|
|
||||||
f = self.tempfile("temp.gif")
|
f = self.tempfile("temp.gif")
|
||||||
im2.save(f, optimize=True)
|
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):
|
def test_palette_434(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/434
|
# see https://github.com/python-pillow/Pillow/issues/434
|
||||||
|
@ -185,108 +201,115 @@ class TestFileGif(PillowTestCase):
|
||||||
return reloaded
|
return reloaded
|
||||||
|
|
||||||
orig = "Tests/images/test.colors.gif"
|
orig = "Tests/images/test.colors.gif"
|
||||||
im = Image.open(orig)
|
with Image.open(orig) as im:
|
||||||
|
|
||||||
self.assert_image_similar(im, roundtrip(im), 1)
|
with roundtrip(im) as reloaded:
|
||||||
self.assert_image_similar(im, roundtrip(im, optimize=True), 1)
|
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")
|
im = im.convert("RGB")
|
||||||
# check automatic P conversion
|
# check automatic P conversion
|
||||||
reloaded = roundtrip(im).convert("RGB")
|
with roundtrip(im) as reloaded:
|
||||||
self.assert_image_equal(im, reloaded)
|
reloaded = reloaded.convert("RGB")
|
||||||
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
||||||
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
||||||
def test_save_netpbm_bmp_mode(self):
|
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")
|
tempfile = self.tempfile("temp.gif")
|
||||||
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
||||||
self.assert_image_similar(img, Image.open(tempfile).convert("RGB"), 0)
|
with Image.open(tempfile) as reloaded:
|
||||||
|
self.assert_image_similar(img, reloaded.convert("RGB"), 0)
|
||||||
|
|
||||||
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
||||||
def test_save_netpbm_l_mode(self):
|
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")
|
tempfile = self.tempfile("temp.gif")
|
||||||
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
GifImagePlugin._save_netpbm(img, 0, tempfile)
|
||||||
self.assert_image_similar(img, Image.open(tempfile).convert("L"), 0)
|
with Image.open(tempfile) as reloaded:
|
||||||
|
self.assert_image_similar(img, reloaded.convert("L"), 0)
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
img = Image.open("Tests/images/dispose_none.gif")
|
with Image.open("Tests/images/dispose_none.gif") as img:
|
||||||
framecount = 0
|
framecount = 0
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
framecount += 1
|
framecount += 1
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
self.assertEqual(framecount, 5)
|
self.assertEqual(framecount, 5)
|
||||||
|
|
||||||
def test_seek_info(self):
|
def test_seek_info(self):
|
||||||
im = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as im:
|
||||||
info = im.info.copy()
|
info = im.info.copy()
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
|
|
||||||
self.assertEqual(im.info, info)
|
self.assertEqual(im.info, info)
|
||||||
|
|
||||||
def test_seek_rewind(self):
|
def test_seek_rewind(self):
|
||||||
im = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as im:
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
|
|
||||||
expected = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as expected:
|
||||||
expected.seek(1)
|
expected.seek(1)
|
||||||
self.assert_image_equal(im, expected)
|
self.assert_image_equal(im, expected)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
for path, n_frames in [[TEST_GIF, 1], ["Tests/images/iss634.gif", 42]]:
|
for path, n_frames in [[TEST_GIF, 1], ["Tests/images/iss634.gif", 42]]:
|
||||||
# Test is_animated before n_frames
|
# Test is_animated before n_frames
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.is_animated, n_frames != 1)
|
self.assertEqual(im.is_animated, n_frames != 1)
|
||||||
|
|
||||||
# Test is_animated after n_frames
|
# Test is_animated after n_frames
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.n_frames, n_frames)
|
self.assertEqual(im.n_frames, n_frames)
|
||||||
self.assertEqual(im.is_animated, n_frames != 1)
|
self.assertEqual(im.is_animated, n_frames != 1)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_dispose_none(self):
|
def test_dispose_none(self):
|
||||||
img = Image.open("Tests/images/dispose_none.gif")
|
with Image.open("Tests/images/dispose_none.gif") as img:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, 1)
|
self.assertEqual(img.disposal_method, 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_dispose_background(self):
|
def test_dispose_background(self):
|
||||||
img = Image.open("Tests/images/dispose_bgnd.gif")
|
with Image.open("Tests/images/dispose_bgnd.gif") as img:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, 2)
|
self.assertEqual(img.disposal_method, 2)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_dispose_previous(self):
|
def test_dispose_previous(self):
|
||||||
img = Image.open("Tests/images/dispose_prev.gif")
|
with Image.open("Tests/images/dispose_prev.gif") as img:
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, 3)
|
self.assertEqual(img.disposal_method, 3)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_save_dispose(self):
|
def test_save_dispose(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -299,10 +322,10 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], disposal=method
|
out, save_all=True, append_images=im_list[1:], disposal=method
|
||||||
)
|
)
|
||||||
img = Image.open(out)
|
with Image.open(out) as img:
|
||||||
for _ in range(2):
|
for _ in range(2):
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, method)
|
self.assertEqual(img.disposal_method, method)
|
||||||
|
|
||||||
# check per frame disposal
|
# check per frame disposal
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
|
@ -312,11 +335,11 @@ class TestFileGif(PillowTestCase):
|
||||||
disposal=tuple(range(len(im_list))),
|
disposal=tuple(range(len(im_list))),
|
||||||
)
|
)
|
||||||
|
|
||||||
img = Image.open(out)
|
with Image.open(out) as img:
|
||||||
|
|
||||||
for i in range(2):
|
for i in range(2):
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
self.assertEqual(img.disposal_method, i + 1)
|
self.assertEqual(img.disposal_method, i + 1)
|
||||||
|
|
||||||
def test_dispose2_palette(self):
|
def test_dispose2_palette(self):
|
||||||
out = self.tempfile("temp.gif")
|
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)
|
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):
|
# Check top left pixel matches background
|
||||||
img.seek(i)
|
self.assertEqual(rgb_img.getpixel((0, 0)), (255, 0, 0))
|
||||||
rgb_img = img.convert("RGB")
|
|
||||||
|
|
||||||
# Check top left pixel matches background
|
# Center remains red every frame
|
||||||
self.assertEqual(rgb_img.getpixel((0, 0)), (255, 0, 0))
|
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):
|
def test_dispose2_diff(self):
|
||||||
out = self.tempfile("temp.gif")
|
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
|
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):
|
# Check left circle is correct colour
|
||||||
img.seek(i)
|
self.assertEqual(rgb_img.getpixel((20, 50)), colours[0])
|
||||||
rgb_img = img.convert("RGBA")
|
|
||||||
|
|
||||||
# Check left circle is correct colour
|
# Check right circle is correct colour
|
||||||
self.assertEqual(rgb_img.getpixel((20, 50)), colours[0])
|
self.assertEqual(rgb_img.getpixel((80, 50)), colours[1])
|
||||||
|
|
||||||
# Check right circle is correct colour
|
# Check BG is correct colour
|
||||||
self.assertEqual(rgb_img.getpixel((80, 50)), colours[1])
|
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):
|
def test_dispose2_background(self):
|
||||||
out = self.tempfile("temp.gif")
|
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
|
out, save_all=True, append_images=im_list[1:], disposal=[0, 2], background=1
|
||||||
)
|
)
|
||||||
|
|
||||||
im = Image.open(out)
|
with Image.open(out) as im:
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.getpixel((0, 0)), 0)
|
self.assertEqual(im.getpixel((0, 0)), 0)
|
||||||
|
|
||||||
def test_iss634(self):
|
def test_iss634(self):
|
||||||
img = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as img:
|
||||||
# seek to the second frame
|
# seek to the second frame
|
||||||
img.seek(img.tell() + 1)
|
img.seek(img.tell() + 1)
|
||||||
# all transparent pixels should be replaced with the color from the
|
# all transparent pixels should be replaced with the color from the
|
||||||
# first frame
|
# first frame
|
||||||
self.assertEqual(img.histogram()[img.info["transparency"]], 0)
|
self.assertEqual(img.histogram()[img.info["transparency"]], 0)
|
||||||
|
|
||||||
def test_duration(self):
|
def test_duration(self):
|
||||||
duration = 1000
|
duration = 1000
|
||||||
|
@ -433,8 +454,8 @@ class TestFileGif(PillowTestCase):
|
||||||
im.info["duration"] = 100
|
im.info["duration"] = 100
|
||||||
im.save(out, duration=duration)
|
im.save(out, duration=duration)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.info["duration"], duration)
|
self.assertEqual(reread.info["duration"], duration)
|
||||||
|
|
||||||
def test_multiple_duration(self):
|
def test_multiple_duration(self):
|
||||||
duration_list = [1000, 2000, 3000]
|
duration_list = [1000, 2000, 3000]
|
||||||
|
@ -450,27 +471,27 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
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:
|
for duration in duration_list:
|
||||||
self.assertEqual(reread.info["duration"], duration)
|
self.assertEqual(reread.info["duration"], duration)
|
||||||
try:
|
try:
|
||||||
reread.seek(reread.tell() + 1)
|
reread.seek(reread.tell() + 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# duration as tuple
|
# duration as tuple
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=tuple(duration_list)
|
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:
|
for duration in duration_list:
|
||||||
self.assertEqual(reread.info["duration"], duration)
|
self.assertEqual(reread.info["duration"], duration)
|
||||||
try:
|
try:
|
||||||
reread.seek(reread.tell() + 1)
|
reread.seek(reread.tell() + 1)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def test_identical_frames(self):
|
def test_identical_frames(self):
|
||||||
duration_list = [1000, 1500, 2000, 4000]
|
duration_list = [1000, 1500, 2000, 4000]
|
||||||
|
@ -487,13 +508,13 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=duration_list
|
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
|
# Assert that the first three frames were combined
|
||||||
self.assertEqual(reread.n_frames, 2)
|
self.assertEqual(reread.n_frames, 2)
|
||||||
|
|
||||||
# Assert that the new duration is the total of the identical frames
|
# Assert that the new duration is the total of the identical frames
|
||||||
self.assertEqual(reread.info["duration"], 4500)
|
self.assertEqual(reread.info["duration"], 4500)
|
||||||
|
|
||||||
def test_identical_frames_to_single_frame(self):
|
def test_identical_frames_to_single_frame(self):
|
||||||
for duration in ([1000, 1500, 2000, 4000], (1000, 1500, 2000, 4000), 8500):
|
for duration in ([1000, 1500, 2000, 4000], (1000, 1500, 2000, 4000), 8500):
|
||||||
|
@ -507,13 +528,12 @@ class TestFileGif(PillowTestCase):
|
||||||
im_list[0].save(
|
im_list[0].save(
|
||||||
out, save_all=True, append_images=im_list[1:], duration=duration
|
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
|
# Assert that the new duration is the total of the identical frames
|
||||||
self.assertEqual(reread.n_frames, 1)
|
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):
|
def test_number_of_loops(self):
|
||||||
number_of_loops = 2
|
number_of_loops = 2
|
||||||
|
@ -521,18 +541,18 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
im.save(out, loop=number_of_loops)
|
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):
|
def test_background(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
im.info["background"] = 1
|
im.info["background"] = 1
|
||||||
im.save(out)
|
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:
|
if HAVE_WEBP and _webp.HAVE_WEBPANIM:
|
||||||
im = Image.open("Tests/images/hopper.webp")
|
im = Image.open("Tests/images/hopper.webp")
|
||||||
|
@ -540,16 +560,18 @@ class TestFileGif(PillowTestCase):
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
||||||
def test_comment(self):
|
def test_comment(self):
|
||||||
im = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as im:
|
||||||
self.assertEqual(im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0")
|
self.assertEqual(
|
||||||
|
im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0"
|
||||||
|
)
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
im.info["comment"] = b"Test comment text"
|
im.info["comment"] = b"Test comment text"
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(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):
|
def test_comment_over_255(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -559,22 +581,22 @@ class TestFileGif(PillowTestCase):
|
||||||
comment += comment
|
comment += comment
|
||||||
im.info["comment"] = comment
|
im.info["comment"] = comment
|
||||||
im.save(out)
|
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):
|
def test_zero_comment_subblocks(self):
|
||||||
im = Image.open("Tests/images/hopper_zero_comment_subblocks.gif")
|
with Image.open("Tests/images/hopper_zero_comment_subblocks.gif") as im:
|
||||||
expected = Image.open(TEST_GIF)
|
with Image.open(TEST_GIF) as expected:
|
||||||
self.assert_image_equal(im, expected)
|
self.assert_image_equal(im, expected)
|
||||||
|
|
||||||
def test_version(self):
|
def test_version(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
|
||||||
def assertVersionAfterSave(im, version):
|
def assertVersionAfterSave(im, version):
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.info["version"], version)
|
self.assertEqual(reread.info["version"], version)
|
||||||
|
|
||||||
# Test that GIF87a is used by default
|
# Test that GIF87a is used by default
|
||||||
im = Image.new("L", (100, 100), "#000")
|
im = Image.new("L", (100, 100), "#000")
|
||||||
|
@ -590,12 +612,12 @@ class TestFileGif(PillowTestCase):
|
||||||
assertVersionAfterSave(im, b"GIF89a")
|
assertVersionAfterSave(im, b"GIF89a")
|
||||||
|
|
||||||
# Test that a GIF87a image is also saved in that format
|
# Test that a GIF87a image is also saved in that format
|
||||||
im = Image.open("Tests/images/test.colors.gif")
|
with Image.open("Tests/images/test.colors.gif") as im:
|
||||||
assertVersionAfterSave(im, b"GIF87a")
|
assertVersionAfterSave(im, b"GIF87a")
|
||||||
|
|
||||||
# Test that a GIF89a image is also saved in that format
|
# Test that a GIF89a image is also saved in that format
|
||||||
im.info["version"] = b"GIF89a"
|
im.info["version"] = b"GIF89a"
|
||||||
assertVersionAfterSave(im, b"GIF87a")
|
assertVersionAfterSave(im, b"GIF87a")
|
||||||
|
|
||||||
def test_append_images(self):
|
def test_append_images(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -605,8 +627,8 @@ class TestFileGif(PillowTestCase):
|
||||||
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
|
ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
|
||||||
im.copy().save(out, save_all=True, append_images=ims)
|
im.copy().save(out, save_all=True, append_images=ims)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 3)
|
self.assertEqual(reread.n_frames, 3)
|
||||||
|
|
||||||
# Tests appending using a generator
|
# Tests appending using a generator
|
||||||
def imGenerator(ims):
|
def imGenerator(ims):
|
||||||
|
@ -615,16 +637,16 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
im.save(out, save_all=True, append_images=imGenerator(ims))
|
im.save(out, save_all=True, append_images=imGenerator(ims))
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 3)
|
self.assertEqual(reread.n_frames, 3)
|
||||||
|
|
||||||
# Tests appending single and multiple frame images
|
# Tests appending single and multiple frame images
|
||||||
im = Image.open("Tests/images/dispose_none.gif")
|
with Image.open("Tests/images/dispose_none.gif") as im:
|
||||||
ims = [Image.open("Tests/images/dispose_prev.gif")]
|
with Image.open("Tests/images/dispose_prev.gif") as im2:
|
||||||
im.save(out, save_all=True, append_images=ims)
|
im.save(out, save_all=True, append_images=[im2])
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 10)
|
self.assertEqual(reread.n_frames, 10)
|
||||||
|
|
||||||
def test_transparent_optimize(self):
|
def test_transparent_optimize(self):
|
||||||
# from issue #2195, if the transparent color is incorrectly
|
# from issue #2195, if the transparent color is incorrectly
|
||||||
|
@ -642,9 +664,9 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, transparency=253)
|
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):
|
def test_rgb_transparency(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -654,8 +676,8 @@ class TestFileGif(PillowTestCase):
|
||||||
im.info["transparency"] = (255, 0, 0)
|
im.info["transparency"] = (255, 0, 0)
|
||||||
self.assert_warning(UserWarning, im.save, out)
|
self.assert_warning(UserWarning, im.save, out)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertNotIn("transparency", reloaded.info)
|
self.assertNotIn("transparency", reloaded.info)
|
||||||
|
|
||||||
# Multiple frames
|
# Multiple frames
|
||||||
im = Image.new("RGB", (1, 1))
|
im = Image.new("RGB", (1, 1))
|
||||||
|
@ -663,8 +685,8 @@ class TestFileGif(PillowTestCase):
|
||||||
ims = [Image.new("RGB", (1, 1))]
|
ims = [Image.new("RGB", (1, 1))]
|
||||||
self.assert_warning(UserWarning, im.save, out, save_all=True, append_images=ims)
|
self.assert_warning(UserWarning, im.save, out, save_all=True, append_images=ims)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertNotIn("transparency", reloaded.info)
|
self.assertNotIn("transparency", reloaded.info)
|
||||||
|
|
||||||
def test_bbox(self):
|
def test_bbox(self):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
|
@ -673,8 +695,8 @@ class TestFileGif(PillowTestCase):
|
||||||
ims = [Image.new("RGB", (100, 100), "#000")]
|
ims = [Image.new("RGB", (100, 100), "#000")]
|
||||||
im.save(out, save_all=True, append_images=ims)
|
im.save(out, save_all=True, append_images=ims)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual(reread.n_frames, 2)
|
self.assertEqual(reread.n_frames, 2)
|
||||||
|
|
||||||
def test_palette_save_L(self):
|
def test_palette_save_L(self):
|
||||||
# generate an L mode image with a separate palette
|
# generate an L mode image with a separate palette
|
||||||
|
@ -686,9 +708,9 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im_l.save(out, palette=palette)
|
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):
|
def test_palette_save_P(self):
|
||||||
# pass in a different palette, then construct what the image
|
# pass in a different palette, then construct what the image
|
||||||
|
@ -701,9 +723,9 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, palette=palette)
|
im.save(out, palette=palette)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
im.putpalette(palette)
|
im.putpalette(palette)
|
||||||
self.assert_image_equal(reloaded, im)
|
self.assert_image_equal(reloaded, im)
|
||||||
|
|
||||||
def test_palette_save_ImagePalette(self):
|
def test_palette_save_ImagePalette(self):
|
||||||
# pass in a different palette, as an ImagePalette.ImagePalette
|
# pass in a different palette, as an ImagePalette.ImagePalette
|
||||||
|
@ -715,9 +737,9 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out, palette=palette)
|
im.save(out, palette=palette)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
im.putpalette(palette)
|
im.putpalette(palette)
|
||||||
self.assert_image_equal(reloaded, im)
|
self.assert_image_equal(reloaded, im)
|
||||||
|
|
||||||
def test_save_I(self):
|
def test_save_I(self):
|
||||||
# Test saving something that would trigger the auto-convert to 'L'
|
# Test saving something that would trigger the auto-convert to 'L'
|
||||||
|
@ -727,8 +749,8 @@ class TestFileGif(PillowTestCase):
|
||||||
out = self.tempfile("temp.gif")
|
out = self.tempfile("temp.gif")
|
||||||
im.save(out)
|
im.save(out)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assert_image_equal(reloaded.convert("L"), im.convert("L"))
|
self.assert_image_equal(reloaded.convert("L"), im.convert("L"))
|
||||||
|
|
||||||
def test_getdata(self):
|
def test_getdata(self):
|
||||||
# test getheader/getdata against legacy values
|
# test getheader/getdata against legacy values
|
||||||
|
@ -759,14 +781,13 @@ class TestFileGif(PillowTestCase):
|
||||||
|
|
||||||
def test_lzw_bits(self):
|
def test_lzw_bits(self):
|
||||||
# see https://github.com/python-pillow/Pillow/issues/2811
|
# see https://github.com/python-pillow/Pillow/issues/2811
|
||||||
im = Image.open("Tests/images/issue_2811.gif")
|
with Image.open("Tests/images/issue_2811.gif") as im:
|
||||||
|
self.assertEqual(im.tile[0][3][0], 11) # LZW bits
|
||||||
self.assertEqual(im.tile[0][3][0], 11) # LZW bits
|
# codec error prepatch
|
||||||
# codec error prepatch
|
im.load()
|
||||||
im.load()
|
|
||||||
|
|
||||||
def test_extents(self):
|
def test_extents(self):
|
||||||
im = Image.open("Tests/images/test_extents.gif")
|
with Image.open("Tests/images/test_extents.gif") as im:
|
||||||
self.assertEqual(im.size, (100, 100))
|
self.assertEqual(im.size, (100, 100))
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.size, (150, 150))
|
self.assertEqual(im.size, (150, 150))
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/WAlaska.wind.7days.grb"
|
||||||
class TestFileGribStub(PillowTestCase):
|
class TestFileGribStub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "GRIB")
|
self.assertEqual(im.format, "GRIB")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,10 +28,10 @@ class TestFileGribStub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
|
|
@ -8,14 +8,14 @@ TEST_FILE = "Tests/images/hdf5.h5"
|
||||||
class TestFileHdf5Stub(PillowTestCase):
|
class TestFileHdf5Stub(PillowTestCase):
|
||||||
def test_open(self):
|
def test_open(self):
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.format, "HDF5")
|
self.assertEqual(im.format, "HDF5")
|
||||||
|
|
||||||
# Dummy data from the stub
|
# Dummy data from the stub
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (1, 1))
|
self.assertEqual(im.size, (1, 1))
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -28,19 +28,19 @@ class TestFileHdf5Stub(PillowTestCase):
|
||||||
|
|
||||||
def test_load(self):
|
def test_load(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act / Assert: stub cannot load without an implemented handler
|
# Act / Assert: stub cannot load without an implemented handler
|
||||||
self.assertRaises(IOError, im.load)
|
self.assertRaises(IOError, im.load)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
dummy_fp = None
|
dummy_fp = None
|
||||||
dummy_filename = "dummy.filename"
|
dummy_filename = "dummy.filename"
|
||||||
|
|
||||||
# Act / Assert: stub cannot save without an implemented handler
|
# Act / Assert: stub cannot save without an implemented handler
|
||||||
self.assertRaises(IOError, im.save, dummy_filename)
|
self.assertRaises(IOError, im.save, dummy_filename)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
IOError, Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename
|
IOError, Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,14 +15,14 @@ class TestFileIcns(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
# Loading this icon by default should result in the largest size
|
# Loading this icon by default should result in the largest size
|
||||||
# (512x512@2x) being loaded
|
# (512x512@2x) being loaded
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Assert that there is no unclosed file warning
|
# Assert that there is no unclosed file warning
|
||||||
self.assert_warning(None, im.load)
|
self.assert_warning(None, im.load)
|
||||||
|
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (1024, 1024))
|
self.assertEqual(im.size, (1024, 1024))
|
||||||
self.assertEqual(im.format, "ICNS")
|
self.assertEqual(im.format, "ICNS")
|
||||||
|
|
||||||
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
|
@unittest.skipIf(sys.platform != "darwin", "requires macOS")
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
|
@ -56,31 +56,31 @@ class TestFileIcns(PillowTestCase):
|
||||||
def test_sizes(self):
|
def test_sizes(self):
|
||||||
# Check that we can load all of the sizes, and that the final pixel
|
# Check that we can load all of the sizes, and that the final pixel
|
||||||
# dimensions are as expected
|
# dimensions are as expected
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
for w, h, r in im.info["sizes"]:
|
for w, h, r in im.info["sizes"]:
|
||||||
wr = w * r
|
wr = w * r
|
||||||
hr = h * r
|
hr = h * r
|
||||||
im.size = (w, h, r)
|
im.size = (w, h, r)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (wr, hr))
|
self.assertEqual(im.size, (wr, hr))
|
||||||
|
|
||||||
# Check that we cannot load an incorrect size
|
# Check that we cannot load an incorrect size
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
im.size = (1, 1)
|
im.size = (1, 1)
|
||||||
|
|
||||||
def test_older_icon(self):
|
def test_older_icon(self):
|
||||||
# This icon was made with Icon Composer rather than iconutil; it still
|
# 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).
|
# uses PNG rather than JP2, however (since it was made on 10.9).
|
||||||
im = Image.open("Tests/images/pillow2.icns")
|
with Image.open("Tests/images/pillow2.icns") as im:
|
||||||
for w, h, r in im.info["sizes"]:
|
for w, h, r in im.info["sizes"]:
|
||||||
wr = w * r
|
wr = w * r
|
||||||
hr = h * r
|
hr = h * r
|
||||||
im2 = Image.open("Tests/images/pillow2.icns")
|
with Image.open("Tests/images/pillow2.icns") as im2:
|
||||||
im2.size = (w, h, r)
|
im2.size = (w, h, r)
|
||||||
im2.load()
|
im2.load()
|
||||||
self.assertEqual(im2.mode, "RGBA")
|
self.assertEqual(im2.mode, "RGBA")
|
||||||
self.assertEqual(im2.size, (wr, hr))
|
self.assertEqual(im2.size, (wr, hr))
|
||||||
|
|
||||||
def test_jp2_icon(self):
|
def test_jp2_icon(self):
|
||||||
# This icon was made by using Uli Kusterer's oldiconutil to replace
|
# This icon was made by using Uli Kusterer's oldiconutil to replace
|
||||||
|
@ -93,15 +93,15 @@ class TestFileIcns(PillowTestCase):
|
||||||
if not enable_jpeg2k:
|
if not enable_jpeg2k:
|
||||||
return
|
return
|
||||||
|
|
||||||
im = Image.open("Tests/images/pillow3.icns")
|
with Image.open("Tests/images/pillow3.icns") as im:
|
||||||
for w, h, r in im.info["sizes"]:
|
for w, h, r in im.info["sizes"]:
|
||||||
wr = w * r
|
wr = w * r
|
||||||
hr = h * r
|
hr = h * r
|
||||||
im2 = Image.open("Tests/images/pillow3.icns")
|
with Image.open("Tests/images/pillow3.icns") as im2:
|
||||||
im2.size = (w, h, r)
|
im2.size = (w, h, r)
|
||||||
im2.load()
|
im2.load()
|
||||||
self.assertEqual(im2.mode, "RGBA")
|
self.assertEqual(im2.mode, "RGBA")
|
||||||
self.assertEqual(im2.size, (wr, hr))
|
self.assertEqual(im2.size, (wr, hr))
|
||||||
|
|
||||||
def test_getimage(self):
|
def test_getimage(self):
|
||||||
with open(TEST_FILE, "rb") as fp:
|
with open(TEST_FILE, "rb") as fp:
|
||||||
|
|
|
@ -9,8 +9,8 @@ TEST_ICO_FILE = "Tests/images/hopper.ico"
|
||||||
|
|
||||||
class TestFileIco(PillowTestCase):
|
class TestFileIco(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_ICO_FILE)
|
with Image.open(TEST_ICO_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (16, 16))
|
self.assertEqual(im.size, (16, 16))
|
||||||
self.assertEqual(im.format, "ICO")
|
self.assertEqual(im.format, "ICO")
|
||||||
|
@ -46,22 +46,22 @@ class TestFileIco(PillowTestCase):
|
||||||
self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS))
|
self.assert_image_equal(reloaded, hopper().resize((32, 32), Image.LANCZOS))
|
||||||
|
|
||||||
def test_incorrect_size(self):
|
def test_incorrect_size(self):
|
||||||
im = Image.open(TEST_ICO_FILE)
|
with Image.open(TEST_ICO_FILE) as im:
|
||||||
with self.assertRaises(ValueError):
|
with self.assertRaises(ValueError):
|
||||||
im.size = (1, 1)
|
im.size = (1, 1)
|
||||||
|
|
||||||
def test_save_256x256(self):
|
def test_save_256x256(self):
|
||||||
"""Issue #2264 https://github.com/python-pillow/Pillow/issues/2264"""
|
"""Issue #2264 https://github.com/python-pillow/Pillow/issues/2264"""
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/hopper_256x256.ico")
|
with Image.open("Tests/images/hopper_256x256.ico") as im:
|
||||||
outfile = self.tempfile("temp_saved_hopper_256x256.ico")
|
outfile = self.tempfile("temp_saved_hopper_256x256.ico")
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
im_saved = Image.open(outfile)
|
with Image.open(outfile) as im_saved:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im_saved.size, (256, 256))
|
self.assertEqual(im_saved.size, (256, 256))
|
||||||
|
|
||||||
def test_only_save_relevant_sizes(self):
|
def test_only_save_relevant_sizes(self):
|
||||||
"""Issue #2266 https://github.com/python-pillow/Pillow/issues/2266
|
"""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
|
and not in 16x16, 24x24, 32x32, 48x48, 48x48, 48x48, 48x48 sizes
|
||||||
"""
|
"""
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/python.ico") # 16x16, 32x32, 48x48
|
with Image.open("Tests/images/python.ico") as im: # 16x16, 32x32, 48x48
|
||||||
outfile = self.tempfile("temp_saved_python.ico")
|
outfile = self.tempfile("temp_saved_python.ico")
|
||||||
|
# Act
|
||||||
|
im.save(outfile)
|
||||||
|
|
||||||
# Act
|
with Image.open(outfile) as im_saved:
|
||||||
im.save(outfile)
|
# Assert
|
||||||
im_saved = Image.open(outfile)
|
self.assertEqual(
|
||||||
|
im_saved.info["sizes"], {(16, 16), (24, 24), (32, 32), (48, 48)}
|
||||||
# Assert
|
)
|
||||||
self.assertEqual(
|
|
||||||
im_saved.info["sizes"], {(16, 16), (24, 24), (32, 32), (48, 48)}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_unexpected_size(self):
|
def test_unexpected_size(self):
|
||||||
# This image has been manually hexedited to state that it is 16x32
|
# This image has been manually hexedited to state that it is 16x32
|
||||||
# while the image within is still 16x16
|
# while the image within is still 16x16
|
||||||
im = self.assert_warning(
|
def open():
|
||||||
UserWarning, Image.open, "Tests/images/hopper_unexpected.ico"
|
with Image.open("Tests/images/hopper_unexpected.ico") as im:
|
||||||
)
|
self.assertEqual(im.size, (16, 16))
|
||||||
self.assertEqual(im.size, (16, 16))
|
|
||||||
|
self.assert_warning(UserWarning, open)
|
||||||
|
|
||||||
def test_draw_reloaded(self):
|
def test_draw_reloaded(self):
|
||||||
im = Image.open(TEST_ICO_FILE)
|
with Image.open(TEST_ICO_FILE) as im:
|
||||||
outfile = self.tempfile("temp_saved_hopper_draw.ico")
|
outfile = self.tempfile("temp_saved_hopper_draw.ico")
|
||||||
|
|
||||||
draw = ImageDraw.Draw(im)
|
draw = ImageDraw.Draw(im)
|
||||||
draw.line((0, 0) + im.size, "#f00")
|
draw.line((0, 0) + im.size, "#f00")
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
im = Image.open(outfile)
|
with Image.open(outfile) as im:
|
||||||
im.save("Tests/images/hopper_draw.ico")
|
im.save("Tests/images/hopper_draw.ico")
|
||||||
reloaded = Image.open("Tests/images/hopper_draw.ico")
|
with Image.open("Tests/images/hopper_draw.ico") as reloaded:
|
||||||
self.assert_image_equal(im, reloaded)
|
self.assert_image_equal(im, reloaded)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, ImImagePlugin
|
from PIL import Image, ImImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
# sample im
|
# sample im
|
||||||
TEST_IM = "Tests/images/hopper.im"
|
TEST_IM = "Tests/images/hopper.im"
|
||||||
|
@ -8,53 +10,69 @@ TEST_IM = "Tests/images/hopper.im"
|
||||||
|
|
||||||
class TestFileIm(PillowTestCase):
|
class TestFileIm(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "IM")
|
self.assertEqual(im.format, "IM")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_IM)
|
im = Image.open(TEST_IM)
|
||||||
im.load()
|
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)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
frame = im.tell()
|
frame = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(frame, 0)
|
self.assertEqual(frame, 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(TEST_IM)
|
with Image.open(TEST_IM) as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_roundtrip(self):
|
def test_roundtrip(self):
|
||||||
for mode in ["RGB", "P", "PA"]:
|
for mode in ["RGB", "P", "PA"]:
|
||||||
out = self.tempfile("temp.im")
|
out = self.tempfile("temp.im")
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
im.save(out)
|
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):
|
def test_save_unsupported_mode(self):
|
||||||
out = self.tempfile("temp.im")
|
out = self.tempfile("temp.im")
|
||||||
|
|
|
@ -8,20 +8,20 @@ TEST_FILE = "Tests/images/iptc.jpg"
|
||||||
class TestFileIptc(PillowTestCase):
|
class TestFileIptc(PillowTestCase):
|
||||||
def test_getiptcinfo_jpg_none(self):
|
def test_getiptcinfo_jpg_none(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsNone(iptc)
|
self.assertIsNone(iptc)
|
||||||
|
|
||||||
def test_getiptcinfo_jpg_found(self):
|
def test_getiptcinfo_jpg_found(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsInstance(iptc, dict)
|
self.assertIsInstance(iptc, dict)
|
||||||
|
@ -30,10 +30,10 @@ class TestFileIptc(PillowTestCase):
|
||||||
|
|
||||||
def test_getiptcinfo_tiff_none(self):
|
def test_getiptcinfo_tiff_none(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/hopper.tif")
|
with Image.open("Tests/images/hopper.tif") as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
iptc = IptcImagePlugin.getiptcinfo(im)
|
iptc = IptcImagePlugin.getiptcinfo(im)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsNone(iptc)
|
self.assertIsNone(iptc)
|
||||||
|
|
|
@ -53,14 +53,14 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
def test_app(self):
|
def test_app(self):
|
||||||
# Test APP/COM reader (@PIL135)
|
# Test APP/COM reader (@PIL135)
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.applist[0], ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00")
|
im.applist[0], ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00")
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.applist[1], ("COM", b"File written by Adobe Photoshop\xa8 4.0\x00")
|
im.applist[1], ("COM", b"File written by Adobe Photoshop\xa8 4.0\x00")
|
||||||
)
|
)
|
||||||
self.assertEqual(len(im.applist), 2)
|
self.assertEqual(len(im.applist), 2)
|
||||||
|
|
||||||
def test_cmyk(self):
|
def test_cmyk(self):
|
||||||
# Test CMYK handling. Thanks to Tim and Charlie for test data,
|
# Test CMYK handling. Thanks to Tim and Charlie for test data,
|
||||||
|
@ -99,20 +99,20 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
def test_icc(self):
|
def test_icc(self):
|
||||||
# Test ICC support
|
# Test ICC support
|
||||||
im1 = Image.open("Tests/images/rgb.jpg")
|
with Image.open("Tests/images/rgb.jpg") as im1:
|
||||||
icc_profile = im1.info["icc_profile"]
|
icc_profile = im1.info["icc_profile"]
|
||||||
self.assertEqual(len(icc_profile), 3144)
|
self.assertEqual(len(icc_profile), 3144)
|
||||||
# Roundtrip via physical file.
|
# Roundtrip via physical file.
|
||||||
f = self.tempfile("temp.jpg")
|
f = self.tempfile("temp.jpg")
|
||||||
im1.save(f, icc_profile=icc_profile)
|
im1.save(f, icc_profile=icc_profile)
|
||||||
im2 = Image.open(f)
|
with Image.open(f) as im2:
|
||||||
self.assertEqual(im2.info.get("icc_profile"), icc_profile)
|
self.assertEqual(im2.info.get("icc_profile"), icc_profile)
|
||||||
# Roundtrip via memory buffer.
|
# Roundtrip via memory buffer.
|
||||||
im1 = self.roundtrip(hopper())
|
im1 = self.roundtrip(hopper())
|
||||||
im2 = self.roundtrip(hopper(), icc_profile=icc_profile)
|
im2 = self.roundtrip(hopper(), icc_profile=icc_profile)
|
||||||
self.assert_image_equal(im1, im2)
|
self.assert_image_equal(im1, im2)
|
||||||
self.assertFalse(im1.info.get("icc_profile"))
|
self.assertFalse(im1.info.get("icc_profile"))
|
||||||
self.assertTrue(im2.info.get("icc_profile"))
|
self.assertTrue(im2.info.get("icc_profile"))
|
||||||
|
|
||||||
def test_icc_big(self):
|
def test_icc_big(self):
|
||||||
# Make sure that the "extra" support handles large blocks
|
# 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)
|
im.save(f, "JPEG", quality=90, exif=b"1" * 65532)
|
||||||
|
|
||||||
def test_exif_typeerror(self):
|
def test_exif_typeerror(self):
|
||||||
im = Image.open("Tests/images/exif_typeerror.jpg")
|
with Image.open("Tests/images/exif_typeerror.jpg") as im:
|
||||||
# Should not raise a TypeError
|
# Should not raise a TypeError
|
||||||
im._getexif()
|
im._getexif()
|
||||||
|
|
||||||
def test_exif_gps(self):
|
def test_exif_gps(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open("Tests/images/exif_gps.jpg")
|
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||||
gps_index = 34853
|
gps_index = 34853
|
||||||
expected_exif_gps = {
|
expected_exif_gps = {
|
||||||
0: b"\x00\x00\x00\x01",
|
0: b"\x00\x00\x00\x01",
|
||||||
2: (4294967295, 1),
|
2: (4294967295, 1),
|
||||||
5: b"\x01",
|
5: b"\x01",
|
||||||
30: 65535,
|
30: 65535,
|
||||||
29: "1999:99:99 99:99:99",
|
29: "1999:99:99 99:99:99",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
exif = im._getexif()
|
exif = im._getexif()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(exif[gps_index], expected_exif_gps)
|
self.assertEqual(exif[gps_index], expected_exif_gps)
|
||||||
|
@ -256,17 +256,17 @@ class TestFileJpeg(PillowTestCase):
|
||||||
33434: (4294967295, 1),
|
33434: (4294967295, 1),
|
||||||
}
|
}
|
||||||
|
|
||||||
im = Image.open("Tests/images/exif_gps.jpg")
|
with Image.open("Tests/images/exif_gps.jpg") as im:
|
||||||
exif = im._getexif()
|
exif = im._getexif()
|
||||||
|
|
||||||
for tag, value in expected_exif.items():
|
for tag, value in expected_exif.items():
|
||||||
self.assertEqual(value, exif[tag])
|
self.assertEqual(value, exif[tag])
|
||||||
|
|
||||||
def test_exif_gps_typeerror(self):
|
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
|
# Should not raise a TypeError
|
||||||
im._getexif()
|
im._getexif()
|
||||||
|
|
||||||
def test_progressive_compat(self):
|
def test_progressive_compat(self):
|
||||||
im1 = self.roundtrip(hopper())
|
im1 = self.roundtrip(hopper())
|
||||||
|
@ -329,13 +329,13 @@ class TestFileJpeg(PillowTestCase):
|
||||||
self.assertRaises(TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
|
self.assertRaises(TypeError, self.roundtrip, hopper(), subsampling="1:1:1")
|
||||||
|
|
||||||
def test_exif(self):
|
def test_exif(self):
|
||||||
im = Image.open("Tests/images/pil_sample_rgb.jpg")
|
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||||
info = im._getexif()
|
info = im._getexif()
|
||||||
self.assertEqual(info[305], "Adobe Photoshop CS Macintosh")
|
self.assertEqual(info[305], "Adobe Photoshop CS Macintosh")
|
||||||
|
|
||||||
def test_mp(self):
|
def test_mp(self):
|
||||||
im = Image.open("Tests/images/pil_sample_rgb.jpg")
|
with Image.open("Tests/images/pil_sample_rgb.jpg") as im:
|
||||||
self.assertIsNone(im._getmp())
|
self.assertIsNone(im._getmp())
|
||||||
|
|
||||||
def test_quality_keep(self):
|
def test_quality_keep(self):
|
||||||
# RGB
|
# RGB
|
||||||
|
@ -354,11 +354,13 @@ class TestFileJpeg(PillowTestCase):
|
||||||
def test_junk_jpeg_header(self):
|
def test_junk_jpeg_header(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/630
|
# https://github.com/python-pillow/Pillow/issues/630
|
||||||
filename = "Tests/images/junk_jpeg_header.jpg"
|
filename = "Tests/images/junk_jpeg_header.jpg"
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
def test_ff00_jpeg_header(self):
|
def test_ff00_jpeg_header(self):
|
||||||
filename = "Tests/images/jpeg_ff00_header.jpg"
|
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):
|
def test_truncated_jpeg_should_read_all_the_data(self):
|
||||||
filename = "Tests/images/truncated_jpeg.jpg"
|
filename = "Tests/images/truncated_jpeg.jpg"
|
||||||
|
@ -370,14 +372,13 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
def test_truncated_jpeg_throws_IOError(self):
|
def test_truncated_jpeg_throws_IOError(self):
|
||||||
filename = "Tests/images/truncated_jpeg.jpg"
|
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):
|
# Test that the error is raised if loaded a second time
|
||||||
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()
|
|
||||||
|
|
||||||
def _n_qtables_helper(self, n, test_file):
|
def _n_qtables_helper(self, n, test_file):
|
||||||
im = Image.open(test_file)
|
im = Image.open(test_file)
|
||||||
|
@ -483,9 +484,9 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
||||||
def test_load_djpeg(self):
|
def test_load_djpeg(self):
|
||||||
img = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as img:
|
||||||
img.load_djpeg()
|
img.load_djpeg()
|
||||||
self.assert_image_similar(img, Image.open(TEST_FILE), 0)
|
self.assert_image_similar(img, Image.open(TEST_FILE), 0)
|
||||||
|
|
||||||
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
|
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
|
||||||
def test_save_cjpeg(self):
|
def test_save_cjpeg(self):
|
||||||
|
@ -525,10 +526,10 @@ class TestFileJpeg(PillowTestCase):
|
||||||
# Act
|
# Act
|
||||||
# Shouldn't raise error
|
# Shouldn't raise error
|
||||||
fn = "Tests/images/sugarshack_bad_mpo_header.jpg"
|
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
|
# Assert
|
||||||
self.assertEqual(im.format, "JPEG")
|
self.assertEqual(im.format, "JPEG")
|
||||||
|
|
||||||
def test_save_correct_modes(self):
|
def test_save_correct_modes(self):
|
||||||
out = BytesIO()
|
out = BytesIO()
|
||||||
|
@ -558,106 +559,106 @@ class TestFileJpeg(PillowTestCase):
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open("Tests/images/iptc_roundUp.jpg")
|
with Image.open("Tests/images/iptc_roundUp.jpg") as im:
|
||||||
self.assertEqual(im.info["dpi"], (44, 44))
|
self.assertEqual(im.info["dpi"], (44, 44))
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/iptc_roundDown.jpg")
|
with Image.open("Tests/images/iptc_roundDown.jpg") as im:
|
||||||
self.assertEqual(im.info["dpi"], (2, 2))
|
self.assertEqual(im.info["dpi"], (2, 2))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
outfile = self.tempfile("temp.jpg")
|
outfile = self.tempfile("temp.jpg")
|
||||||
im = Image.open("Tests/images/hopper.jpg")
|
im = Image.open("Tests/images/hopper.jpg")
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.2, 72.2))
|
im.save(outfile, dpi=(72.2, 72.2))
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
self.assertEqual(reloaded.info["dpi"], (72, 72))
|
||||||
|
|
||||||
im.save(outfile, dpi=(72.8, 72.8))
|
im.save(outfile, dpi=(72.8, 72.8))
|
||||||
reloaded = Image.open(outfile)
|
with Image.open(outfile) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
self.assertEqual(reloaded.info["dpi"], (73, 73))
|
||||||
|
|
||||||
def test_dpi_tuple_from_exif(self):
|
def test_dpi_tuple_from_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This Photoshop CC 2017 image has DPI in EXIF not metadata
|
# This Photoshop CC 2017 image has DPI in EXIF not metadata
|
||||||
# EXIF XResolution is (2000000, 10000)
|
# 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
|
# Act / Assert
|
||||||
self.assertEqual(im.info.get("dpi"), (200, 200))
|
self.assertEqual(im.info.get("dpi"), (200, 200))
|
||||||
|
|
||||||
def test_dpi_int_from_exif(self):
|
def test_dpi_int_from_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This image has DPI in EXIF not metadata
|
# This image has DPI in EXIF not metadata
|
||||||
# EXIF XResolution is 72
|
# 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
|
# Act / Assert
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_dpi_from_dpcm_exif(self):
|
def test_dpi_from_dpcm_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This is photoshop-200dpi.jpg with EXIF resolution unit set to cm:
|
# This is photoshop-200dpi.jpg with EXIF resolution unit set to cm:
|
||||||
# exiftool -exif:ResolutionUnit=cm photoshop-200dpi.jpg
|
# 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
|
# Act / Assert
|
||||||
self.assertEqual(im.info.get("dpi"), (508, 508))
|
self.assertEqual(im.info.get("dpi"), (508, 508))
|
||||||
|
|
||||||
def test_dpi_exif_zero_division(self):
|
def test_dpi_exif_zero_division(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This is photoshop-200dpi.jpg with EXIF resolution set to 0/0:
|
# This is photoshop-200dpi.jpg with EXIF resolution set to 0/0:
|
||||||
# exiftool -XResolution=0/0 -YResolution=0/0 photoshop-200dpi.jpg
|
# 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
|
# Act / Assert
|
||||||
# This should return the default, and not raise a ZeroDivisionError
|
# This should return the default, and not raise a ZeroDivisionError
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_no_dpi_in_exif(self):
|
def test_no_dpi_in_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This is photoshop-200dpi.jpg with resolution removed from EXIF:
|
# This is photoshop-200dpi.jpg with resolution removed from EXIF:
|
||||||
# exiftool "-*resolution*"= photoshop-200dpi.jpg
|
# 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
|
# Act / Assert
|
||||||
# "When the image resolution is unknown, 72 [dpi] is designated."
|
# "When the image resolution is unknown, 72 [dpi] is designated."
|
||||||
# http://www.exiv2.org/tags.html
|
# http://www.exiv2.org/tags.html
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_invalid_exif(self):
|
def test_invalid_exif(self):
|
||||||
# This is no-dpi-in-exif with the tiff header of the exif block
|
# This is no-dpi-in-exif with the tiff header of the exif block
|
||||||
# hexedited from MM * to FF FF FF FF
|
# 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
|
# This should return the default, and not a SyntaxError or
|
||||||
# OSError for unidentified image.
|
# OSError for unidentified image.
|
||||||
self.assertEqual(im.info.get("dpi"), (72, 72))
|
self.assertEqual(im.info.get("dpi"), (72, 72))
|
||||||
|
|
||||||
def test_ifd_offset_exif(self):
|
def test_ifd_offset_exif(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# This image has been manually hexedited to have an IFD offset of 10,
|
# This image has been manually hexedited to have an IFD offset of 10,
|
||||||
# in contrast to normal 8
|
# 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
|
# Act / Assert
|
||||||
self.assertEqual(im._getexif()[306], "2017:03:13 23:03:09")
|
self.assertEqual(im._getexif()[306], "2017:03:13 23:03:09")
|
||||||
|
|
||||||
def test_photoshop(self):
|
def test_photoshop(self):
|
||||||
im = Image.open("Tests/images/photoshop-200dpi.jpg")
|
with Image.open("Tests/images/photoshop-200dpi.jpg") as im:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.info["photoshop"][0x03ED],
|
im.info["photoshop"][0x03ED],
|
||||||
{
|
{
|
||||||
"XResolution": 200.0,
|
"XResolution": 200.0,
|
||||||
"DisplayedUnitsX": 1,
|
"DisplayedUnitsX": 1,
|
||||||
"YResolution": 200.0,
|
"YResolution": 200.0,
|
||||||
"DisplayedUnitsY": 1,
|
"DisplayedUnitsY": 1,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
# This image does not contain a Photoshop header string
|
# This image does not contain a Photoshop header string
|
||||||
im = Image.open("Tests/images/app13.jpg")
|
with Image.open("Tests/images/app13.jpg") as im:
|
||||||
self.assertNotIn("photoshop", im.info)
|
self.assertNotIn("photoshop", im.info)
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipUnless(is_win32(), "Windows only")
|
@unittest.skipUnless(is_win32(), "Windows only")
|
||||||
|
|
|
@ -42,9 +42,9 @@ class TestFileJpeg2k(PillowTestCase):
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/jp2")
|
self.assertEqual(im.get_format_mimetype(), "image/jp2")
|
||||||
|
|
||||||
def test_jpf(self):
|
def test_jpf(self):
|
||||||
im = Image.open("Tests/images/balloon.jpf")
|
with Image.open("Tests/images/balloon.jpf") as im:
|
||||||
self.assertEqual(im.format, "JPEG2000")
|
self.assertEqual(im.format, "JPEG2000")
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/jpx")
|
self.assertEqual(im.get_format_mimetype(), "image/jpx")
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
invalid_file = "Tests/images/flower.jpg"
|
invalid_file = "Tests/images/flower.jpg"
|
||||||
|
|
|
@ -144,15 +144,14 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
def test_write_metadata(self):
|
def test_write_metadata(self):
|
||||||
""" Test metadata writing through libtiff """
|
""" Test metadata writing through libtiff """
|
||||||
for legacy_api in [False, True]:
|
for legacy_api in [False, True]:
|
||||||
img = Image.open("Tests/images/hopper_g4.tif")
|
|
||||||
f = self.tempfile("temp.tiff")
|
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()
|
||||||
if legacy_api:
|
else:
|
||||||
original = img.tag.named()
|
original = img.tag_v2.named()
|
||||||
else:
|
|
||||||
original = img.tag_v2.named()
|
|
||||||
|
|
||||||
# PhotometricInterpretation is set from SAVE_INFO,
|
# PhotometricInterpretation is set from SAVE_INFO,
|
||||||
# not the original image.
|
# not the original image.
|
||||||
|
@ -163,11 +162,11 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
"PhotometricInterpretation",
|
"PhotometricInterpretation",
|
||||||
]
|
]
|
||||||
|
|
||||||
loaded = Image.open(f)
|
with Image.open(f) as loaded:
|
||||||
if legacy_api:
|
if legacy_api:
|
||||||
reloaded = loaded.tag.named()
|
reloaded = loaded.tag.named()
|
||||||
else:
|
else:
|
||||||
reloaded = loaded.tag_v2.named()
|
reloaded = loaded.tag_v2.named()
|
||||||
|
|
||||||
for tag, value in itertools.chain(reloaded.items(), original.items()):
|
for tag, value in itertools.chain(reloaded.items(), original.items()):
|
||||||
if tag not in ignored:
|
if tag not in ignored:
|
||||||
|
@ -302,21 +301,21 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
out = self.tempfile("temp.tif")
|
out = self.tempfile("temp.tif")
|
||||||
im.save(out, tiffinfo=tiffinfo)
|
im.save(out, tiffinfo=tiffinfo)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
for tag, value in tiffinfo.items():
|
for tag, value in tiffinfo.items():
|
||||||
reloaded_value = reloaded.tag_v2[tag]
|
reloaded_value = reloaded.tag_v2[tag]
|
||||||
if (
|
if (
|
||||||
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
|
isinstance(reloaded_value, TiffImagePlugin.IFDRational)
|
||||||
and libtiff
|
and libtiff
|
||||||
):
|
):
|
||||||
# libtiff does not support real RATIONALS
|
# libtiff does not support real RATIONALS
|
||||||
self.assertAlmostEqual(float(reloaded_value), float(value))
|
self.assertAlmostEqual(float(reloaded_value), float(value))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if libtiff and isinstance(value, bytes):
|
if libtiff and isinstance(value, bytes):
|
||||||
value = value.decode()
|
value = value.decode()
|
||||||
|
|
||||||
self.assertEqual(reloaded_value, value)
|
self.assertEqual(reloaded_value, value)
|
||||||
|
|
||||||
# Test with types
|
# Test with types
|
||||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
|
@ -343,8 +342,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = True
|
TiffImagePlugin.WRITE_LIBTIFF = True
|
||||||
im.save(out, dpi=(72, 72))
|
im.save(out, dpi=(72, 72))
|
||||||
TiffImagePlugin.WRITE_LIBTIFF = False
|
TiffImagePlugin.WRITE_LIBTIFF = False
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertEqual(reloaded.info["dpi"], (72.0, 72.0))
|
self.assertEqual(reloaded.info["dpi"], (72.0, 72.0))
|
||||||
|
|
||||||
def test_g3_compression(self):
|
def test_g3_compression(self):
|
||||||
i = Image.open("Tests/images/hopper_g4_500.tif")
|
i = Image.open("Tests/images/hopper_g4_500.tif")
|
||||||
|
@ -412,9 +411,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
orig.tag[269] = "temp.tif"
|
orig.tag[269] = "temp.tif"
|
||||||
orig.save(out)
|
orig.save(out)
|
||||||
|
|
||||||
reread = Image.open(out)
|
with Image.open(out) as reread:
|
||||||
self.assertEqual("temp.tif", reread.tag_v2[269])
|
self.assertEqual("temp.tif", reread.tag_v2[269])
|
||||||
self.assertEqual("temp.tif", reread.tag[269][0])
|
self.assertEqual("temp.tif", reread.tag[269][0])
|
||||||
|
|
||||||
def test_12bit_rawmode(self):
|
def test_12bit_rawmode(self):
|
||||||
""" Are we generating the same interpretation
|
""" Are we generating the same interpretation
|
||||||
|
@ -521,36 +520,36 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
def test_multipage(self):
|
def test_multipage(self):
|
||||||
# issue #862
|
# issue #862
|
||||||
TiffImagePlugin.READ_LIBTIFF = True
|
TiffImagePlugin.READ_LIBTIFF = True
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
|
# file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
||||||
self.assertTrue(im.tag.next)
|
self.assertTrue(im.tag.next)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
||||||
self.assertTrue(im.tag.next)
|
self.assertTrue(im.tag.next)
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
self.assertFalse(im.tag.next)
|
self.assertFalse(im.tag.next)
|
||||||
self.assertEqual(im.size, (20, 20))
|
self.assertEqual(im.size, (20, 20))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
||||||
|
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
|
|
||||||
def test_multipage_nframes(self):
|
def test_multipage_nframes(self):
|
||||||
# issue #862
|
# issue #862
|
||||||
TiffImagePlugin.READ_LIBTIFF = True
|
TiffImagePlugin.READ_LIBTIFF = True
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
frames = im.n_frames
|
frames = im.n_frames
|
||||||
self.assertEqual(frames, 3)
|
self.assertEqual(frames, 3)
|
||||||
for _ in range(frames):
|
for _ in range(frames):
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
# Should not raise ValueError: I/O operation on closed file
|
# Should not raise ValueError: I/O operation on closed file
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
|
|
||||||
|
@ -656,9 +655,9 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
# /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
|
# /usr/bin/gs -q -sDEVICE=tiffg3 -sOutputFile=total-pages-zero.tif
|
||||||
# -dNOPAUSE /tmp/test.pdf -c quit
|
# -dNOPAUSE /tmp/test.pdf -c quit
|
||||||
infile = "Tests/images/total-pages-zero.tif"
|
infile = "Tests/images/total-pages-zero.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
# Should not divide by zero
|
# Should not divide by zero
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
def test_fd_duplication(self):
|
def test_fd_duplication(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/1651
|
# https://github.com/python-pillow/Pillow/issues/1651
|
||||||
|
@ -686,21 +685,21 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
self.assertEqual(icc, icc_libtiff)
|
self.assertEqual(icc, icc_libtiff)
|
||||||
|
|
||||||
def test_multipage_compression(self):
|
def test_multipage_compression(self):
|
||||||
im = Image.open("Tests/images/compression.tif")
|
with Image.open("Tests/images/compression.tif") as im:
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im._compression, "tiff_ccitt")
|
self.assertEqual(im._compression, "tiff_ccitt")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im._compression, "packbits")
|
self.assertEqual(im._compression, "packbits")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im._compression, "tiff_ccitt")
|
self.assertEqual(im._compression, "tiff_ccitt")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
def test_save_tiff_with_jpegtables(self):
|
def test_save_tiff_with_jpegtables(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -836,8 +835,8 @@ class TestFileLibTiff(LibTiffTestCase):
|
||||||
def test_no_rows_per_strip(self):
|
def test_no_rows_per_strip(self):
|
||||||
# This image does not have a RowsPerStrip TIFF tag
|
# This image does not have a RowsPerStrip TIFF tag
|
||||||
infile = "Tests/images/no_rows_per_strip.tif"
|
infile = "Tests/images/no_rows_per_strip.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (950, 975))
|
self.assertEqual(im.size, (950, 975))
|
||||||
|
|
||||||
def test_orientation(self):
|
def test_orientation(self):
|
||||||
|
|
|
@ -16,42 +16,42 @@ TEST_FILE = "Tests/images/hopper.mic"
|
||||||
@unittest.skipUnless(features.check("libtiff"), "libtiff not installed")
|
@unittest.skipUnless(features.check("libtiff"), "libtiff not installed")
|
||||||
class TestFileMic(PillowTestCase):
|
class TestFileMic(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGBA")
|
self.assertEqual(im.mode, "RGBA")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "MIC")
|
self.assertEqual(im.format, "MIC")
|
||||||
|
|
||||||
# Adjust for the gamma of 2.2 encoded into the file
|
# Adjust for the gamma of 2.2 encoded into the file
|
||||||
lut = ImagePalette.make_gamma_lut(1 / 2.2)
|
lut = ImagePalette.make_gamma_lut(1 / 2.2)
|
||||||
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
|
im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()])
|
||||||
|
|
||||||
im2 = hopper("RGBA")
|
im2 = hopper("RGBA")
|
||||||
self.assert_image_similar(im, im2, 10)
|
self.assert_image_similar(im, im2, 10)
|
||||||
|
|
||||||
def test_n_frames(self):
|
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):
|
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):
|
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):
|
def test_seek(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
self.assertRaises(EOFError, im.seek, 99)
|
self.assertRaises(EOFError, im.seek, 99)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
# Test an invalid OLE file
|
# Test an invalid OLE file
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
import unittest
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase, is_pypy
|
||||||
|
|
||||||
test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"]
|
test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"]
|
||||||
|
|
||||||
|
@ -25,78 +26,97 @@ class TestFileMpo(PillowTestCase):
|
||||||
|
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (640, 480))
|
self.assertEqual(im.size, (640, 480))
|
||||||
self.assertEqual(im.format, "MPO")
|
self.assertEqual(im.format, "MPO")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(test_files[0])
|
im = Image.open(test_files[0])
|
||||||
im.load()
|
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)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_app(self):
|
def test_app(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
# Test APP/COM reader (@PIL135)
|
# Test APP/COM reader (@PIL135)
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.applist[0][0], "APP1")
|
self.assertEqual(im.applist[0][0], "APP1")
|
||||||
self.assertEqual(im.applist[1][0], "APP2")
|
self.assertEqual(im.applist[1][0], "APP2")
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
im.applist[1][1][:16], b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00"
|
im.applist[1][1][:16],
|
||||||
)
|
b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00",
|
||||||
self.assertEqual(len(im.applist), 2)
|
)
|
||||||
|
self.assertEqual(len(im.applist), 2)
|
||||||
|
|
||||||
def test_exif(self):
|
def test_exif(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
info = im._getexif()
|
info = im._getexif()
|
||||||
self.assertEqual(info[272], "Nintendo 3DS")
|
self.assertEqual(info[272], "Nintendo 3DS")
|
||||||
self.assertEqual(info[296], 2)
|
self.assertEqual(info[296], 2)
|
||||||
self.assertEqual(info[34665], 188)
|
self.assertEqual(info[34665], 188)
|
||||||
|
|
||||||
def test_frame_size(self):
|
def test_frame_size(self):
|
||||||
# This image has been hexedited to contain a different size
|
# This image has been hexedited to contain a different size
|
||||||
# in the EXIF data of the second frame
|
# in the EXIF data of the second frame
|
||||||
im = Image.open("Tests/images/sugarshack_frame_size.mpo")
|
with Image.open("Tests/images/sugarshack_frame_size.mpo") as im:
|
||||||
self.assertEqual(im.size, (640, 480))
|
self.assertEqual(im.size, (640, 480))
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.size, (680, 480))
|
self.assertEqual(im.size, (680, 480))
|
||||||
|
|
||||||
def test_parallax(self):
|
def test_parallax(self):
|
||||||
# Nintendo
|
# Nintendo
|
||||||
im = Image.open("Tests/images/sugarshack.mpo")
|
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
self.assertEqual(exif.get_ifd(0x927C)[0x1101]["Parallax"], -44.798187255859375)
|
self.assertEqual(
|
||||||
|
exif.get_ifd(0x927C)[0x1101]["Parallax"], -44.798187255859375
|
||||||
|
)
|
||||||
|
|
||||||
# Fujifilm
|
# Fujifilm
|
||||||
im = Image.open("Tests/images/fujifilm.mpo")
|
with Image.open("Tests/images/fujifilm.mpo") as im:
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
self.assertEqual(exif.get_ifd(0x927C)[0xB211], -3.125)
|
self.assertEqual(exif.get_ifd(0x927C)[0xB211], -3.125)
|
||||||
|
|
||||||
def test_mp(self):
|
def test_mp(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
self.assertEqual(mpinfo[45056], b"0100")
|
self.assertEqual(mpinfo[45056], b"0100")
|
||||||
self.assertEqual(mpinfo[45057], 2)
|
self.assertEqual(mpinfo[45057], 2)
|
||||||
|
|
||||||
def test_mp_offset(self):
|
def test_mp_offset(self):
|
||||||
# This image has been manually hexedited to have an IFD offset of 10
|
# This image has been manually hexedited to have an IFD offset of 10
|
||||||
# in APP2 data, in contrast to normal 8
|
# in APP2 data, in contrast to normal 8
|
||||||
im = Image.open("Tests/images/sugarshack_ifd_offset.mpo")
|
with Image.open("Tests/images/sugarshack_ifd_offset.mpo") as im:
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
self.assertEqual(mpinfo[45056], b"0100")
|
self.assertEqual(mpinfo[45056], b"0100")
|
||||||
self.assertEqual(mpinfo[45057], 2)
|
self.assertEqual(mpinfo[45057], 2)
|
||||||
|
|
||||||
def test_mp_attribute(self):
|
def test_mp_attribute(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
mpinfo = im._getmp()
|
mpinfo = im._getmp()
|
||||||
frameNumber = 0
|
frameNumber = 0
|
||||||
for mpentry in mpinfo[45058]:
|
for mpentry in mpinfo[45058]:
|
||||||
mpattr = mpentry["Attribute"]
|
mpattr = mpentry["Attribute"]
|
||||||
|
@ -113,62 +133,62 @@ class TestFileMpo(PillowTestCase):
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
# prior to first image raises an error, both blatant and borderline
|
# prior to first image raises an error, both blatant and borderline
|
||||||
self.assertRaises(EOFError, im.seek, -1)
|
self.assertRaises(EOFError, im.seek, -1)
|
||||||
self.assertRaises(EOFError, im.seek, -523)
|
self.assertRaises(EOFError, im.seek, -523)
|
||||||
# after the final image raises an error,
|
# after the final image raises an error,
|
||||||
# both blatant and borderline
|
# both blatant and borderline
|
||||||
self.assertRaises(EOFError, im.seek, 2)
|
self.assertRaises(EOFError, im.seek, 2)
|
||||||
self.assertRaises(EOFError, im.seek, 523)
|
self.assertRaises(EOFError, im.seek, 523)
|
||||||
# bad calls shouldn't change the frame
|
# bad calls shouldn't change the frame
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
# this one will work
|
# this one will work
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.tell(), 1)
|
self.assertEqual(im.tell(), 1)
|
||||||
# and this one, too
|
# and this one, too
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open("Tests/images/sugarshack.mpo")
|
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||||
self.assertEqual(im.n_frames, 2)
|
self.assertEqual(im.n_frames, 2)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open("Tests/images/sugarshack.mpo")
|
with Image.open("Tests/images/sugarshack.mpo") as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_image_grab(self):
|
def test_image_grab(self):
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
im0 = im.tobytes()
|
im0 = im.tobytes()
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.tell(), 1)
|
self.assertEqual(im.tell(), 1)
|
||||||
im1 = im.tobytes()
|
im1 = im.tobytes()
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
im02 = im.tobytes()
|
im02 = im.tobytes()
|
||||||
self.assertEqual(im0, im02)
|
self.assertEqual(im0, im02)
|
||||||
self.assertNotEqual(im0, im1)
|
self.assertNotEqual(im0, im1)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
# Note that only individual frames can be saved at present
|
# Note that only individual frames can be saved at present
|
||||||
for test_file in test_files:
|
for test_file in test_files:
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
jpg0 = self.frame_roundtrip(im)
|
jpg0 = self.frame_roundtrip(im)
|
||||||
self.assert_image_similar(im, jpg0, 30)
|
self.assert_image_similar(im, jpg0, 30)
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
self.assertEqual(im.tell(), 1)
|
self.assertEqual(im.tell(), 1)
|
||||||
jpg1 = self.frame_roundtrip(im)
|
jpg1 = self.frame_roundtrip(im)
|
||||||
self.assert_image_similar(im, jpg1, 30)
|
self.assert_image_similar(im, jpg1, 30)
|
||||||
|
|
|
@ -82,27 +82,27 @@ class TestFilePdf(PillowTestCase):
|
||||||
self.helper_save_as_pdf("RGB", save_all=True)
|
self.helper_save_as_pdf("RGB", save_all=True)
|
||||||
|
|
||||||
# Multiframe image
|
# 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")
|
outfile = self.tempfile("temp.pdf")
|
||||||
im.save(outfile, save_all=True)
|
im.save(outfile, save_all=True)
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
|
||||||
# Append images
|
# Append images
|
||||||
ims = [hopper()]
|
ims = [hopper()]
|
||||||
im.copy().save(outfile, save_all=True, append_images=ims)
|
im.copy().save(outfile, save_all=True, append_images=ims)
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
|
||||||
# Test appending using a generator
|
# Test appending using a generator
|
||||||
def imGenerator(ims):
|
def imGenerator(ims):
|
||||||
for im in ims:
|
for im in ims:
|
||||||
yield im
|
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.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
@ -116,10 +116,10 @@ class TestFilePdf(PillowTestCase):
|
||||||
|
|
||||||
def test_multiframe_normal_save(self):
|
def test_multiframe_normal_save(self):
|
||||||
# Test saving a multiframe image without save_all
|
# 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")
|
outfile = self.tempfile("temp.pdf")
|
||||||
im.save(outfile)
|
im.save(outfile)
|
||||||
|
|
||||||
self.assertTrue(os.path.isfile(outfile))
|
self.assertTrue(os.path.isfile(outfile))
|
||||||
self.assertGreater(os.path.getsize(outfile), 0)
|
self.assertGreater(os.path.getsize(outfile), 0)
|
||||||
|
|
|
@ -81,12 +81,12 @@ class TestFilePng(PillowTestCase):
|
||||||
|
|
||||||
hopper("RGB").save(test_file)
|
hopper("RGB").save(test_file)
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "PNG")
|
self.assertEqual(im.format, "PNG")
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/png")
|
self.assertEqual(im.get_format_mimetype(), "image/png")
|
||||||
|
|
||||||
for mode in ["1", "L", "P", "RGB", "I", "I;16"]:
|
for mode in ["1", "L", "P", "RGB", "I", "I;16"]:
|
||||||
im = hopper(mode)
|
im = hopper(mode)
|
||||||
|
@ -393,12 +393,12 @@ class TestFilePng(PillowTestCase):
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open(TEST_PNG_FILE)
|
with Image.open(TEST_PNG_FILE) as im:
|
||||||
self.assertEqual(im.info["dpi"], (96, 96))
|
self.assertEqual(im.info["dpi"], (96, 96))
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/icc_profile_none.png")
|
with Image.open("Tests/images/icc_profile_none.png") as im:
|
||||||
self.assertEqual(im.info["dpi"], (72, 72))
|
self.assertEqual(im.info["dpi"], (72, 72))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
im = Image.open(TEST_PNG_FILE)
|
im = Image.open(TEST_PNG_FILE)
|
||||||
|
@ -462,8 +462,13 @@ class TestFilePng(PillowTestCase):
|
||||||
if py3:
|
if py3:
|
||||||
rt_text(" Aa" + chr(0xA0) + chr(0xC4) + chr(0xFF)) # Latin1
|
rt_text(" Aa" + chr(0xA0) + chr(0xC4) + chr(0xFF)) # Latin1
|
||||||
rt_text(chr(0x400) + chr(0x472) + chr(0x4FF)) # Cyrillic
|
rt_text(chr(0x400) + chr(0x472) + chr(0x4FF)) # Cyrillic
|
||||||
# CJK:
|
rt_text(
|
||||||
rt_text(chr(0x4E00) + chr(0x66F0) + chr(0x9FBA) + chr(0x3042) + chr(0xAC00))
|
chr(0x4E00)
|
||||||
|
+ chr(0x66F0)
|
||||||
|
+ chr(0x9FBA) # CJK
|
||||||
|
+ chr(0x3042)
|
||||||
|
+ chr(0xAC00)
|
||||||
|
)
|
||||||
rt_text("A" + chr(0xC4) + chr(0x472) + chr(0x3042)) # Combined
|
rt_text("A" + chr(0xC4) + chr(0x472) + chr(0x3042)) # Combined
|
||||||
|
|
||||||
def test_scary(self):
|
def test_scary(self):
|
||||||
|
@ -509,19 +514,19 @@ class TestFilePng(PillowTestCase):
|
||||||
def test_trns_null(self):
|
def test_trns_null(self):
|
||||||
# Check reading images with null tRNS value, issue #1239
|
# Check reading images with null tRNS value, issue #1239
|
||||||
test_file = "Tests/images/tRNS_null_1x1.png"
|
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):
|
def test_save_icc_profile(self):
|
||||||
im = Image.open("Tests/images/icc_profile_none.png")
|
with Image.open("Tests/images/icc_profile_none.png") as im:
|
||||||
self.assertIsNone(im.info["icc_profile"])
|
self.assertIsNone(im.info["icc_profile"])
|
||||||
|
|
||||||
with_icc = Image.open("Tests/images/icc_profile.png")
|
with Image.open("Tests/images/icc_profile.png") as with_icc:
|
||||||
expected_icc = with_icc.info["icc_profile"]
|
expected_icc = with_icc.info["icc_profile"]
|
||||||
|
|
||||||
im = roundtrip(im, icc_profile=expected_icc)
|
im = roundtrip(im, icc_profile=expected_icc)
|
||||||
self.assertEqual(im.info["icc_profile"], expected_icc)
|
self.assertEqual(im.info["icc_profile"], expected_icc)
|
||||||
|
|
||||||
def test_discard_icc_profile(self):
|
def test_discard_icc_profile(self):
|
||||||
im = Image.open("Tests/images/icc_profile.png")
|
im = Image.open("Tests/images/icc_profile.png")
|
||||||
|
@ -614,8 +619,8 @@ class TestFilePng(PillowTestCase):
|
||||||
test_file = self.tempfile("temp.png")
|
test_file = self.tempfile("temp.png")
|
||||||
im.save(test_file)
|
im.save(test_file)
|
||||||
|
|
||||||
reloaded = Image.open(test_file)
|
with Image.open(test_file) as reloaded:
|
||||||
exif = reloaded._getexif()
|
exif = reloaded._getexif()
|
||||||
self.assertEqual(exif[274], 1)
|
self.assertEqual(exif[274], 1)
|
||||||
|
|
||||||
def test_exif_from_jpg(self):
|
def test_exif_from_jpg(self):
|
||||||
|
@ -624,8 +629,8 @@ class TestFilePng(PillowTestCase):
|
||||||
test_file = self.tempfile("temp.png")
|
test_file = self.tempfile("temp.png")
|
||||||
im.save(test_file)
|
im.save(test_file)
|
||||||
|
|
||||||
reloaded = Image.open(test_file)
|
with Image.open(test_file) as reloaded:
|
||||||
exif = reloaded._getexif()
|
exif = reloaded._getexif()
|
||||||
self.assertEqual(exif[305], "Adobe Photoshop CS Macintosh")
|
self.assertEqual(exif[305], "Adobe Photoshop CS Macintosh")
|
||||||
|
|
||||||
def test_exif_argument(self):
|
def test_exif_argument(self):
|
||||||
|
@ -634,8 +639,8 @@ class TestFilePng(PillowTestCase):
|
||||||
test_file = self.tempfile("temp.png")
|
test_file = self.tempfile("temp.png")
|
||||||
im.save(test_file, exif=b"exifstring")
|
im.save(test_file, exif=b"exifstring")
|
||||||
|
|
||||||
reloaded = Image.open(test_file)
|
with Image.open(test_file) as reloaded:
|
||||||
self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring")
|
self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring")
|
||||||
|
|
||||||
@unittest.skipUnless(
|
@unittest.skipUnless(
|
||||||
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP support not installed with animation"
|
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP support not installed with animation"
|
||||||
|
|
|
@ -66,10 +66,10 @@ class TestFilePpm(PillowTestCase):
|
||||||
|
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
f.write("P4\n128 128\n255")
|
f.write("P4\n128 128\n255")
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/x-portable-bitmap")
|
self.assertEqual(im.get_format_mimetype(), "image/x-portable-bitmap")
|
||||||
|
|
||||||
with open(path, "w") as f:
|
with open(path, "w") as f:
|
||||||
f.write("PyCMYK\n128 128\n255")
|
f.write("PyCMYK\n128 128\n255")
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.get_format_mimetype(), "image/x-portable-anymap")
|
self.assertEqual(im.get_format_mimetype(), "image/x-portable-anymap")
|
||||||
|
|
|
@ -1,26 +1,45 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, PsdImagePlugin
|
from PIL import Image, PsdImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
test_file = "Tests/images/hopper.psd"
|
test_file = "Tests/images/hopper.psd"
|
||||||
|
|
||||||
|
|
||||||
class TestImagePsd(PillowTestCase):
|
class TestImagePsd(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "PSD")
|
self.assertEqual(im.format, "PSD")
|
||||||
|
|
||||||
im2 = hopper()
|
im2 = hopper()
|
||||||
self.assert_image_similar(im, im2, 4.8)
|
self.assert_image_similar(im, im2, 4.8)
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(test_file)
|
im = Image.open(test_file)
|
||||||
im.load()
|
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)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_invalid_file(self):
|
def test_invalid_file(self):
|
||||||
|
@ -29,64 +48,63 @@ class TestImagePsd(PillowTestCase):
|
||||||
self.assertRaises(SyntaxError, PsdImagePlugin.PsdImageFile, invalid_file)
|
self.assertRaises(SyntaxError, PsdImagePlugin.PsdImageFile, invalid_file)
|
||||||
|
|
||||||
def test_n_frames(self):
|
def test_n_frames(self):
|
||||||
im = Image.open("Tests/images/hopper_merged.psd")
|
with Image.open("Tests/images/hopper_merged.psd") as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertEqual(im.n_frames, 2)
|
self.assertEqual(im.n_frames, 2)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
# PSD seek index starts at 1 rather than 0
|
# PSD seek index starts at 1 rather than 0
|
||||||
n_frames = im.n_frames + 1
|
n_frames = im.n_frames + 1
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_seek_tell(self):
|
def test_seek_tell(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
self.assertRaises(EOFError, im.seek, 0)
|
self.assertRaises(EOFError, im.seek, 0)
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 1)
|
self.assertEqual(layer_number, 1)
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
layer_number = im.tell()
|
layer_number = im.tell()
|
||||||
self.assertEqual(layer_number, 2)
|
self.assertEqual(layer_number, 2)
|
||||||
|
|
||||||
def test_seek_eoferror(self):
|
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):
|
def test_open_after_exclusive_load(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
im.load()
|
im.load()
|
||||||
im.seek(im.tell() + 1)
|
im.seek(im.tell() + 1)
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
def test_icc_profile(self):
|
def test_icc_profile(self):
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
self.assertIn("icc_profile", im.info)
|
self.assertIn("icc_profile", im.info)
|
||||||
|
|
||||||
icc_profile = im.info["icc_profile"]
|
icc_profile = im.info["icc_profile"]
|
||||||
self.assertEqual(len(icc_profile), 3144)
|
self.assertEqual(len(icc_profile), 3144)
|
||||||
|
|
||||||
def test_no_icc_profile(self):
|
def test_no_icc_profile(self):
|
||||||
im = Image.open("Tests/images/hopper_merged.psd")
|
with Image.open("Tests/images/hopper_merged.psd") as im:
|
||||||
|
self.assertNotIn("icc_profile", im.info)
|
||||||
self.assertNotIn("icc_profile", im.info)
|
|
||||||
|
|
||||||
def test_combined_larger_than_size(self):
|
def test_combined_larger_than_size(self):
|
||||||
# The 'combined' sizes of the individual parts is larger than the
|
# The 'combined' sizes of the individual parts is larger than the
|
||||||
|
|
|
@ -1,26 +1,43 @@
|
||||||
import tempfile
|
import tempfile
|
||||||
|
import unittest
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from PIL import Image, ImageSequence, SpiderImagePlugin
|
from PIL import Image, ImageSequence, SpiderImagePlugin
|
||||||
|
|
||||||
from .helper import PillowTestCase, hopper
|
from .helper import PillowTestCase, hopper, is_pypy
|
||||||
|
|
||||||
TEST_FILE = "Tests/images/hopper.spider"
|
TEST_FILE = "Tests/images/hopper.spider"
|
||||||
|
|
||||||
|
|
||||||
class TestImageSpider(PillowTestCase):
|
class TestImageSpider(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "F")
|
self.assertEqual(im.mode, "F")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "SPIDER")
|
self.assertEqual(im.format, "SPIDER")
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open(TEST_FILE)
|
im = Image.open(TEST_FILE)
|
||||||
im.load()
|
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)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
|
@ -32,10 +49,10 @@ class TestImageSpider(PillowTestCase):
|
||||||
im.save(temp, "SPIDER")
|
im.save(temp, "SPIDER")
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
im2 = Image.open(temp)
|
with Image.open(temp) as im2:
|
||||||
self.assertEqual(im2.mode, "F")
|
self.assertEqual(im2.mode, "F")
|
||||||
self.assertEqual(im2.size, (128, 128))
|
self.assertEqual(im2.size, (128, 128))
|
||||||
self.assertEqual(im2.format, "SPIDER")
|
self.assertEqual(im2.format, "SPIDER")
|
||||||
|
|
||||||
def test_tempfile(self):
|
def test_tempfile(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -57,18 +74,18 @@ class TestImageSpider(PillowTestCase):
|
||||||
|
|
||||||
def test_tell(self):
|
def test_tell(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
index = im.tell()
|
index = im.tell()
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(index, 0)
|
self.assertEqual(index, 0)
|
||||||
|
|
||||||
def test_n_frames(self):
|
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)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
def test_loadImageSeries(self):
|
def test_loadImageSeries(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -109,15 +126,14 @@ class TestImageSpider(PillowTestCase):
|
||||||
self.assertRaises(IOError, Image.open, invalid_file)
|
self.assertRaises(IOError, Image.open, invalid_file)
|
||||||
|
|
||||||
def test_nonstack_file(self):
|
def test_nonstack_file(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
|
self.assertRaises(EOFError, im.seek, 0)
|
||||||
self.assertRaises(EOFError, im.seek, 0)
|
|
||||||
|
|
||||||
def test_nonstack_dos(self):
|
def test_nonstack_dos(self):
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
for i, frame in enumerate(ImageSequence.Iterator(im)):
|
for i, frame in enumerate(ImageSequence.Iterator(im)):
|
||||||
if i > 1:
|
if i > 1:
|
||||||
self.fail("Non-stack DOS file test failed")
|
self.fail("Non-stack DOS file test failed")
|
||||||
|
|
||||||
# for issue #4093
|
# for issue #4093
|
||||||
def test_odd_size(self):
|
def test_odd_size(self):
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
import unittest
|
||||||
|
|
||||||
from PIL import Image, TarIO
|
from PIL import Image, TarIO
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase, is_pypy
|
||||||
|
|
||||||
codecs = dir(Image.core)
|
codecs = dir(Image.core)
|
||||||
|
|
||||||
|
@ -19,17 +21,30 @@ class TestFileTar(PillowTestCase):
|
||||||
["jpeg_decoder", "hopper.jpg", "JPEG"],
|
["jpeg_decoder", "hopper.jpg", "JPEG"],
|
||||||
]:
|
]:
|
||||||
if codec in codecs:
|
if codec in codecs:
|
||||||
tar = TarIO.TarIO(TEST_TAR_FILE, test_path)
|
with TarIO.TarIO(TEST_TAR_FILE, test_path) as tar:
|
||||||
im = Image.open(tar)
|
im = Image.open(tar)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, format)
|
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):
|
def test_close(self):
|
||||||
tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
|
def open():
|
||||||
tar.close()
|
tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
|
||||||
|
tar.close()
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_contextmanager(self):
|
def test_contextmanager(self):
|
||||||
with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"):
|
def open():
|
||||||
pass
|
with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"):
|
||||||
|
pass
|
||||||
|
|
||||||
|
self.assert_warning(None, open)
|
||||||
|
|
|
@ -76,20 +76,20 @@ class TestFileTga(PillowTestCase):
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (100, 100))
|
self.assertEqual(im.size, (100, 100))
|
||||||
|
|
||||||
def test_id_field_rle(self):
|
def test_id_field_rle(self):
|
||||||
# tga file with id field
|
# tga file with id field
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(test_file)
|
with Image.open(test_file) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.size, (199, 199))
|
self.assertEqual(im.size, (199, 199))
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
|
@ -99,14 +99,14 @@ class TestFileTga(PillowTestCase):
|
||||||
|
|
||||||
# Save
|
# Save
|
||||||
im.save(out)
|
im.save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (100, 100))
|
self.assertEqual(test_im.size, (100, 100))
|
||||||
self.assertEqual(test_im.info["id_section"], im.info["id_section"])
|
self.assertEqual(test_im.info["id_section"], im.info["id_section"])
|
||||||
|
|
||||||
# RGBA save
|
# RGBA save
|
||||||
im.convert("RGBA").save(out)
|
im.convert("RGBA").save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (100, 100))
|
self.assertEqual(test_im.size, (100, 100))
|
||||||
|
|
||||||
def test_save_id_section(self):
|
def test_save_id_section(self):
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
|
@ -116,27 +116,27 @@ class TestFileTga(PillowTestCase):
|
||||||
|
|
||||||
# Check there is no id section
|
# Check there is no id section
|
||||||
im.save(out)
|
im.save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertNotIn("id_section", test_im.info)
|
self.assertNotIn("id_section", test_im.info)
|
||||||
|
|
||||||
# Save with custom id section
|
# Save with custom id section
|
||||||
im.save(out, id_section=b"Test content")
|
im.save(out, id_section=b"Test content")
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["id_section"], b"Test content")
|
self.assertEqual(test_im.info["id_section"], b"Test content")
|
||||||
|
|
||||||
# Save with custom id section greater than 255 characters
|
# Save with custom id section greater than 255 characters
|
||||||
id_section = b"Test content" * 25
|
id_section = b"Test content" * 25
|
||||||
self.assert_warning(UserWarning, lambda: im.save(out, id_section=id_section))
|
self.assert_warning(UserWarning, lambda: im.save(out, id_section=id_section))
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["id_section"], id_section[:255])
|
self.assertEqual(test_im.info["id_section"], id_section[:255])
|
||||||
|
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
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
|
# Save with no id section
|
||||||
im.save(out, id_section="")
|
im.save(out, id_section="")
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertNotIn("id_section", test_im.info)
|
self.assertNotIn("id_section", test_im.info)
|
||||||
|
|
||||||
def test_save_orientation(self):
|
def test_save_orientation(self):
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
|
@ -146,8 +146,8 @@ class TestFileTga(PillowTestCase):
|
||||||
out = self.tempfile("temp.tga")
|
out = self.tempfile("temp.tga")
|
||||||
|
|
||||||
im.save(out, orientation=1)
|
im.save(out, orientation=1)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["orientation"], 1)
|
self.assertEqual(test_im.info["orientation"], 1)
|
||||||
|
|
||||||
def test_save_rle(self):
|
def test_save_rle(self):
|
||||||
test_file = "Tests/images/rgb32rle.tga"
|
test_file = "Tests/images/rgb32rle.tga"
|
||||||
|
@ -158,19 +158,19 @@ class TestFileTga(PillowTestCase):
|
||||||
|
|
||||||
# Save
|
# Save
|
||||||
im.save(out)
|
im.save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (199, 199))
|
self.assertEqual(test_im.size, (199, 199))
|
||||||
self.assertEqual(test_im.info["compression"], "tga_rle")
|
self.assertEqual(test_im.info["compression"], "tga_rle")
|
||||||
|
|
||||||
# Save without compression
|
# Save without compression
|
||||||
im.save(out, compression=None)
|
im.save(out, compression=None)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertNotIn("compression", test_im.info)
|
self.assertNotIn("compression", test_im.info)
|
||||||
|
|
||||||
# RGBA save
|
# RGBA save
|
||||||
im.convert("RGBA").save(out)
|
im.convert("RGBA").save(out)
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.size, (199, 199))
|
self.assertEqual(test_im.size, (199, 199))
|
||||||
|
|
||||||
test_file = "Tests/images/tga_id_field.tga"
|
test_file = "Tests/images/tga_id_field.tga"
|
||||||
im = Image.open(test_file)
|
im = Image.open(test_file)
|
||||||
|
@ -178,8 +178,8 @@ class TestFileTga(PillowTestCase):
|
||||||
|
|
||||||
# Save with compression
|
# Save with compression
|
||||||
im.save(out, compression="tga_rle")
|
im.save(out, compression="tga_rle")
|
||||||
test_im = Image.open(out)
|
with Image.open(out) as test_im:
|
||||||
self.assertEqual(test_im.info["compression"], "tga_rle")
|
self.assertEqual(test_im.info["compression"], "tga_rle")
|
||||||
|
|
||||||
def test_save_l_transparency(self):
|
def test_save_l_transparency(self):
|
||||||
# There are 559 transparent pixels in la.tga.
|
# There are 559 transparent pixels in la.tga.
|
||||||
|
|
|
@ -6,7 +6,7 @@ from PIL import Image, TiffImagePlugin
|
||||||
from PIL._util import py3
|
from PIL._util import py3
|
||||||
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
|
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__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -18,32 +18,53 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
hopper("RGB").save(filename)
|
hopper("RGB").save(filename)
|
||||||
|
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
self.assertEqual(im.format, "TIFF")
|
self.assertEqual(im.format, "TIFF")
|
||||||
|
|
||||||
hopper("1").save(filename)
|
hopper("1").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("L").save(filename)
|
hopper("L").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("P").save(filename)
|
hopper("P").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("RGB").save(filename)
|
hopper("RGB").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
hopper("I").save(filename)
|
hopper("I").save(filename)
|
||||||
Image.open(filename)
|
with Image.open(filename):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@unittest.skipIf(is_pypy(), "Requires CPython")
|
||||||
def test_unclosed_file(self):
|
def test_unclosed_file(self):
|
||||||
def open():
|
def open():
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
im = Image.open("Tests/images/multipage.tiff")
|
||||||
im.load()
|
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)
|
self.assert_warning(None, open)
|
||||||
|
|
||||||
def test_mac_tiff(self):
|
def test_mac_tiff(self):
|
||||||
|
@ -75,55 +96,55 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
def test_xyres_tiff(self):
|
def test_xyres_tiff(self):
|
||||||
filename = "Tests/images/pil168.tif"
|
filename = "Tests/images/pil168.tif"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# legacy api
|
# legacy api
|
||||||
self.assertIsInstance(im.tag[X_RESOLUTION][0], tuple)
|
self.assertIsInstance(im.tag[X_RESOLUTION][0], tuple)
|
||||||
self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple)
|
self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple)
|
||||||
|
|
||||||
# v2 api
|
# v2 api
|
||||||
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertIsInstance(im.tag_v2[Y_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):
|
def test_xyres_fallback_tiff(self):
|
||||||
filename = "Tests/images/compression.tif"
|
filename = "Tests/images/compression.tif"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# v2 api
|
# v2 api
|
||||||
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational)
|
||||||
self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT])
|
self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT])
|
||||||
|
|
||||||
# Legacy.
|
# Legacy.
|
||||||
self.assertEqual(im.info["resolution"], (100.0, 100.0))
|
self.assertEqual(im.info["resolution"], (100.0, 100.0))
|
||||||
# Fallback "inch".
|
# Fallback "inch".
|
||||||
self.assertEqual(im.info["dpi"], (100.0, 100.0))
|
self.assertEqual(im.info["dpi"], (100.0, 100.0))
|
||||||
|
|
||||||
def test_int_resolution(self):
|
def test_int_resolution(self):
|
||||||
filename = "Tests/images/pil168.tif"
|
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
|
# Try to read a file where X,Y_RESOLUTION are ints
|
||||||
im.tag_v2[X_RESOLUTION] = 71
|
im.tag_v2[X_RESOLUTION] = 71
|
||||||
im.tag_v2[Y_RESOLUTION] = 71
|
im.tag_v2[Y_RESOLUTION] = 71
|
||||||
im._setup()
|
im._setup()
|
||||||
self.assertEqual(im.info["dpi"], (71.0, 71.0))
|
self.assertEqual(im.info["dpi"], (71.0, 71.0))
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
for resolutionUnit, dpi in ((None, (72, 73)), (2, (72, 73)), (3, (183, 185))):
|
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"
|
"Tests/images/hopper_roundDown_" + str(resolutionUnit) + ".tif"
|
||||||
)
|
) as im:
|
||||||
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
||||||
self.assertEqual(im.info["dpi"], (dpi[0], dpi[0]))
|
self.assertEqual(im.info["dpi"], (dpi[0], dpi[0]))
|
||||||
|
|
||||||
im = Image.open(
|
with Image.open(
|
||||||
"Tests/images/hopper_roundUp_" + str(resolutionUnit) + ".tif"
|
"Tests/images/hopper_roundUp_" + str(resolutionUnit) + ".tif"
|
||||||
)
|
) as im:
|
||||||
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit)
|
||||||
self.assertEqual(im.info["dpi"], (dpi[1], dpi[1]))
|
self.assertEqual(im.info["dpi"], (dpi[1], dpi[1]))
|
||||||
|
|
||||||
def test_save_dpi_rounding(self):
|
def test_save_dpi_rounding(self):
|
||||||
outfile = self.tempfile("temp.tif")
|
outfile = self.tempfile("temp.tif")
|
||||||
|
@ -155,9 +176,9 @@ class TestFileTiff(PillowTestCase):
|
||||||
TiffImagePlugin.PREFIXES.pop()
|
TiffImagePlugin.PREFIXES.pop()
|
||||||
|
|
||||||
def test_bad_exif(self):
|
def test_bad_exif(self):
|
||||||
i = Image.open("Tests/images/hopper_bad_exif.jpg")
|
with Image.open("Tests/images/hopper_bad_exif.jpg") as i:
|
||||||
# Should not raise struct.error.
|
# Should not raise struct.error.
|
||||||
self.assert_warning(UserWarning, i._getexif)
|
self.assert_warning(UserWarning, i._getexif)
|
||||||
|
|
||||||
def test_save_rgba(self):
|
def test_save_rgba(self):
|
||||||
im = hopper("RGBA")
|
im = hopper("RGBA")
|
||||||
|
@ -238,44 +259,44 @@ class TestFileTiff(PillowTestCase):
|
||||||
["Tests/images/multipage-lastframe.tif", 1],
|
["Tests/images/multipage-lastframe.tif", 1],
|
||||||
["Tests/images/multipage.tiff", 3],
|
["Tests/images/multipage.tiff", 3],
|
||||||
]:
|
]:
|
||||||
im = Image.open(path)
|
with Image.open(path) as im:
|
||||||
self.assertEqual(im.n_frames, n_frames)
|
self.assertEqual(im.n_frames, n_frames)
|
||||||
self.assertEqual(im.is_animated, n_frames != 1)
|
self.assertEqual(im.is_animated, n_frames != 1)
|
||||||
|
|
||||||
def test_eoferror(self):
|
def test_eoferror(self):
|
||||||
im = Image.open("Tests/images/multipage-lastframe.tif")
|
with Image.open("Tests/images/multipage-lastframe.tif") as im:
|
||||||
n_frames = im.n_frames
|
n_frames = im.n_frames
|
||||||
|
|
||||||
# Test seeking past the last frame
|
# Test seeking past the last frame
|
||||||
self.assertRaises(EOFError, im.seek, n_frames)
|
self.assertRaises(EOFError, im.seek, n_frames)
|
||||||
self.assertLess(im.tell(), n_frames)
|
self.assertLess(im.tell(), n_frames)
|
||||||
|
|
||||||
# Test that seeking to the last frame does not raise an error
|
# Test that seeking to the last frame does not raise an error
|
||||||
im.seek(n_frames - 1)
|
im.seek(n_frames - 1)
|
||||||
|
|
||||||
def test_multipage(self):
|
def test_multipage(self):
|
||||||
# issue #862
|
# issue #862
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
# file is a multipage tiff: 10x10 green, 10x10 red, 20x20 blue
|
# file is a multipage tiff: 10x10 green, 10x10 red, 20x20 blue
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
||||||
|
|
||||||
im.seek(1)
|
im.seek(1)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0))
|
||||||
|
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0))
|
||||||
|
|
||||||
im.seek(2)
|
im.seek(2)
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.size, (20, 20))
|
self.assertEqual(im.size, (20, 20))
|
||||||
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255))
|
||||||
|
|
||||||
def test_multipage_last_frame(self):
|
def test_multipage_last_frame(self):
|
||||||
im = Image.open("Tests/images/multipage-lastframe.tif")
|
im = Image.open("Tests/images/multipage-lastframe.tif")
|
||||||
|
@ -285,62 +306,62 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
def test___str__(self):
|
def test___str__(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
ret = str(im.ifd)
|
ret = str(im.ifd)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertIsInstance(ret, str)
|
self.assertIsInstance(ret, str)
|
||||||
|
|
||||||
def test_dict(self):
|
def test_dict(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# v2 interface
|
# v2 interface
|
||||||
v2_tags = {
|
v2_tags = {
|
||||||
256: 55,
|
256: 55,
|
||||||
257: 43,
|
257: 43,
|
||||||
258: (8, 8, 8, 8),
|
258: (8, 8, 8, 8),
|
||||||
259: 1,
|
259: 1,
|
||||||
262: 2,
|
262: 2,
|
||||||
296: 2,
|
296: 2,
|
||||||
273: (8,),
|
273: (8,),
|
||||||
338: (1,),
|
338: (1,),
|
||||||
277: 4,
|
277: 4,
|
||||||
279: (9460,),
|
279: (9460,),
|
||||||
282: 72.0,
|
282: 72.0,
|
||||||
283: 72.0,
|
283: 72.0,
|
||||||
284: 1,
|
284: 1,
|
||||||
}
|
}
|
||||||
self.assertEqual(dict(im.tag_v2), v2_tags)
|
self.assertEqual(dict(im.tag_v2), v2_tags)
|
||||||
|
|
||||||
# legacy interface
|
# legacy interface
|
||||||
legacy_tags = {
|
legacy_tags = {
|
||||||
256: (55,),
|
256: (55,),
|
||||||
257: (43,),
|
257: (43,),
|
||||||
258: (8, 8, 8, 8),
|
258: (8, 8, 8, 8),
|
||||||
259: (1,),
|
259: (1,),
|
||||||
262: (2,),
|
262: (2,),
|
||||||
296: (2,),
|
296: (2,),
|
||||||
273: (8,),
|
273: (8,),
|
||||||
338: (1,),
|
338: (1,),
|
||||||
277: (4,),
|
277: (4,),
|
||||||
279: (9460,),
|
279: (9460,),
|
||||||
282: ((720000, 10000),),
|
282: ((720000, 10000),),
|
||||||
283: ((720000, 10000),),
|
283: ((720000, 10000),),
|
||||||
284: (1,),
|
284: (1,),
|
||||||
}
|
}
|
||||||
self.assertEqual(dict(im.tag), legacy_tags)
|
self.assertEqual(dict(im.tag), legacy_tags)
|
||||||
|
|
||||||
def test__delitem__(self):
|
def test__delitem__(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
len_before = len(dict(im.ifd))
|
len_before = len(dict(im.ifd))
|
||||||
del im.ifd[256]
|
del im.ifd[256]
|
||||||
len_after = len(dict(im.ifd))
|
len_after = len(dict(im.ifd))
|
||||||
self.assertEqual(len_before, len_after + 1)
|
self.assertEqual(len_before, len_after + 1)
|
||||||
|
|
||||||
def test_load_byte(self):
|
def test_load_byte(self):
|
||||||
for legacy_api in [False, True]:
|
for legacy_api in [False, True]:
|
||||||
|
@ -369,16 +390,16 @@ class TestFileTiff(PillowTestCase):
|
||||||
|
|
||||||
def test_seek(self):
|
def test_seek(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
|
|
||||||
def test_seek_eof(self):
|
def test_seek_eof(self):
|
||||||
filename = "Tests/images/pil136.tiff"
|
filename = "Tests/images/pil136.tiff"
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
self.assertEqual(im.tell(), 0)
|
self.assertEqual(im.tell(), 0)
|
||||||
self.assertRaises(EOFError, im.seek, -1)
|
self.assertRaises(EOFError, im.seek, -1)
|
||||||
self.assertRaises(EOFError, im.seek, 1)
|
self.assertRaises(EOFError, im.seek, 1)
|
||||||
|
|
||||||
def test__limit_rational_int(self):
|
def test__limit_rational_int(self):
|
||||||
from PIL.TiffImagePlugin import _limit_rational
|
from PIL.TiffImagePlugin import _limit_rational
|
||||||
|
@ -439,15 +460,15 @@ class TestFileTiff(PillowTestCase):
|
||||||
kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36}
|
kwargs = {"resolution_unit": "inch", "x_resolution": 72, "y_resolution": 36}
|
||||||
filename = self.tempfile("temp.tif")
|
filename = self.tempfile("temp.tif")
|
||||||
hopper("RGB").save(filename, **kwargs)
|
hopper("RGB").save(filename, **kwargs)
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# legacy interface
|
# legacy interface
|
||||||
self.assertEqual(im.tag[X_RESOLUTION][0][0], 72)
|
self.assertEqual(im.tag[X_RESOLUTION][0][0], 72)
|
||||||
self.assertEqual(im.tag[Y_RESOLUTION][0][0], 36)
|
self.assertEqual(im.tag[Y_RESOLUTION][0][0], 36)
|
||||||
|
|
||||||
# v2 interface
|
# v2 interface
|
||||||
self.assertEqual(im.tag_v2[X_RESOLUTION], 72)
|
self.assertEqual(im.tag_v2[X_RESOLUTION], 72)
|
||||||
self.assertEqual(im.tag_v2[Y_RESOLUTION], 36)
|
self.assertEqual(im.tag_v2[Y_RESOLUTION], 36)
|
||||||
|
|
||||||
def test_roundtrip_tiff_uint16(self):
|
def test_roundtrip_tiff_uint16(self):
|
||||||
# Test an image of all '0' values
|
# Test an image of all '0' values
|
||||||
|
@ -480,9 +501,8 @@ class TestFileTiff(PillowTestCase):
|
||||||
def test_strip_planar_raw_with_overviews(self):
|
def test_strip_planar_raw_with_overviews(self):
|
||||||
# gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16
|
# gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16
|
||||||
infile = "Tests/images/tiff_strip_planar_raw_with_overviews.tif"
|
infile = "Tests/images/tiff_strip_planar_raw_with_overviews.tif"
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
|
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
||||||
self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png")
|
|
||||||
|
|
||||||
def test_tiled_planar_raw(self):
|
def test_tiled_planar_raw(self):
|
||||||
# gdal_translate -of GTiff -co TILED=YES -co BLOCKXSIZE=32 \
|
# 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.
|
# Try save-load round trip to make sure both handle icc_profile.
|
||||||
tmpfile = self.tempfile("temp.tif")
|
tmpfile = self.tempfile("temp.tif")
|
||||||
im.save(tmpfile, "TIFF", compression="raw")
|
im.save(tmpfile, "TIFF", compression="raw")
|
||||||
reloaded = Image.open(tmpfile)
|
with Image.open(tmpfile) as reloaded:
|
||||||
|
self.assertEqual(b"Dummy value", reloaded.info["icc_profile"])
|
||||||
self.assertEqual(b"Dummy value", reloaded.info["icc_profile"])
|
|
||||||
|
|
||||||
def test_close_on_load_exclusive(self):
|
def test_close_on_load_exclusive(self):
|
||||||
# similar to test_fd_leak, but runs on unixlike os
|
# similar to test_fd_leak, but runs on unixlike os
|
||||||
|
|
|
@ -52,77 +52,81 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
|
|
||||||
img.save(f, tiffinfo=info)
|
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[ImageJMetaDataByteCounts], (len(bindata),))
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),))
|
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),))
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
|
self.assertEqual(loaded.tag[ImageJMetaData], bindata)
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
|
self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata)
|
||||||
|
|
||||||
self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata,))
|
self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata,))
|
||||||
self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata)
|
self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata)
|
||||||
|
|
||||||
loaded_float = loaded.tag[tag_ids["RollAngle"]][0]
|
loaded_float = loaded.tag[tag_ids["RollAngle"]][0]
|
||||||
self.assertAlmostEqual(loaded_float, floatdata, places=5)
|
self.assertAlmostEqual(loaded_float, floatdata, places=5)
|
||||||
loaded_double = loaded.tag[tag_ids["YawAngle"]][0]
|
loaded_double = loaded.tag[tag_ids["YawAngle"]][0]
|
||||||
self.assertAlmostEqual(loaded_double, doubledata)
|
self.assertAlmostEqual(loaded_double, doubledata)
|
||||||
|
|
||||||
# check with 2 element ImageJMetaDataByteCounts, issue #2006
|
# check with 2 element ImageJMetaDataByteCounts, issue #2006
|
||||||
|
|
||||||
info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8)
|
info[ImageJMetaDataByteCounts] = (8, len(bindata) - 8)
|
||||||
img.save(f, tiffinfo=info)
|
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(
|
||||||
self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8))
|
loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8)
|
||||||
|
)
|
||||||
|
|
||||||
def test_read_metadata(self):
|
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(
|
self.assertEqual(
|
||||||
{
|
{
|
||||||
"YResolution": IFDRational(4294967295, 113653537),
|
"YResolution": IFDRational(4294967295, 113653537),
|
||||||
"PlanarConfiguration": 1,
|
"PlanarConfiguration": 1,
|
||||||
"BitsPerSample": (1,),
|
"BitsPerSample": (1,),
|
||||||
"ImageLength": 128,
|
"ImageLength": 128,
|
||||||
"Compression": 4,
|
"Compression": 4,
|
||||||
"FillOrder": 1,
|
"FillOrder": 1,
|
||||||
"RowsPerStrip": 128,
|
"RowsPerStrip": 128,
|
||||||
"ResolutionUnit": 3,
|
"ResolutionUnit": 3,
|
||||||
"PhotometricInterpretation": 0,
|
"PhotometricInterpretation": 0,
|
||||||
"PageNumber": (0, 1),
|
"PageNumber": (0, 1),
|
||||||
"XResolution": IFDRational(4294967295, 113653537),
|
"XResolution": IFDRational(4294967295, 113653537),
|
||||||
"ImageWidth": 128,
|
"ImageWidth": 128,
|
||||||
"Orientation": 1,
|
"Orientation": 1,
|
||||||
"StripByteCounts": (1968,),
|
"StripByteCounts": (1968,),
|
||||||
"SamplesPerPixel": 1,
|
"SamplesPerPixel": 1,
|
||||||
"StripOffsets": (8,),
|
"StripOffsets": (8,),
|
||||||
},
|
},
|
||||||
img.tag_v2.named(),
|
img.tag_v2.named(),
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
{
|
{
|
||||||
"YResolution": ((4294967295, 113653537),),
|
"YResolution": ((4294967295, 113653537),),
|
||||||
"PlanarConfiguration": (1,),
|
"PlanarConfiguration": (1,),
|
||||||
"BitsPerSample": (1,),
|
"BitsPerSample": (1,),
|
||||||
"ImageLength": (128,),
|
"ImageLength": (128,),
|
||||||
"Compression": (4,),
|
"Compression": (4,),
|
||||||
"FillOrder": (1,),
|
"FillOrder": (1,),
|
||||||
"RowsPerStrip": (128,),
|
"RowsPerStrip": (128,),
|
||||||
"ResolutionUnit": (3,),
|
"ResolutionUnit": (3,),
|
||||||
"PhotometricInterpretation": (0,),
|
"PhotometricInterpretation": (0,),
|
||||||
"PageNumber": (0, 1),
|
"PageNumber": (0, 1),
|
||||||
"XResolution": ((4294967295, 113653537),),
|
"XResolution": ((4294967295, 113653537),),
|
||||||
"ImageWidth": (128,),
|
"ImageWidth": (128,),
|
||||||
"Orientation": (1,),
|
"Orientation": (1,),
|
||||||
"StripByteCounts": (1968,),
|
"StripByteCounts": (1968,),
|
||||||
"SamplesPerPixel": (1,),
|
"SamplesPerPixel": (1,),
|
||||||
"StripOffsets": (8,),
|
"StripOffsets": (8,),
|
||||||
},
|
},
|
||||||
img.tag.named(),
|
img.tag.named(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_write_metadata(self):
|
def test_write_metadata(self):
|
||||||
""" Test metadata writing through the python code """
|
""" Test metadata writing through the python code """
|
||||||
|
@ -131,10 +135,10 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
f = self.tempfile("temp.tiff")
|
f = self.tempfile("temp.tiff")
|
||||||
img.save(f, tiffinfo=img.tag)
|
img.save(f, tiffinfo=img.tag)
|
||||||
|
|
||||||
loaded = Image.open(f)
|
with Image.open(f) as loaded:
|
||||||
|
|
||||||
original = img.tag_v2.named()
|
original = img.tag_v2.named()
|
||||||
reloaded = loaded.tag_v2.named()
|
reloaded = loaded.tag_v2.named()
|
||||||
|
|
||||||
for k, v in original.items():
|
for k, v in original.items():
|
||||||
if isinstance(v, IFDRational):
|
if isinstance(v, IFDRational):
|
||||||
|
@ -187,18 +191,18 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
out = self.tempfile("temp.tiff")
|
out = self.tempfile("temp.tiff")
|
||||||
|
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertNotIsInstance(im.info["icc_profile"], tuple)
|
self.assertNotIsInstance(im.info["icc_profile"], tuple)
|
||||||
self.assertEqual(im.info["icc_profile"], reloaded.info["icc_profile"])
|
self.assertEqual(im.info["icc_profile"], reloaded.info["icc_profile"])
|
||||||
|
|
||||||
def test_iccprofile_binary(self):
|
def test_iccprofile_binary(self):
|
||||||
# https://github.com/python-pillow/Pillow/issues/1526
|
# https://github.com/python-pillow/Pillow/issues/1526
|
||||||
# We should be able to load this,
|
# We should be able to load this,
|
||||||
# but probably won't be able to save it.
|
# but probably won't be able to save it.
|
||||||
|
|
||||||
im = Image.open("Tests/images/hopper.iccprofile_binary.tif")
|
with Image.open("Tests/images/hopper.iccprofile_binary.tif") as im:
|
||||||
self.assertEqual(im.tag_v2.tagtype[34675], 1)
|
self.assertEqual(im.tag_v2.tagtype[34675], 1)
|
||||||
self.assertTrue(im.info["icc_profile"])
|
self.assertTrue(im.info["icc_profile"])
|
||||||
|
|
||||||
def test_iccprofile_save_png(self):
|
def test_iccprofile_save_png(self):
|
||||||
im = Image.open("Tests/images/hopper.iccprofile.tif")
|
im = Image.open("Tests/images/hopper.iccprofile.tif")
|
||||||
|
@ -218,9 +222,9 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
out = self.tempfile("temp.tiff")
|
out = self.tempfile("temp.tiff")
|
||||||
im.save(out, tiffinfo=info, compression="raw")
|
im.save(out, tiffinfo=info, compression="raw")
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertEqual(0, reloaded.tag_v2[41988].numerator)
|
self.assertEqual(0, reloaded.tag_v2[41988].numerator)
|
||||||
self.assertEqual(0, reloaded.tag_v2[41988].denominator)
|
self.assertEqual(0, reloaded.tag_v2[41988].denominator)
|
||||||
|
|
||||||
def test_empty_values(self):
|
def test_empty_values(self):
|
||||||
data = io.BytesIO(
|
data = io.BytesIO(
|
||||||
|
@ -243,9 +247,9 @@ class TestFileTiffMetadata(PillowTestCase):
|
||||||
self.assertIsInstance(im.tag_v2[34377][0], bytes)
|
self.assertIsInstance(im.tag_v2[34377][0], bytes)
|
||||||
out = self.tempfile("temp.tiff")
|
out = self.tempfile("temp.tiff")
|
||||||
im.save(out)
|
im.save(out)
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertEqual(len(reloaded.tag_v2[34377]), 1)
|
self.assertEqual(len(reloaded.tag_v2[34377]), 1)
|
||||||
self.assertIsInstance(reloaded.tag_v2[34377][0], bytes)
|
self.assertIsInstance(reloaded.tag_v2[34377][0], bytes)
|
||||||
|
|
||||||
def test_too_many_entries(self):
|
def test_too_many_entries(self):
|
||||||
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
ifd = TiffImagePlugin.ImageFileDirectory_v2()
|
||||||
|
|
|
@ -158,19 +158,19 @@ class TestFileWebp(PillowTestCase):
|
||||||
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP save all not available"
|
HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP save all not available"
|
||||||
)
|
)
|
||||||
def test_background_from_gif(self):
|
def test_background_from_gif(self):
|
||||||
im = Image.open("Tests/images/chi.gif")
|
with Image.open("Tests/images/chi.gif") as im:
|
||||||
original_value = im.convert("RGB").getpixel((1, 1))
|
original_value = im.convert("RGB").getpixel((1, 1))
|
||||||
|
|
||||||
# Save as WEBP
|
# Save as WEBP
|
||||||
out_webp = self.tempfile("temp.webp")
|
out_webp = self.tempfile("temp.webp")
|
||||||
im.save(out_webp, save_all=True)
|
im.save(out_webp, save_all=True)
|
||||||
|
|
||||||
# Save as GIF
|
# Save as GIF
|
||||||
out_gif = self.tempfile("temp.gif")
|
out_gif = self.tempfile("temp.gif")
|
||||||
Image.open(out_webp).save(out_gif)
|
Image.open(out_webp).save(out_gif)
|
||||||
|
|
||||||
reread = Image.open(out_gif)
|
with Image.open(out_gif) as reread:
|
||||||
reread_value = reread.convert("RGB").getpixel((1, 1))
|
reread_value = reread.convert("RGB").getpixel((1, 1))
|
||||||
difference = sum(
|
difference = sum(
|
||||||
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
|
[abs(original_value[i] - reread_value[i]) for i in range(0, 3)]
|
||||||
)
|
)
|
||||||
|
|
|
@ -103,7 +103,8 @@ class TestFileWebpAlpha(PillowTestCase):
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
file_path = "Tests/images/transparent.gif"
|
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)
|
image = Image.open(temp_file)
|
||||||
|
|
||||||
self.assertEqual(image.mode, "RGBA")
|
self.assertEqual(image.mode, "RGBA")
|
||||||
|
@ -112,6 +113,7 @@ class TestFileWebpAlpha(PillowTestCase):
|
||||||
|
|
||||||
image.load()
|
image.load()
|
||||||
image.getdata()
|
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)
|
self.assert_image_similar(image, target, 25.0)
|
||||||
|
|
|
@ -28,13 +28,13 @@ class TestFileWebpAnimation(PillowTestCase):
|
||||||
attributes correctly.
|
attributes correctly.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
im = Image.open("Tests/images/hopper.webp")
|
with Image.open("Tests/images/hopper.webp") as im:
|
||||||
self.assertEqual(im.n_frames, 1)
|
self.assertEqual(im.n_frames, 1)
|
||||||
self.assertFalse(im.is_animated)
|
self.assertFalse(im.is_animated)
|
||||||
|
|
||||||
im = Image.open("Tests/images/iss634.webp")
|
with Image.open("Tests/images/iss634.webp") as im:
|
||||||
self.assertEqual(im.n_frames, 42)
|
self.assertEqual(im.n_frames, 42)
|
||||||
self.assertTrue(im.is_animated)
|
self.assertTrue(im.is_animated)
|
||||||
|
|
||||||
def test_write_animation_L(self):
|
def test_write_animation_L(self):
|
||||||
"""
|
"""
|
||||||
|
@ -43,23 +43,23 @@ class TestFileWebpAnimation(PillowTestCase):
|
||||||
visually similar.
|
visually similar.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
orig = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as orig:
|
||||||
self.assertGreater(orig.n_frames, 1)
|
self.assertGreater(orig.n_frames, 1)
|
||||||
|
|
||||||
temp_file = self.tempfile("temp.webp")
|
temp_file = self.tempfile("temp.webp")
|
||||||
orig.save(temp_file, save_all=True)
|
orig.save(temp_file, save_all=True)
|
||||||
im = Image.open(temp_file)
|
im = Image.open(temp_file)
|
||||||
self.assertEqual(im.n_frames, orig.n_frames)
|
self.assertEqual(im.n_frames, orig.n_frames)
|
||||||
|
|
||||||
# Compare first and last frames to the original animated GIF
|
# Compare first and last frames to the original animated GIF
|
||||||
orig.load()
|
orig.load()
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
||||||
orig.seek(orig.n_frames - 1)
|
orig.seek(orig.n_frames - 1)
|
||||||
im.seek(im.n_frames - 1)
|
im.seek(im.n_frames - 1)
|
||||||
orig.load()
|
orig.load()
|
||||||
im.load()
|
im.load()
|
||||||
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
self.assert_image_similar(im, orig.convert("RGBA"), 25.0)
|
||||||
|
|
||||||
def test_write_animation_RGB(self):
|
def test_write_animation_RGB(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -24,21 +24,21 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
def test_read_exif_metadata(self):
|
def test_read_exif_metadata(self):
|
||||||
|
|
||||||
file_path = "Tests/images/flower.webp"
|
file_path = "Tests/images/flower.webp"
|
||||||
image = Image.open(file_path)
|
with Image.open(file_path) as image:
|
||||||
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
exif_data = image.info.get("exif", None)
|
exif_data = image.info.get("exif", None)
|
||||||
self.assertTrue(exif_data)
|
self.assertTrue(exif_data)
|
||||||
|
|
||||||
exif = image._getexif()
|
exif = image._getexif()
|
||||||
|
|
||||||
# camera make
|
# camera make
|
||||||
self.assertEqual(exif[271], "Canon")
|
self.assertEqual(exif[271], "Canon")
|
||||||
|
|
||||||
jpeg_image = Image.open("Tests/images/flower.jpg")
|
with Image.open("Tests/images/flower.jpg") as jpeg_image:
|
||||||
expected_exif = jpeg_image.info["exif"]
|
expected_exif = jpeg_image.info["exif"]
|
||||||
|
|
||||||
self.assertEqual(exif_data, expected_exif)
|
self.assertEqual(exif_data, expected_exif)
|
||||||
|
|
||||||
def test_write_exif_metadata(self):
|
def test_write_exif_metadata(self):
|
||||||
file_path = "Tests/images/flower.jpg"
|
file_path = "Tests/images/flower.jpg"
|
||||||
|
@ -60,17 +60,17 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
def test_read_icc_profile(self):
|
def test_read_icc_profile(self):
|
||||||
|
|
||||||
file_path = "Tests/images/flower2.webp"
|
file_path = "Tests/images/flower2.webp"
|
||||||
image = Image.open(file_path)
|
with Image.open(file_path) as image:
|
||||||
|
|
||||||
self.assertEqual(image.format, "WEBP")
|
self.assertEqual(image.format, "WEBP")
|
||||||
self.assertTrue(image.info.get("icc_profile", None))
|
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")
|
with Image.open("Tests/images/flower2.jpg") as jpeg_image:
|
||||||
expected_icc = jpeg_image.info["icc_profile"]
|
expected_icc = jpeg_image.info["icc_profile"]
|
||||||
|
|
||||||
self.assertEqual(icc, expected_icc)
|
self.assertEqual(icc, expected_icc)
|
||||||
|
|
||||||
def test_write_icc_metadata(self):
|
def test_write_icc_metadata(self):
|
||||||
file_path = "Tests/images/flower2.jpg"
|
file_path = "Tests/images/flower2.jpg"
|
||||||
|
@ -126,10 +126,10 @@ class TestFileWebpMetadata(PillowTestCase):
|
||||||
xmp=xmp_data,
|
xmp=xmp_data,
|
||||||
)
|
)
|
||||||
|
|
||||||
image = Image.open(temp_file)
|
with Image.open(temp_file) as image:
|
||||||
self.assertIn("icc_profile", image.info)
|
self.assertIn("icc_profile", image.info)
|
||||||
self.assertIn("exif", image.info)
|
self.assertIn("exif", image.info)
|
||||||
self.assertIn("xmp", image.info)
|
self.assertIn("xmp", image.info)
|
||||||
self.assertEqual(iccp_data, image.info.get("icc_profile", None))
|
self.assertEqual(iccp_data, image.info.get("icc_profile", None))
|
||||||
self.assertEqual(exif_data, image.info.get("exif", None))
|
self.assertEqual(exif_data, image.info.get("exif", None))
|
||||||
self.assertEqual(xmp_data, image.info.get("xmp", None))
|
self.assertEqual(xmp_data, image.info.get("xmp", None))
|
||||||
|
|
|
@ -7,24 +7,24 @@ class TestFileWmf(PillowTestCase):
|
||||||
def test_load_raw(self):
|
def test_load_raw(self):
|
||||||
|
|
||||||
# Test basic EMF open and rendering
|
# Test basic EMF open and rendering
|
||||||
im = Image.open("Tests/images/drawing.emf")
|
with Image.open("Tests/images/drawing.emf") as im:
|
||||||
if hasattr(Image.core, "drawwmf"):
|
if hasattr(Image.core, "drawwmf"):
|
||||||
# Currently, support for WMF/EMF is Windows-only
|
# Currently, support for WMF/EMF is Windows-only
|
||||||
im.load()
|
im.load()
|
||||||
# Compare to reference rendering
|
# Compare to reference rendering
|
||||||
imref = Image.open("Tests/images/drawing_emf_ref.png")
|
imref = Image.open("Tests/images/drawing_emf_ref.png")
|
||||||
imref.load()
|
imref.load()
|
||||||
self.assert_image_similar(im, imref, 0)
|
self.assert_image_similar(im, imref, 0)
|
||||||
|
|
||||||
# Test basic WMF open and rendering
|
# Test basic WMF open and rendering
|
||||||
im = Image.open("Tests/images/drawing.wmf")
|
with Image.open("Tests/images/drawing.wmf") as im:
|
||||||
if hasattr(Image.core, "drawwmf"):
|
if hasattr(Image.core, "drawwmf"):
|
||||||
# Currently, support for WMF/EMF is Windows-only
|
# Currently, support for WMF/EMF is Windows-only
|
||||||
im.load()
|
im.load()
|
||||||
# Compare to reference rendering
|
# Compare to reference rendering
|
||||||
imref = Image.open("Tests/images/drawing_wmf_ref.png")
|
imref = Image.open("Tests/images/drawing_wmf_ref.png")
|
||||||
imref.load()
|
imref.load()
|
||||||
self.assert_image_similar(im, imref, 2.0)
|
self.assert_image_similar(im, imref, 2.0)
|
||||||
|
|
||||||
def test_register_handler(self):
|
def test_register_handler(self):
|
||||||
class TestHandler:
|
class TestHandler:
|
||||||
|
@ -46,12 +46,12 @@ class TestFileWmf(PillowTestCase):
|
||||||
|
|
||||||
def test_load_dpi_rounding(self):
|
def test_load_dpi_rounding(self):
|
||||||
# Round up
|
# Round up
|
||||||
im = Image.open("Tests/images/drawing.emf")
|
with Image.open("Tests/images/drawing.emf") as im:
|
||||||
self.assertEqual(im.info["dpi"], 1424)
|
self.assertEqual(im.info["dpi"], 1424)
|
||||||
|
|
||||||
# Round down
|
# Round down
|
||||||
im = Image.open("Tests/images/drawing_roundDown.emf")
|
with Image.open("Tests/images/drawing_roundDown.emf") as im:
|
||||||
self.assertEqual(im.info["dpi"], 1426)
|
self.assertEqual(im.info["dpi"], 1426)
|
||||||
|
|
||||||
def test_save(self):
|
def test_save(self):
|
||||||
im = hopper()
|
im = hopper()
|
||||||
|
|
|
@ -42,11 +42,11 @@ class TestFileXbm(PillowTestCase):
|
||||||
filename = "Tests/images/hopper.xbm"
|
filename = "Tests/images/hopper.xbm"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.mode, "1")
|
self.assertEqual(im.mode, "1")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
|
|
||||||
def test_open_filename_with_underscore(self):
|
def test_open_filename_with_underscore(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
|
@ -54,8 +54,8 @@ class TestFileXbm(PillowTestCase):
|
||||||
filename = "Tests/images/hopper_underscore.xbm"
|
filename = "Tests/images/hopper_underscore.xbm"
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
im = Image.open(filename)
|
with Image.open(filename) as im:
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(im.mode, "1")
|
self.assertEqual(im.mode, "1")
|
||||||
self.assertEqual(im.size, (128, 128))
|
self.assertEqual(im.size, (128, 128))
|
||||||
|
|
|
@ -23,11 +23,11 @@ class TestFileXpm(PillowTestCase):
|
||||||
|
|
||||||
def test_load_read(self):
|
def test_load_read(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = Image.open(TEST_FILE)
|
with Image.open(TEST_FILE) as im:
|
||||||
dummy_bytes = 1
|
dummy_bytes = 1
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
data = im.load_read(dummy_bytes)
|
data = im.load_read(dummy_bytes)
|
||||||
|
|
||||||
# Assert
|
# Assert
|
||||||
self.assertEqual(len(data), 16384)
|
self.assertEqual(len(data), 16384)
|
||||||
|
|
|
@ -97,9 +97,9 @@ class TestImage(PillowTestCase):
|
||||||
def test_pathlib(self):
|
def test_pathlib(self):
|
||||||
from PIL.Image import Path
|
from PIL.Image import Path
|
||||||
|
|
||||||
im = Image.open(Path("Tests/images/multipage-mmap.tiff"))
|
with Image.open(Path("Tests/images/multipage-mmap.tiff")) as im:
|
||||||
self.assertEqual(im.mode, "P")
|
self.assertEqual(im.mode, "P")
|
||||||
self.assertEqual(im.size, (10, 10))
|
self.assertEqual(im.size, (10, 10))
|
||||||
|
|
||||||
im = Image.open(Path("Tests/images/hopper.jpg"))
|
im = Image.open(Path("Tests/images/hopper.jpg"))
|
||||||
self.assertEqual(im.mode, "RGB")
|
self.assertEqual(im.mode, "RGB")
|
||||||
|
@ -346,7 +346,8 @@ class TestImage(PillowTestCase):
|
||||||
def test_registered_extensions(self):
|
def test_registered_extensions(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
# Open an image to trigger plugin registration
|
# Open an image to trigger plugin registration
|
||||||
Image.open("Tests/images/rgb.jpg")
|
with Image.open("Tests/images/rgb.jpg"):
|
||||||
|
pass
|
||||||
|
|
||||||
# Act
|
# Act
|
||||||
extensions = Image.registered_extensions()
|
extensions = Image.registered_extensions()
|
||||||
|
@ -448,10 +449,10 @@ class TestImage(PillowTestCase):
|
||||||
|
|
||||||
def test_offset_not_implemented(self):
|
def test_offset_not_implemented(self):
|
||||||
# Arrange
|
# Arrange
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
|
|
||||||
# Act / Assert
|
# Act / Assert
|
||||||
self.assertRaises(NotImplementedError, im.offset, None)
|
self.assertRaises(NotImplementedError, im.offset, None)
|
||||||
|
|
||||||
def test_fromstring(self):
|
def test_fromstring(self):
|
||||||
self.assertRaises(NotImplementedError, Image.fromstring)
|
self.assertRaises(NotImplementedError, Image.fromstring)
|
||||||
|
@ -522,8 +523,8 @@ class TestImage(PillowTestCase):
|
||||||
|
|
||||||
def test_remap_palette(self):
|
def test_remap_palette(self):
|
||||||
# Test illegal image mode
|
# Test illegal image mode
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
self.assertRaises(ValueError, im.remap_palette, None)
|
self.assertRaises(ValueError, im.remap_palette, None)
|
||||||
|
|
||||||
def test__new(self):
|
def test__new(self):
|
||||||
from PIL import ImagePalette
|
from PIL import ImagePalette
|
||||||
|
@ -587,12 +588,12 @@ class TestImage(PillowTestCase):
|
||||||
|
|
||||||
def test_overrun(self):
|
def test_overrun(self):
|
||||||
for file in ["fli_overrun.bin", "sgi_overrun.bin", "pcx_overrun.bin"]:
|
for file in ["fli_overrun.bin", "sgi_overrun.bin", "pcx_overrun.bin"]:
|
||||||
im = Image.open(os.path.join("Tests/images", file))
|
with Image.open(os.path.join("Tests/images", file)) as im:
|
||||||
try:
|
try:
|
||||||
im.load()
|
im.load()
|
||||||
self.assertFail()
|
self.assertFail()
|
||||||
except IOError as e:
|
except IOError as e:
|
||||||
self.assertEqual(str(e), "buffer overrun when reading image file")
|
self.assertEqual(str(e), "buffer overrun when reading image file")
|
||||||
|
|
||||||
|
|
||||||
class MockEncoder(object):
|
class MockEncoder(object):
|
||||||
|
|
|
@ -144,11 +144,11 @@ class TestImageConvert(PillowTestCase):
|
||||||
|
|
||||||
def test_gif_with_rgba_palette_to_p(self):
|
def test_gif_with_rgba_palette_to_p(self):
|
||||||
# See https://github.com/python-pillow/Pillow/issues/2433
|
# See https://github.com/python-pillow/Pillow/issues/2433
|
||||||
im = Image.open("Tests/images/hopper.gif")
|
with Image.open("Tests/images/hopper.gif") as im:
|
||||||
im.info["transparency"] = 255
|
im.info["transparency"] = 255
|
||||||
im.load()
|
im.load()
|
||||||
self.assertEqual(im.palette.mode, "RGBA")
|
self.assertEqual(im.palette.mode, "RGBA")
|
||||||
im_p = im.convert("P")
|
im_p = im.convert("P")
|
||||||
|
|
||||||
# Should not raise ValueError: unrecognized raw mode
|
# Should not raise ValueError: unrecognized raw mode
|
||||||
im_p.load()
|
im_p.load()
|
||||||
|
|
|
@ -5,12 +5,15 @@ from .test_imageqt import PillowQtTestCase
|
||||||
|
|
||||||
|
|
||||||
class TestFromQImage(PillowQtTestCase, PillowTestCase):
|
class TestFromQImage(PillowQtTestCase, PillowTestCase):
|
||||||
|
def setUp(self):
|
||||||
files_to_test = [
|
super(TestFromQImage, self).setUp()
|
||||||
hopper(),
|
self.files_to_test = [
|
||||||
Image.open("Tests/images/transparent.png"),
|
hopper(),
|
||||||
Image.open("Tests/images/7x13.png"),
|
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):
|
def roundtrip(self, expected):
|
||||||
# PIL -> Qt
|
# PIL -> Qt
|
||||||
|
|
|
@ -6,8 +6,8 @@ from .helper import PillowTestCase, hopper
|
||||||
class TestImageMode(PillowTestCase):
|
class TestImageMode(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
|
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
im.mode
|
im.mode
|
||||||
|
|
||||||
from PIL import ImageMode
|
from PIL import ImageMode
|
||||||
|
|
||||||
|
|
|
@ -148,5 +148,5 @@ class TestImageResize(PillowTestCase):
|
||||||
resize(mode, (188, 214))
|
resize(mode, (188, 214))
|
||||||
|
|
||||||
# Test unknown resampling filter
|
# Test unknown resampling filter
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
self.assertRaises(ValueError, im.resize, (10, 10), "unknown")
|
self.assertRaises(ValueError, im.resize, (10, 10), "unknown")
|
||||||
|
|
|
@ -39,11 +39,11 @@ class TestImageThumbnail(PillowTestCase):
|
||||||
|
|
||||||
def test_no_resize(self):
|
def test_no_resize(self):
|
||||||
# Check that draft() can resize the image to the destination size
|
# Check that draft() can resize the image to the destination size
|
||||||
im = Image.open("Tests/images/hopper.jpg")
|
with Image.open("Tests/images/hopper.jpg") as im:
|
||||||
im.draft(None, (64, 64))
|
im.draft(None, (64, 64))
|
||||||
self.assertEqual(im.size, (64, 64))
|
self.assertEqual(im.size, (64, 64))
|
||||||
|
|
||||||
# Test thumbnail(), where only draft() is necessary to resize the image
|
# Test thumbnail(), where only draft() is necessary to resize the image
|
||||||
im = Image.open("Tests/images/hopper.jpg")
|
with Image.open("Tests/images/hopper.jpg") as im:
|
||||||
im.thumbnail((64, 64))
|
im.thumbnail((64, 64))
|
||||||
self.assert_image(im, im.mode, (64, 64))
|
self.assert_image(im, im.mode, (64, 64))
|
||||||
|
|
|
@ -156,21 +156,21 @@ class TestImageTransform(PillowTestCase):
|
||||||
self.test_mesh()
|
self.test_mesh()
|
||||||
|
|
||||||
def test_missing_method_data(self):
|
def test_missing_method_data(self):
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
self.assertRaises(ValueError, im.transform, (100, 100), None)
|
self.assertRaises(ValueError, im.transform, (100, 100), None)
|
||||||
|
|
||||||
def test_unknown_resampling_filter(self):
|
def test_unknown_resampling_filter(self):
|
||||||
im = hopper()
|
with hopper() as im:
|
||||||
(w, h) = im.size
|
(w, h) = im.size
|
||||||
for resample in (Image.BOX, "unknown"):
|
for resample in (Image.BOX, "unknown"):
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
ValueError,
|
ValueError,
|
||||||
im.transform,
|
im.transform,
|
||||||
(100, 100),
|
(100, 100),
|
||||||
Image.EXTENT,
|
Image.EXTENT,
|
||||||
(0, 0, w, h),
|
(0, 0, w, h),
|
||||||
resample,
|
resample,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestImageTransformAffine(PillowTestCase):
|
class TestImageTransformAffine(PillowTestCase):
|
||||||
|
|
|
@ -58,10 +58,10 @@ class TestImageCms(PillowTestCase):
|
||||||
i = ImageCms.applyTransform(hopper(), t)
|
i = ImageCms.applyTransform(hopper(), t)
|
||||||
self.assert_image(i, "RGB", (128, 128))
|
self.assert_image(i, "RGB", (128, 128))
|
||||||
|
|
||||||
i = hopper()
|
with hopper() as i:
|
||||||
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
|
t = ImageCms.buildTransform(SRGB, SRGB, "RGB", "RGB")
|
||||||
ImageCms.applyTransform(hopper(), t, inPlace=True)
|
ImageCms.applyTransform(hopper(), t, inPlace=True)
|
||||||
self.assert_image(i, "RGB", (128, 128))
|
self.assert_image(i, "RGB", (128, 128))
|
||||||
|
|
||||||
p = ImageCms.createProfile("sRGB")
|
p = ImageCms.createProfile("sRGB")
|
||||||
o = ImageCms.getOpenProfile(SRGB)
|
o = ImageCms.getOpenProfile(SRGB)
|
||||||
|
@ -151,8 +151,8 @@ class TestImageCms(PillowTestCase):
|
||||||
def test_extensions(self):
|
def test_extensions(self):
|
||||||
# extensions
|
# extensions
|
||||||
|
|
||||||
i = Image.open("Tests/images/rgb.jpg")
|
with Image.open("Tests/images/rgb.jpg") as i:
|
||||||
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
|
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
ImageCms.getProfileName(p).strip(),
|
ImageCms.getProfileName(p).strip(),
|
||||||
"IEC 61966-2.1 Default RGB colour space - sRGB",
|
"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"))
|
self.assertRaises(ValueError, t.apply_in_place, hopper("RGBA"))
|
||||||
|
|
||||||
# the procedural pyCMS API uses PyCMSError for all sorts of errors
|
# the procedural pyCMS API uses PyCMSError for all sorts of errors
|
||||||
self.assertRaises(
|
with hopper() as im:
|
||||||
ImageCms.PyCMSError, ImageCms.profileToProfile, hopper(), "foo", "bar"
|
self.assertRaises(
|
||||||
)
|
ImageCms.PyCMSError, ImageCms.profileToProfile, im, "foo", "bar"
|
||||||
|
)
|
||||||
self.assertRaises(
|
self.assertRaises(
|
||||||
ImageCms.PyCMSError, ImageCms.buildTransform, "foo", "bar", "RGB", "RGB"
|
ImageCms.PyCMSError, ImageCms.buildTransform, "foo", "bar", "RGB", "RGB"
|
||||||
)
|
)
|
||||||
|
@ -266,8 +267,8 @@ class TestImageCms(PillowTestCase):
|
||||||
self.assert_image_similar(hopper(), out, 2)
|
self.assert_image_similar(hopper(), out, 2)
|
||||||
|
|
||||||
def test_profile_tobytes(self):
|
def test_profile_tobytes(self):
|
||||||
i = Image.open("Tests/images/rgb.jpg")
|
with Image.open("Tests/images/rgb.jpg") as i:
|
||||||
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
|
p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"]))
|
||||||
|
|
||||||
p2 = ImageCms.getOpenProfile(BytesIO(p.tobytes()))
|
p2 = ImageCms.getOpenProfile(BytesIO(p.tobytes()))
|
||||||
|
|
||||||
|
|
|
@ -45,10 +45,10 @@ class TestImageDraw(PillowTestCase):
|
||||||
draw.rectangle(list(range(4)))
|
draw.rectangle(list(range(4)))
|
||||||
|
|
||||||
def test_valueerror(self):
|
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 = ImageDraw.Draw(im)
|
||||||
draw.line((0, 0), fill=(0, 0, 0))
|
draw.line((0, 0), fill=(0, 0, 0))
|
||||||
|
|
||||||
def test_mode_mismatch(self):
|
def test_mode_mismatch(self):
|
||||||
im = hopper("RGB").copy()
|
im = hopper("RGB").copy()
|
||||||
|
|
|
@ -115,13 +115,13 @@ class TestImageFile(PillowTestCase):
|
||||||
if "zip_encoder" not in codecs:
|
if "zip_encoder" not in codecs:
|
||||||
self.skipTest("PNG (zlib) encoder not available")
|
self.skipTest("PNG (zlib) encoder not available")
|
||||||
|
|
||||||
im = Image.open("Tests/images/truncated_image.png")
|
with Image.open("Tests/images/truncated_image.png") as im:
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
# Test that the error is raised if loaded a second time
|
# Test that the error is raised if loaded a second time
|
||||||
with self.assertRaises(IOError):
|
with self.assertRaises(IOError):
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
def test_truncated_without_errors(self):
|
def test_truncated_without_errors(self):
|
||||||
if "zip_encoder" not in codecs:
|
if "zip_encoder" not in codecs:
|
||||||
|
@ -258,12 +258,12 @@ class TestPyDecoder(PillowTestCase):
|
||||||
exif[40963] = 455
|
exif[40963] = 455
|
||||||
exif[11] = "Pillow test"
|
exif[11] = "Pillow test"
|
||||||
im.save(out, exif=exif)
|
im.save(out, exif=exif)
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
reloaded_exif = reloaded.getexif()
|
reloaded_exif = reloaded.getexif()
|
||||||
self.assertEqual(reloaded_exif[258], 8)
|
self.assertEqual(reloaded_exif[258], 8)
|
||||||
self.assertNotIn(40960, exif)
|
self.assertNotIn(40960, exif)
|
||||||
self.assertEqual(reloaded_exif[40963], 455)
|
self.assertEqual(reloaded_exif[40963], 455)
|
||||||
self.assertEqual(exif[11], "Pillow test")
|
self.assertEqual(exif[11], "Pillow test")
|
||||||
|
|
||||||
im = Image.open("Tests/images/no-dpi-in-exif.jpg") # Big endian
|
im = Image.open("Tests/images/no-dpi-in-exif.jpg") # Big endian
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
|
@ -278,12 +278,12 @@ class TestPyDecoder(PillowTestCase):
|
||||||
exif[40963] = 455
|
exif[40963] = 455
|
||||||
exif[305] = "Pillow test"
|
exif[305] = "Pillow test"
|
||||||
im.save(out, exif=exif)
|
im.save(out, exif=exif)
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
reloaded_exif = reloaded.getexif()
|
reloaded_exif = reloaded.getexif()
|
||||||
self.assertEqual(reloaded_exif[258], 8)
|
self.assertEqual(reloaded_exif[258], 8)
|
||||||
self.assertNotIn(40960, exif)
|
self.assertNotIn(40960, exif)
|
||||||
self.assertEqual(reloaded_exif[40963], 455)
|
self.assertEqual(reloaded_exif[40963], 455)
|
||||||
self.assertEqual(exif[305], "Pillow test")
|
self.assertEqual(exif[305], "Pillow test")
|
||||||
|
|
||||||
@unittest.skipIf(
|
@unittest.skipIf(
|
||||||
not HAVE_WEBP or not _webp.HAVE_WEBPANIM,
|
not HAVE_WEBP or not _webp.HAVE_WEBPANIM,
|
||||||
|
@ -300,11 +300,11 @@ class TestPyDecoder(PillowTestCase):
|
||||||
exif[305] = "Pillow test"
|
exif[305] = "Pillow test"
|
||||||
|
|
||||||
def check_exif():
|
def check_exif():
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
reloaded_exif = reloaded.getexif()
|
reloaded_exif = reloaded.getexif()
|
||||||
self.assertEqual(reloaded_exif[258], 8)
|
self.assertEqual(reloaded_exif[258], 8)
|
||||||
self.assertEqual(reloaded_exif[40963], 455)
|
self.assertEqual(reloaded_exif[40963], 455)
|
||||||
self.assertEqual(exif[305], "Pillow test")
|
self.assertEqual(exif[305], "Pillow test")
|
||||||
|
|
||||||
im.save(out, exif=exif)
|
im.save(out, exif=exif)
|
||||||
check_exif()
|
check_exif()
|
||||||
|
@ -323,23 +323,13 @@ class TestPyDecoder(PillowTestCase):
|
||||||
exif[305] = "Pillow test"
|
exif[305] = "Pillow test"
|
||||||
im.save(out, exif=exif)
|
im.save(out, exif=exif)
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
reloaded_exif = reloaded.getexif()
|
reloaded_exif = reloaded.getexif()
|
||||||
self.assertEqual(reloaded_exif, {258: 8, 40963: 455, 305: "Pillow test"})
|
self.assertEqual(reloaded_exif, {258: 8, 40963: 455, 305: "Pillow test"})
|
||||||
|
|
||||||
def test_exif_interop(self):
|
def test_exif_interop(self):
|
||||||
im = Image.open("Tests/images/flower.jpg")
|
with Image.open("Tests/images/flower.jpg") as im:
|
||||||
exif = im.getexif()
|
exif = im.getexif()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
exif.get_ifd(0xA005), {1: "R98", 2: b"0100", 4097: 2272, 4098: 1704}
|
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}")
|
|
||||||
|
|
|
@ -2,57 +2,61 @@ from PIL import Image, ImageFilter
|
||||||
|
|
||||||
from .helper import PillowTestCase
|
from .helper import PillowTestCase
|
||||||
|
|
||||||
im = Image.open("Tests/images/hopper.ppm")
|
|
||||||
snakes = Image.open("Tests/images/color_snakes.png")
|
|
||||||
|
|
||||||
|
|
||||||
class TestImageOpsUsm(PillowTestCase):
|
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):
|
def test_filter_api(self):
|
||||||
|
|
||||||
test_filter = ImageFilter.GaussianBlur(2.0)
|
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.mode, "RGB")
|
||||||
self.assertEqual(i.size, (128, 128))
|
self.assertEqual(i.size, (128, 128))
|
||||||
|
|
||||||
test_filter = ImageFilter.UnsharpMask(2.0, 125, 8)
|
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.mode, "RGB")
|
||||||
self.assertEqual(i.size, (128, 128))
|
self.assertEqual(i.size, (128, 128))
|
||||||
|
|
||||||
def test_usm_formats(self):
|
def test_usm_formats(self):
|
||||||
|
|
||||||
usm = ImageFilter.UnsharpMask
|
usm = ImageFilter.UnsharpMask
|
||||||
self.assertRaises(ValueError, im.convert("1").filter, usm)
|
self.assertRaises(ValueError, self.im.convert("1").filter, usm)
|
||||||
im.convert("L").filter(usm)
|
self.im.convert("L").filter(usm)
|
||||||
self.assertRaises(ValueError, im.convert("I").filter, usm)
|
self.assertRaises(ValueError, self.im.convert("I").filter, usm)
|
||||||
self.assertRaises(ValueError, im.convert("F").filter, usm)
|
self.assertRaises(ValueError, self.im.convert("F").filter, usm)
|
||||||
im.convert("RGB").filter(usm)
|
self.im.convert("RGB").filter(usm)
|
||||||
im.convert("RGBA").filter(usm)
|
self.im.convert("RGBA").filter(usm)
|
||||||
im.convert("CMYK").filter(usm)
|
self.im.convert("CMYK").filter(usm)
|
||||||
self.assertRaises(ValueError, im.convert("YCbCr").filter, usm)
|
self.assertRaises(ValueError, self.im.convert("YCbCr").filter, usm)
|
||||||
|
|
||||||
def test_blur_formats(self):
|
def test_blur_formats(self):
|
||||||
|
|
||||||
blur = ImageFilter.GaussianBlur
|
blur = ImageFilter.GaussianBlur
|
||||||
self.assertRaises(ValueError, im.convert("1").filter, blur)
|
self.assertRaises(ValueError, self.im.convert("1").filter, blur)
|
||||||
blur(im.convert("L"))
|
blur(self.im.convert("L"))
|
||||||
self.assertRaises(ValueError, im.convert("I").filter, blur)
|
self.assertRaises(ValueError, self.im.convert("I").filter, blur)
|
||||||
self.assertRaises(ValueError, im.convert("F").filter, blur)
|
self.assertRaises(ValueError, self.im.convert("F").filter, blur)
|
||||||
im.convert("RGB").filter(blur)
|
self.im.convert("RGB").filter(blur)
|
||||||
im.convert("RGBA").filter(blur)
|
self.im.convert("RGBA").filter(blur)
|
||||||
im.convert("CMYK").filter(blur)
|
self.im.convert("CMYK").filter(blur)
|
||||||
self.assertRaises(ValueError, im.convert("YCbCr").filter, blur)
|
self.assertRaises(ValueError, self.im.convert("YCbCr").filter, blur)
|
||||||
|
|
||||||
def test_usm_accuracy(self):
|
def test_usm_accuracy(self):
|
||||||
|
|
||||||
src = snakes.convert("RGB")
|
src = self.snakes.convert("RGB")
|
||||||
i = src.filter(ImageFilter.UnsharpMask(5, 1024, 0))
|
i = src.filter(ImageFilter.UnsharpMask(5, 1024, 0))
|
||||||
# Image should not be changed because it have only 0 and 255 levels.
|
# Image should not be changed because it have only 0 and 255 levels.
|
||||||
self.assertEqual(i.tobytes(), src.tobytes())
|
self.assertEqual(i.tobytes(), src.tobytes())
|
||||||
|
|
||||||
def test_blur_accuracy(self):
|
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.
|
# These pixels surrounded with pixels with 255 intensity.
|
||||||
# They must be very close to 255.
|
# They must be very close to 255.
|
||||||
for x, y, c in [
|
for x, y, c in [
|
||||||
|
|
|
@ -24,25 +24,25 @@ class TestImageSequence(PillowTestCase):
|
||||||
self.assertRaises(AttributeError, ImageSequence.Iterator, 0)
|
self.assertRaises(AttributeError, ImageSequence.Iterator, 0)
|
||||||
|
|
||||||
def test_iterator(self):
|
def test_iterator(self):
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
i = ImageSequence.Iterator(im)
|
i = ImageSequence.Iterator(im)
|
||||||
for index in range(0, im.n_frames):
|
for index in range(0, im.n_frames):
|
||||||
self.assertEqual(i[index], next(i))
|
self.assertEqual(i[index], next(i))
|
||||||
self.assertRaises(IndexError, lambda: i[index + 1])
|
self.assertRaises(IndexError, lambda: i[index + 1])
|
||||||
self.assertRaises(StopIteration, next, i)
|
self.assertRaises(StopIteration, next, i)
|
||||||
|
|
||||||
def test_iterator_min_frame(self):
|
def test_iterator_min_frame(self):
|
||||||
im = Image.open("Tests/images/hopper.psd")
|
with Image.open("Tests/images/hopper.psd") as im:
|
||||||
i = ImageSequence.Iterator(im)
|
i = ImageSequence.Iterator(im)
|
||||||
for index in range(1, im.n_frames):
|
for index in range(1, im.n_frames):
|
||||||
self.assertEqual(i[index], next(i))
|
self.assertEqual(i[index], next(i))
|
||||||
|
|
||||||
def _test_multipage_tiff(self):
|
def _test_multipage_tiff(self):
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
for index, frame in enumerate(ImageSequence.Iterator(im)):
|
for index, frame in enumerate(ImageSequence.Iterator(im)):
|
||||||
frame.load()
|
frame.load()
|
||||||
self.assertEqual(index, im.tell())
|
self.assertEqual(index, im.tell())
|
||||||
frame.convert("RGB")
|
frame.convert("RGB")
|
||||||
|
|
||||||
def test_tiff(self):
|
def test_tiff(self):
|
||||||
self._test_multipage_tiff()
|
self._test_multipage_tiff()
|
||||||
|
@ -58,41 +58,41 @@ class TestImageSequence(PillowTestCase):
|
||||||
TiffImagePlugin.READ_LIBTIFF = False
|
TiffImagePlugin.READ_LIBTIFF = False
|
||||||
|
|
||||||
def test_consecutive(self):
|
def test_consecutive(self):
|
||||||
im = Image.open("Tests/images/multipage.tiff")
|
with Image.open("Tests/images/multipage.tiff") as im:
|
||||||
firstFrame = None
|
firstFrame = None
|
||||||
for frame in ImageSequence.Iterator(im):
|
for frame in ImageSequence.Iterator(im):
|
||||||
if firstFrame is None:
|
if firstFrame is None:
|
||||||
firstFrame = frame.copy()
|
firstFrame = frame.copy()
|
||||||
for frame in ImageSequence.Iterator(im):
|
for frame in ImageSequence.Iterator(im):
|
||||||
self.assert_image_equal(frame, firstFrame)
|
self.assert_image_equal(frame, firstFrame)
|
||||||
break
|
break
|
||||||
|
|
||||||
def test_palette_mmap(self):
|
def test_palette_mmap(self):
|
||||||
# Using mmap in ImageFile can require to reload the palette.
|
# Using mmap in ImageFile can require to reload the palette.
|
||||||
im = Image.open("Tests/images/multipage-mmap.tiff")
|
with Image.open("Tests/images/multipage-mmap.tiff") as im:
|
||||||
color1 = im.getpalette()[0:3]
|
color1 = im.getpalette()[0:3]
|
||||||
im.seek(0)
|
im.seek(0)
|
||||||
color2 = im.getpalette()[0:3]
|
color2 = im.getpalette()[0:3]
|
||||||
self.assertEqual(color1, color2)
|
self.assertEqual(color1, color2)
|
||||||
|
|
||||||
def test_all_frames(self):
|
def test_all_frames(self):
|
||||||
# Test a single image
|
# Test a single image
|
||||||
im = Image.open("Tests/images/iss634.gif")
|
with Image.open("Tests/images/iss634.gif") as im:
|
||||||
ims = ImageSequence.all_frames(im)
|
ims = ImageSequence.all_frames(im)
|
||||||
|
|
||||||
self.assertEqual(len(ims), 42)
|
self.assertEqual(len(ims), 42)
|
||||||
for i, im_frame in enumerate(ims):
|
for i, im_frame in enumerate(ims):
|
||||||
self.assertFalse(im_frame is im)
|
self.assertFalse(im_frame is im)
|
||||||
|
|
||||||
im.seek(i)
|
im.seek(i)
|
||||||
self.assert_image_equal(im, im_frame)
|
self.assert_image_equal(im, im_frame)
|
||||||
|
|
||||||
# Test a series of images
|
# Test a series of images
|
||||||
ims = ImageSequence.all_frames([im, hopper(), im])
|
ims = ImageSequence.all_frames([im, hopper(), im])
|
||||||
self.assertEqual(len(ims), 85)
|
self.assertEqual(len(ims), 85)
|
||||||
|
|
||||||
# Test an operation
|
# Test an operation
|
||||||
ims = ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90))
|
ims = ImageSequence.all_frames(im, lambda im_frame: im_frame.rotate(90))
|
||||||
for i, im_frame in enumerate(ims):
|
for i, im_frame in enumerate(ims):
|
||||||
im.seek(i)
|
im.seek(i)
|
||||||
self.assert_image_equal(im.rotate(90), im_frame)
|
self.assert_image_equal(im.rotate(90), im_frame)
|
||||||
|
|
|
@ -27,8 +27,8 @@ class TestImageShow(PillowTestCase):
|
||||||
ImageShow.register(viewer, -1)
|
ImageShow.register(viewer, -1)
|
||||||
|
|
||||||
for mode in ("1", "I;16", "LA", "RGB", "RGBA"):
|
for mode in ("1", "I;16", "LA", "RGB", "RGBA"):
|
||||||
im = hopper(mode)
|
with hopper() as im:
|
||||||
self.assertTrue(ImageShow.show(im))
|
self.assertTrue(ImageShow.show(im))
|
||||||
self.assertTrue(viewer.methodCalled)
|
self.assertTrue(viewer.methodCalled)
|
||||||
|
|
||||||
# Restore original state
|
# Restore original state
|
||||||
|
|
|
@ -26,13 +26,15 @@ path = "Tests/images/hopper.jpg"
|
||||||
|
|
||||||
class TestLocale(PillowTestCase):
|
class TestLocale(PillowTestCase):
|
||||||
def test_sanity(self):
|
def test_sanity(self):
|
||||||
Image.open(path)
|
with Image.open(path):
|
||||||
|
pass
|
||||||
try:
|
try:
|
||||||
locale.setlocale(locale.LC_ALL, "polish")
|
locale.setlocale(locale.LC_ALL, "polish")
|
||||||
except locale.Error:
|
except locale.Error:
|
||||||
unittest.skip("Polish locale not available")
|
unittest.skip("Polish locale not available")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
Image.open(path)
|
with Image.open(path):
|
||||||
|
pass
|
||||||
finally:
|
finally:
|
||||||
locale.setlocale(locale.LC_ALL, (None, None))
|
locale.setlocale(locale.LC_ALL, (None, None))
|
||||||
|
|
|
@ -23,9 +23,9 @@ class TestMap(PillowTestCase):
|
||||||
Image.MAX_IMAGE_PIXELS = None
|
Image.MAX_IMAGE_PIXELS = None
|
||||||
|
|
||||||
# This image hits the offset test.
|
# This image hits the offset test.
|
||||||
im = Image.open("Tests/images/l2rgb_read.bmp")
|
with Image.open("Tests/images/l2rgb_read.bmp") as im:
|
||||||
with self.assertRaises((ValueError, MemoryError, IOError)):
|
with self.assertRaises((ValueError, MemoryError, IOError)):
|
||||||
im.load()
|
im.load()
|
||||||
|
|
||||||
Image.MAX_IMAGE_PIXELS = max_pixels
|
Image.MAX_IMAGE_PIXELS = max_pixels
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,10 @@ class TestModeI16(PillowTestCase):
|
||||||
filename = self.tempfile("temp.im")
|
filename = self.tempfile("temp.im")
|
||||||
imIn.save(filename)
|
imIn.save(filename)
|
||||||
|
|
||||||
imOut = Image.open(filename)
|
with Image.open(filename) as imOut:
|
||||||
|
|
||||||
self.verify(imIn)
|
self.verify(imIn)
|
||||||
self.verify(imOut)
|
self.verify(imOut)
|
||||||
|
|
||||||
imOut = imIn.crop((0, 0, w, h))
|
imOut = imIn.crop((0, 0, w, h))
|
||||||
self.verify(imOut)
|
self.verify(imOut)
|
||||||
|
|
|
@ -24,7 +24,8 @@ class TestShellInjection(PillowTestCase):
|
||||||
dest_file = self.tempfile(filename)
|
dest_file = self.tempfile(filename)
|
||||||
save_func(src_img, 0, dest_file)
|
save_func(src_img, 0, dest_file)
|
||||||
# If file can't be opened, shell injection probably occurred
|
# 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")
|
@unittest.skipUnless(djpeg_available(), "djpeg not available")
|
||||||
def test_load_djpeg_filename(self):
|
def test_load_djpeg_filename(self):
|
||||||
|
@ -32,8 +33,8 @@ class TestShellInjection(PillowTestCase):
|
||||||
src_file = self.tempfile(filename)
|
src_file = self.tempfile(filename)
|
||||||
shutil.copy(TEST_JPG, src_file)
|
shutil.copy(TEST_JPG, src_file)
|
||||||
|
|
||||||
im = Image.open(src_file)
|
with Image.open(src_file) as im:
|
||||||
im.load_djpeg()
|
im.load_djpeg()
|
||||||
|
|
||||||
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
|
@unittest.skipUnless(cjpeg_available(), "cjpeg not available")
|
||||||
def test_save_cjpeg_filename(self):
|
def test_save_cjpeg_filename(self):
|
||||||
|
@ -42,10 +43,12 @@ class TestShellInjection(PillowTestCase):
|
||||||
|
|
||||||
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
||||||
def test_save_netpbm_filename_bmp_mode(self):
|
def test_save_netpbm_filename_bmp_mode(self):
|
||||||
im = Image.open(TEST_GIF).convert("RGB")
|
with Image.open(TEST_GIF) as im:
|
||||||
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
|
im = im.convert("RGB")
|
||||||
|
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
|
||||||
|
|
||||||
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
@unittest.skipUnless(netpbm_available(), "netpbm not available")
|
||||||
def test_save_netpbm_filename_l_mode(self):
|
def test_save_netpbm_filename_l_mode(self):
|
||||||
im = Image.open(TEST_GIF).convert("L")
|
with Image.open(TEST_GIF) as im:
|
||||||
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
|
im = im.convert("L")
|
||||||
|
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)
|
||||||
|
|
|
@ -54,5 +54,7 @@ class Test_IFDRational(PillowTestCase):
|
||||||
res = IFDRational(301, 1)
|
res = IFDRational(301, 1)
|
||||||
im.save(out, dpi=(res, res), compression="raw")
|
im.save(out, dpi=(res, res), compression="raw")
|
||||||
|
|
||||||
reloaded = Image.open(out)
|
with Image.open(out) as reloaded:
|
||||||
self.assertEqual(float(IFDRational(301, 1)), float(reloaded.tag_v2[282]))
|
self.assertEqual(
|
||||||
|
float(IFDRational(301, 1)), float(reloaded.tag_v2[282])
|
||||||
|
)
|
||||||
|
|
|
@ -99,9 +99,9 @@ Create JPEG thumbnails
|
||||||
outfile = os.path.splitext(infile)[0] + ".thumbnail"
|
outfile = os.path.splitext(infile)[0] + ".thumbnail"
|
||||||
if infile != outfile:
|
if infile != outfile:
|
||||||
try:
|
try:
|
||||||
im = Image.open(infile)
|
with Image.open(infile) as im:
|
||||||
im.thumbnail(size)
|
im.thumbnail(size)
|
||||||
im.save(outfile, "JPEG")
|
im.save(outfile, "JPEG")
|
||||||
except IOError:
|
except IOError:
|
||||||
print("cannot create thumbnail for", infile)
|
print("cannot create thumbnail for", infile)
|
||||||
|
|
||||||
|
@ -267,7 +267,8 @@ Converting between modes
|
||||||
::
|
::
|
||||||
|
|
||||||
from PIL import Image
|
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”
|
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
|
and “RGB” modes. To convert between other modes, you may have to use an
|
||||||
|
@ -383,15 +384,15 @@ Reading sequences
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
im = Image.open("animation.gif")
|
with Image.open("animation.gif") as im:
|
||||||
im.seek(1) # skip to the second frame
|
im.seek(1) # skip to the second frame
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while 1:
|
while 1:
|
||||||
im.seek(im.tell()+1)
|
im.seek(im.tell()+1)
|
||||||
# do something to im
|
# do something to im
|
||||||
except EOFError:
|
except EOFError:
|
||||||
pass # end of sequence
|
pass # end of sequence
|
||||||
|
|
||||||
As seen in this example, you’ll get an :py:exc:`EOFError` exception when the
|
As seen in this example, you’ll get an :py:exc:`EOFError` exception when the
|
||||||
sequence ends.
|
sequence ends.
|
||||||
|
@ -422,32 +423,34 @@ Drawing Postscript
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL import PSDraw
|
from PIL import PSDraw
|
||||||
|
|
||||||
im = Image.open("hopper.ppm")
|
with Image.open("hopper.ppm") as im:
|
||||||
title = "hopper"
|
title = "hopper"
|
||||||
box = (1*72, 2*72, 7*72, 10*72) # in points
|
box = (1*72, 2*72, 7*72, 10*72) # in points
|
||||||
|
|
||||||
ps = PSDraw.PSDraw() # default is sys.stdout
|
ps = PSDraw.PSDraw() # default is sys.stdout
|
||||||
ps.begin_document(title)
|
ps.begin_document(title)
|
||||||
|
|
||||||
# draw the image (75 dpi)
|
# draw the image (75 dpi)
|
||||||
ps.image(box, im, 75)
|
ps.image(box, im, 75)
|
||||||
ps.rectangle(box)
|
ps.rectangle(box)
|
||||||
|
|
||||||
# draw title
|
# draw title
|
||||||
ps.setfont("HelveticaNarrow-Bold", 36)
|
ps.setfont("HelveticaNarrow-Bold", 36)
|
||||||
ps.text((3*72, 4*72), title)
|
ps.text((3*72, 4*72), title)
|
||||||
|
|
||||||
ps.end_document()
|
ps.end_document()
|
||||||
|
|
||||||
More on reading images
|
More on reading images
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
As described earlier, the :py:func:`~PIL.Image.open` function of the
|
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
|
: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
|
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.
|
If everything goes well, the result is an :py:class:`PIL.Image.Image` object.
|
||||||
Otherwise, an :exc:`IOError` exception is raised.
|
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 PIL import Image
|
||||||
from __future__ import print_function
|
|
||||||
im = Image.open(file)
|
|
||||||
print("original =", im.mode, im.size)
|
|
||||||
|
|
||||||
im.draft("L", (100, 100))
|
with Image.open(file) as im:
|
||||||
print("draft =", im.mode, im.size)
|
print("original =", im.mode, im.size)
|
||||||
|
|
||||||
|
im.draft("L", (100, 100))
|
||||||
|
print("draft =", im.mode, im.size)
|
||||||
|
|
||||||
This prints something like::
|
This prints something like::
|
||||||
|
|
||||||
|
|
|
@ -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
|
file, so for the rest of this article, they will all be treated as a file-like
|
||||||
object.
|
object.
|
||||||
|
|
||||||
The first four of these items are equivalent, the last is dangerous
|
The following are all equivalent::
|
||||||
and may fail::
|
|
||||||
|
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import io
|
import io
|
||||||
import pathlib
|
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')
|
with open('test.jpg', 'rb') as f:
|
||||||
im3 = Image.open(f)
|
im3 = Image.open(f)
|
||||||
|
...
|
||||||
|
|
||||||
with open('test.jpg', 'rb') as f:
|
with open('test.jpg', 'rb') as f:
|
||||||
im4 = Image.open(io.BytesIO(f.read()))
|
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
|
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
|
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
|
Pillow cannot in general close and reopen a file, so any access to
|
||||||
that file needs to be prior to the close.
|
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
|
Image Lifecycle
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
@ -70,9 +61,9 @@ Image Lifecycle
|
||||||
... # image operations here.
|
... # image operations here.
|
||||||
|
|
||||||
|
|
||||||
The lifecycle of a single-frame image is relatively simple. The file
|
The lifecycle of a single-frame image is relatively simple. The file must
|
||||||
must remain open until the ``load()`` or ``close()`` function is
|
remain open until the ``load()`` or ``close()`` function is called or the
|
||||||
called.
|
context manager exits.
|
||||||
|
|
||||||
Multi-frame images are more complicated. The ``load()`` method is not
|
Multi-frame images are more complicated. The ``load()`` method is not
|
||||||
a terminal method, so it should not close the underlying file. In general,
|
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
|
libtiff (if working on an actual file). Since libtiff closes the file
|
||||||
descriptor internally, it is duplicated prior to passing it into libtiff.
|
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
|
* After a file has been closed, operations that require file access will fail::
|
||||||
changing the lazy loading::
|
|
||||||
|
|
||||||
# Dangerous FAIL:
|
|
||||||
with open('test.jpg', 'rb') as f:
|
with open('test.jpg', 'rb') as f:
|
||||||
im5 = Image.open(f)
|
im5 = Image.open(f)
|
||||||
im5.load() # FAILS, closed file
|
im5.load() # FAILS, closed file
|
||||||
|
|
||||||
|
with Image.open('test.jpg') as im6:
|
||||||
|
pass
|
||||||
|
im6.load() # FAILS, closed file
|
||||||
|
|
||||||
|
|
||||||
Proposed File Handling
|
Proposed File Handling
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -104,5 +97,6 @@ Proposed File Handling
|
||||||
|
|
||||||
* ``Image.Image.seek()`` should never close the image file.
|
* ``Image.Image.seek()`` should never close the image file.
|
||||||
|
|
||||||
* Users of the library should call ``Image.Image.close()`` on any
|
* Users of the library should use a context manager or call
|
||||||
multi-frame image to ensure that the underlying file is closed.
|
``Image.Image.close()`` on any image opened with a filename or ``Path``
|
||||||
|
object to ensure that the underlying file is closed.
|
||||||
|
|
|
@ -42,8 +42,8 @@ def testimage():
|
||||||
|
|
||||||
Or open existing files:
|
Or open existing files:
|
||||||
|
|
||||||
>>> im = Image.open("Tests/images/hopper.gif")
|
>>> with Image.open("Tests/images/hopper.gif") as im:
|
||||||
>>> _info(im)
|
... _info(im)
|
||||||
('GIF', 'P', (128, 128))
|
('GIF', 'P', (128, 128))
|
||||||
>>> _info(Image.open("Tests/images/hopper.ppm"))
|
>>> _info(Image.open("Tests/images/hopper.ppm"))
|
||||||
('PPM', 'RGB', (128, 128))
|
('PPM', 'RGB', (128, 128))
|
||||||
|
|
|
@ -622,11 +622,6 @@ class Image(object):
|
||||||
# object is gone.
|
# object is gone.
|
||||||
self.im = deferred_error(ValueError("Operation on closed image"))
|
self.im = deferred_error(ValueError("Operation on closed image"))
|
||||||
|
|
||||||
if sys.version_info.major >= 3:
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.__exit__()
|
|
||||||
|
|
||||||
def _copy(self):
|
def _copy(self):
|
||||||
self.load()
|
self.load()
|
||||||
self.im = self.im.copy()
|
self.im = self.im.copy()
|
||||||
|
|
|
@ -103,21 +103,24 @@ class ImageFile(Image.Image):
|
||||||
self._exclusive_fp = None
|
self._exclusive_fp = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._open()
|
try:
|
||||||
except (
|
self._open()
|
||||||
IndexError, # end of data
|
except (
|
||||||
TypeError, # end of data (ord)
|
IndexError, # end of data
|
||||||
KeyError, # unsupported mode
|
TypeError, # end of data (ord)
|
||||||
EOFError, # got header but not the first frame
|
KeyError, # unsupported mode
|
||||||
struct.error,
|
EOFError, # got header but not the first frame
|
||||||
) as v:
|
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
|
# close the file only if we have opened it this constructor
|
||||||
if self._exclusive_fp:
|
if self._exclusive_fp:
|
||||||
self.fp.close()
|
self.fp.close()
|
||||||
raise SyntaxError(v)
|
raise
|
||||||
|
|
||||||
if not self.mode or self.size[0] <= 0:
|
|
||||||
raise SyntaxError("not identified by this driver")
|
|
||||||
|
|
||||||
def draft(self, mode, size):
|
def draft(self, mode, size):
|
||||||
"""Set draft mode"""
|
"""Set draft mode"""
|
||||||
|
|
|
@ -219,7 +219,8 @@ def loadImageSeries(filelist=None):
|
||||||
print("unable to find %s" % img)
|
print("unable to find %s" % img)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
im = Image.open(img).convert2byte()
|
with Image.open(img) as im:
|
||||||
|
im = im.convert2byte()
|
||||||
except Exception:
|
except Exception:
|
||||||
if not isSpiderImage(img):
|
if not isSpiderImage(img):
|
||||||
print(img + " is not a Spider image file")
|
print(img + " is not a Spider image file")
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import io
|
import io
|
||||||
import sys
|
|
||||||
|
|
||||||
from . import ContainerIO
|
from . import ContainerIO
|
||||||
|
|
||||||
|
@ -64,10 +63,5 @@ class TarIO(ContainerIO.ContainerIO):
|
||||||
def __exit__(self, *args):
|
def __exit__(self, *args):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
if sys.version_info.major >= 3:
|
|
||||||
|
|
||||||
def __del__(self):
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.fh.close()
|
self.fh.close()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user