Merge remote-tracking branch 'upstream/master' into rm-2.7

This commit is contained in:
Hugo 2019-11-01 13:22:56 +02:00
commit cc63f66575
80 changed files with 1850 additions and 1669 deletions

View File

@ -2,7 +2,7 @@
set -e set -e
brew install libtiff libjpeg webp little-cms2 brew install libtiff libjpeg openjpeg libimagequant webp little-cms2 freetype
PYTHONOPTIMIZE=0 pip install cffi PYTHONOPTIMIZE=0 pip install cffi
pip install coverage pip install coverage

View File

@ -19,8 +19,8 @@ jobs:
centos-7-amd64, centos-7-amd64,
amazon-1-amd64, amazon-1-amd64,
amazon-2-amd64, amazon-2-amd64,
fedora-29-amd64,
fedora-30-amd64, fedora-30-amd64,
fedora-31-amd64,
] ]
dockerTag: [master] dockerTag: [master]

View File

@ -42,8 +42,8 @@ matrix:
- env: DOCKER="centos-7-amd64" DOCKER_TAG="master" - env: DOCKER="centos-7-amd64" DOCKER_TAG="master"
- env: DOCKER="amazon-1-amd64" DOCKER_TAG="master" - env: DOCKER="amazon-1-amd64" DOCKER_TAG="master"
- env: DOCKER="amazon-2-amd64" DOCKER_TAG="master" - env: DOCKER="amazon-2-amd64" DOCKER_TAG="master"
- env: DOCKER="fedora-29-amd64" DOCKER_TAG="master"
- env: DOCKER="fedora-30-amd64" DOCKER_TAG="master" - env: DOCKER="fedora-30-amd64" DOCKER_TAG="master"
- env: DOCKER="fedora-31-amd64" DOCKER_TAG="master"
services: services:
- docker - docker

View File

@ -5,6 +5,9 @@ Changelog (Pillow)
7.0.0 (unreleased) 7.0.0 (unreleased)
------------------ ------------------
- Removed CI testing of Fedora 29 #4165
[hugovk]
- Added pypy3 to tox envlist #4137 - Added pypy3 to tox envlist #4137
[jdufresne] [jdufresne]

View File

@ -22,7 +22,7 @@ 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
@ -46,7 +46,7 @@ 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)
@ -87,9 +87,9 @@ 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

View File

@ -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):

View File

@ -5,13 +5,13 @@ 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):

View File

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

View File

@ -8,7 +8,7 @@ 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")
@ -28,7 +28,7 @@ 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)

View File

@ -11,7 +11,7 @@ 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())

View File

@ -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,7 +13,7 @@ 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))
@ -19,11 +21,27 @@ class TestFileDcx(PillowTestCase):
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,7 +50,7 @@ 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()
@ -41,12 +59,12 @@ class TestFileDcx(PillowTestCase):
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
@ -58,7 +76,7 @@ class TestFileDcx(PillowTestCase):
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

View File

@ -25,26 +25,26 @@ 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))
@ -57,7 +57,7 @@ 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))
@ -73,9 +73,8 @@ class TestFileEps(PillowTestCase):
@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
@ -84,14 +83,14 @@ class TestFileEps(PillowTestCase):
@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 open(self.tempfile("temp_iobase.eps"), "wb") as fh: with open(self.tempfile("temp_iobase.eps"), "wb") as fh:
image1.save(fh, "EPS") image1.save(fh, "EPS")
@ -120,14 +119,14 @@ 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()
@ -141,14 +140,14 @@ 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()
@ -156,42 +155,29 @@ class TestFileEps(PillowTestCase):
@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)
# Act self.assertEqual(im.size, new_size)
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:
with Image.open(file1) as im:
new_size = (100, 100) new_size = (100, 100)
im.thumbnail(new_size)
# Act self.assertEqual(max(im.size), max(new_size))
image1.thumbnail(new_size)
image2.thumbnail(new_size)
# Assert
self.assertEqual(max(image1.size), max(new_size))
self.assertEqual(max(image2.size), max(new_size))
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,7 +225,7 @@ 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")
@ -247,7 +233,7 @@ class TestFileEps(PillowTestCase):
# 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))

View File

@ -8,7 +8,7 @@ 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")
@ -28,14 +28,14 @@ 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"

View File

@ -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,30 +14,46 @@ 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()
@ -49,16 +67,16 @@ 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
@ -69,7 +87,7 @@ class TestFileFli(PillowTestCase):
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)
@ -91,8 +109,8 @@ class TestFileFli(PillowTestCase):
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)

View File

@ -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)

View File

@ -7,7 +7,7 @@ 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")

View File

@ -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):
@ -110,17 +126,17 @@ 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)
@ -129,29 +145,29 @@ class TestFileGif(PillowTestCase):
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])
@ -159,7 +175,7 @@ class TestFileGif(PillowTestCase):
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)
@ -168,7 +184,7 @@ class TestFileGif(PillowTestCase):
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)
@ -183,34 +199,41 @@ 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:
reloaded = reloaded.convert("RGB")
self.assert_image_equal(im, reloaded) 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:
@ -220,7 +243,7 @@ class TestFileGif(PillowTestCase):
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)
@ -229,27 +252,27 @@ class TestFileGif(PillowTestCase):
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
@ -260,7 +283,7 @@ class TestFileGif(PillowTestCase):
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)
@ -269,7 +292,7 @@ class TestFileGif(PillowTestCase):
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)
@ -278,7 +301,7 @@ class TestFileGif(PillowTestCase):
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)
@ -297,7 +320,7 @@ 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)
@ -310,7 +333,7 @@ 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)
@ -334,8 +357,7 @@ 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): for i, circle in enumerate(circles):
img.seek(i) img.seek(i)
rgb_img = img.convert("RGB") rgb_img = img.convert("RGB")
@ -373,8 +395,7 @@ 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): for i, colours in enumerate(circles):
img.seek(i) img.seek(i)
rgb_img = img.convert("RGBA") rgb_img = img.convert("RGBA")
@ -409,12 +430,12 @@ 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
@ -431,7 +452,7 @@ 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):
@ -448,7 +469,7 @@ 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)
@ -461,7 +482,7 @@ class TestFileGif(PillowTestCase):
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)
@ -485,7 +506,7 @@ 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)
@ -505,8 +526,7 @@ 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 # Assert that all frames were combined
self.assertEqual(reread.n_frames, 1) self.assertEqual(reread.n_frames, 1)
@ -519,7 +539,7 @@ 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)
@ -528,7 +548,7 @@ class TestFileGif(PillowTestCase):
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"])
@ -538,14 +558,16 @@ 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"])
@ -557,13 +579,13 @@ 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):
@ -571,7 +593,7 @@ class TestFileGif(PillowTestCase):
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
@ -588,7 +610,7 @@ 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
@ -603,7 +625,7 @@ 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
@ -612,15 +634,15 @@ 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):
@ -639,7 +661,7 @@ 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)
@ -651,7 +673,7 @@ 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
@ -660,7 +682,7 @@ 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):
@ -670,7 +692,7 @@ 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):
@ -683,7 +705,7 @@ 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"))
@ -698,7 +720,7 @@ 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)
@ -712,7 +734,7 @@ 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)
@ -724,7 +746,7 @@ 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):
@ -756,14 +778,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))

View File

@ -8,7 +8,7 @@ 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")
@ -28,7 +28,7 @@ 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)

View File

@ -8,7 +8,7 @@ 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")
@ -28,14 +28,14 @@ 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"

View File

@ -15,7 +15,7 @@ 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)
@ -56,7 +56,7 @@ 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
@ -72,11 +72,11 @@ class TestFileIcns(PillowTestCase):
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")
@ -93,11 +93,11 @@ 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")

View File

@ -9,7 +9,7 @@ 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))
@ -46,19 +46,19 @@ 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))
@ -69,13 +69,12 @@ 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 # Act
im.save(outfile) im.save(outfile)
im_saved = Image.open(outfile)
with Image.open(outfile) as im_saved:
# Assert # Assert
self.assertEqual( self.assertEqual(
im_saved.info["sizes"], {(16, 16), (24, 24), (32, 32), (48, 48)} im_saved.info["sizes"], {(16, 16), (24, 24), (32, 32), (48, 48)}
@ -84,20 +83,21 @@ class TestFileIco(PillowTestCase):
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)

View File

@ -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,22 +10,38 @@ 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()
@ -32,12 +50,12 @@ class TestFileIm(PillowTestCase):
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
@ -52,7 +70,7 @@ class TestFileIm(PillowTestCase):
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)

View File

@ -11,7 +11,7 @@ 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)
@ -21,7 +21,7 @@ class TestFileIptc(PillowTestCase):
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)
@ -33,7 +33,7 @@ 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)

View File

@ -53,7 +53,7 @@ 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")
) )
@ -99,13 +99,13 @@ 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())
@ -205,13 +205,13 @@ 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",
@ -256,14 +256,14 @@ 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()
@ -329,12 +329,12 @@ 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):
@ -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,8 +372,7 @@ 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): with self.assertRaises(IOError):
im.load() im.load()
@ -483,7 +484,7 @@ 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)
@ -525,7 +526,7 @@ 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")
@ -558,11 +559,11 @@ 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):
@ -570,18 +571,18 @@ class TestFileJpeg(PillowTestCase):
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))
@ -590,7 +591,7 @@ class TestFileJpeg(PillowTestCase):
# 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))
@ -599,7 +600,7 @@ class TestFileJpeg(PillowTestCase):
# 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))
@ -608,7 +609,7 @@ class TestFileJpeg(PillowTestCase):
# 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
@ -618,7 +619,7 @@ class TestFileJpeg(PillowTestCase):
# 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."
@ -628,7 +629,7 @@ class TestFileJpeg(PillowTestCase):
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.
@ -638,13 +639,13 @@ class TestFileJpeg(PillowTestCase):
# 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],
{ {
@ -656,7 +657,7 @@ class TestFileJpeg(PillowTestCase):
) )
# 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)

View File

@ -42,7 +42,7 @@ 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")

View File

@ -141,9 +141,8 @@ 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: if legacy_api:
@ -160,7 +159,7 @@ 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:
@ -298,7 +297,7 @@ 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 (
@ -339,7 +338,7 @@ 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):
@ -400,7 +399,7 @@ 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])
@ -509,7 +508,7 @@ 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)
@ -532,7 +531,7 @@ class TestFileLibTiff(LibTiffTestCase):
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):
@ -644,7 +643,7 @@ 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)
@ -674,7 +673,7 @@ 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")
@ -824,7 +823,7 @@ 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))

View File

@ -16,7 +16,7 @@ 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))
@ -30,22 +30,22 @@ class TestFileMic(PillowTestCase):
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)

View 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,33 +26,50 @@ 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)
@ -60,7 +78,7 @@ class TestFileMpo(PillowTestCase):
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)
@ -68,19 +86,21 @@ class TestFileMpo(PillowTestCase):
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)
@ -88,14 +108,14 @@ class TestFileMpo(PillowTestCase):
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]:
@ -113,7 +133,7 @@ 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)
@ -132,12 +152,12 @@ class TestFileMpo(PillowTestCase):
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
@ -149,7 +169,7 @@ class TestFileMpo(PillowTestCase):
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)
@ -164,7 +184,7 @@ class TestFileMpo(PillowTestCase):
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)

View File

@ -82,7 +82,7 @@ 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)
@ -115,7 +115,7 @@ 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)

View File

@ -80,7 +80,7 @@ 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))
@ -392,11 +392,11 @@ 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):
@ -505,15 +505,15 @@ 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)
@ -610,7 +610,7 @@ 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)
@ -620,7 +620,7 @@ 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")
@ -630,7 +630,7 @@ 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(

View File

@ -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")

View File

@ -1,13 +1,15 @@
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))
@ -16,11 +18,28 @@ class TestImagePsd(PillowTestCase):
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,16 +48,16 @@ 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
@ -50,7 +69,7 @@ class TestImagePsd(PillowTestCase):
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)
@ -66,26 +85,25 @@ class TestImagePsd(PillowTestCase):
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):

View File

@ -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,7 +49,7 @@ 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")
@ -57,7 +74,7 @@ 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()
@ -66,7 +83,7 @@ class TestImageSpider(PillowTestCase):
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)
@ -109,12 +126,11 @@ 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")

View File

@ -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):
def open():
tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg") tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg")
tar.close() tar.close()
self.assert_warning(None, open)
def test_contextmanager(self): def test_contextmanager(self):
def open():
with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"): with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"):
pass pass
self.assert_warning(None, open)

View File

@ -76,7 +76,7 @@ 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))
@ -86,7 +86,7 @@ class TestFileTga(PillowTestCase):
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))
@ -99,13 +99,13 @@ 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):
@ -116,26 +116,26 @@ 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):
@ -146,7 +146,7 @@ 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):
@ -158,18 +158,18 @@ 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"
@ -178,7 +178,7 @@ 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):

View File

@ -5,7 +5,7 @@ from io import BytesIO
from PIL import Image, TiffImagePlugin from PIL import Image, TiffImagePlugin
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__)
@ -17,32 +17,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):
@ -74,7 +95,7 @@ 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)
@ -88,7 +109,7 @@ class TestFileTiff(PillowTestCase):
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)
@ -102,7 +123,7 @@ class TestFileTiff(PillowTestCase):
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
@ -112,15 +133,15 @@ class TestFileTiff(PillowTestCase):
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]))
@ -154,7 +175,7 @@ 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)
@ -229,12 +250,12 @@ 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
@ -246,7 +267,7 @@ class TestFileTiff(PillowTestCase):
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)
@ -276,7 +297,7 @@ 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)
@ -287,7 +308,7 @@ class TestFileTiff(PillowTestCase):
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 = {
@ -327,7 +348,7 @@ class TestFileTiff(PillowTestCase):
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))
@ -360,13 +381,13 @@ 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)
@ -430,7 +451,7 @@ 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)
@ -471,8 +492,7 @@ 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):
@ -535,8 +555,7 @@ 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):

View File

@ -52,7 +52,7 @@ 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),))
@ -72,13 +72,17 @@ class TestFileTiffMetadata(PillowTestCase):
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(
{ {
@ -131,7 +135,7 @@ 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()
@ -187,7 +191,7 @@ 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"])
@ -196,7 +200,7 @@ class TestFileTiffMetadata(PillowTestCase):
# 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"])
@ -218,7 +222,7 @@ 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)
@ -243,7 +247,7 @@ 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)

View File

@ -158,7 +158,7 @@ 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
@ -169,7 +169,7 @@ class TestFileWebp(PillowTestCase):
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)]

View File

@ -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)

View File

@ -28,11 +28,11 @@ 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)
@ -43,7 +43,7 @@ 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")

View File

@ -24,7 +24,7 @@ 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)
@ -35,7 +35,7 @@ class TestFileWebpMetadata(PillowTestCase):
# 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)
@ -60,14 +60,14 @@ 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)
@ -126,7 +126,7 @@ 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)

View File

@ -7,7 +7,7 @@ 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()
@ -17,7 +17,7 @@ class TestFileWmf(PillowTestCase):
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()
@ -46,11 +46,11 @@ 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):

View File

@ -42,7 +42,7 @@ 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")
@ -54,7 +54,7 @@ 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")

View File

@ -23,7 +23,7 @@ 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

View File

@ -90,7 +90,7 @@ 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))
@ -339,7 +339,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()
@ -441,7 +442,7 @@ 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)
@ -515,7 +516,7 @@ 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):
@ -580,7 +581,7 @@ 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()

View File

@ -144,7 +144,7 @@ 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")

View File

@ -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()
self.files_to_test = [
hopper(), hopper(),
Image.open("Tests/images/transparent.png"), Image.open("Tests/images/transparent.png"),
Image.open("Tests/images/7x13.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

View File

@ -6,7 +6,7 @@ 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

View File

@ -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")

View File

@ -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))

View File

@ -156,11 +156,11 @@ 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(

View File

@ -58,7 +58,7 @@ 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))
@ -151,7 +151,7 @@ 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(),
@ -166,8 +166,9 @@ 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
with hopper() as im:
self.assertRaises( self.assertRaises(
ImageCms.PyCMSError, ImageCms.profileToProfile, hopper(), "foo", "bar" 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,7 +267,7 @@ 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()))

View File

@ -45,7 +45,7 @@ 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))

View File

@ -115,7 +115,7 @@ 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()
@ -258,7 +258,7 @@ 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)
@ -278,7 +278,7 @@ 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)
@ -300,7 +300,7 @@ 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)
@ -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}")

View File

@ -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 [

View File

@ -24,7 +24,7 @@ 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))
@ -32,13 +32,13 @@ class TestImageSequence(PillowTestCase):
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())
@ -58,7 +58,7 @@ 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:
@ -69,7 +69,7 @@ class TestImageSequence(PillowTestCase):
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]
@ -77,7 +77,7 @@ class TestImageSequence(PillowTestCase):
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)

View File

@ -27,7 +27,7 @@ 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)

View File

@ -24,13 +24,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))

View File

@ -23,7 +23,7 @@ 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()

View File

@ -47,7 +47,7 @@ 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)

View File

@ -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,7 +33,7 @@ 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")
@ -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:
im = im.convert("RGB")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm) 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:
im = im.convert("L")
self.assert_save_filename_check(im, GifImagePlugin._save_netpbm) self.assert_save_filename_check(im, GifImagePlugin._save_netpbm)

View File

@ -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])
)

View File

@ -63,10 +63,10 @@ jobs:
- template: .azure-pipelines/jobs/test-docker.yml - template: .azure-pipelines/jobs/test-docker.yml
parameters: parameters:
docker: 'fedora-29-amd64' docker: 'fedora-30-amd64'
name: 'fedora_29_amd64' name: 'fedora_30_amd64'
- template: .azure-pipelines/jobs/test-docker.yml - template: .azure-pipelines/jobs/test-docker.yml
parameters: parameters:
docker: 'fedora-30-amd64' docker: 'fedora-31-amd64'
name: 'fedora_30_amd64' name: 'fedora_31_amd64'

View File

@ -1,19 +1,15 @@
#!/bin/bash #!/bin/bash
# install extra test images # install extra test images
rm -rf test_images
# Use SVN to just fetch a single Git subdirectory # Use SVN to just fetch a single Git subdirectory
svn_checkout() svn_export()
{ {
if [ ! -z $1 ]; then if [ ! -z $1 ]; then
echo "" echo ""
echo "Retrying svn checkout..." echo "Retrying svn export..."
echo "" echo ""
fi fi
svn checkout https://github.com/python-pillow/pillow-depends/trunk/test_images svn export --force https://github.com/python-pillow/pillow-depends/trunk/test_images ../Tests/images
} }
svn_checkout || svn_checkout retry || svn_checkout retry || svn_checkout retry svn_export || svn_export retry || svn_export retry || svn_export retry
cp -r test_images/* ../Tests/images

View File

@ -6,12 +6,13 @@ Goals
The fork author's goal is to foster and support active development of PIL through: The fork author's goal is to foster and support active development of PIL through:
- Continuous integration testing via `Travis CI`_ and `AppVeyor`_ - Continuous integration testing via `Travis CI`_, `AppVeyor`_ and `GitHub Actions`_
- Publicized development activity on `GitHub`_ - Publicized development activity on `GitHub`_
- Regular releases to the `Python Package Index`_ - Regular releases to the `Python Package Index`_
.. _Travis CI: https://travis-ci.org/python-pillow/Pillow .. _Travis CI: https://travis-ci.org/python-pillow/Pillow
.. _AppVeyor: https://ci.appveyor.com/project/Python-pillow/pillow .. _AppVeyor: https://ci.appveyor.com/project/Python-pillow/pillow
.. _GitHub Actions: https://github.com/python-pillow/Pillow/actions
.. _GitHub: https://github.com/python-pillow/Pillow .. _GitHub: https://github.com/python-pillow/Pillow
.. _Python Package Index: https://pypi.org/project/Pillow/ .. _Python Package Index: https://pypi.org/project/Pillow/

View File

@ -96,7 +96,7 @@ 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:
@ -263,7 +263,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
@ -379,7 +380,7 @@ 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:
@ -418,7 +419,7 @@ 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
@ -440,10 +441,12 @@ 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.
@ -509,7 +512,8 @@ This is only available for JPEG and MPO files.
:: ::
from PIL import Image from PIL import Image
im = Image.open(file)
with Image.open(file) as im:
print("original =", im.mode, im.size) print("original =", im.mode, im.size)
im.draft("L", (100, 100)) im.draft("L", (100, 100))

View File

@ -400,10 +400,10 @@ These platforms are built and tested for every change.
+----------------------------------+-------------------------------+-----------------------+ +----------------------------------+-------------------------------+-----------------------+
| Debian 10 Buster | 2.7, 3.7 |x86 | | Debian 10 Buster | 2.7, 3.7 |x86 |
+----------------------------------+-------------------------------+-----------------------+ +----------------------------------+-------------------------------+-----------------------+
| Fedora 29 | 2.7, 3.7 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| Fedora 30 | 2.7, 3.7 |x86-64 | | Fedora 30 | 2.7, 3.7 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+ +----------------------------------+-------------------------------+-----------------------+
| Fedora 31 | 3.7 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+
| macOS 10.13 High Sierra* | 2.7, 3.5, 3.6, 3.7, 3.8 |x86-64 | | macOS 10.13 High Sierra* | 2.7, 3.5, 3.6, 3.7, 3.8 |x86-64 |
+----------------------------------+-------------------------------+-----------------------+ +----------------------------------+-------------------------------+-----------------------+
| Ubuntu Linux 16.04 LTS | 2.7, 3.5, 3.6, 3.7, 3.8, |x86-64 | | Ubuntu Linux 16.04 LTS | 2.7, 3.5, 3.6, 3.7, 3.8, |x86-64 |

View File

@ -8,27 +8,25 @@ object, or a file-like object. Pillow uses the filename or ``Path`` to open a
file, so for the rest of this article, they will all be treated as a file-like 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.

View File

@ -41,8 +41,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))

View File

@ -119,7 +119,7 @@ def decode_dxt3(data):
bits = struct.unpack_from("<8B", block) bits = struct.unpack_from("<8B", block)
color0, color1 = struct.unpack_from("<HH", block, 8) color0, color1 = struct.unpack_from("<HH", block, 8)
code, = struct.unpack_from("<I", block, 12) (code,) = struct.unpack_from("<I", block, 12)
r0, g0, b0 = unpack_565(color0) r0, g0, b0 = unpack_565(color0)
r1, g1, b1 = unpack_565(color1) r1, g1, b1 = unpack_565(color1)
@ -177,7 +177,7 @@ def decode_dxt5(data):
color0, color1 = struct.unpack_from("<HH", block, 8) color0, color1 = struct.unpack_from("<HH", block, 8)
code, = struct.unpack_from("<I", block, 12) (code,) = struct.unpack_from("<I", block, 12)
r0, g0, b0 = unpack_565(color0) r0, g0, b0 = unpack_565(color0)
r1, g1, b1 = unpack_565(color1) r1, g1, b1 = unpack_565(color1)
@ -255,19 +255,19 @@ class BlpImageFile(ImageFile.ImageFile):
self.tile = [(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))] self.tile = [(decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))]
def _read_blp_header(self): def _read_blp_header(self):
self._blp_compression, = struct.unpack("<i", self.fp.read(4)) (self._blp_compression,) = struct.unpack("<i", self.fp.read(4))
self._blp_encoding, = struct.unpack("<b", self.fp.read(1)) (self._blp_encoding,) = struct.unpack("<b", self.fp.read(1))
self._blp_alpha_depth, = struct.unpack("<b", self.fp.read(1)) (self._blp_alpha_depth,) = struct.unpack("<b", self.fp.read(1))
self._blp_alpha_encoding, = struct.unpack("<b", self.fp.read(1)) (self._blp_alpha_encoding,) = struct.unpack("<b", self.fp.read(1))
self._blp_mips, = struct.unpack("<b", self.fp.read(1)) (self._blp_mips,) = struct.unpack("<b", self.fp.read(1))
self._size = struct.unpack("<II", self.fp.read(8)) self._size = struct.unpack("<II", self.fp.read(8))
if self.magic == b"BLP1": if self.magic == b"BLP1":
# Only present for BLP1 # Only present for BLP1
self._blp_encoding, = struct.unpack("<i", self.fp.read(4)) (self._blp_encoding,) = struct.unpack("<i", self.fp.read(4))
self._blp_subtype, = struct.unpack("<i", self.fp.read(4)) (self._blp_subtype,) = struct.unpack("<i", self.fp.read(4))
self._blp_offsets = struct.unpack("<16I", self.fp.read(16 * 4)) self._blp_offsets = struct.unpack("<16I", self.fp.read(16 * 4))
self._blp_lengths = struct.unpack("<16I", self.fp.read(16 * 4)) self._blp_lengths = struct.unpack("<16I", self.fp.read(16 * 4))
@ -297,19 +297,19 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
return ret return ret
def _read_blp_header(self): def _read_blp_header(self):
self._blp_compression, = struct.unpack("<i", self.fd.read(4)) (self._blp_compression,) = struct.unpack("<i", self.fd.read(4))
self._blp_encoding, = struct.unpack("<b", self.fd.read(1)) (self._blp_encoding,) = struct.unpack("<b", self.fd.read(1))
self._blp_alpha_depth, = struct.unpack("<b", self.fd.read(1)) (self._blp_alpha_depth,) = struct.unpack("<b", self.fd.read(1))
self._blp_alpha_encoding, = struct.unpack("<b", self.fd.read(1)) (self._blp_alpha_encoding,) = struct.unpack("<b", self.fd.read(1))
self._blp_mips, = struct.unpack("<b", self.fd.read(1)) (self._blp_mips,) = struct.unpack("<b", self.fd.read(1))
self.size = struct.unpack("<II", self.fd.read(8)) self.size = struct.unpack("<II", self.fd.read(8))
if self.magic == b"BLP1": if self.magic == b"BLP1":
# Only present for BLP1 # Only present for BLP1
self._blp_encoding, = struct.unpack("<i", self.fd.read(4)) (self._blp_encoding,) = struct.unpack("<i", self.fd.read(4))
self._blp_subtype, = struct.unpack("<i", self.fd.read(4)) (self._blp_subtype,) = struct.unpack("<i", self.fd.read(4))
self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4)) self._blp_offsets = struct.unpack("<16I", self.fd.read(16 * 4))
self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4)) self._blp_lengths = struct.unpack("<16I", self.fd.read(16 * 4))
@ -327,7 +327,7 @@ class BLP1Decoder(_BLPBaseDecoder):
_data = BytesIO(self.fd.read(self._blp_lengths[0])) _data = BytesIO(self.fd.read(self._blp_lengths[0]))
while True: while True:
try: try:
offset, = struct.unpack("<B", _data.read(1)) (offset,) = struct.unpack("<B", _data.read(1))
except struct.error: except struct.error:
break break
b, g, r, a = palette[offset] b, g, r, a = palette[offset]
@ -346,7 +346,7 @@ class BLP1Decoder(_BLPBaseDecoder):
def _decode_jpeg_stream(self): def _decode_jpeg_stream(self):
from PIL.JpegImagePlugin import JpegImageFile from PIL.JpegImagePlugin import JpegImageFile
jpeg_header_size, = struct.unpack("<I", self.fd.read(4)) (jpeg_header_size,) = struct.unpack("<I", self.fd.read(4))
jpeg_header = self.fd.read(jpeg_header_size) jpeg_header = self.fd.read(jpeg_header_size)
self.fd.read(self._blp_offsets[0] - self.fd.tell()) # What IS this? self.fd.read(self._blp_offsets[0] - self.fd.tell()) # What IS this?
data = self.fd.read(self._blp_lengths[0]) data = self.fd.read(self._blp_lengths[0])
@ -372,7 +372,7 @@ class BLP2Decoder(_BLPBaseDecoder):
_data = BytesIO(self.fd.read(self._blp_lengths[0])) _data = BytesIO(self.fd.read(self._blp_lengths[0]))
while True: while True:
try: try:
offset, = struct.unpack("<B", _data.read(1)) (offset,) = struct.unpack("<B", _data.read(1))
except struct.error: except struct.error:
break break
b, g, r, a = palette[offset] b, g, r, a = palette[offset]

View File

@ -122,7 +122,7 @@ class DdsImageFile(ImageFile.ImageFile):
# pixel format # pixel format
pfsize, pfflags = struct.unpack("<2I", header.read(8)) pfsize, pfflags = struct.unpack("<2I", header.read(8))
fourcc = header.read(4) fourcc = header.read(4)
bitcount, = struct.unpack("<I", header.read(4)) (bitcount,) = struct.unpack("<I", header.read(4))
masks = struct.unpack("<4I", header.read(16)) masks = struct.unpack("<4I", header.read(16))
if pfflags & 0x40: if pfflags & 0x40:
# DDPF_RGB - Texture contains uncompressed RGB data # DDPF_RGB - Texture contains uncompressed RGB data

View File

@ -79,7 +79,7 @@ class FtexImageFile(ImageFile.ImageFile):
format, where = struct.unpack("<2i", self.fp.read(8)) format, where = struct.unpack("<2i", self.fp.read(8))
self.fp.seek(where) self.fp.seek(where)
mipmap_size, = struct.unpack("<i", self.fp.read(4)) (mipmap_size,) = struct.unpack("<i", self.fp.read(4))
data = self.fp.read(mipmap_size) data = self.fp.read(mipmap_size)

View File

@ -597,9 +597,6 @@ class Image:
# object is gone. # object is gone.
self.im = deferred_error(ValueError("Operation on closed image")) self.im = deferred_error(ValueError("Operation on closed image"))
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()
@ -3190,7 +3187,7 @@ class Exif(MutableMapping):
continue continue
size = count * unit_size size = count * unit_size
if size > 4: if size > 4:
offset, = struct.unpack("<L", data) (offset,) = struct.unpack("<L", data)
data = ifd_data[offset - 12 : offset + size - 12] data = ifd_data[offset - 12 : offset + size - 12]
else: else:
data = data[:size] data = data[:size]
@ -3220,7 +3217,7 @@ class Exif(MutableMapping):
) )
if ifd_tag == 0x1101: if ifd_tag == 0x1101:
# CameraInfo # CameraInfo
offset, = struct.unpack(">L", data) (offset,) = struct.unpack(">L", data)
self.fp.seek(offset) self.fp.seek(offset)
camerainfo = {"ModelID": self.fp.read(4)} camerainfo = {"ModelID": self.fp.read(4)}

View File

@ -102,6 +102,7 @@ class ImageFile(Image.Image):
# can be overridden # can be overridden
self._exclusive_fp = None self._exclusive_fp = None
try:
try: try:
self._open() self._open()
except ( except (
@ -111,13 +112,15 @@ class ImageFile(Image.Image):
EOFError, # got header but not the first frame EOFError, # got header but not the first frame
struct.error, struct.error,
) as v: ) as v:
# close the file only if we have opened it this constructor
if self._exclusive_fp:
self.fp.close()
raise SyntaxError(v) raise SyntaxError(v)
if not self.mode or self.size[0] <= 0: if not self.mode or self.size[0] <= 0:
raise SyntaxError("not identified by this driver") raise SyntaxError("not identified by this driver")
except BaseException:
# close the file only if we have opened it this constructor
if self._exclusive_fp:
self.fp.close()
raise
def draft(self, mode, size): def draft(self, mode, size):
"""Set draft mode""" """Set draft mode"""

View File

@ -216,7 +216,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")

View File

@ -63,8 +63,5 @@ class TarIO(ContainerIO.ContainerIO):
def __exit__(self, *args): def __exit__(self, *args):
self.close() self.close()
def __del__(self):
self.close()
def close(self): def close(self):
self.fh.close() self.fh.close()

View File

@ -470,7 +470,7 @@ class ImageFileDirectory_v2(MutableMapping):
else: else:
raise SyntaxError("not a TIFF IFD") raise SyntaxError("not a TIFF IFD")
self.reset() self.reset()
self.next, = self._unpack("L", ifh[4:]) (self.next,) = self._unpack("L", ifh[4:])
self._legacy_api = False self._legacy_api = False
prefix = property(lambda self: self._prefix) prefix = property(lambda self: self._prefix)
@ -570,7 +570,7 @@ class ImageFileDirectory_v2(MutableMapping):
]: # rationals ]: # rationals
values = (values,) values = (values,)
try: try:
dest[tag], = values (dest[tag],) = values
except ValueError: except ValueError:
# We've got a builtin tag with 1 expected entry # We've got a builtin tag with 1 expected entry
warnings.warn( warnings.warn(
@ -738,7 +738,7 @@ class ImageFileDirectory_v2(MutableMapping):
size = count * unit_size size = count * unit_size
if size > 4: if size > 4:
here = fp.tell() here = fp.tell()
offset, = self._unpack("L", data) (offset,) = self._unpack("L", data)
if DEBUG: if DEBUG:
print( print(
"Tag Location: {} - Data Location: {}".format(here, offset), "Tag Location: {} - Data Location: {}".format(here, offset),
@ -770,7 +770,7 @@ class ImageFileDirectory_v2(MutableMapping):
else: else:
print("- value:", self[tag]) print("- value:", self[tag])
self.next, = self._unpack("L", self._ensure_read(fp, 4)) (self.next,) = self._unpack("L", self._ensure_read(fp, 4))
except OSError as msg: except OSError as msg:
warnings.warn(str(msg)) warnings.warn(str(msg))
return return
@ -1766,11 +1766,11 @@ class AppendingTiffWriter:
return self.f.write(data) return self.f.write(data)
def readShort(self): def readShort(self):
value, = struct.unpack(self.shortFmt, self.f.read(2)) (value,) = struct.unpack(self.shortFmt, self.f.read(2))
return value return value
def readLong(self): def readLong(self):
value, = struct.unpack(self.longFmt, self.f.read(4)) (value,) = struct.unpack(self.longFmt, self.f.read(4))
return value return value
def rewriteLastShortToLong(self, value): def rewriteLastShortToLong(self, value):

View File

@ -45,8 +45,6 @@ def extract(src, dest):
def extract_libs(): def extract_libs():
for name, lib in libs.items(): for name, lib in libs.items():
filename = lib["filename"]
if not os.path.exists(filename):
filename = fetch(lib["url"]) filename = fetch(lib["url"])
if name == "openjpeg": if name == "openjpeg":
for compiler in all_compilers(): for compiler in all_compilers():

View File

@ -26,96 +26,95 @@ libs = {
# }, # },
"zlib": { "zlib": {
"url": "http://zlib.net/zlib1211.zip", "url": "http://zlib.net/zlib1211.zip",
"filename": PILLOW_DEPENDS_DIR + "zlib1211.zip", "filename": "zlib1211.zip",
"dir": "zlib-1.2.11", "dir": "zlib-1.2.11",
}, },
"jpeg": { "jpeg": {
"url": "http://www.ijg.org/files/jpegsr9c.zip", "url": "http://www.ijg.org/files/jpegsr9c.zip",
"filename": PILLOW_DEPENDS_DIR + "jpegsr9c.zip", "filename": "jpegsr9c.zip",
"dir": "jpeg-9c", "dir": "jpeg-9c",
}, },
"tiff": { "tiff": {
"url": "ftp://download.osgeo.org/libtiff/tiff-4.0.10.tar.gz", "url": "ftp://download.osgeo.org/libtiff/tiff-4.0.10.tar.gz",
"filename": PILLOW_DEPENDS_DIR + "tiff-4.0.10.tar.gz", "filename": "tiff-4.0.10.tar.gz",
"dir": "tiff-4.0.10", "dir": "tiff-4.0.10",
}, },
"freetype": { "freetype": {
"url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz", # noqa: E501 "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.1.tar.gz", # noqa: E501
"filename": PILLOW_DEPENDS_DIR + "freetype-2.10.1.tar.gz", "filename": "freetype-2.10.1.tar.gz",
"dir": "freetype-2.10.1", "dir": "freetype-2.10.1",
}, },
"lcms-2.7": { "lcms-2.7": {
"url": SF_MIRROR + "/project/lcms/lcms/2.7/lcms2-2.7.zip", "url": SF_MIRROR + "/project/lcms/lcms/2.7/lcms2-2.7.zip",
"filename": PILLOW_DEPENDS_DIR + "lcms2-2.7.zip", "filename": "lcms2-2.7.zip",
"dir": "lcms2-2.7", "dir": "lcms2-2.7",
}, },
"lcms-2.8": { "lcms-2.8": {
"url": SF_MIRROR + "/project/lcms/lcms/2.8/lcms2-2.8.zip", "url": SF_MIRROR + "/project/lcms/lcms/2.8/lcms2-2.8.zip",
"filename": PILLOW_DEPENDS_DIR + "lcms2-2.8.zip", "filename": "lcms2-2.8.zip",
"dir": "lcms2-2.8", "dir": "lcms2-2.8",
}, },
"ghostscript": { "ghostscript": {
"url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs927/ghostscript-9.27.tar.gz", # noqa: E501 "url": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs927/ghostscript-9.27.tar.gz", # noqa: E501
"filename": PILLOW_DEPENDS_DIR + "ghostscript-9.27.tar.gz", "filename": "ghostscript-9.27.tar.gz",
"dir": "ghostscript-9.27", "dir": "ghostscript-9.27",
}, },
"tcl-8.5": { "tcl-8.5": {
"url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tcl8519-src.zip", "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tcl8519-src.zip",
"filename": PILLOW_DEPENDS_DIR + "tcl8519-src.zip", "filename": "tcl8519-src.zip",
"dir": "", "dir": "",
}, },
"tk-8.5": { "tk-8.5": {
"url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tk8519-src.zip", "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tk8519-src.zip",
"filename": PILLOW_DEPENDS_DIR + "tk8519-src.zip", "filename": "tk8519-src.zip",
"dir": "", "dir": "",
"version": "8.5.19", "version": "8.5.19",
}, },
"tcl-8.6": { "tcl-8.6": {
"url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tcl869-src.zip", "url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tcl869-src.zip",
"filename": PILLOW_DEPENDS_DIR + "tcl869-src.zip", "filename": "tcl869-src.zip",
"dir": "", "dir": "",
}, },
"tk-8.6": { "tk-8.6": {
"url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tk869-src.zip", "url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tk869-src.zip",
"filename": PILLOW_DEPENDS_DIR + "tk869-src.zip", "filename": "tk869-src.zip",
"dir": "", "dir": "",
"version": "8.6.9", "version": "8.6.9",
}, },
"webp": { "webp": {
"url": "http://downloads.webmproject.org/releases/webp/libwebp-1.0.3.tar.gz", "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.0.3.tar.gz",
"filename": PILLOW_DEPENDS_DIR + "libwebp-1.0.3.tar.gz", "filename": "libwebp-1.0.3.tar.gz",
"dir": "libwebp-1.0.3", "dir": "libwebp-1.0.3",
}, },
"openjpeg": { "openjpeg": {
"url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz", "url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz",
"filename": PILLOW_DEPENDS_DIR + "openjpeg-2.3.1.tar.gz", "filename": "openjpeg-2.3.1.tar.gz",
"dir": "openjpeg-2.3.1", "dir": "openjpeg-2.3.1",
}, },
"jpeg-turbo": { "jpeg-turbo": {
"url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz", "url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz",
"filename": PILLOW_DEPENDS_DIR + "libjpeg-turbo-2.0.3.tar.gz", "filename": "libjpeg-turbo-2.0.3.tar.gz",
"dir": "libjpeg-turbo-2.0.3", "dir": "libjpeg-turbo-2.0.3",
}, },
# ba653c8: Merge tag '2.12.5' into msvc # ba653c8: Merge tag '2.12.5' into msvc
"imagequant": { "imagequant": {
"url": "https://github.com/ImageOptim/libimagequant/archive/ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip", # noqa: E501 "url": "https://github.com/ImageOptim/libimagequant/archive/ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip", # noqa: E501
"filename": PILLOW_DEPENDS_DIR "filename": "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip",
+ "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971.zip",
"dir": "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971", "dir": "libimagequant-ba653c8ccb34dde4e21c6076d85a72d21ed9d971",
}, },
"harfbuzz": { "harfbuzz": {
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.1.zip", "url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.1.zip",
"filename": PILLOW_DEPENDS_DIR + "harfbuzz-2.6.1.zip", "filename": "harfbuzz-2.6.1.zip",
"dir": "harfbuzz-2.6.1", "dir": "harfbuzz-2.6.1",
}, },
"fribidi": { "fribidi": {
"url": "https://github.com/fribidi/fribidi/archive/v1.0.7.zip", "url": "https://github.com/fribidi/fribidi/archive/v1.0.7.zip",
"filename": PILLOW_DEPENDS_DIR + "fribidi-1.0.7.zip", "filename": "fribidi-1.0.7.zip",
"dir": "fribidi-1.0.7", "dir": "fribidi-1.0.7",
}, },
"libraqm": { "libraqm": {
"url": "https://github.com/HOST-Oman/libraqm/archive/v0.7.0.zip", "url": "https://github.com/HOST-Oman/libraqm/archive/v0.7.0.zip",
"filename": PILLOW_DEPENDS_DIR + "libraqm-0.7.0.zip", "filename": "libraqm-0.7.0.zip",
"dir": "libraqm-0.7.0", "dir": "libraqm-0.7.0",
}, },
} }

View File

@ -3,16 +3,37 @@ import sys
import urllib.parse import urllib.parse
import urllib.request import urllib.request
from config import libs
def fetch(url): def fetch(url):
depends_filename = None
for lib in libs.values():
if lib["url"] == url:
depends_filename = lib["filename"]
break
if depends_filename and os.path.exists(depends_filename):
return depends_filename
name = urllib.parse.urlsplit(url)[2].split("/")[-1] name = urllib.parse.urlsplit(url)[2].split("/")[-1]
if not os.path.exists(name): if not os.path.exists(name):
print("Fetching", url)
def retrieve(request_url):
print("Fetching", request_url)
try: try:
r = urllib.request.urlopen(url) return urllib.request.urlopen(request_url)
except urllib.error.URLError: except urllib.error.URLError:
r = urllib.request.urlopen(url) return urllib.request.urlopen(request_url)
try:
r = retrieve(url)
except urllib.error.HTTPError:
if depends_filename:
r = retrieve(
"https://github.com/python-pillow/pillow-depends/raw/master/"
+ depends_filename
)
name = depends_filename
content = r.read() content = r.read()
with open(name, "wb") as fd: with open(name, "wb") as fd:
fd.write(content) fd.write(content)