diff --git a/Tests/check_j2k_leaks.py b/Tests/check_j2k_leaks.py index 1635f1001..a7a91f782 100755 --- a/Tests/check_j2k_leaks.py +++ b/Tests/check_j2k_leaks.py @@ -3,22 +3,18 @@ from io import BytesIO from PIL import Image -from .helper import PillowTestCase, is_win32 +from .helper import PillowTestCase, is_win32, skip_unless_feature # Limits for testing the leak mem_limit = 1024 * 1048576 stack_size = 8 * 1048576 iterations = int((mem_limit / stack_size) * 2) -codecs = dir(Image.core) test_file = "Tests/images/rgb_trns_ycbc.jp2" @unittest.skipIf(is_win32(), "requires Unix or macOS") +@skip_unless_feature("jpg_2000") class TestJpegLeaks(PillowTestCase): - def setUp(self): - if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs: - self.skipTest("JPEG 2000 support not available") - def test_leak_load(self): from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK diff --git a/Tests/helper.py b/Tests/helper.py index 3b2341012..621830333 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -12,7 +12,7 @@ import unittest from io import BytesIO import pytest -from PIL import Image, ImageMath +from PIL import Image, ImageMath, features logger = logging.getLogger(__name__) @@ -172,6 +172,11 @@ def skip_known_bad_test(msg=None): pytest.skip(msg or "Known bad test") +def skip_unless_feature(feature): + reason = "%s not available" % feature + return pytest.mark.skipif(not features.check(feature), reason=reason) + + class PillowTestCase(unittest.TestCase): def delete_tempfile(self, path): try: diff --git a/Tests/test_features.py b/Tests/test_features.py index 88d10f652..10799df33 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -3,12 +3,12 @@ import io import pytest from PIL import features +from .helper import skip_unless_feature + try: from PIL import _webp - - HAVE_WEBP = True except ImportError: - HAVE_WEBP = False + pass def test_check(): @@ -21,18 +21,18 @@ def test_check(): assert features.check_feature(feature) == features.check(feature) -@pytest.mark.skipif(not HAVE_WEBP, reason="WebP not available") +@skip_unless_feature("webp") def test_webp_transparency(): assert features.check("transp_webp") != _webp.WebPDecoderBuggyAlpha() assert features.check("transp_webp") == _webp.HAVE_TRANSPARENCY -@pytest.mark.skipif(not HAVE_WEBP, reason="WebP not available") +@skip_unless_feature("webp") def test_webp_mux(): assert features.check("webp_mux") == _webp.HAVE_WEBPMUX -@pytest.mark.skipif(not HAVE_WEBP, reason="WebP not available") +@skip_unless_feature("webp") def test_webp_anim(): assert features.check("webp_anim") == _webp.HAVE_WEBPANIM diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index c49139d82..77fbe9c4d 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -1,9 +1,9 @@ import io import unittest -from PIL import EpsImagePlugin, Image +from PIL import EpsImagePlugin, Image, features -from .helper import PillowTestCase, assert_image_similar, hopper +from .helper import PillowTestCase, assert_image_similar, hopper, skip_unless_feature HAS_GHOSTSCRIPT = EpsImagePlugin.has_ghostscript() @@ -67,7 +67,7 @@ class TestFileEps(PillowTestCase): cmyk_image.load() self.assertEqual(cmyk_image.mode, "RGB") - if "jpeg_decoder" in dir(Image.core): + if features.check("jpg"): with Image.open("Tests/images/pil_sample_rgb.jpg") as target: assert_image_similar(cmyk_image, target, 10) @@ -114,11 +114,9 @@ class TestFileEps(PillowTestCase): self.assertRaises(ValueError, im.save, tmpfile) @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") + @skip_unless_feature("zlib") def test_render_scale1(self): # We need png support for these render test - codecs = dir(Image.core) - if "zip_encoder" not in codecs or "zip_decoder" not in codecs: - self.skipTest("zip/deflate support not available") # Zero bounding box with Image.open(file1) as image1_scale1: @@ -137,11 +135,9 @@ class TestFileEps(PillowTestCase): assert_image_similar(image2_scale1, image2_scale1_compare, 10) @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") + @skip_unless_feature("zlib") def test_render_scale2(self): # We need png support for these render test - codecs = dir(Image.core) - if "zip_encoder" not in codecs or "zip_decoder" not in codecs: - self.skipTest("zip/deflate support not available") # Zero bounding box with Image.open(file1) as image1_scale2: diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index d9226cdba..7da1b9d05 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -2,7 +2,7 @@ import unittest from io import BytesIO import pytest -from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette +from PIL import GifImagePlugin, Image, ImageDraw, ImagePalette, features from .helper import ( PillowTestCase, @@ -13,15 +13,6 @@ from .helper import ( netpbm_available, ) -try: - from PIL import _webp - - HAVE_WEBP = True -except ImportError: - HAVE_WEBP = False - -codecs = dir(Image.core) - # sample gif stream TEST_GIF = "Tests/images/hopper.gif" @@ -30,10 +21,6 @@ with open(TEST_GIF, "rb") as f: class TestFileGif(PillowTestCase): - def setUp(self): - if "gif_encoder" not in codecs or "gif_decoder" not in codecs: - self.skipTest("gif support not available") # can this happen? - def test_sanity(self): with Image.open(TEST_GIF) as im: im.load() @@ -562,7 +549,7 @@ class TestFileGif(PillowTestCase): self.assertEqual(reread.info["background"], im.info["background"]) - if HAVE_WEBP and _webp.HAVE_WEBPANIM: + if features.check("webp") and features.check("webp_anim"): with Image.open("Tests/images/hopper.webp") as im: self.assertIsInstance(im.info["background"], tuple) im.save(out) diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 2c0c46d88..a2a848b41 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -13,19 +13,15 @@ from .helper import ( djpeg_available, hopper, is_win32, + skip_unless_feature, unittest, ) -codecs = dir(Image.core) - TEST_FILE = "Tests/images/hopper.jpg" +@skip_unless_feature("jpg") class TestFileJpeg(PillowTestCase): - def setUp(self): - if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: - self.skipTest("jpeg support not available") - def roundtrip(self, im, **options): out = BytesIO() im.save(out, "JPEG", **options) @@ -687,11 +683,8 @@ class TestFileJpeg(PillowTestCase): @unittest.skipUnless(is_win32(), "Windows only") +@skip_unless_feature("jpg") class TestFileCloseW32(PillowTestCase): - def setUp(self): - if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: - self.skipTest("jpeg support not available") - def test_fd_leak(self): tmpfile = self.tempfile("temp.jpg") diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index daa5ee375..a1b812429 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -9,10 +9,9 @@ from .helper import ( assert_image_similar, is_big_endian, on_ci, + skip_unless_feature, ) -codecs = dir(Image.core) - test_card = Image.open("Tests/images/test-card.png") test_card.load() @@ -21,11 +20,8 @@ test_card.load() # 'Not enough memory to handle tile data' +@skip_unless_feature("jpg_2000") class TestFileJpeg2k(PillowTestCase): - def setUp(self): - if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs: - self.skipTest("JPEG 2000 support not available") - def roundtrip(self, im, **options): out = BytesIO() im.save(out, "JPEG2000", **options) diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index cf160389f..c43bdb7a1 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -6,7 +6,7 @@ import os from collections import namedtuple from ctypes import c_float -from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags, features +from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags from .helper import ( PillowTestCase, @@ -15,16 +15,14 @@ from .helper import ( assert_image_similar, assert_image_similar_tofile, hopper, + skip_unless_feature, ) logger = logging.getLogger(__name__) +@skip_unless_feature("libtiff") class LibTiffTestCase(PillowTestCase): - def setUp(self): - if not features.check("libtiff"): - self.skipTest("tiff support not available") - def _assert_noerr(self, im): """Helper tests that assert basic sanity about the g4 tiff reading""" # 1 bit @@ -727,13 +725,9 @@ class TestFileLibTiff(LibTiffTestCase): assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png") + @skip_unless_feature("jpg") def test_gimp_tiff(self): # Read TIFF JPEG images from GIMP [@PIL168] - - codecs = dir(Image.core) - if "jpeg_decoder" not in codecs: - self.skipTest("jpeg support not available") - filename = "Tests/images/pil168.tif" with Image.open(filename) as im: self.assertEqual(im.mode, "RGB") diff --git a/Tests/test_file_mic.py b/Tests/test_file_mic.py index 0ef41b10e..5003090c7 100644 --- a/Tests/test_file_mic.py +++ b/Tests/test_file_mic.py @@ -1,16 +1,12 @@ import pytest -from PIL import Image, ImagePalette, features +from PIL import Image, ImagePalette -from .helper import assert_image_similar, hopper +from .helper import assert_image_similar, hopper, skip_unless_feature MicImagePlugin = pytest.importorskip( "PIL.MicImagePlugin", reason="olefile not installed" ) - -pytestmark = pytest.mark.skipif( - not features.check("libtiff"), reason="libtiff not installed" -) - +pytestmark = skip_unless_feature("libtiff") TEST_FILE = "Tests/images/hopper.mic" diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index fd951eff0..893f9075d 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -3,15 +3,11 @@ from io import BytesIO import pytest from PIL import Image -from .helper import assert_image_similar, is_pypy +from .helper import assert_image_similar, is_pypy, skip_unless_feature test_files = ["Tests/images/sugarshack.mpo", "Tests/images/frozenpond.mpo"] - -def setup_module(): - codecs = dir(Image.core) - if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: - pytest.skip("jpeg support not available") +pytestmark = skip_unless_feature("jpg") def frame_roundtrip(im, **options): diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index a3d3c0897..b319bc41e 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -15,18 +15,9 @@ from .helper import ( is_big_endian, is_win32, on_ci, + skip_unless_feature, ) -try: - from PIL import _webp - - HAVE_WEBP = True -except ImportError: - HAVE_WEBP = False - -codecs = dir(Image.core) - - # sample png stream TEST_PNG_FILE = "Tests/images/hopper.png" @@ -63,11 +54,8 @@ def roundtrip(im, **options): return Image.open(out) +@skip_unless_feature("zlib") class TestFilePng(PillowTestCase): - def setUp(self): - if "zip_encoder" not in codecs or "zip_decoder" not in codecs: - self.skipTest("zip/deflate support not available") - def get_chunks(self, filename): chunks = [] with open(filename, "rb") as fp: @@ -632,9 +620,8 @@ class TestFilePng(PillowTestCase): with Image.open(test_file) as reloaded: self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring") - @unittest.skipUnless( - HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP support not installed with animation" - ) + @skip_unless_feature("webp") + @skip_unless_feature("webp_anim") def test_apng(self): with Image.open("Tests/images/iss634.apng") as im: self.assertEqual(im.get_format_mimetype(), "image/apng") @@ -645,14 +632,11 @@ class TestFilePng(PillowTestCase): @unittest.skipIf(is_win32(), "requires Unix or macOS") +@skip_unless_feature("zlib") class TestTruncatedPngPLeaks(PillowLeakTestCase): mem_limit = 2 * 1024 # max increase in K iterations = 100 # Leak is 56k/iteration, this will leak 5.6megs - def setUp(self): - if "zip_encoder" not in codecs or "zip_decoder" not in codecs: - self.skipTest("zip/deflate support not available") - def test_leak_load(self): with open("Tests/images/hopper.png", "rb") as f: DATA = BytesIO(f.read(16 * 1024)) diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py index b0e4926b7..3fe0cd04e 100644 --- a/Tests/test_file_tar.py +++ b/Tests/test_file_tar.py @@ -1,25 +1,18 @@ import pytest -from PIL import Image, TarIO +from PIL import Image, TarIO, features from .helper import is_pypy -codecs = dir(Image.core) - # Sample tar archive TEST_TAR_FILE = "Tests/images/hopper.tar" -def setup_module(): - if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs: - pytest.skip("neither jpeg nor zip support available") - - def test_sanity(): for codec, test_path, format in [ - ["zip_decoder", "hopper.png", "PNG"], - ["jpeg_decoder", "hopper.jpg", "JPEG"], + ["zlib", "hopper.png", "PNG"], + ["jpg", "hopper.jpg", "JPEG"], ]: - if codec in codecs: + if features.check(codec): with TarIO.TarIO(TEST_TAR_FILE, test_path) as tar: with Image.open(tar) as im: im.load() diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 81742c659..df179d256 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -1,5 +1,3 @@ -import unittest - import pytest from PIL import Image, WebPImagePlugin @@ -8,6 +6,7 @@ from .helper import ( assert_image_similar, assert_image_similar_tofile, hopper, + skip_unless_feature, ) try: @@ -32,7 +31,7 @@ class TestUnsupportedWebp(PillowTestCase): WebPImagePlugin.SUPPORTED = True -@unittest.skipUnless(HAVE_WEBP, "WebP support not installed") +@skip_unless_feature("webp") class TestFileWebp(PillowTestCase): def setUp(self): self.rgb_mode = "RGB" @@ -155,9 +154,8 @@ class TestFileWebp(PillowTestCase): Image.open(blob).load() Image.open(blob).load() - @unittest.skipUnless( - HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP save all not available" - ) + @skip_unless_feature("webp") + @skip_unless_feature("webp_anim") def test_background_from_gif(self): with Image.open("Tests/images/chi.gif") as im: original_value = im.convert("RGB").getpixel((1, 1)) diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index df0c850d8..a846a6db4 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -1,16 +1,18 @@ import pytest from PIL import Image -from .helper import assert_image_equal, assert_image_similar, is_big_endian, on_ci +from .helper import ( + assert_image_equal, + assert_image_similar, + is_big_endian, + on_ci, + skip_unless_feature, +) -_webp = pytest.importorskip("PIL._webp", reason="WebP support not installed") - - -def setup_module(): - if not _webp.HAVE_WEBPANIM: - pytest.skip( - "WebP library does not contain animation support, not testing animation" - ) +pytestmark = [ + skip_unless_feature("webp"), + skip_unless_feature("webp_anim"), +] def test_n_frames(): diff --git a/Tests/test_file_webp_lossless.py b/Tests/test_file_webp_lossless.py index 79150a37d..4d06f53b1 100644 --- a/Tests/test_file_webp_lossless.py +++ b/Tests/test_file_webp_lossless.py @@ -4,7 +4,6 @@ from PIL import Image from .helper import assert_image_equal, hopper _webp = pytest.importorskip("PIL._webp", reason="WebP support not installed") - RGB_MODE = "RGB" diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index e4b6802a3..720f197f6 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -3,12 +3,13 @@ from io import BytesIO import pytest from PIL import Image +from .helper import skip_unless_feature + _webp = pytest.importorskip("PIL._webp", reason="WebP support not installed") - - -def setup_module(): - if not _webp.HAVE_WEBPMUX: - pytest.skip("WebPMux support not installed") +pytestmark = [ + skip_unless_feature("webp"), + skip_unless_feature("webp_mux"), +] def test_read_exif_metadata(): @@ -93,10 +94,8 @@ def test_read_no_exif(): assert not webp_image._getexif() +@skip_unless_feature("webp_anim") def test_write_animated_metadata(tmp_path): - if not _webp.HAVE_WEBPANIM: - pytest.skip("WebP animation support not available") - iccp_data = b"" exif_data = b"" xmp_data = b"" diff --git a/Tests/test_font_leaks.py b/Tests/test_font_leaks.py index be0612fa2..015210b4d 100644 --- a/Tests/test_font_leaks.py +++ b/Tests/test_font_leaks.py @@ -1,8 +1,6 @@ -import unittest +from PIL import Image, ImageDraw, ImageFont -from PIL import Image, ImageDraw, ImageFont, features - -from .helper import PillowLeakTestCase +from .helper import PillowLeakTestCase, skip_unless_feature class TestTTypeFontLeak(PillowLeakTestCase): @@ -19,7 +17,7 @@ class TestTTypeFontLeak(PillowLeakTestCase): ) ) - @unittest.skipUnless(features.check("freetype2"), "Test requires freetype2") + @skip_unless_feature("freetype2") def test_leak(self): ttype = ImageFont.truetype("Tests/fonts/FreeMono.ttf", 20) self._test_font(ttype) diff --git a/Tests/test_font_pcf.py b/Tests/test_font_pcf.py index 4a4edf889..358798948 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -1,19 +1,19 @@ from PIL import FontFile, Image, ImageDraw, ImageFont, PcfFontFile -from .helper import PillowTestCase, assert_image_equal, assert_image_similar - -codecs = dir(Image.core) +from .helper import ( + PillowTestCase, + assert_image_equal, + assert_image_similar, + skip_unless_feature, +) fontname = "Tests/fonts/10x20-ISO8859-1.pcf" message = "hello, world" +@skip_unless_feature("zlib") class TestFontPcf(PillowTestCase): - def setUp(self): - if "zip_encoder" not in codecs or "zip_decoder" not in codecs: - self.skipTest("zlib support not available") - def save_font(self): with open(fontname, "rb") as test_file: font = PcfFontFile.PcfFontFile(test_file) diff --git a/Tests/test_image_draft.py b/Tests/test_image_draft.py index 0090ce378..8b4b44768 100644 --- a/Tests/test_image_draft.py +++ b/Tests/test_image_draft.py @@ -1,13 +1,8 @@ -import pytest from PIL import Image -from .helper import fromstring, tostring +from .helper import fromstring, skip_unless_feature, tostring - -def setup_module(): - codecs = dir(Image.core) - if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: - pytest.skip("jpeg support not available") +pytestmark = skip_unless_feature("jpg") def draft_roundtrip(in_mode, in_size, req_mode, req_size): diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py index 1be7eaf46..80a531103 100644 --- a/Tests/test_image_split.py +++ b/Tests/test_image_split.py @@ -1,4 +1,4 @@ -from PIL import Image +from PIL import Image, features from .helper import PillowTestCase, assert_image_equal, hopper @@ -44,9 +44,7 @@ class TestImageSplit(PillowTestCase): assert_image_equal(hopper("YCbCr"), split_merge("YCbCr")) def test_split_open(self): - codecs = dir(Image.core) - - if "zip_encoder" in codecs: + if features.check("zlib"): test_file = self.tempfile("temp.png") else: test_file = self.tempfile("temp.pcx") @@ -60,5 +58,5 @@ class TestImageSplit(PillowTestCase): self.assertEqual(split_open("L"), 1) self.assertEqual(split_open("P"), 1) self.assertEqual(split_open("RGB"), 3) - if "zip_encoder" in codecs: + if features.check("zlib"): self.assertEqual(split_open("RGBA"), 4) diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index 46ce0fa7d..9036e1842 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -1,9 +1,14 @@ import os.path import pytest -from PIL import Image, ImageColor, ImageDraw, ImageFont, features +from PIL import Image, ImageColor, ImageDraw, ImageFont -from .helper import assert_image_equal, assert_image_similar, hopper +from .helper import ( + assert_image_equal, + assert_image_similar, + hopper, + skip_unless_feature, +) BLACK = (0, 0, 0) WHITE = (255, 255, 255) @@ -30,8 +35,6 @@ POINTS2 = [10, 10, 20, 40, 30, 30] KITE_POINTS = [(10, 50), (70, 10), (90, 50), (70, 90), (10, 50)] -HAS_FREETYPE = features.check("freetype2") - def test_sanity(): im = hopper("RGB").copy() @@ -912,7 +915,7 @@ def test_textsize_empty_string(): draw.textsize("test\n") -@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +@skip_unless_feature("freetype2") def test_textsize_stroke(): # Arrange im = Image.new("RGB", (W, H)) @@ -924,7 +927,7 @@ def test_textsize_stroke(): assert draw.multiline_textsize("ABC\nAaaa", font, stroke_width=2) == (52, 44) -@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +@skip_unless_feature("freetype2") def test_stroke(): for suffix, stroke_fill in {"same": None, "different": "#0f0"}.items(): # Arrange @@ -941,7 +944,7 @@ def test_stroke(): ) -@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +@skip_unless_feature("freetype2") def test_stroke_multiline(): # Arrange im = Image.new("RGB", (100, 250)) diff --git a/Tests/test_imagedraw2.py b/Tests/test_imagedraw2.py index c9ea687ec..72cbb79b8 100644 --- a/Tests/test_imagedraw2.py +++ b/Tests/test_imagedraw2.py @@ -1,9 +1,13 @@ import os.path -import pytest -from PIL import Image, ImageDraw, ImageDraw2, features +from PIL import Image, ImageDraw, ImageDraw2 -from .helper import assert_image_equal, assert_image_similar, hopper +from .helper import ( + assert_image_equal, + assert_image_similar, + hopper, + skip_unless_feature, +) BLACK = (0, 0, 0) WHITE = (255, 255, 255) @@ -30,7 +34,6 @@ POINTS2 = [10, 10, 20, 40, 30, 30] KITE_POINTS = [(10, 50), (70, 10), (90, 50), (70, 90), (10, 50)] -HAS_FREETYPE = features.check("freetype2") FONT_PATH = "Tests/fonts/FreeMono.ttf" @@ -178,7 +181,7 @@ def test_big_rectangle(): assert_image_similar(im, Image.open(expected), 1) -@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +@skip_unless_feature("freetype2") def test_text(): # Arrange im = Image.new("RGB", (W, H)) @@ -193,7 +196,7 @@ def test_text(): assert_image_similar(im, Image.open(expected), 13) -@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +@skip_unless_feature("freetype2") def test_textsize(): # Arrange im = Image.new("RGB", (W, H)) @@ -207,7 +210,7 @@ def test_textsize(): assert size[1] == 12 -@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +@skip_unless_feature("freetype2") def test_textsize_empty_string(): # Arrange im = Image.new("RGB", (W, H)) @@ -222,7 +225,7 @@ def test_textsize_empty_string(): draw.textsize("test\n", font) -@pytest.mark.skipif(not HAS_FREETYPE, reason="ImageFont not available") +@skip_unless_feature("freetype2") def test_flush(): # Arrange im = Image.new("RGB", (W, H)) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index 13fe3e192..18771fd41 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -1,7 +1,6 @@ -import unittest from io import BytesIO -from PIL import EpsImagePlugin, Image, ImageFile +from PIL import EpsImagePlugin, Image, ImageFile, features from .helper import ( PillowTestCase, @@ -10,19 +9,10 @@ from .helper import ( assert_image_similar, fromstring, hopper, + skip_unless_feature, tostring, ) -try: - from PIL import _webp - - HAVE_WEBP = True -except ImportError: - HAVE_WEBP = False - - -codecs = dir(Image.core) - # save original block sizes MAXBLOCK = ImageFile.MAXBLOCK SAFEBLOCK = ImageFile.SAFEBLOCK @@ -53,7 +43,7 @@ class TestImageFile(PillowTestCase): assert_image_similar(im1.convert("P"), im2, 1) assert_image_equal(*roundtrip("IM")) assert_image_equal(*roundtrip("MSP")) - if "zip_encoder" in codecs: + if features.check("zlib"): try: # force multiple blocks in PNG driver ImageFile.MAXBLOCK = 8192 @@ -77,7 +67,7 @@ class TestImageFile(PillowTestCase): # EPS comes back in RGB: assert_image_similar(im1, im2.convert("L"), 20) - if "jpeg_encoder" in codecs: + if features.check("jpg"): im1, im2 = roundtrip("JPEG") # lossy compression assert_image(im1, im2.mode, im2.size) @@ -90,10 +80,8 @@ class TestImageFile(PillowTestCase): p.feed(data) self.assertEqual((48, 48), p.image.size) + @skip_unless_feature("zlib") def test_safeblock(self): - if "zip_encoder" not in codecs: - self.skipTest("PNG (zlib) encoder not available") - im1 = hopper() try: @@ -120,10 +108,8 @@ class TestImageFile(PillowTestCase): with self.assertRaises(IOError): p.close() + @skip_unless_feature("zlib") def test_truncated_with_errors(self): - if "zip_encoder" not in codecs: - self.skipTest("PNG (zlib) encoder not available") - with Image.open("Tests/images/truncated_image.png") as im: with self.assertRaises(IOError): im.load() @@ -132,10 +118,8 @@ class TestImageFile(PillowTestCase): with self.assertRaises(IOError): im.load() + @skip_unless_feature("zlib") def test_truncated_without_errors(self): - if "zip_encoder" not in codecs: - self.skipTest("PNG (zlib) encoder not available") - with Image.open("Tests/images/truncated_image.png") as im: ImageFile.LOAD_TRUNCATED_IMAGES = True try: @@ -143,18 +127,14 @@ class TestImageFile(PillowTestCase): finally: ImageFile.LOAD_TRUNCATED_IMAGES = False + @skip_unless_feature("zlib") def test_broken_datastream_with_errors(self): - if "zip_encoder" not in codecs: - self.skipTest("PNG (zlib) encoder not available") - with Image.open("Tests/images/broken_data_stream.png") as im: with self.assertRaises(IOError): im.load() + @skip_unless_feature("zlib") def test_broken_datastream_without_errors(self): - if "zip_encoder" not in codecs: - self.skipTest("PNG (zlib) encoder not available") - with Image.open("Tests/images/broken_data_stream.png") as im: ImageFile.LOAD_TRUNCATED_IMAGES = True try: @@ -292,10 +272,8 @@ class TestPyDecoder(PillowTestCase): self.assertEqual(reloaded_exif[40963], 455) self.assertEqual(exif[305], "Pillow test") - @unittest.skipIf( - not HAVE_WEBP or not _webp.HAVE_WEBPANIM, - "WebP support not installed with animation", - ) + @skip_unless_feature("webp") + @skip_unless_feature("webp_anim") def test_exif_webp(self): with Image.open("Tests/images/hopper.webp") as im: exif = im.getexif() diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 12e6ea55a..93adc0911 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -3,11 +3,11 @@ import distutils.version import os import re import shutil -import sys import unittest from io import BytesIO +from unittest import mock -from PIL import Image, ImageDraw, ImageFont, features +from PIL import Image, ImageDraw, ImageFont from .helper import ( PillowTestCase, @@ -16,6 +16,7 @@ from .helper import ( assert_image_similar_tofile, is_pypy, is_win32, + skip_unless_feature, ) FONT_PATH = "Tests/fonts/FreeMono.ttf" @@ -23,37 +24,8 @@ FONT_SIZE = 20 TEST_TEXT = "hey you\nyou are awesome\nthis looks awkward" -HAS_FREETYPE = features.check("freetype2") -HAS_RAQM = features.check("raqm") - -class SimplePatcher: - def __init__(self, parent_obj, attr_name, value): - self._parent_obj = parent_obj - self._attr_name = attr_name - self._saved = None - self._is_saved = False - self._value = value - - def __enter__(self): - # Patch the attr on the object - if hasattr(self._parent_obj, self._attr_name): - self._saved = getattr(self._parent_obj, self._attr_name) - setattr(self._parent_obj, self._attr_name, self._value) - self._is_saved = True - else: - setattr(self._parent_obj, self._attr_name, self._value) - self._is_saved = False - - def __exit__(self, type, value, traceback): - # Restore the original value - if self._is_saved: - setattr(self._parent_obj, self._attr_name, self._saved) - else: - delattr(self._parent_obj, self._attr_name) - - -@unittest.skipUnless(HAS_FREETYPE, "ImageFont not available") +@skip_unless_feature("freetype2") class TestImageFont(PillowTestCase): LAYOUT_ENGINE = ImageFont.LAYOUT_BASIC @@ -491,7 +463,7 @@ class TestImageFont(PillowTestCase): def _test_fake_loading_font(self, path_to_fake, fontname): # Make a copy of FreeTypeFont so we can patch the original free_type_font = copy.deepcopy(ImageFont.FreeTypeFont) - with SimplePatcher(ImageFont, "_FreeTypeFont", free_type_font): + with mock.patch.object(ImageFont, "_FreeTypeFont", free_type_font, create=True): def loadable_font(filepath, size, index, encoding, *args, **kwargs): if filepath == path_to_fake: @@ -502,7 +474,7 @@ class TestImageFont(PillowTestCase): filepath, size, index, encoding, *args, **kwargs ) - with SimplePatcher(ImageFont, "FreeTypeFont", loadable_font): + with mock.patch.object(ImageFont, "FreeTypeFont", loadable_font): font = ImageFont.truetype(fontname) # Make sure it's loaded name = font.getname() @@ -513,10 +485,9 @@ class TestImageFont(PillowTestCase): # A lot of mocking here - this is more for hitting code and # catching syntax like errors font_directory = "/usr/local/share/fonts" - with SimplePatcher(sys, "platform", "linux"): - patched_env = copy.deepcopy(os.environ) - patched_env["XDG_DATA_DIRS"] = "/usr/share/:/usr/local/share/" - with SimplePatcher(os, "environ", patched_env): + with mock.patch("sys.platform", "linux"): + patched_env = {"XDG_DATA_DIRS": "/usr/share/:/usr/local/share/"} + with mock.patch.dict(os.environ, patched_env): def fake_walker(path): if path == font_directory: @@ -534,7 +505,7 @@ class TestImageFont(PillowTestCase): ] return [(path, [], ["some_random_font.ttf"])] - with SimplePatcher(os, "walk", fake_walker): + with mock.patch("os.walk", fake_walker): # Test that the font loads both with and without the # extension self._test_fake_loading_font( @@ -559,7 +530,7 @@ class TestImageFont(PillowTestCase): # Like the linux test, more cover hitting code rather than testing # correctness. font_directory = "/System/Library/Fonts" - with SimplePatcher(sys, "platform", "darwin"): + with mock.patch("sys.platform", "darwin"): def fake_walker(path): if path == font_directory: @@ -577,7 +548,7 @@ class TestImageFont(PillowTestCase): ] return [(path, [], ["some_random_font.ttf"])] - with SimplePatcher(os, "walk", fake_walker): + with mock.patch("os.walk", fake_walker): self._test_fake_loading_font(font_directory + "/Arial.ttf", "Arial.ttf") self._test_fake_loading_font(font_directory + "/Arial.ttf", "Arial") self._test_fake_loading_font(font_directory + "/Single.otf", "Single") @@ -752,6 +723,6 @@ class TestImageFont(PillowTestCase): _check_text(font, "Tests/images/variation_tiny_axes.png", 32.5) -@unittest.skipUnless(HAS_RAQM, "Raqm not Available") +@skip_unless_feature("raqm") class TestImageFont_RaqmLayout(TestImageFont): LAYOUT_ENGINE = ImageFont.LAYOUT_RAQM diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index 796059e5d..8f619b9f9 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -1,14 +1,12 @@ -import unittest +from PIL import Image, ImageDraw, ImageFont -from PIL import Image, ImageDraw, ImageFont, features - -from .helper import PillowTestCase, assert_image_similar +from .helper import PillowTestCase, assert_image_similar, skip_unless_feature FONT_SIZE = 20 FONT_PATH = "Tests/fonts/DejaVuSans.ttf" -@unittest.skipUnless(features.check("raqm"), "Raqm Library is not installed.") +@skip_unless_feature("raqm") class TestImagecomplextext(PillowTestCase): def test_english(self): # smoke test, this should not fail diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index af78c1197..3d0afba9c 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -1,5 +1,5 @@ import pytest -from PIL import Image, ImageOps +from PIL import Image, ImageOps, features from .helper import ( assert_image_equal, @@ -8,13 +8,6 @@ from .helper import ( hopper, ) -try: - from PIL import _webp - - HAVE_WEBP = True -except ImportError: - HAVE_WEBP = False - class Deformer: def getmesh(self, im): @@ -274,7 +267,7 @@ def test_colorize_3color_offset(): def test_exif_transpose(): exts = [".jpg"] - if HAVE_WEBP and _webp.HAVE_WEBPANIM: + if features.check("webp") and features.check("webp_anim"): exts.append(".webp") for ext in exts: with Image.open("Tests/images/hopper" + ext) as base_im: diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 741d71a1c..e0c3f8dec 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -1,6 +1,6 @@ from PIL import Image, ImageSequence, TiffImagePlugin -from .helper import PillowTestCase, assert_image_equal, hopper +from .helper import PillowTestCase, assert_image_equal, hopper, skip_unless_feature class TestImageSequence(PillowTestCase): @@ -47,12 +47,8 @@ class TestImageSequence(PillowTestCase): def test_tiff(self): self._test_multipage_tiff() + @skip_unless_feature("libtiff") def test_libtiff(self): - codecs = dir(Image.core) - - if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs: - self.skipTest("tiff support not available") - TiffImagePlugin.READ_LIBTIFF = True self._test_multipage_tiff() TiffImagePlugin.READ_LIBTIFF = False diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index dedbbfe6d..e570ecb99 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -1,6 +1,6 @@ from fractions import Fraction -from PIL import Image, TiffImagePlugin +from PIL import Image, TiffImagePlugin, features from PIL.TiffImagePlugin import IFDRational from .helper import PillowTestCase, hopper @@ -43,7 +43,7 @@ class Test_IFDRational(PillowTestCase): def test_ifd_rational_save(self): methods = (True, False) - if "libtiff_encoder" not in dir(Image.core): + if not features.check("libtiff"): methods = (False,) for libtiff in methods: diff --git a/Tests/test_webp_leaks.py b/Tests/test_webp_leaks.py index 713fc161e..34197c14f 100644 --- a/Tests/test_webp_leaks.py +++ b/Tests/test_webp_leaks.py @@ -1,14 +1,13 @@ -import unittest from io import BytesIO -from PIL import Image, features +from PIL import Image -from .helper import PillowLeakTestCase +from .helper import PillowLeakTestCase, skip_unless_feature test_file = "Tests/images/hopper.webp" -@unittest.skipUnless(features.check("webp"), "WebP is not installed") +@skip_unless_feature("webp") class TestWebPLeaks(PillowLeakTestCase): mem_limit = 3 * 1024 # kb