diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index eff9824bb2d..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -name: Issue report -about: Create a report to help us improve Pillow ---- -### What did you do? - -### What did you expect to happen? - -### What actually happened? - -### What are your OS, Python and Pillow versions? - -* OS: -* Python: -* Pillow: - -Please include **code** that reproduces the issue and whenever possible, an **image** that demonstrates the issue. Please upload images to GitHub, not to third-party file hosting sites. If necessary, add the image to a zip or tar archive. - -The best reproductions are self-contained scripts with minimal dependencies. If you are using a framework such as plone, Django, or buildout, try to replicate the issue just using Pillow. - -```python -code goes here -``` diff --git a/.github/ISSUE_TEMPLATE/ISSUE_REPORT.md b/.github/ISSUE_TEMPLATE/ISSUE_REPORT.md new file mode 100644 index 00000000000..115f6135dfb --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ISSUE_REPORT.md @@ -0,0 +1,59 @@ +--- +name: Issue report +about: Create a report to help us improve Pillow +--- + + + +### What did you do? + +### What did you expect to happen? + +### What actually happened? + +### What are your OS, Python and Pillow versions? + +* OS: +* Python: +* Pillow: + + + +```python +code goes here +``` diff --git a/CHANGES.rst b/CHANGES.rst index 11dfd2a7bd0..599eaa345d8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,30 @@ Changelog (Pillow) 6.1.0 (unreleased) ------------------ +- Fixed bugs in calculating text size #3864 + [radarhere] + +- Add __main__.py to output basic format and support information #3870 + [jdufresne] + +- Added variation font support #3802 + [radarhere] + +- Do not down-convert if image is LA when showing with PNG format #3869 + [radarhere] + +- Improve handling of PSD frames #3759 + [radarhere] + +- Improved ICO and ICNS loading #3897 + [radarhere] + +- Changed Preview application path so that it is no longer static #3896 + [radarhere] + +- Corrected ttb text positioning #3856 + [radarhere] + - Handle unexpected ICO image sizes #3836 [radarhere] diff --git a/Tests/32bit_segfault_check.py b/Tests/32bit_segfault_check.py index a601f762e85..59a22c9d633 100755 --- a/Tests/32bit_segfault_check.py +++ b/Tests/32bit_segfault_check.py @@ -4,5 +4,5 @@ import sys -if sys.maxsize < 2**32: - im = Image.new('L', (999999, 999999), 0) +if sys.maxsize < 2 ** 32: + im = Image.new("L", (999999, 999999), 0) diff --git a/Tests/bench_cffi_access.py b/Tests/bench_cffi_access.py index 79427dca3c2..249db3c9aee 100644 --- a/Tests/bench_cffi_access.py +++ b/Tests/bench_cffi_access.py @@ -26,18 +26,21 @@ def timer(func, label, *args): starttime = time.time() for x in range(iterations): func(*args) - if time.time()-starttime > 10: - print("%s: breaking at %s iterations, %.6f per iteration" % ( - label, x+1, (time.time()-starttime)/(x+1.0))) + if time.time() - starttime > 10: + print( + "%s: breaking at %s iterations, %.6f per iteration" + % (label, x + 1, (time.time() - starttime) / (x + 1.0)) + ) break - if x == iterations-1: + if x == iterations - 1: endtime = time.time() - print("%s: %.4f s %.6f per iteration" % ( - label, endtime-starttime, (endtime-starttime)/(x+1.0))) + print( + "%s: %.4f s %.6f per iteration" + % (label, endtime - starttime, (endtime - starttime) / (x + 1.0)) + ) class BenchCffiAccess(PillowTestCase): - def test_direct(self): im = hopper() im.load() @@ -48,11 +51,11 @@ def test_direct(self): self.assertEqual(caccess[(0, 0)], access[(0, 0)]) print("Size: %sx%s" % im.size) - timer(iterate_get, 'PyAccess - get', im.size, access) - timer(iterate_set, 'PyAccess - set', im.size, access) - timer(iterate_get, 'C-api - get', im.size, caccess) - timer(iterate_set, 'C-api - set', im.size, caccess) + timer(iterate_get, "PyAccess - get", im.size, access) + timer(iterate_set, "PyAccess - set", im.size, access) + timer(iterate_get, "C-api - get", im.size, caccess) + timer(iterate_set, "C-api - set", im.size, caccess) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/bench_get.py b/Tests/bench_get.py index 68ac2c9a24e..12d7d06fc45 100644 --- a/Tests/bench_get.py +++ b/Tests/bench_get.py @@ -2,6 +2,7 @@ import timeit import sys + sys.path.insert(0, ".") diff --git a/Tests/check_fli_overflow.py b/Tests/check_fli_overflow.py index 3f7c5801559..6b16f03716f 100644 --- a/Tests/check_fli_overflow.py +++ b/Tests/check_fli_overflow.py @@ -12,5 +12,5 @@ def test_fli_overflow(self): im.load() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/check_icns_dos.py b/Tests/check_icns_dos.py index d4c3cf7cb55..aaa13ddd25e 100644 --- a/Tests/check_icns_dos.py +++ b/Tests/check_icns_dos.py @@ -6,7 +6,6 @@ from io import BytesIO if py3: - Image.open(BytesIO(bytes('icns\x00\x00\x00\x10hang\x00\x00\x00\x00', - 'latin-1'))) + Image.open(BytesIO(bytes("icns\x00\x00\x00\x10hang\x00\x00\x00\x00", "latin-1"))) else: - Image.open(BytesIO(bytes('icns\x00\x00\x00\x10hang\x00\x00\x00\x00'))) + Image.open(BytesIO(bytes("icns\x00\x00\x00\x10hang\x00\x00\x00\x00"))) diff --git a/Tests/check_imaging_leaks.py b/Tests/check_imaging_leaks.py index 7fa0663e85c..e616f20f21f 100755 --- a/Tests/check_imaging_leaks.py +++ b/Tests/check_imaging_leaks.py @@ -9,11 +9,11 @@ max_iterations = 10000 -@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") +@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") class TestImagingLeaks(PillowTestCase): - def _get_mem_usage(self): from resource import getpagesize, getrusage, RUSAGE_SELF + mem = getrusage(RUSAGE_SELF).ru_maxrss return mem * getpagesize() / 1024 / 1024 @@ -25,20 +25,22 @@ def _test_leak(self, min_iterations, max_iterations, fn, *args, **kwargs): if i < min_iterations: mem_limit = mem + 1 continue - msg = 'memory usage limit exceeded after %d iterations' % (i + 1) + msg = "memory usage limit exceeded after %d iterations" % (i + 1) self.assertLessEqual(mem, mem_limit, msg) def test_leak_putdata(self): - im = Image.new('RGB', (25, 25)) - self._test_leak(min_iterations, max_iterations, - im.putdata, im.getdata()) + im = Image.new("RGB", (25, 25)) + self._test_leak(min_iterations, max_iterations, im.putdata, im.getdata()) def test_leak_getlist(self): - im = Image.new('P', (25, 25)) - self._test_leak(min_iterations, max_iterations, - # Pass a new list at each iteration. - lambda: im.point(range(256))) + im = Image.new("P", (25, 25)) + self._test_leak( + min_iterations, + max_iterations, + # Pass a new list at each iteration. + lambda: im.point(range(256)), + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/check_j2k_dos.py b/Tests/check_j2k_dos.py index 4ea31cec212..e036f5dbd49 100644 --- a/Tests/check_j2k_dos.py +++ b/Tests/check_j2k_dos.py @@ -6,10 +6,16 @@ from io import BytesIO if py3: - Image.open(BytesIO(bytes( - '\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang', - 'latin-1'))) + Image.open( + BytesIO( + bytes( + "\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang", + "latin-1", + ) + ) + ) else: - Image.open(BytesIO(bytes( - '\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang'))) + Image.open( + BytesIO(bytes("\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang")) + ) diff --git a/Tests/check_j2k_leaks.py b/Tests/check_j2k_leaks.py index d87b7f0413a..19aabc81b6a 100755 --- a/Tests/check_j2k_leaks.py +++ b/Tests/check_j2k_leaks.py @@ -4,21 +4,22 @@ from io import BytesIO # Limits for testing the leak -mem_limit = 1024*1048576 -stack_size = 8*1048576 -iterations = int((mem_limit/stack_size)*2) +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(sys.platform.startswith('win32'), "requires Unix or macOS") +@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") 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') + self.skipTest("JPEG 2000 support not available") def test_leak_load(self): from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK + setrlimit(RLIMIT_STACK, (stack_size, stack_size)) setrlimit(RLIMIT_AS, (mem_limit, mem_limit)) for _ in range(iterations): @@ -27,6 +28,7 @@ def test_leak_load(self): def test_leak_save(self): from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK + setrlimit(RLIMIT_STACK, (stack_size, stack_size)) setrlimit(RLIMIT_AS, (mem_limit, mem_limit)) for _ in range(iterations): @@ -38,5 +40,5 @@ def test_leak_save(self): test_output.read() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/check_j2k_overflow.py b/Tests/check_j2k_overflow.py index f456ebb32d9..32bbdbf42dd 100644 --- a/Tests/check_j2k_overflow.py +++ b/Tests/check_j2k_overflow.py @@ -5,11 +5,11 @@ class TestJ2kEncodeOverflow(PillowTestCase): def test_j2k_overflow(self): - im = Image.new('RGBA', (1024, 131584)) - target = self.tempfile('temp.jpc') + im = Image.new("RGBA", (1024, 131584)) + target = self.tempfile("temp.jpc") with self.assertRaises(IOError): im.save(target) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/check_jpeg_leaks.py b/Tests/check_jpeg_leaks.py index 97a5650e08c..a6fd0028019 100644 --- a/Tests/check_jpeg_leaks.py +++ b/Tests/check_jpeg_leaks.py @@ -14,7 +14,7 @@ """ -@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") +@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") class TestJpegLeaks(PillowTestCase): """ @@ -74,9 +74,11 @@ class TestJpegLeaks(PillowTestCase): """ def test_qtables_leak(self): - im = hopper('RGB') + im = hopper("RGB") - standard_l_qtable = [int(s) for s in """ + standard_l_qtable = [ + int(s) + for s in """ 16 11 10 16 24 40 51 61 12 12 14 19 26 58 60 55 14 13 16 24 40 57 69 56 @@ -85,9 +87,14 @@ def test_qtables_leak(self): 24 35 55 64 81 104 113 92 49 64 78 87 103 121 120 101 72 92 95 98 112 100 103 99 - """.split(None)] - - standard_chrominance_qtable = [int(s) for s in """ + """.split( + None + ) + ] + + standard_chrominance_qtable = [ + int(s) + for s in """ 17 18 24 47 99 99 99 99 18 21 26 66 99 99 99 99 24 26 56 99 99 99 99 99 @@ -96,10 +103,12 @@ def test_qtables_leak(self): 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 - """.split(None)] + """.split( + None + ) + ] - qtables = [standard_l_qtable, - standard_chrominance_qtable] + qtables = [standard_l_qtable, standard_chrominance_qtable] for _ in range(iterations): test_output = BytesIO() @@ -161,8 +170,8 @@ def test_exif_leak(self): 0 11.33 """ - im = hopper('RGB') - exif = b'12345678'*4096 + im = hopper("RGB") + exif = b"12345678" * 4096 for _ in range(iterations): test_output = BytesIO() @@ -195,12 +204,12 @@ def test_base_save(self): 0 +----------------------------------------------------------------------->Gi 0 7.882 """ - im = hopper('RGB') + im = hopper("RGB") for _ in range(iterations): test_output = BytesIO() im.save(test_output, "JPEG") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/check_large_memory.py b/Tests/check_large_memory.py index a2fcc206e28..7e0e83088fb 100644 --- a/Tests/check_large_memory.py +++ b/Tests/check_large_memory.py @@ -22,12 +22,11 @@ XDIM = 48000 -@unittest.skipIf(sys.maxsize <= 2**32, "requires 64-bit system") +@unittest.skipIf(sys.maxsize <= 2 ** 32, "requires 64-bit system") class LargeMemoryTest(PillowTestCase): - def _write_png(self, xdim, ydim): - f = self.tempfile('temp.png') - im = Image.new('L', (xdim, ydim), 0) + f = self.tempfile("temp.png") + im = Image.new("L", (xdim, ydim), 0) im.save(f) def test_large(self): @@ -44,5 +43,5 @@ def test_size_greater_than_int(self): Image.fromarray(arr) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/check_large_memory_numpy.py b/Tests/check_large_memory_numpy.py index b66988fd58d..3607bcb972a 100644 --- a/Tests/check_large_memory_numpy.py +++ b/Tests/check_large_memory_numpy.py @@ -11,6 +11,7 @@ # Raspberry Pis). from PIL import Image + try: import numpy as np except ImportError: @@ -20,14 +21,13 @@ XDIM = 48000 -@unittest.skipIf(sys.maxsize <= 2**32, "requires 64-bit system") +@unittest.skipIf(sys.maxsize <= 2 ** 32, "requires 64-bit system") class LargeMemoryNumpyTest(PillowTestCase): - def _write_png(self, xdim, ydim): dtype = np.uint8 a = np.zeros((xdim, ydim), dtype=dtype) - f = self.tempfile('temp.png') - im = Image.fromarray(a, 'L') + f = self.tempfile("temp.png") + im = Image.fromarray(a, "L") im.save(f) def test_large(self): @@ -39,5 +39,5 @@ def test_2gpx(self): self._write_png(XDIM, XDIM) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/check_libtiff_segfault.py b/Tests/check_libtiff_segfault.py index f8c4a309064..99e3056bfd0 100644 --- a/Tests/check_libtiff_segfault.py +++ b/Tests/check_libtiff_segfault.py @@ -15,5 +15,5 @@ def test_segfault(self): im.load() -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/check_png_dos.py b/Tests/check_png_dos.py index 9a446bf840c..edcb0d95228 100644 --- a/Tests/check_png_dos.py +++ b/Tests/check_png_dos.py @@ -17,10 +17,10 @@ def test_ignore_dos_text(self): ImageFile.LOAD_TRUNCATED_IMAGES = False for s in im.text.values(): - self.assertLess(len(s), 1024*1024, "Text chunk larger than 1M") + self.assertLess(len(s), 1024 * 1024, "Text chunk larger than 1M") for s in im.info.values(): - self.assertLess(len(s), 1024*1024, "Text chunk larger than 1M") + self.assertLess(len(s), 1024 * 1024, "Text chunk larger than 1M") def test_dos_text(self): @@ -32,20 +32,20 @@ def test_dos_text(self): return for s in im.text.values(): - self.assertLess(len(s), 1024*1024, "Text chunk larger than 1M") + self.assertLess(len(s), 1024 * 1024, "Text chunk larger than 1M") def test_dos_total_memory(self): - im = Image.new('L', (1, 1)) - compressed_data = zlib.compress(b'a'*1024*1023) + im = Image.new("L", (1, 1)) + compressed_data = zlib.compress(b"a" * 1024 * 1023) info = PngImagePlugin.PngInfo() for x in range(64): - info.add_text('t%s' % x, compressed_data, zip=True) - info.add_itxt('i%s' % x, compressed_data, zip=True) + info.add_text("t%s" % x, compressed_data, zip=True) + info.add_itxt("i%s" % x, compressed_data, zip=True) b = BytesIO() - im.save(b, 'PNG', pnginfo=info) + im.save(b, "PNG", pnginfo=info) b.seek(0) try: @@ -57,9 +57,10 @@ def test_dos_total_memory(self): total_len = 0 for txt in im2.text.values(): total_len += len(txt) - self.assertLess(total_len, 64*1024*1024, - "Total text chunks greater than 64M") + self.assertLess( + total_len, 64 * 1024 * 1024, "Total text chunks greater than 64M" + ) -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/fonts/AdobeVFPrototype.ttf b/Tests/fonts/AdobeVFPrototype.ttf new file mode 100644 index 00000000000..64f5ea8e1ed Binary files /dev/null and b/Tests/fonts/AdobeVFPrototype.ttf differ diff --git a/Tests/fonts/ArefRuqaa-Regular.ttf b/Tests/fonts/ArefRuqaa-Regular.ttf new file mode 100644 index 00000000000..940cb58f4dc Binary files /dev/null and b/Tests/fonts/ArefRuqaa-Regular.ttf differ diff --git a/Tests/fonts/LICENSE.txt b/Tests/fonts/LICENSE.txt index ee9daee592c..0e0baaabb05 100644 --- a/Tests/fonts/LICENSE.txt +++ b/Tests/fonts/LICENSE.txt @@ -1,13 +1,13 @@ -NotoNastaliqUrdu-Regular.ttf: +NotoNastaliqUrdu-Regular.ttf, from https://github.com/googlei18n/noto-fonts +NotoSansJP-Thin.otf, from https://www.google.com/get/noto/help/cjk/ +AdobeVFPrototype.ttf, from https://github.com/adobe-fonts/adobe-variable-font-prototype +TINY5x3GX.ttf, from http://velvetyne.fr/fonts/tiny +ArefRuqaa-Regular.ttf, from https://github.com/google/fonts/tree/master/ofl/arefruqaa -(from https://github.com/googlei18n/noto-fonts) +All of the above fonts are published under the SIL Open Font License (OFL) v1.1 (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL), which allows you to copy, modify, and redistribute them if you need to. -All Noto fonts are published under the SIL Open Font License (OFL) v1.1 (http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL), which allows you to copy, modify, and redistribute them if you need to. - -10x20-ISO8859-1.pcf - -(from https://packages.ubuntu.com/xenial/xfonts-base) +10x20-ISO8859-1.pcf, from https://packages.ubuntu.com/xenial/xfonts-base "Public domain font. Share and enjoy." diff --git a/Tests/fonts/NotoSansJP-Regular.otf b/Tests/fonts/NotoSansJP-Regular.otf new file mode 100644 index 00000000000..fbccd9f16a0 Binary files /dev/null and b/Tests/fonts/NotoSansJP-Regular.otf differ diff --git a/Tests/fonts/TINY5x3GX.ttf b/Tests/fonts/TINY5x3GX.ttf new file mode 100755 index 00000000000..bd6e208dece Binary files /dev/null and b/Tests/fonts/TINY5x3GX.ttf differ diff --git a/Tests/helper.py b/Tests/helper.py index b47604a6029..7038566bf79 100644 --- a/Tests/helper.py +++ b/Tests/helper.py @@ -11,12 +11,13 @@ from PIL._util import py3 import logging + logger = logging.getLogger(__name__) HAS_UPLOADER = False -if os.environ.get('SHOW_ERRORS', None): +if os.environ.get("SHOW_ERRORS", None): # local img.show for errors. HAS_UPLOADER = True @@ -25,9 +26,12 @@ class test_image_results: def upload(self, a, b): a.show() b.show() + + else: try: import test_image_results + HAS_UPLOADER = True except ImportError: pass @@ -35,19 +39,18 @@ def upload(self, a, b): def convert_to_comparable(a, b): new_a, new_b = a, b - if a.mode == 'P': - new_a = Image.new('L', a.size) - new_b = Image.new('L', b.size) + if a.mode == "P": + new_a = Image.new("L", a.size) + new_b = Image.new("L", b.size) new_a.putdata(a.getdata()) new_b.putdata(b.getdata()) - elif a.mode == 'I;16': - new_a = a.convert('I') - new_b = b.convert('I') + elif a.mode == "I;16": + new_a = a.convert("I") + new_b = b.convert("I") return new_a, new_b class PillowTestCase(unittest.TestCase): - def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) # holds last result object passed to run method: @@ -75,32 +78,32 @@ def delete_tempfile(self, path): def assert_deep_equal(self, a, b, msg=None): try: self.assertEqual( - len(a), len(b), - msg or "got length %s, expected %s" % (len(a), len(b))) + len(a), len(b), msg or "got length %s, expected %s" % (len(a), len(b)) + ) self.assertTrue( - all(x == y for x, y in zip(a, b)), - msg or "got %s, expected %s" % (a, b)) + all(x == y for x, y in zip(a, b)), msg or "got %s, expected %s" % (a, b) + ) except Exception: self.assertEqual(a, b, msg) def assert_image(self, im, mode, size, msg=None): if mode is not None: self.assertEqual( - im.mode, mode, - msg or "got mode %r, expected %r" % (im.mode, mode)) + im.mode, mode, msg or "got mode %r, expected %r" % (im.mode, mode) + ) if size is not None: self.assertEqual( - im.size, size, - msg or "got size %r, expected %r" % (im.size, size)) + im.size, size, msg or "got size %r, expected %r" % (im.size, size) + ) def assert_image_equal(self, a, b, msg=None): self.assertEqual( - a.mode, b.mode, - msg or "got mode %r, expected %r" % (a.mode, b.mode)) + a.mode, b.mode, msg or "got mode %r, expected %r" % (a.mode, b.mode) + ) self.assertEqual( - a.size, b.size, - msg or "got size %r, expected %r" % (a.size, b.size)) + a.size, b.size, msg or "got size %r, expected %r" % (a.size, b.size) + ) if a.tobytes() != b.tobytes(): if HAS_UPLOADER: try: @@ -120,26 +123,28 @@ def assert_image_equal_tofile(self, a, filename, msg=None, mode=None): def assert_image_similar(self, a, b, epsilon, msg=None): epsilon = float(epsilon) self.assertEqual( - a.mode, b.mode, - msg or "got mode %r, expected %r" % (a.mode, b.mode)) + a.mode, b.mode, msg or "got mode %r, expected %r" % (a.mode, b.mode) + ) self.assertEqual( - a.size, b.size, - msg or "got size %r, expected %r" % (a.size, b.size)) + a.size, b.size, msg or "got size %r, expected %r" % (a.size, b.size) + ) a, b = convert_to_comparable(a, b) diff = 0 for ach, bch in zip(a.split(), b.split()): - chdiff = ImageMath.eval("abs(a - b)", a=ach, b=bch).convert('L') + chdiff = ImageMath.eval("abs(a - b)", a=ach, b=bch).convert("L") diff += sum(i * num for i, num in enumerate(chdiff.histogram())) - ave_diff = float(diff)/(a.size[0]*a.size[1]) + ave_diff = float(diff) / (a.size[0] * a.size[1]) try: self.assertGreaterEqual( - epsilon, ave_diff, - (msg or '') + - " average pixel value difference %.4f > epsilon %.4f" % ( - ave_diff, epsilon)) + epsilon, + ave_diff, + (msg or "") + + " average pixel value difference %.4f > epsilon %.4f" + % (ave_diff, epsilon), + ) except Exception as e: if HAS_UPLOADER: try: @@ -149,8 +154,7 @@ def assert_image_similar(self, a, b, epsilon, msg=None): pass raise e - def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, - mode=None): + def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, mode=None): with Image.open(filename) as img: if mode: img = img.convert(mode) @@ -168,9 +172,9 @@ def assert_warning(self, warn_class, func, *args, **kwargs): # Verify some things. if warn_class is None: - self.assertEqual(len(w), 0, - "Expected no warnings, got %s" % - [v.category for v in w]) + self.assertEqual( + len(w), 0, "Expected no warnings, got %s" % [v.category for v in w] + ) else: self.assertGreaterEqual(len(w), 1) found = False @@ -192,27 +196,26 @@ def assert_tuple_approx_equal(self, actuals, targets, threshold, msg): value = True for i, target in enumerate(targets): - value *= (target - threshold <= actuals[i] <= target + threshold) + value *= target - threshold <= actuals[i] <= target + threshold - self.assertTrue(value, - msg + ': ' + repr(actuals) + ' != ' + repr(targets)) + self.assertTrue(value, msg + ": " + repr(actuals) + " != " + repr(targets)) - def skipKnownBadTest(self, msg=None, platform=None, - travis=None, interpreter=None): + def skipKnownBadTest(self, msg=None, platform=None, travis=None, interpreter=None): # Skip if platform/travis matches, and # PILLOW_RUN_KNOWN_BAD is not true in the environment. - if os.environ.get('PILLOW_RUN_KNOWN_BAD', False): - print(os.environ.get('PILLOW_RUN_KNOWN_BAD', False)) + if os.environ.get("PILLOW_RUN_KNOWN_BAD", False): + print(os.environ.get("PILLOW_RUN_KNOWN_BAD", False)) return skip = True if platform is not None: skip = sys.platform.startswith(platform) if travis is not None: - skip = skip and (travis == bool(os.environ.get('TRAVIS', False))) + skip = skip and (travis == bool(os.environ.get("TRAVIS", False))) if interpreter is not None: - skip = skip and (interpreter == 'pypy' and - hasattr(sys, 'pypy_version_info')) + skip = skip and ( + interpreter == "pypy" and hasattr(sys, "pypy_version_info") + ) if skip: self.skipTest(msg or "Known Bad Test") @@ -234,7 +237,7 @@ def open_withImagemagick(self, f): raise IOError() -@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") +@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") class PillowLeakTestCase(PillowTestCase): # requires unix/macOS iterations = 100 # count @@ -249,8 +252,9 @@ def _get_mem_usage(self): """ from resource import getrusage, RUSAGE_SELF + mem = getrusage(RUSAGE_SELF).ru_maxrss - if sys.platform == 'darwin': + if sys.platform == "darwin": # man 2 getrusage: # ru_maxrss # This is the maximum resident set size utilized (in bytes). @@ -266,8 +270,8 @@ def _test_leak(self, core): start_mem = self._get_mem_usage() for cycle in range(self.iterations): core() - mem = (self._get_mem_usage() - start_mem) - msg = 'memory usage limit exceeded in iteration %d' % cycle + mem = self._get_mem_usage() - start_mem + msg = "memory usage limit exceeded in iteration %d" % cycle self.assertLess(mem, self.mem_limit, msg) @@ -281,11 +285,13 @@ def _test_leak(self, core): def fromstring(data): from io import BytesIO + return Image.open(BytesIO(data)) def tostring(im, string_format, **options): from io import BytesIO + out = BytesIO() im.save(out, string_format, **options) return out.getvalue() @@ -318,7 +324,8 @@ def command_succeeds(cmd): command succeeds, or False if an OSError was raised by subprocess.Popen. """ import subprocess - with open(os.devnull, 'wb') as f: + + with open(os.devnull, "wb") as f: try: subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT) except OSError: @@ -327,40 +334,41 @@ def command_succeeds(cmd): def djpeg_available(): - return command_succeeds(['djpeg', '-version']) + return command_succeeds(["djpeg", "-version"]) def cjpeg_available(): - return command_succeeds(['cjpeg', '-version']) + return command_succeeds(["cjpeg", "-version"]) def netpbm_available(): - return (command_succeeds(["ppmquant", "--version"]) and - command_succeeds(["ppmtogif", "--version"])) + return command_succeeds(["ppmquant", "--version"]) and command_succeeds( + ["ppmtogif", "--version"] + ) def imagemagick_available(): - return IMCONVERT and command_succeeds([IMCONVERT, '-version']) + return IMCONVERT and command_succeeds([IMCONVERT, "-version"]) def on_appveyor(): - return 'APPVEYOR' in os.environ + return "APPVEYOR" in os.environ -if sys.platform == 'win32': - IMCONVERT = os.environ.get('MAGICK_HOME', '') +if sys.platform == "win32": + IMCONVERT = os.environ.get("MAGICK_HOME", "") if IMCONVERT: - IMCONVERT = os.path.join(IMCONVERT, 'convert.exe') + IMCONVERT = os.path.join(IMCONVERT, "convert.exe") else: - IMCONVERT = 'convert' + IMCONVERT = "convert" def distro(): - if os.path.exists('/etc/os-release'): - with open('/etc/os-release', 'r') as f: + if os.path.exists("/etc/os-release"): + with open("/etc/os-release", "r") as f: for line in f: - if 'ID=' in line: - return line.strip().split('=')[1] + if "ID=" in line: + return line.strip().split("=")[1] class cached_property(object): diff --git a/Tests/images/hopper_draw.ico b/Tests/images/hopper_draw.ico new file mode 100644 index 00000000000..01471189693 Binary files /dev/null and b/Tests/images/hopper_draw.ico differ diff --git a/Tests/images/test_direction_ttb.png b/Tests/images/test_direction_ttb.png new file mode 100644 index 00000000000..825f3213ec8 Binary files /dev/null and b/Tests/images/test_direction_ttb.png differ diff --git a/Tests/images/test_x_max_and_y_offset.png b/Tests/images/test_x_max_and_y_offset.png new file mode 100644 index 00000000000..f8bec3e95e5 Binary files /dev/null and b/Tests/images/test_x_max_and_y_offset.png differ diff --git a/Tests/images/test_y_offset.png b/Tests/images/test_y_offset.png index 5a166be8c2e..2d57890cb5f 100644 Binary files a/Tests/images/test_y_offset.png and b/Tests/images/test_y_offset.png differ diff --git a/Tests/images/variation_adobe.png b/Tests/images/variation_adobe.png new file mode 100644 index 00000000000..71b879bc5a2 Binary files /dev/null and b/Tests/images/variation_adobe.png differ diff --git a/Tests/images/variation_adobe_axes.png b/Tests/images/variation_adobe_axes.png new file mode 100644 index 00000000000..9376c1d7b8f Binary files /dev/null and b/Tests/images/variation_adobe_axes.png differ diff --git a/Tests/images/variation_adobe_name.png b/Tests/images/variation_adobe_name.png new file mode 100644 index 00000000000..9e5fe70e539 Binary files /dev/null and b/Tests/images/variation_adobe_name.png differ diff --git a/Tests/images/variation_tiny.png b/Tests/images/variation_tiny.png new file mode 100644 index 00000000000..a0ff3f5946e Binary files /dev/null and b/Tests/images/variation_tiny.png differ diff --git a/Tests/images/variation_tiny_axes.png b/Tests/images/variation_tiny_axes.png new file mode 100644 index 00000000000..d06ac7a60e7 Binary files /dev/null and b/Tests/images/variation_tiny_axes.png differ diff --git a/Tests/images/variation_tiny_name.png b/Tests/images/variation_tiny_name.png new file mode 100644 index 00000000000..a0c6ffe3f1f Binary files /dev/null and b/Tests/images/variation_tiny_name.png differ diff --git a/Tests/import_all.py b/Tests/import_all.py index 11682237b28..ccfc53aef6e 100644 --- a/Tests/import_all.py +++ b/Tests/import_all.py @@ -5,6 +5,7 @@ import traceback import sys + sys.path.insert(0, ".") for file in glob.glob("src/PIL/*.py"): diff --git a/Tests/make_hash.py b/Tests/make_hash.py index c52e009eca8..bacb391faad 100644 --- a/Tests/make_hash.py +++ b/Tests/make_hash.py @@ -4,21 +4,33 @@ modes = [ "1", - "L", "LA", "La", - "I", "I;16", "I;16L", "I;16B", "I;32L", "I;32B", + "L", + "LA", + "La", + "I", + "I;16", + "I;16L", + "I;16B", + "I;32L", + "I;32B", "F", - "P", "PA", - "RGB", "RGBA", "RGBa", "RGBX", + "P", + "PA", + "RGB", + "RGBA", + "RGBa", + "RGBX", "CMYK", "YCbCr", - "LAB", "HSV", - ] + "LAB", + "HSV", +] def hash(s, i): # djb2 hash: multiply by 33 and xor character for c in s: - i = (((i << 5) + i) ^ ord(c)) & 0xffffffff + i = (((i << 5) + i) ^ ord(c)) & 0xFFFFFFFF return i diff --git a/Tests/test_000_sanity.py b/Tests/test_000_sanity.py index b9dd413f932..0dccb6f5953 100644 --- a/Tests/test_000_sanity.py +++ b/Tests/test_000_sanity.py @@ -5,7 +5,6 @@ class TestSanity(PillowTestCase): - def test_sanity(self): # Make sure we have the binary extension @@ -13,7 +12,7 @@ def test_sanity(self): # Create an image and do stuff with it. im = PIL.Image.new("1", (100, 100)) - self.assertEqual((im.mode, im.size), ('1', (100, 100))) + self.assertEqual((im.mode, im.size), ("1", (100, 100))) self.assertEqual(len(im.tobytes()), 1300) # Create images in all remaining major modes. diff --git a/Tests/test_binary.py b/Tests/test_binary.py index bf9ba1e5fc2..8b46192cd3e 100644 --- a/Tests/test_binary.py +++ b/Tests/test_binary.py @@ -4,21 +4,20 @@ class TestBinary(PillowTestCase): - def test_standard(self): - self.assertEqual(_binary.i8(b'*'), 42) - self.assertEqual(_binary.o8(42), b'*') + self.assertEqual(_binary.i8(b"*"), 42) + self.assertEqual(_binary.o8(42), b"*") def test_little_endian(self): - self.assertEqual(_binary.i16le(b'\xff\xff\x00\x00'), 65535) - self.assertEqual(_binary.i32le(b'\xff\xff\x00\x00'), 65535) + self.assertEqual(_binary.i16le(b"\xff\xff\x00\x00"), 65535) + self.assertEqual(_binary.i32le(b"\xff\xff\x00\x00"), 65535) - self.assertEqual(_binary.o16le(65535), b'\xff\xff') - self.assertEqual(_binary.o32le(65535), b'\xff\xff\x00\x00') + self.assertEqual(_binary.o16le(65535), b"\xff\xff") + self.assertEqual(_binary.o32le(65535), b"\xff\xff\x00\x00") def test_big_endian(self): - self.assertEqual(_binary.i16be(b'\x00\x00\xff\xff'), 0) - self.assertEqual(_binary.i32be(b'\x00\x00\xff\xff'), 65535) + self.assertEqual(_binary.i16be(b"\x00\x00\xff\xff"), 0) + self.assertEqual(_binary.i32be(b"\x00\x00\xff\xff"), 65535) - self.assertEqual(_binary.o16be(65535), b'\xff\xff') - self.assertEqual(_binary.o32be(65535), b'\x00\x00\xff\xff') + self.assertEqual(_binary.o16be(65535), b"\xff\xff") + self.assertEqual(_binary.o32be(65535), b"\x00\x00\xff\xff") diff --git a/Tests/test_bmp_reference.py b/Tests/test_bmp_reference.py index 0e32c93dd15..fcdf20e5a43 100644 --- a/Tests/test_bmp_reference.py +++ b/Tests/test_bmp_reference.py @@ -4,19 +4,22 @@ from PIL import Image import os -base = os.path.join('Tests', 'images', 'bmp') +base = os.path.join("Tests", "images", "bmp") class TestBmpReference(PillowTestCase): - - def get_files(self, d, ext='.bmp'): - return [os.path.join(base, d, f) for f - in os.listdir(os.path.join(base, d)) if ext in f] + def get_files(self, d, ext=".bmp"): + return [ + os.path.join(base, d, f) + for f in os.listdir(os.path.join(base, d)) + if ext in f + ] def test_bad(self): """ These shouldn't crash/dos, but they shouldn't return anything either """ - for f in self.get_files('b'): + for f in self.get_files("b"): + def open(f): try: im = Image.open(f) @@ -41,13 +44,12 @@ def test_questionable(self): "pal8os2sp.bmp", "rgb32bf-xbgr.bmp", ] - for f in self.get_files('q'): + for f in self.get_files("q"): try: im = Image.open(f) im.load() if os.path.basename(f) not in supported: - print("Please add %s to the partially supported" - " bmp specs." % f) + print("Please add %s to the partially supported bmp specs." % f) except Exception: # as msg: if os.path.basename(f) in supported: raise @@ -57,49 +59,52 @@ def test_good(self): html directory that we can compare against. """ # Target files, if they're not just replacing the extension - file_map = {'pal1wb.bmp': 'pal1.png', - 'pal4rle.bmp': 'pal4.png', - 'pal8-0.bmp': 'pal8.png', - 'pal8rle.bmp': 'pal8.png', - 'pal8topdown.bmp': 'pal8.png', - 'pal8nonsquare.bmp': 'pal8nonsquare-v.png', - 'pal8os2.bmp': 'pal8.png', - 'pal8os2sp.bmp': 'pal8.png', - 'pal8os2v2.bmp': 'pal8.png', - 'pal8os2v2-16.bmp': 'pal8.png', - 'pal8v4.bmp': 'pal8.png', - 'pal8v5.bmp': 'pal8.png', - 'rgb16-565pal.bmp': 'rgb16-565.png', - 'rgb24pal.bmp': 'rgb24.png', - 'rgb32.bmp': 'rgb24.png', - 'rgb32bf.bmp': 'rgb24.png' - } + file_map = { + "pal1wb.bmp": "pal1.png", + "pal4rle.bmp": "pal4.png", + "pal8-0.bmp": "pal8.png", + "pal8rle.bmp": "pal8.png", + "pal8topdown.bmp": "pal8.png", + "pal8nonsquare.bmp": "pal8nonsquare-v.png", + "pal8os2.bmp": "pal8.png", + "pal8os2sp.bmp": "pal8.png", + "pal8os2v2.bmp": "pal8.png", + "pal8os2v2-16.bmp": "pal8.png", + "pal8v4.bmp": "pal8.png", + "pal8v5.bmp": "pal8.png", + "rgb16-565pal.bmp": "rgb16-565.png", + "rgb24pal.bmp": "rgb24.png", + "rgb32.bmp": "rgb24.png", + "rgb32bf.bmp": "rgb24.png", + } def get_compare(f): name = os.path.split(f)[1] if name in file_map: - return os.path.join(base, 'html', file_map[name]) + return os.path.join(base, "html", file_map[name]) name = os.path.splitext(name)[0] - return os.path.join(base, 'html', "%s.png" % name) + return os.path.join(base, "html", "%s.png" % name) - for f in self.get_files('g'): + for f in self.get_files("g"): try: im = Image.open(f) im.load() compare = Image.open(get_compare(f)) compare.load() - if im.mode == 'P': + if im.mode == "P": # assert image similar doesn't really work # with paletized image, since the palette might # be differently ordered for an equivalent image. - im = im.convert('RGBA') - compare = im.convert('RGBA') + im = im.convert("RGBA") + compare = im.convert("RGBA") self.assert_image_similar(im, compare, 5) except Exception as msg: # there are three here that are unsupported: - unsupported = (os.path.join(base, 'g', 'rgb32bf.bmp'), - os.path.join(base, 'g', 'pal8rle.bmp'), - os.path.join(base, 'g', 'pal4rle.bmp')) + unsupported = ( + os.path.join(base, "g", "rgb32bf.bmp"), + os.path.join(base, "g", "pal8rle.bmp"), + os.path.join(base, "g", "pal4rle.bmp"), + ) if f not in unsupported: self.fail("Unsupported Image %s: %s" % (f, msg)) diff --git a/Tests/test_box_blur.py b/Tests/test_box_blur.py index a2012dcd90d..ae898558056 100644 --- a/Tests/test_box_blur.py +++ b/Tests/test_box_blur.py @@ -4,6 +4,7 @@ sample = Image.new("L", (7, 5)) +# fmt: off sample.putdata(sum([ [210, 50, 20, 10, 220, 230, 80], [190, 210, 20, 180, 170, 40, 110], @@ -11,10 +12,10 @@ [220, 40, 230, 80, 130, 250, 40], [250, 0, 80, 30, 60, 20, 110], ], [])) +# fmt: on class TestBoxBlurApi(PillowTestCase): - def test_imageops_box_blur(self): i = sample.filter(ImageFilter.BoxBlur(1)) self.assertEqual(i.mode, sample.mode) @@ -23,7 +24,6 @@ def test_imageops_box_blur(self): class TestBoxBlur(PillowTestCase): - def box_blur(self, image, radius=1, n=1): return image._new(image.im.box_blur(radius, n)) @@ -32,8 +32,7 @@ def assertImage(self, im, data, delta=0): for data_row in data: im_row = [next(it) for _ in range(im.size[0])] if any( - abs(data_v - im_v) > delta - for data_v, im_v in zip(data_row, im_row) + abs(data_v - im_v) > delta for data_v, im_v in zip(data_row, im_row) ): self.assertEqual(im_row, data_row) self.assertRaises(StopIteration, next, it) @@ -41,7 +40,7 @@ def assertImage(self, im, data, delta=0): def assertBlur(self, im, radius, data, passes=1, delta=0): # check grayscale image self.assertImage(self.box_blur(im, radius, passes), data, delta) - rgba = Image.merge('RGBA', (im, im, im, im)) + rgba = Image.merge("RGBA", (im, im, im, im)) for band in self.box_blur(rgba, radius, passes).split(): self.assertImage(band, data, delta) @@ -61,110 +60,135 @@ def test_color_modes(self): def test_radius_0(self): self.assertBlur( - sample, 0, + sample, + 0, [ + # fmt: off [210, 50, 20, 10, 220, 230, 80], [190, 210, 20, 180, 170, 40, 110], [120, 210, 250, 60, 220, 0, 220], [220, 40, 230, 80, 130, 250, 40], [250, 0, 80, 30, 60, 20, 110], - ] + # fmt: on + ], ) def test_radius_0_02(self): self.assertBlur( - sample, 0.02, + sample, + 0.02, [ + # fmt: off [206, 55, 20, 17, 215, 223, 83], [189, 203, 31, 171, 169, 46, 110], [125, 206, 241, 69, 210, 13, 210], [215, 49, 221, 82, 131, 235, 48], [244, 7, 80, 32, 60, 27, 107], + # fmt: on ], delta=2, ) def test_radius_0_05(self): self.assertBlur( - sample, 0.05, + sample, + 0.05, [ + # fmt: off [202, 62, 22, 27, 209, 215, 88], [188, 194, 44, 161, 168, 56, 111], [131, 201, 229, 81, 198, 31, 198], [209, 62, 209, 86, 133, 216, 59], [237, 17, 80, 36, 60, 35, 103], + # fmt: on ], delta=2, ) def test_radius_0_1(self): self.assertBlur( - sample, 0.1, + sample, + 0.1, [ + # fmt: off [196, 72, 24, 40, 200, 203, 93], [187, 183, 62, 148, 166, 68, 111], [139, 193, 213, 96, 182, 54, 182], [201, 78, 193, 91, 133, 191, 73], [227, 31, 80, 42, 61, 47, 99], + # fmt: on ], delta=1, ) def test_radius_0_5(self): self.assertBlur( - sample, 0.5, + sample, + 0.5, [ + # fmt: off [176, 101, 46, 83, 163, 165, 111], [176, 149, 108, 122, 144, 120, 117], [164, 171, 159, 141, 134, 119, 129], [170, 136, 133, 114, 116, 124, 109], [184, 95, 72, 70, 69, 81, 89], + # fmt: on ], delta=1, ) def test_radius_1(self): self.assertBlur( - sample, 1, + sample, + 1, [ + # fmt: off [170, 109, 63, 97, 146, 153, 116], [168, 142, 112, 128, 126, 143, 121], [169, 166, 142, 149, 126, 131, 114], [159, 156, 109, 127, 94, 117, 112], [164, 128, 63, 87, 76, 89, 90], + # fmt: on ], delta=1, ) def test_radius_1_5(self): self.assertBlur( - sample, 1.5, + sample, + 1.5, [ + # fmt: off [155, 120, 105, 112, 124, 137, 130], [160, 136, 124, 125, 127, 134, 130], [166, 147, 130, 125, 120, 121, 119], [168, 145, 119, 109, 103, 105, 110], [168, 134, 96, 85, 85, 89, 97], + # fmt: on ], delta=1, ) def test_radius_bigger_then_half(self): self.assertBlur( - sample, 3, + sample, + 3, [ + # fmt: off [144, 145, 142, 128, 114, 115, 117], [148, 145, 137, 122, 109, 111, 112], [152, 145, 131, 117, 103, 107, 108], [156, 144, 126, 111, 97, 102, 103], [160, 144, 121, 106, 92, 98, 99], + # fmt: on ], delta=1, ) def test_radius_bigger_then_width(self): self.assertBlur( - sample, 10, + sample, + 10, [ [158, 153, 147, 141, 135, 129, 123], [159, 153, 147, 141, 136, 130, 124], @@ -177,7 +201,8 @@ def test_radius_bigger_then_width(self): def test_extreme_large_radius(self): self.assertBlur( - sample, 600, + sample, + 600, [ [162, 162, 162, 162, 162, 162, 162], [162, 162, 162, 162, 162, 162, 162], @@ -190,13 +215,16 @@ def test_extreme_large_radius(self): def test_two_passes(self): self.assertBlur( - sample, 1, + sample, + 1, [ + # fmt: off [153, 123, 102, 109, 132, 135, 129], [159, 138, 123, 121, 133, 131, 126], [162, 147, 136, 124, 127, 121, 121], [159, 140, 125, 108, 111, 106, 108], [154, 126, 105, 87, 94, 93, 97], + # fmt: on ], passes=2, delta=1, @@ -204,13 +232,16 @@ def test_two_passes(self): def test_three_passes(self): self.assertBlur( - sample, 1, + sample, + 1, [ + # fmt: off [146, 131, 116, 118, 126, 131, 130], [151, 138, 125, 123, 126, 128, 127], [154, 143, 129, 123, 120, 120, 119], [152, 139, 122, 113, 108, 108, 108], [148, 132, 112, 102, 97, 99, 100], + # fmt: on ], passes=3, delta=1, diff --git a/Tests/test_color_lut.py b/Tests/test_color_lut.py index 97035c79341..6208b6a80ea 100644 --- a/Tests/test_color_lut.py +++ b/Tests/test_color_lut.py @@ -20,185 +20,197 @@ def generate_identity_table(self, channels, size): table = [ [ - r / float(size1D-1) if size1D != 1 else 0, - g / float(size2D-1) if size2D != 1 else 0, - b / float(size3D-1) if size3D != 1 else 0, - r / float(size1D-1) if size1D != 1 else 0, - g / float(size2D-1) if size2D != 1 else 0, + r / float(size1D - 1) if size1D != 1 else 0, + g / float(size2D - 1) if size2D != 1 else 0, + b / float(size3D - 1) if size3D != 1 else 0, + r / float(size1D - 1) if size1D != 1 else 0, + g / float(size2D - 1) if size2D != 1 else 0, ][:channels] for b in range(size3D) for g in range(size2D) for r in range(size1D) ] return ( - channels, size1D, size2D, size3D, - [item for sublist in table for item in sublist]) + channels, + size1D, + size2D, + size3D, + [item for sublist in table for item in sublist], + ) def test_wrong_args(self): - im = Image.new('RGB', (10, 10), 0) + im = Image.new("RGB", (10, 10), 0) with self.assertRaisesRegex(ValueError, "filter"): - im.im.color_lut_3d('RGB', - Image.CUBIC, - *self.generate_identity_table(3, 3)) + im.im.color_lut_3d("RGB", Image.CUBIC, *self.generate_identity_table(3, 3)) with self.assertRaisesRegex(ValueError, "image mode"): - im.im.color_lut_3d('wrong', - Image.LINEAR, - *self.generate_identity_table(3, 3)) + im.im.color_lut_3d( + "wrong", Image.LINEAR, *self.generate_identity_table(3, 3) + ) with self.assertRaisesRegex(ValueError, "table_channels"): - im.im.color_lut_3d('RGB', - Image.LINEAR, - *self.generate_identity_table(5, 3)) + im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(5, 3)) with self.assertRaisesRegex(ValueError, "table_channels"): - im.im.color_lut_3d('RGB', - Image.LINEAR, - *self.generate_identity_table(1, 3)) + im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(1, 3)) with self.assertRaisesRegex(ValueError, "table_channels"): - im.im.color_lut_3d('RGB', - Image.LINEAR, - *self.generate_identity_table(2, 3)) + im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(2, 3)) with self.assertRaisesRegex(ValueError, "Table size"): - im.im.color_lut_3d('RGB', - Image.LINEAR, - *self.generate_identity_table(3, (1, 3, 3))) + im.im.color_lut_3d( + "RGB", Image.LINEAR, *self.generate_identity_table(3, (1, 3, 3)) + ) with self.assertRaisesRegex(ValueError, "Table size"): - im.im.color_lut_3d('RGB', - Image.LINEAR, - *self.generate_identity_table(3, (66, 3, 3))) + im.im.color_lut_3d( + "RGB", Image.LINEAR, *self.generate_identity_table(3, (66, 3, 3)) + ) with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"): - im.im.color_lut_3d('RGB', - Image.LINEAR, - 3, 2, 2, 2, [0, 0, 0] * 7) + im.im.color_lut_3d("RGB", Image.LINEAR, 3, 2, 2, 2, [0, 0, 0] * 7) with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"): - im.im.color_lut_3d('RGB', - Image.LINEAR, - 3, 2, 2, 2, [0, 0, 0] * 9) + im.im.color_lut_3d("RGB", Image.LINEAR, 3, 2, 2, 2, [0, 0, 0] * 9) with self.assertRaises(TypeError): - im.im.color_lut_3d('RGB', - Image.LINEAR, - 3, 2, 2, 2, [0, 0, "0"] * 8) + im.im.color_lut_3d("RGB", Image.LINEAR, 3, 2, 2, 2, [0, 0, "0"] * 8) with self.assertRaises(TypeError): - im.im.color_lut_3d('RGB', - Image.LINEAR, - 3, 2, 2, 2, 16) + im.im.color_lut_3d("RGB", Image.LINEAR, 3, 2, 2, 2, 16) def test_correct_args(self): - im = Image.new('RGB', (10, 10), 0) + im = Image.new("RGB", (10, 10), 0) - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, 3)) + im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(3, 3)) - im.im.color_lut_3d('CMYK', Image.LINEAR, - *self.generate_identity_table(4, 3)) + im.im.color_lut_3d("CMYK", Image.LINEAR, *self.generate_identity_table(4, 3)) - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (2, 3, 3))) + im.im.color_lut_3d( + "RGB", Image.LINEAR, *self.generate_identity_table(3, (2, 3, 3)) + ) - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (65, 3, 3))) + im.im.color_lut_3d( + "RGB", Image.LINEAR, *self.generate_identity_table(3, (65, 3, 3)) + ) - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (3, 65, 3))) + im.im.color_lut_3d( + "RGB", Image.LINEAR, *self.generate_identity_table(3, (3, 65, 3)) + ) - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (3, 3, 65))) + im.im.color_lut_3d( + "RGB", Image.LINEAR, *self.generate_identity_table(3, (3, 3, 65)) + ) def test_wrong_mode(self): with self.assertRaisesRegex(ValueError, "wrong mode"): - im = Image.new('L', (10, 10), 0) - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, 3)) + im = Image.new("L", (10, 10), 0) + im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(3, 3)) with self.assertRaisesRegex(ValueError, "wrong mode"): - im = Image.new('RGB', (10, 10), 0) - im.im.color_lut_3d('L', Image.LINEAR, - *self.generate_identity_table(3, 3)) + im = Image.new("RGB", (10, 10), 0) + im.im.color_lut_3d("L", Image.LINEAR, *self.generate_identity_table(3, 3)) with self.assertRaisesRegex(ValueError, "wrong mode"): - im = Image.new('L', (10, 10), 0) - im.im.color_lut_3d('L', Image.LINEAR, - *self.generate_identity_table(3, 3)) + im = Image.new("L", (10, 10), 0) + im.im.color_lut_3d("L", Image.LINEAR, *self.generate_identity_table(3, 3)) with self.assertRaisesRegex(ValueError, "wrong mode"): - im = Image.new('RGB', (10, 10), 0) - im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(3, 3)) + im = Image.new("RGB", (10, 10), 0) + im.im.color_lut_3d( + "RGBA", Image.LINEAR, *self.generate_identity_table(3, 3) + ) with self.assertRaisesRegex(ValueError, "wrong mode"): - im = Image.new('RGB', (10, 10), 0) - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(4, 3)) + im = Image.new("RGB", (10, 10), 0) + im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(4, 3)) def test_correct_mode(self): - im = Image.new('RGBA', (10, 10), 0) - im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(3, 3)) + im = Image.new("RGBA", (10, 10), 0) + im.im.color_lut_3d("RGBA", Image.LINEAR, *self.generate_identity_table(3, 3)) - im = Image.new('RGBA', (10, 10), 0) - im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(4, 3)) + im = Image.new("RGBA", (10, 10), 0) + im.im.color_lut_3d("RGBA", Image.LINEAR, *self.generate_identity_table(4, 3)) - im = Image.new('RGB', (10, 10), 0) - im.im.color_lut_3d('HSV', Image.LINEAR, - *self.generate_identity_table(3, 3)) + im = Image.new("RGB", (10, 10), 0) + im.im.color_lut_3d("HSV", Image.LINEAR, *self.generate_identity_table(3, 3)) - im = Image.new('RGB', (10, 10), 0) - im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(4, 3)) + im = Image.new("RGB", (10, 10), 0) + im.im.color_lut_3d("RGBA", Image.LINEAR, *self.generate_identity_table(4, 3)) def test_identities(self): - g = Image.linear_gradient('L') - im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), - g.transpose(Image.ROTATE_180)]) + g = Image.linear_gradient("L") + im = Image.merge( + "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + ) # Fast test with small cubes for size in [2, 3, 5, 7, 11, 16, 17]: - self.assert_image_equal(im, im._new( - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, size)))) + self.assert_image_equal( + im, + im._new( + im.im.color_lut_3d( + "RGB", Image.LINEAR, *self.generate_identity_table(3, size) + ) + ), + ) # Not so fast - self.assert_image_equal(im, im._new( - im.im.color_lut_3d('RGB', Image.LINEAR, - *self.generate_identity_table(3, (2, 2, 65))))) + self.assert_image_equal( + im, + im._new( + im.im.color_lut_3d( + "RGB", Image.LINEAR, *self.generate_identity_table(3, (2, 2, 65)) + ) + ), + ) def test_identities_4_channels(self): - g = Image.linear_gradient('L') - im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), - g.transpose(Image.ROTATE_180)]) + g = Image.linear_gradient("L") + im = Image.merge( + "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + ) # Red channel copied to alpha self.assert_image_equal( - Image.merge('RGBA', (im.split()*2)[:4]), - im._new(im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(4, 17)))) + Image.merge("RGBA", (im.split() * 2)[:4]), + im._new( + im.im.color_lut_3d( + "RGBA", Image.LINEAR, *self.generate_identity_table(4, 17) + ) + ), + ) def test_copy_alpha_channel(self): - g = Image.linear_gradient('L') - im = Image.merge('RGBA', [g, g.transpose(Image.ROTATE_90), - g.transpose(Image.ROTATE_180), - g.transpose(Image.ROTATE_270)]) + g = Image.linear_gradient("L") + im = Image.merge( + "RGBA", + [ + g, + g.transpose(Image.ROTATE_90), + g.transpose(Image.ROTATE_180), + g.transpose(Image.ROTATE_270), + ], + ) - self.assert_image_equal(im, im._new( - im.im.color_lut_3d('RGBA', Image.LINEAR, - *self.generate_identity_table(3, 17)))) + self.assert_image_equal( + im, + im._new( + im.im.color_lut_3d( + "RGBA", Image.LINEAR, *self.generate_identity_table(3, 17) + ) + ), + ) def test_channels_order(self): - g = Image.linear_gradient('L') - im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), - g.transpose(Image.ROTATE_180)]) + g = Image.linear_gradient("L") + im = Image.merge( + "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + ) # Reverse channels by splitting and using table + # fmt: off self.assert_image_equal( Image.merge('RGB', im.split()[::-1]), im._new(im.im.color_lut_3d('RGB', Image.LINEAR, @@ -209,12 +221,15 @@ def test_channels_order(self): 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, ]))) + # fmt: on def test_overflow(self): - g = Image.linear_gradient('L') - im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), - g.transpose(Image.ROTATE_180)]) + g = Image.linear_gradient("L") + im = Image.merge( + "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + ) + # fmt: off transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR, 3, 2, 2, 2, [ @@ -224,6 +239,7 @@ def test_overflow(self): -1, -1, 2, 2, -1, 2, -1, 2, 2, 2, 2, 2, ])).load() + # fmt: on self.assertEqual(transformed[0, 0], (0, 0, 255)) self.assertEqual(transformed[50, 50], (0, 0, 255)) self.assertEqual(transformed[255, 0], (0, 255, 255)) @@ -233,6 +249,7 @@ def test_overflow(self): self.assertEqual(transformed[255, 255], (255, 255, 0)) self.assertEqual(transformed[205, 205], (255, 255, 0)) + # fmt: off transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR, 3, 2, 2, 2, [ @@ -242,6 +259,7 @@ def test_overflow(self): -3, -3, 5, 5, -3, 5, -3, 5, 5, 5, 5, 5, ])).load() + # fmt: on self.assertEqual(transformed[0, 0], (0, 0, 255)) self.assertEqual(transformed[50, 50], (0, 0, 255)) self.assertEqual(transformed[255, 0], (0, 255, 255)) @@ -286,14 +304,15 @@ def test_convert_table(self): self.assertEqual(tuple(lut.size), (2, 2, 2)) self.assertEqual(lut.name, "Color 3D LUT") + # fmt: off lut = ImageFilter.Color3DLUT((2, 2, 2), [ (0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (12, 13, 14), (15, 16, 17), (18, 19, 20), (21, 22, 23)]) + # fmt: on self.assertEqual(tuple(lut.size), (2, 2, 2)) self.assertEqual(lut.table, list(range(24))) - lut = ImageFilter.Color3DLUT((2, 2, 2), [(0, 1, 2, 3)] * 8, - channels=4) + lut = ImageFilter.Color3DLUT((2, 2, 2), [(0, 1, 2, 3)] * 8, channels=4) self.assertEqual(tuple(lut.size), (2, 2, 2)) self.assertEqual(lut.table, list(range(4)) * 8) @@ -318,7 +337,7 @@ def test_numpy_sources(self): self.assertEqual(lut.table.shape, (table.size,)) # Check application - Image.new('RGB', (10, 10), 0).filter(lut) + Image.new("RGB", (10, 10), 0).filter(lut) # Check copy table[0] = 33 @@ -332,40 +351,34 @@ def test_numpy_sources(self): @unittest.skipIf(numpy is None, "Numpy is not installed") def test_numpy_formats(self): - g = Image.linear_gradient('L') - im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), - g.transpose(Image.ROTATE_180)]) + g = Image.linear_gradient("L") + im = Image.merge( + "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + ) - lut = ImageFilter.Color3DLUT.generate((7, 9, 11), - lambda r, g, b: (r, g, b)) + lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b)) lut.table = numpy.array(lut.table, dtype=numpy.float32)[:-1] with self.assertRaisesRegex(ValueError, "should have table_channels"): im.filter(lut) - lut = ImageFilter.Color3DLUT.generate((7, 9, 11), - lambda r, g, b: (r, g, b)) - lut.table = (numpy.array(lut.table, dtype=numpy.float32) - .reshape((7 * 9 * 11), 3)) + lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b)) + lut.table = numpy.array(lut.table, dtype=numpy.float32).reshape((7 * 9 * 11), 3) with self.assertRaisesRegex(ValueError, "should have table_channels"): im.filter(lut) - lut = ImageFilter.Color3DLUT.generate((7, 9, 11), - lambda r, g, b: (r, g, b)) + lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b)) lut.table = numpy.array(lut.table, dtype=numpy.float16) self.assert_image_equal(im, im.filter(lut)) - lut = ImageFilter.Color3DLUT.generate((7, 9, 11), - lambda r, g, b: (r, g, b)) + lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b)) lut.table = numpy.array(lut.table, dtype=numpy.float32) self.assert_image_equal(im, im.filter(lut)) - lut = ImageFilter.Color3DLUT.generate((7, 9, 11), - lambda r, g, b: (r, g, b)) + lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b)) lut.table = numpy.array(lut.table, dtype=numpy.float64) self.assert_image_equal(im, im.filter(lut)) - lut = ImageFilter.Color3DLUT.generate((7, 9, 11), - lambda r, g, b: (r, g, b)) + lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b)) lut.table = numpy.array(lut.table, dtype=numpy.int32) im.filter(lut) lut.table = numpy.array(lut.table, dtype=numpy.int8) @@ -373,54 +386,65 @@ def test_numpy_formats(self): def test_repr(self): lut = ImageFilter.Color3DLUT(2, [0, 1, 2] * 8) - self.assertEqual(repr(lut), - "") + self.assertEqual(repr(lut), "") lut = ImageFilter.Color3DLUT( - (3, 4, 5), array('f', [0, 0, 0, 0] * (3 * 4 * 5)), - channels=4, target_mode='YCbCr', _copy_table=False) + (3, 4, 5), + array("f", [0, 0, 0, 0] * (3 * 4 * 5)), + channels=4, + target_mode="YCbCr", + _copy_table=False, + ) self.assertEqual( - repr(lut), - "") + repr(lut), "" + ) class TestGenerateColorLut3D(PillowTestCase): def test_wrong_channels_count(self): with self.assertRaisesRegex(ValueError, "3 or 4 output channels"): ImageFilter.Color3DLUT.generate( - 5, channels=2, callback=lambda r, g, b: (r, g, b)) + 5, channels=2, callback=lambda r, g, b: (r, g, b) + ) with self.assertRaisesRegex(ValueError, "should have either channels"): ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b, r)) with self.assertRaisesRegex(ValueError, "should have either channels"): ImageFilter.Color3DLUT.generate( - 5, channels=4, callback=lambda r, g, b: (r, g, b)) + 5, channels=4, callback=lambda r, g, b: (r, g, b) + ) def test_3_channels(self): lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b)) self.assertEqual(tuple(lut.size), (5, 5, 5)) self.assertEqual(lut.name, "Color 3D LUT") + # fmt: off self.assertEqual(lut.table[:24], [ 0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 0.5, 0.0, 0.0, 0.75, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.25, 0.25, 0.0, 0.5, 0.25, 0.0]) + # fmt: on def test_4_channels(self): lut = ImageFilter.Color3DLUT.generate( - 5, channels=4, callback=lambda r, g, b: (b, r, g, (r+g+b) / 2)) + 5, channels=4, callback=lambda r, g, b: (b, r, g, (r + g + b) / 2) + ) self.assertEqual(tuple(lut.size), (5, 5, 5)) self.assertEqual(lut.name, "Color 3D LUT") + # fmt: off self.assertEqual(lut.table[:24], [ 0.0, 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.125, 0.0, 0.5, 0.0, 0.25, 0.0, 0.75, 0.0, 0.375, 0.0, 1.0, 0.0, 0.5, 0.0, 0.0, 0.25, 0.125 ]) + # fmt: on def test_apply(self): lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b)) - g = Image.linear_gradient('L') - im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), - g.transpose(Image.ROTATE_180)]) + g = Image.linear_gradient("L") + im = Image.merge( + "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)] + ) self.assertEqual(im, im.filter(lut)) @@ -442,80 +466,96 @@ def test_wrong_args(self): def test_target_mode(self): source = ImageFilter.Color3DLUT.generate( - 2, lambda r, g, b: (r, g, b), target_mode='HSV') + 2, lambda r, g, b: (r, g, b), target_mode="HSV" + ) lut = source.transform(lambda r, g, b: (r, g, b)) - self.assertEqual(lut.mode, 'HSV') + self.assertEqual(lut.mode, "HSV") - lut = source.transform(lambda r, g, b: (r, g, b), target_mode='RGB') - self.assertEqual(lut.mode, 'RGB') + lut = source.transform(lambda r, g, b: (r, g, b), target_mode="RGB") + self.assertEqual(lut.mode, "RGB") def test_3_to_3_channels(self): - source = ImageFilter.Color3DLUT.generate( - (3, 4, 5), lambda r, g, b: (r, g, b)) - lut = source.transform(lambda r, g, b: (r*r, g*g, b*b)) + source = ImageFilter.Color3DLUT.generate((3, 4, 5), lambda r, g, b: (r, g, b)) + lut = source.transform(lambda r, g, b: (r * r, g * g, b * b)) self.assertEqual(tuple(lut.size), tuple(source.size)) self.assertEqual(len(lut.table), len(source.table)) self.assertNotEqual(lut.table, source.table) - self.assertEqual(lut.table[0:10], [ - 0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]) + self.assertEqual( + lut.table[0:10], [0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0] + ) def test_3_to_4_channels(self): - source = ImageFilter.Color3DLUT.generate( - (6, 5, 4), lambda r, g, b: (r, g, b)) - lut = source.transform(lambda r, g, b: (r*r, g*g, b*b, 1), channels=4) + source = ImageFilter.Color3DLUT.generate((6, 5, 4), lambda r, g, b: (r, g, b)) + lut = source.transform(lambda r, g, b: (r * r, g * g, b * b, 1), channels=4) self.assertEqual(tuple(lut.size), tuple(source.size)) self.assertNotEqual(len(lut.table), len(source.table)) self.assertNotEqual(lut.table, source.table) + # fmt: off self.assertEqual(lut.table[0:16], [ 0.0, 0.0, 0.0, 1, 0.2**2, 0.0, 0.0, 1, 0.4**2, 0.0, 0.0, 1, 0.6**2, 0.0, 0.0, 1]) + # fmt: on def test_4_to_3_channels(self): source = ImageFilter.Color3DLUT.generate( - (3, 6, 5), lambda r, g, b: (r, g, b, 1), channels=4) - lut = source.transform(lambda r, g, b, a: (a - r*r, a - g*g, a - b*b), - channels=3) + (3, 6, 5), lambda r, g, b: (r, g, b, 1), channels=4 + ) + lut = source.transform( + lambda r, g, b, a: (a - r * r, a - g * g, a - b * b), channels=3 + ) self.assertEqual(tuple(lut.size), tuple(source.size)) self.assertNotEqual(len(lut.table), len(source.table)) self.assertNotEqual(lut.table, source.table) + # fmt: off self.assertEqual(lut.table[0:18], [ 1.0, 1.0, 1.0, 0.75, 1.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.96, 1.0, 0.75, 0.96, 1.0, 0.0, 0.96, 1.0]) + # fmt: on def test_4_to_4_channels(self): source = ImageFilter.Color3DLUT.generate( - (6, 5, 4), lambda r, g, b: (r, g, b, 1), channels=4) - lut = source.transform(lambda r, g, b, a: (r*r, g*g, b*b, a - 0.5)) + (6, 5, 4), lambda r, g, b: (r, g, b, 1), channels=4 + ) + lut = source.transform(lambda r, g, b, a: (r * r, g * g, b * b, a - 0.5)) self.assertEqual(tuple(lut.size), tuple(source.size)) self.assertEqual(len(lut.table), len(source.table)) self.assertNotEqual(lut.table, source.table) + # fmt: off self.assertEqual(lut.table[0:16], [ 0.0, 0.0, 0.0, 0.5, 0.2**2, 0.0, 0.0, 0.5, 0.4**2, 0.0, 0.0, 0.5, 0.6**2, 0.0, 0.0, 0.5]) + # fmt: on def test_with_normals_3_channels(self): source = ImageFilter.Color3DLUT.generate( - (6, 5, 4), lambda r, g, b: (r*r, g*g, b*b)) + (6, 5, 4), lambda r, g, b: (r * r, g * g, b * b) + ) lut = source.transform( - lambda nr, ng, nb, r, g, b: (nr - r, ng - g, nb - b), - with_normals=True) + lambda nr, ng, nb, r, g, b: (nr - r, ng - g, nb - b), with_normals=True + ) self.assertEqual(tuple(lut.size), tuple(source.size)) self.assertEqual(len(lut.table), len(source.table)) self.assertNotEqual(lut.table, source.table) + # fmt: off self.assertEqual(lut.table[0:18], [ 0.0, 0.0, 0.0, 0.16, 0.0, 0.0, 0.24, 0.0, 0.0, 0.24, 0.0, 0.0, 0.8 - (0.8**2), 0, 0, 0, 0, 0]) + # fmt: on def test_with_normals_4_channels(self): source = ImageFilter.Color3DLUT.generate( - (3, 6, 5), lambda r, g, b: (r*r, g*g, b*b, 1), channels=4) + (3, 6, 5), lambda r, g, b: (r * r, g * g, b * b, 1), channels=4 + ) lut = source.transform( - lambda nr, ng, nb, r, g, b, a: (nr - r, ng - g, nb - b, a-0.5), - with_normals=True) + lambda nr, ng, nb, r, g, b, a: (nr - r, ng - g, nb - b, a - 0.5), + with_normals=True, + ) self.assertEqual(tuple(lut.size), tuple(source.size)) self.assertEqual(len(lut.table), len(source.table)) self.assertNotEqual(lut.table, source.table) + # fmt: off self.assertEqual(lut.table[0:16], [ 0.0, 0.0, 0.0, 0.5, 0.25, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.5, 0.0, 0.16, 0.0, 0.5]) + # fmt: on diff --git a/Tests/test_core_resources.py b/Tests/test_core_resources.py index fe9f5ccc442..d5e358f3196 100644 --- a/Tests/test_core_resources.py +++ b/Tests/test_core_resources.py @@ -6,39 +6,39 @@ from PIL import Image -is_pypy = hasattr(sys, 'pypy_version_info') +is_pypy = hasattr(sys, "pypy_version_info") class TestCoreStats(PillowTestCase): def test_get_stats(self): # Create at least one image - Image.new('RGB', (10, 10)) + Image.new("RGB", (10, 10)) stats = Image.core.get_stats() - self.assertIn('new_count', stats) - self.assertIn('reused_blocks', stats) - self.assertIn('freed_blocks', stats) - self.assertIn('allocated_blocks', stats) - self.assertIn('reallocated_blocks', stats) - self.assertIn('blocks_cached', stats) + self.assertIn("new_count", stats) + self.assertIn("reused_blocks", stats) + self.assertIn("freed_blocks", stats) + self.assertIn("allocated_blocks", stats) + self.assertIn("reallocated_blocks", stats) + self.assertIn("blocks_cached", stats) def test_reset_stats(self): Image.core.reset_stats() stats = Image.core.get_stats() - self.assertEqual(stats['new_count'], 0) - self.assertEqual(stats['reused_blocks'], 0) - self.assertEqual(stats['freed_blocks'], 0) - self.assertEqual(stats['allocated_blocks'], 0) - self.assertEqual(stats['reallocated_blocks'], 0) - self.assertEqual(stats['blocks_cached'], 0) + self.assertEqual(stats["new_count"], 0) + self.assertEqual(stats["reused_blocks"], 0) + self.assertEqual(stats["freed_blocks"], 0) + self.assertEqual(stats["allocated_blocks"], 0) + self.assertEqual(stats["reallocated_blocks"], 0) + self.assertEqual(stats["blocks_cached"], 0) class TestCoreMemory(PillowTestCase): def tearDown(self): # Restore default values Image.core.set_alignment(1) - Image.core.set_block_size(1024*1024) + Image.core.set_block_size(1024 * 1024) Image.core.set_blocks_max(0) Image.core.clear_cache() @@ -54,7 +54,7 @@ def test_set_alignment(self): self.assertEqual(alignment, i) # Try to construct new image - Image.new('RGB', (10, 10)) + Image.new("RGB", (10, 10)) self.assertRaises(ValueError, Image.core.set_alignment, 0) self.assertRaises(ValueError, Image.core.set_alignment, -1) @@ -66,13 +66,13 @@ def test_get_block_size(self): self.assertGreaterEqual(block_size, 4096) def test_set_block_size(self): - for i in [4096, 2*4096, 3*4096]: + for i in [4096, 2 * 4096, 3 * 4096]: Image.core.set_block_size(i) block_size = Image.core.get_block_size() self.assertEqual(block_size, i) # Try to construct new image - Image.new('RGB', (10, 10)) + Image.new("RGB", (10, 10)) self.assertRaises(ValueError, Image.core.set_block_size, 0) self.assertRaises(ValueError, Image.core.set_block_size, -1) @@ -82,13 +82,13 @@ def test_set_block_size_stats(self): Image.core.reset_stats() Image.core.set_blocks_max(0) Image.core.set_block_size(4096) - Image.new('RGB', (256, 256)) + Image.new("RGB", (256, 256)) stats = Image.core.get_stats() - self.assertGreaterEqual(stats['new_count'], 1) - self.assertGreaterEqual(stats['allocated_blocks'], 64) + self.assertGreaterEqual(stats["new_count"], 1) + self.assertGreaterEqual(stats["allocated_blocks"], 64) if not is_pypy: - self.assertGreaterEqual(stats['freed_blocks'], 64) + self.assertGreaterEqual(stats["freed_blocks"], 64) def test_get_blocks_max(self): blocks_max = Image.core.get_blocks_max() @@ -102,7 +102,7 @@ def test_set_blocks_max(self): self.assertEqual(blocks_max, i) # Try to construct new image - Image.new('RGB', (10, 10)) + Image.new("RGB", (10, 10)) self.assertRaises(ValueError, Image.core.set_blocks_max, -1) @@ -111,15 +111,15 @@ def test_set_blocks_max_stats(self): Image.core.reset_stats() Image.core.set_blocks_max(128) Image.core.set_block_size(4096) - Image.new('RGB', (256, 256)) - Image.new('RGB', (256, 256)) + Image.new("RGB", (256, 256)) + Image.new("RGB", (256, 256)) stats = Image.core.get_stats() - self.assertGreaterEqual(stats['new_count'], 2) - self.assertGreaterEqual(stats['allocated_blocks'], 64) - self.assertGreaterEqual(stats['reused_blocks'], 64) - self.assertEqual(stats['freed_blocks'], 0) - self.assertEqual(stats['blocks_cached'], 64) + self.assertGreaterEqual(stats["new_count"], 2) + self.assertGreaterEqual(stats["allocated_blocks"], 64) + self.assertGreaterEqual(stats["reused_blocks"], 64) + self.assertEqual(stats["freed_blocks"], 0) + self.assertEqual(stats["blocks_cached"], 64) @unittest.skipIf(is_pypy, "images are not collected") def test_clear_cache_stats(self): @@ -127,55 +127,55 @@ def test_clear_cache_stats(self): Image.core.clear_cache() Image.core.set_blocks_max(128) Image.core.set_block_size(4096) - Image.new('RGB', (256, 256)) - Image.new('RGB', (256, 256)) + Image.new("RGB", (256, 256)) + Image.new("RGB", (256, 256)) # Keep 16 blocks in cache Image.core.clear_cache(16) stats = Image.core.get_stats() - self.assertGreaterEqual(stats['new_count'], 2) - self.assertGreaterEqual(stats['allocated_blocks'], 64) - self.assertGreaterEqual(stats['reused_blocks'], 64) - self.assertGreaterEqual(stats['freed_blocks'], 48) - self.assertEqual(stats['blocks_cached'], 16) + self.assertGreaterEqual(stats["new_count"], 2) + self.assertGreaterEqual(stats["allocated_blocks"], 64) + self.assertGreaterEqual(stats["reused_blocks"], 64) + self.assertGreaterEqual(stats["freed_blocks"], 48) + self.assertEqual(stats["blocks_cached"], 16) def test_large_images(self): Image.core.reset_stats() Image.core.set_blocks_max(0) Image.core.set_block_size(4096) - Image.new('RGB', (2048, 16)) + Image.new("RGB", (2048, 16)) Image.core.clear_cache() stats = Image.core.get_stats() - self.assertGreaterEqual(stats['new_count'], 1) - self.assertGreaterEqual(stats['allocated_blocks'], 16) - self.assertGreaterEqual(stats['reused_blocks'], 0) - self.assertEqual(stats['blocks_cached'], 0) + self.assertGreaterEqual(stats["new_count"], 1) + self.assertGreaterEqual(stats["allocated_blocks"], 16) + self.assertGreaterEqual(stats["reused_blocks"], 0) + self.assertEqual(stats["blocks_cached"], 0) if not is_pypy: - self.assertGreaterEqual(stats['freed_blocks'], 16) + self.assertGreaterEqual(stats["freed_blocks"], 16) class TestEnvVars(PillowTestCase): def tearDown(self): # Restore default values Image.core.set_alignment(1) - Image.core.set_block_size(1024*1024) + Image.core.set_block_size(1024 * 1024) Image.core.set_blocks_max(0) Image.core.clear_cache() def test_units(self): - Image._apply_env_variables({'PILLOW_BLOCKS_MAX': '2K'}) - self.assertEqual(Image.core.get_blocks_max(), 2*1024) - Image._apply_env_variables({'PILLOW_BLOCK_SIZE': '2m'}) - self.assertEqual(Image.core.get_block_size(), 2*1024*1024) + Image._apply_env_variables({"PILLOW_BLOCKS_MAX": "2K"}) + self.assertEqual(Image.core.get_blocks_max(), 2 * 1024) + Image._apply_env_variables({"PILLOW_BLOCK_SIZE": "2m"}) + self.assertEqual(Image.core.get_block_size(), 2 * 1024 * 1024) def test_warnings(self): self.assert_warning( - UserWarning, Image._apply_env_variables, - {'PILLOW_ALIGNMENT': '15'}) + UserWarning, Image._apply_env_variables, {"PILLOW_ALIGNMENT": "15"} + ) self.assert_warning( - UserWarning, Image._apply_env_variables, - {'PILLOW_BLOCK_SIZE': '1024'}) + UserWarning, Image._apply_env_variables, {"PILLOW_BLOCK_SIZE": "1024"} + ) self.assert_warning( - UserWarning, Image._apply_env_variables, - {'PILLOW_BLOCKS_MAX': 'wat'}) + UserWarning, Image._apply_env_variables, {"PILLOW_BLOCKS_MAX": "wat"} + ) diff --git a/Tests/test_decompression_bomb.py b/Tests/test_decompression_bomb.py index bc0bab52585..6b224632d6b 100644 --- a/Tests/test_decompression_bomb.py +++ b/Tests/test_decompression_bomb.py @@ -8,7 +8,6 @@ class TestDecompressionBomb(PillowTestCase): - def tearDown(self): Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT @@ -33,20 +32,17 @@ def test_warning(self): 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) + self.assert_warning(Image.DecompressionBombWarning, Image.open, TEST_FILE) def test_exception(self): # Set limit to trigger exception on the test file Image.MAX_IMAGE_PIXELS = 64 * 128 - 1 self.assertEqual(Image.MAX_IMAGE_PIXELS, 64 * 128 - 1) - self.assertRaises(Image.DecompressionBombError, - lambda: Image.open(TEST_FILE)) + self.assertRaises(Image.DecompressionBombError, lambda: Image.open(TEST_FILE)) class TestDecompressionCrop(PillowTestCase): - def setUp(self): self.src = hopper() Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width * 4 - 1 @@ -58,21 +54,17 @@ def testEnlargeCrop(self): # Crops can extend the extents, therefore we should have the # same decompression bomb warnings on them. box = (0, 0, self.src.width * 2, self.src.height * 2) - self.assert_warning(Image.DecompressionBombWarning, - self.src.crop, box) + self.assert_warning(Image.DecompressionBombWarning, self.src.crop, box) def test_crop_decompression_checks(self): im = Image.new("RGB", (100, 100)) - good_values = ((-9999, -9999, -9990, -9990), - (-999, -999, -990, -990)) + good_values = ((-9999, -9999, -9990, -9990), (-999, -999, -990, -990)) - warning_values = ((-160, -160, 99, 99), - (160, 160, -99, -99)) + warning_values = ((-160, -160, 99, 99), (160, 160, -99, -99)) - error_values = ((-99909, -99990, 99999, 99999), - (99909, 99990, -99999, -99999)) + error_values = ((-99909, -99990, 99999, 99999), (99909, 99990, -99999, -99999)) for value in good_values: self.assertEqual(im.crop(value).size, (9, 9)) diff --git a/Tests/test_features.py b/Tests/test_features.py index 15b5982cee6..d7e505ac76c 100644 --- a/Tests/test_features.py +++ b/Tests/test_features.py @@ -1,44 +1,43 @@ +from __future__ import unicode_literals + +import io + from .helper import unittest, PillowTestCase from PIL import features try: from PIL import _webp + HAVE_WEBP = True except ImportError: HAVE_WEBP = False class TestFeatures(PillowTestCase): - def test_check(self): # Check the correctness of the convenience function for module in features.modules: - self.assertEqual(features.check_module(module), - features.check(module)) + self.assertEqual(features.check_module(module), features.check(module)) for codec in features.codecs: - self.assertEqual(features.check_codec(codec), - features.check(codec)) + self.assertEqual(features.check_codec(codec), features.check(codec)) for feature in features.features: - self.assertEqual(features.check_feature(feature), - features.check(feature)) + self.assertEqual(features.check_feature(feature), features.check(feature)) @unittest.skipUnless(HAVE_WEBP, "WebP not available") def test_webp_transparency(self): - self.assertEqual(features.check('transp_webp'), - not _webp.WebPDecoderBuggyAlpha()) - self.assertEqual(features.check('transp_webp'), - _webp.HAVE_TRANSPARENCY) + self.assertEqual( + features.check("transp_webp"), not _webp.WebPDecoderBuggyAlpha() + ) + self.assertEqual(features.check("transp_webp"), _webp.HAVE_TRANSPARENCY) @unittest.skipUnless(HAVE_WEBP, "WebP not available") def test_webp_mux(self): - self.assertEqual(features.check('webp_mux'), - _webp.HAVE_WEBPMUX) + self.assertEqual(features.check("webp_mux"), _webp.HAVE_WEBPMUX) @unittest.skipUnless(HAVE_WEBP, "WebP not available") def test_webp_anim(self): - self.assertEqual(features.check('webp_anim'), - _webp.HAVE_WEBPANIM) + self.assertEqual(features.check("webp_anim"), _webp.HAVE_WEBPANIM) def test_check_modules(self): for feature in features.modules: @@ -63,3 +62,27 @@ def test_unsupported_module(self): module = "unsupported_module" # Act / Assert self.assertRaises(ValueError, features.check_module, module) + + def test_pilinfo(self): + buf = io.StringIO() + features.pilinfo(buf) + out = buf.getvalue() + lines = out.splitlines() + self.assertEqual(lines[0], "-" * 68) + self.assertTrue(lines[1].startswith("Pillow ")) + self.assertEqual(lines[2], "-" * 68) + self.assertTrue(lines[3].startswith("Python modules loaded from ")) + self.assertTrue(lines[4].startswith("Binary modules loaded from ")) + self.assertEqual(lines[5], "-" * 68) + self.assertTrue(lines[6].startswith("Python ")) + jpeg = ( + "\n" + + "-" * 68 + + "\n" + + "JPEG image/jpeg\n" + + "Extensions: .jfif, .jpe, .jpeg, .jpg\n" + + "Features: open, save\n" + + "-" * 68 + + "\n" + ) + self.assertIn(jpeg, out) diff --git a/Tests/test_file_bmp.py b/Tests/test_file_bmp.py index b97a8422797..71ea7275159 100644 --- a/Tests/test_file_bmp.py +++ b/Tests/test_file_bmp.py @@ -5,11 +5,10 @@ class TestFileBmp(PillowTestCase): - def roundtrip(self, im): outfile = self.tempfile("temp.bmp") - im.save(outfile, 'BMP') + im.save(outfile, "BMP") reloaded = Image.open(outfile) reloaded.load() @@ -28,8 +27,7 @@ def test_sanity(self): def test_invalid_file(self): with open("Tests/images/flower.jpg", "rb") as fp: - self.assertRaises(SyntaxError, - BmpImagePlugin.BmpImageFile, fp) + self.assertRaises(SyntaxError, BmpImagePlugin.BmpImageFile, fp) def test_save_to_bytes(self): output = io.BytesIO() @@ -62,27 +60,27 @@ def test_save_bmp_with_dpi(self): im = Image.open("Tests/images/hopper.bmp") # Act - im.save(outfile, 'JPEG', dpi=im.info['dpi']) + im.save(outfile, "JPEG", dpi=im.info["dpi"]) # Assert reloaded = Image.open(outfile) reloaded.load() - self.assertEqual(im.info['dpi'], reloaded.info['dpi']) + self.assertEqual(im.info["dpi"], reloaded.info["dpi"]) self.assertEqual(im.size, reloaded.size) self.assertEqual(reloaded.format, "JPEG") def test_load_dpi_rounding(self): # Round up - im = Image.open('Tests/images/hopper.bmp') + im = Image.open("Tests/images/hopper.bmp") self.assertEqual(im.info["dpi"], (96, 96)) # Round down - im = Image.open('Tests/images/hopper_roundDown.bmp') + im = Image.open("Tests/images/hopper_roundDown.bmp") self.assertEqual(im.info["dpi"], (72, 72)) def test_save_dpi_rounding(self): outfile = self.tempfile("temp.bmp") - im = Image.open('Tests/images/hopper.bmp') + im = Image.open("Tests/images/hopper.bmp") im.save(outfile, dpi=(72.2, 72.2)) reloaded = Image.open(outfile) @@ -94,17 +92,17 @@ def test_save_dpi_rounding(self): def test_load_dib(self): # test for #1293, Imagegrab returning Unsupported Bitfields Format - im = Image.open('Tests/images/clipboard.dib') + im = Image.open("Tests/images/clipboard.dib") self.assertEqual(im.format, "DIB") self.assertEqual(im.get_format_mimetype(), "image/bmp") - target = Image.open('Tests/images/clipboard_target.png') + target = Image.open("Tests/images/clipboard_target.png") self.assert_image_equal(im, target) def test_save_dib(self): outfile = self.tempfile("temp.dib") - im = Image.open('Tests/images/clipboard.dib') + im = Image.open("Tests/images/clipboard.dib") im.save(outfile) reloaded = Image.open(outfile) diff --git a/Tests/test_file_bufrstub.py b/Tests/test_file_bufrstub.py index 3070291365b..eb59b24694d 100644 --- a/Tests/test_file_bufrstub.py +++ b/Tests/test_file_bufrstub.py @@ -6,7 +6,6 @@ class TestFileBufrStub(PillowTestCase): - def test_open(self): # Act im = Image.open(TEST_FILE) @@ -23,8 +22,9 @@ def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" # Act / Assert - self.assertRaises(SyntaxError, - BufrStubImagePlugin.BufrStubImageFile, invalid_file) + self.assertRaises( + SyntaxError, BufrStubImagePlugin.BufrStubImageFile, invalid_file + ) def test_load(self): # Arrange diff --git a/Tests/test_file_container.py b/Tests/test_file_container.py index 04f055464f5..eb13d5f1816 100644 --- a/Tests/test_file_container.py +++ b/Tests/test_file_container.py @@ -7,7 +7,6 @@ class TestFileContainer(PillowTestCase): - def test_sanity(self): dir(Image) dir(ContainerIO) @@ -106,14 +105,16 @@ def test_readline(self): def test_readlines(self): # Arrange - expected = ["This is line 1\n", - "This is line 2\n", - "This is line 3\n", - "This is line 4\n", - "This is line 5\n", - "This is line 6\n", - "This is line 7\n", - "This is line 8\n"] + expected = [ + "This is line 1\n", + "This is line 2\n", + "This is line 3\n", + "This is line 4\n", + "This is line 5\n", + "This is line 6\n", + "This is line 7\n", + "This is line 8\n", + ] with open(TEST_FILE) as fh: container = ContainerIO.ContainerIO(fh, 0, 120) diff --git a/Tests/test_file_cur.py b/Tests/test_file_cur.py index 734c330e628..a2ffb2e601d 100644 --- a/Tests/test_file_cur.py +++ b/Tests/test_file_cur.py @@ -6,7 +6,6 @@ class TestFileCur(PillowTestCase): - def test_sanity(self): im = Image.open(TEST_FILE) @@ -20,8 +19,7 @@ def test_sanity(self): def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - CurImagePlugin.CurImageFile, invalid_file) + self.assertRaises(SyntaxError, CurImagePlugin.CurImageFile, invalid_file) no_cursors_file = "Tests/images/no_cursors.cur" diff --git a/Tests/test_file_dcx.py b/Tests/test_file_dcx.py index 0da36464817..8e6ab111fa9 100644 --- a/Tests/test_file_dcx.py +++ b/Tests/test_file_dcx.py @@ -7,7 +7,6 @@ class TestFileDcx(PillowTestCase): - def test_sanity(self): # Arrange @@ -24,12 +23,12 @@ def test_unclosed_file(self): def open(): im = Image.open(TEST_FILE) im.load() + self.assert_warning(None, open) def test_invalid_file(self): with open("Tests/images/flower.jpg", "rb") as fp: - self.assertRaises(SyntaxError, - DcxImagePlugin.DcxImageFile, fp) + self.assertRaises(SyntaxError, DcxImagePlugin.DcxImageFile, fp) def test_tell(self): # Arrange @@ -55,7 +54,7 @@ def test_eoferror(self): self.assertLess(im.tell(), n_frames) # Test that seeking to the last frame does not raise an error - im.seek(n_frames-1) + im.seek(n_frames - 1) def test_seek_too_far(self): # Arrange diff --git a/Tests/test_file_dds.py b/Tests/test_file_dds.py index 605c5f69ba8..80b452da218 100644 --- a/Tests/test_file_dds.py +++ b/Tests/test_file_dds.py @@ -15,7 +15,7 @@ class TestFileDds(PillowTestCase): def test_sanity_dxt1(self): """Check DXT1 images can be opened""" - target = Image.open(TEST_FILE_DXT1.replace('.dds', '.png')) + target = Image.open(TEST_FILE_DXT1.replace(".dds", ".png")) im = Image.open(TEST_FILE_DXT1) im.load() @@ -24,12 +24,12 @@ def test_sanity_dxt1(self): self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (256, 256)) - self.assert_image_equal(target.convert('RGBA'), im) + self.assert_image_equal(target.convert("RGBA"), im) def test_sanity_dxt5(self): """Check DXT5 images can be opened""" - target = Image.open(TEST_FILE_DXT5.replace('.dds', '.png')) + target = Image.open(TEST_FILE_DXT5.replace(".dds", ".png")) im = Image.open(TEST_FILE_DXT5) im.load() @@ -43,7 +43,7 @@ def test_sanity_dxt5(self): def test_sanity_dxt3(self): """Check DXT3 images can be opened""" - target = Image.open(TEST_FILE_DXT3.replace('.dds', '.png')) + target = Image.open(TEST_FILE_DXT3.replace(".dds", ".png")) im = Image.open(TEST_FILE_DXT3) im.load() @@ -57,7 +57,7 @@ def test_sanity_dxt3(self): def test_dx10_bc7(self): """Check DX10 images can be opened""" - target = Image.open(TEST_FILE_DX10_BC7.replace('.dds', '.png')) + target = Image.open(TEST_FILE_DX10_BC7.replace(".dds", ".png")) im = Image.open(TEST_FILE_DX10_BC7) im.load() @@ -69,13 +69,16 @@ def test_dx10_bc7(self): self.assert_image_equal(target, im) def test_unimplemented_dxgi_format(self): - self.assertRaises(NotImplementedError, Image.open, - "Tests/images/unimplemented_dxgi_format.dds") + self.assertRaises( + NotImplementedError, + Image.open, + "Tests/images/unimplemented_dxgi_format.dds", + ) def test_uncompressed_rgb(self): """Check uncompressed RGB images can be opened""" - target = Image.open(TEST_FILE_UNCOMPRESSED_RGB.replace('.dds', '.png')) + target = Image.open(TEST_FILE_UNCOMPRESSED_RGB.replace(".dds", ".png")) im = Image.open(TEST_FILE_UNCOMPRESSED_RGB) im.load() @@ -110,7 +113,7 @@ def test__validate_false(self): def test_short_header(self): """ Check a short header""" - with open(TEST_FILE_DXT5, 'rb') as f: + with open(TEST_FILE_DXT5, "rb") as f: img_file = f.read() def short_header(): @@ -121,7 +124,7 @@ def short_header(): def test_short_file(self): """ Check that the appropriate error is thrown for a short file""" - with open(TEST_FILE_DXT5, 'rb') as f: + with open(TEST_FILE_DXT5, "rb") as f: img_file = f.read() def short_file(): @@ -131,5 +134,8 @@ def short_file(): self.assertRaises(IOError, short_file) def test_unimplemented_pixel_format(self): - self.assertRaises(NotImplementedError, Image.open, - "Tests/images/unimplemented_pixel_format.dds") + self.assertRaises( + NotImplementedError, + Image.open, + "Tests/images/unimplemented_pixel_format.dds", + ) diff --git a/Tests/test_file_eps.py b/Tests/test_file_eps.py index 1f6025d697c..00736c38cc1 100644 --- a/Tests/test_file_eps.py +++ b/Tests/test_file_eps.py @@ -21,7 +21,6 @@ class TestFileEps(PillowTestCase): - @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") def test_sanity(self): # Regular scale @@ -53,8 +52,7 @@ def test_sanity(self): def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - EpsImagePlugin.EpsImageFile, invalid_file) + self.assertRaises(SyntaxError, EpsImagePlugin.EpsImageFile, invalid_file) @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") def test_cmyk(self): @@ -67,8 +65,8 @@ def test_cmyk(self): cmyk_image.load() self.assertEqual(cmyk_image.mode, "RGB") - if 'jpeg_decoder' in dir(Image.core): - target = Image.open('Tests/images/pil_sample_rgb.jpg') + if "jpeg_decoder" in dir(Image.core): + target = Image.open("Tests/images/pil_sample_rgb.jpg") self.assert_image_similar(cmyk_image, target, 10) @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") @@ -86,19 +84,19 @@ def test_showpage(self): def test_file_object(self): # issue 479 image1 = Image.open(file1) - with open(self.tempfile('temp_file.eps'), 'wb') as fh: - image1.save(fh, 'EPS') + with open(self.tempfile("temp_file.eps"), "wb") as fh: + image1.save(fh, "EPS") @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") def test_iobase_object(self): # issue 479 image1 = Image.open(file1) - with io.open(self.tempfile('temp_iobase.eps'), 'wb') as fh: - image1.save(fh, 'EPS') + with io.open(self.tempfile("temp_iobase.eps"), "wb") as fh: + image1.save(fh, "EPS") @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") def test_bytesio_object(self): - with open(file1, 'rb') as f: + with open(file1, "rb") as f: img_bytes = io.BytesIO(f.read()) img = Image.open(img_bytes) @@ -110,7 +108,7 @@ def test_bytesio_object(self): def test_image_mode_not_supported(self): im = hopper("RGBA") - tmpfile = self.tempfile('temp.eps') + tmpfile = self.tempfile("temp.eps") self.assertRaises(ValueError, im.save, tmpfile) @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") @@ -195,33 +193,33 @@ def test_read_binary_preview(self): Image.open(file3) def _test_readline(self, t, ending): - ending = "Failure with line ending: %s" % ("".join( - "%s" % ord(s) - for s in ending)) - self.assertEqual(t.readline().strip('\r\n'), 'something', ending) - self.assertEqual(t.readline().strip('\r\n'), 'else', ending) - self.assertEqual(t.readline().strip('\r\n'), 'baz', ending) - self.assertEqual(t.readline().strip('\r\n'), 'bif', ending) + ending = "Failure with line ending: %s" % ( + "".join("%s" % ord(s) for s in ending) + ) + self.assertEqual(t.readline().strip("\r\n"), "something", ending) + self.assertEqual(t.readline().strip("\r\n"), "else", ending) + self.assertEqual(t.readline().strip("\r\n"), "baz", ending) + self.assertEqual(t.readline().strip("\r\n"), "bif", ending) def _test_readline_io_psfile(self, test_string, ending): - f = io.BytesIO(test_string.encode('latin-1')) + f = io.BytesIO(test_string.encode("latin-1")) t = EpsImagePlugin.PSFile(f) self._test_readline(t, ending) def _test_readline_file_psfile(self, test_string, ending): - f = self.tempfile('temp.txt') - with open(f, 'wb') as w: - w.write(test_string.encode('latin-1')) + f = self.tempfile("temp.txt") + with open(f, "wb") as w: + w.write(test_string.encode("latin-1")) - with open(f, 'rb') as r: + with open(f, "rb") as r: t = EpsImagePlugin.PSFile(r) self._test_readline(t, ending) def test_readline(self): # check all the freaking line endings possible from the spec # test_string = u'something\r\nelse\n\rbaz\rbif\n' - line_endings = ['\r\n', '\n', '\n\r', '\r'] - strings = ['something', 'else', 'baz', 'bif'] + line_endings = ["\r\n", "\n", "\n\r", "\r"] + strings = ["something", "else", "baz", "bif"] for ending in line_endings: s = ending.join(strings) @@ -231,10 +229,12 @@ def test_readline(self): def test_open_eps(self): # https://github.com/python-pillow/Pillow/issues/1104 # Arrange - FILES = ["Tests/images/illu10_no_preview.eps", - "Tests/images/illu10_preview.eps", - "Tests/images/illuCS6_no_preview.eps", - "Tests/images/illuCS6_preview.eps"] + FILES = [ + "Tests/images/illu10_no_preview.eps", + "Tests/images/illu10_preview.eps", + "Tests/images/illuCS6_no_preview.eps", + "Tests/images/illuCS6_preview.eps", + ] # Act / Assert for filename in FILES: diff --git a/Tests/test_file_fitsstub.py b/Tests/test_file_fitsstub.py index 6cb2de1103b..86ddecb9d3f 100644 --- a/Tests/test_file_fitsstub.py +++ b/Tests/test_file_fitsstub.py @@ -6,7 +6,6 @@ class TestFileFitsStub(PillowTestCase): - def test_open(self): # Act im = Image.open(TEST_FILE) @@ -23,8 +22,9 @@ def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" # Act / Assert - self.assertRaises(SyntaxError, - FitsStubImagePlugin.FITSStubImageFile, invalid_file) + self.assertRaises( + SyntaxError, FitsStubImagePlugin.FITSStubImageFile, invalid_file + ) def test_load(self): # Arrange @@ -42,5 +42,5 @@ def test_save(self): # Act / Assert: stub cannot save without an implemented handler self.assertRaises(IOError, im.save, dummy_filename) self.assertRaises( - IOError, - FitsStubImagePlugin._save, im, dummy_fp, dummy_filename) + IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename + ) diff --git a/Tests/test_file_fli.py b/Tests/test_file_fli.py index f67f0ada1f2..e5dadc3dc59 100644 --- a/Tests/test_file_fli.py +++ b/Tests/test_file_fli.py @@ -11,7 +11,6 @@ class TestFileFli(PillowTestCase): - def test_sanity(self): im = Image.open(static_test_file) im.load() @@ -31,6 +30,7 @@ def test_unclosed_file(self): def open(): im = Image.open(static_test_file) im.load() + self.assert_warning(None, open) def test_tell(self): @@ -46,8 +46,7 @@ def test_tell(self): def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - FliImagePlugin.FliImageFile, invalid_file) + self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file) def test_n_frames(self): im = Image.open(static_test_file) @@ -67,7 +66,7 @@ def test_eoferror(self): self.assertLess(im.tell(), n_frames) # Test that seeking to the last frame does not raise an error - im.seek(n_frames-1) + im.seek(n_frames - 1) def test_seek_tell(self): im = Image.open(animated_test_file) diff --git a/Tests/test_file_fpx.py b/Tests/test_file_fpx.py index 7283ba088b2..a426ca9049a 100644 --- a/Tests/test_file_fpx.py +++ b/Tests/test_file_fpx.py @@ -10,14 +10,11 @@ @unittest.skipUnless(olefile_installed, "olefile package not installed") class TestFileFpx(PillowTestCase): - def test_invalid_file(self): # Test an invalid OLE file invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - FpxImagePlugin.FpxImageFile, invalid_file) + self.assertRaises(SyntaxError, FpxImagePlugin.FpxImageFile, invalid_file) # Test a valid OLE file, but not an FPX file ole_file = "Tests/images/test-ole-file.doc" - self.assertRaises(SyntaxError, - FpxImagePlugin.FpxImageFile, ole_file) + self.assertRaises(SyntaxError, FpxImagePlugin.FpxImageFile, ole_file) diff --git a/Tests/test_file_ftex.py b/Tests/test_file_ftex.py index 07e29d7e099..16ecab2d177 100644 --- a/Tests/test_file_ftex.py +++ b/Tests/test_file_ftex.py @@ -3,14 +3,13 @@ class TestFileFtex(PillowTestCase): - def test_load_raw(self): - im = Image.open('Tests/images/ftex_uncompressed.ftu') - target = Image.open('Tests/images/ftex_uncompressed.png') + im = Image.open("Tests/images/ftex_uncompressed.ftu") + target = Image.open("Tests/images/ftex_uncompressed.png") self.assert_image_equal(im, target) def test_load_dxt1(self): - im = Image.open('Tests/images/ftex_dxt1.ftc') - target = Image.open('Tests/images/ftex_dxt1.png') - self.assert_image_similar(im, target.convert('RGBA'), 15) + im = Image.open("Tests/images/ftex_dxt1.ftc") + target = Image.open("Tests/images/ftex_dxt1.png") + self.assert_image_similar(im, target.convert("RGBA"), 15) diff --git a/Tests/test_file_gbr.py b/Tests/test_file_gbr.py index 1eb66264d6c..bdb52a6faf1 100644 --- a/Tests/test_file_gbr.py +++ b/Tests/test_file_gbr.py @@ -4,16 +4,14 @@ class TestFileGbr(PillowTestCase): - def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - GbrImagePlugin.GbrImageFile, invalid_file) + self.assertRaises(SyntaxError, GbrImagePlugin.GbrImageFile, invalid_file) def test_gbr_file(self): - im = Image.open('Tests/images/gbr.gbr') + im = Image.open("Tests/images/gbr.gbr") - target = Image.open('Tests/images/gbr.png') + target = Image.open("Tests/images/gbr.png") self.assert_image_equal(target, im) diff --git a/Tests/test_file_gd.py b/Tests/test_file_gd.py index 67c9fba7b88..82b089a5f14 100644 --- a/Tests/test_file_gd.py +++ b/Tests/test_file_gd.py @@ -6,15 +6,13 @@ class TestFileGd(PillowTestCase): - def test_sanity(self): im = GdImageFile.open(TEST_GD_FILE) self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "GD") def test_bad_mode(self): - self.assertRaises(ValueError, - GdImageFile.open, TEST_GD_FILE, 'bad mode') + self.assertRaises(ValueError, GdImageFile.open, TEST_GD_FILE, "bad mode") def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" diff --git a/Tests/test_file_gif.py b/Tests/test_file_gif.py index f9c31212469..69a1ee5be97 100644 --- a/Tests/test_file_gif.py +++ b/Tests/test_file_gif.py @@ -6,6 +6,7 @@ try: from PIL import _webp + HAVE_WEBP = True except ImportError: HAVE_WEBP = False @@ -20,7 +21,6 @@ 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? @@ -37,13 +37,13 @@ def test_unclosed_file(self): def open(): im = Image.open(TEST_GIF) im.load() + self.assert_warning(None, open) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - GifImagePlugin.GifImageFile, invalid_file) + self.assertRaises(SyntaxError, GifImagePlugin.GifImageFile, invalid_file) def test_optimize(self): def test_grayscale(optimize): @@ -70,19 +70,22 @@ def test_optimize_correctness(self): # Check for correctness after conversion back to RGB def check(colors, size, expected_palette_length): # make an image with empty colors in the start of the palette range - im = Image.frombytes('P', (colors, colors), - bytes(bytearray(range(256-colors, 256))*colors)) + im = Image.frombytes( + "P", + (colors, colors), + bytes(bytearray(range(256 - colors, 256)) * colors), + ) im = im.resize((size, size)) outfile = BytesIO() - im.save(outfile, 'GIF') + im.save(outfile, "GIF") outfile.seek(0) reloaded = Image.open(outfile) # check palette length - palette_length = max(i+1 for i, v in enumerate(reloaded.histogram()) if v) + palette_length = max(i + 1 for i, v in enumerate(reloaded.histogram()) if v) self.assertEqual(expected_palette_length, palette_length) - self.assert_image_equal(im.convert('RGB'), reloaded.convert('RGB')) + self.assert_image_equal(im.convert("RGB"), reloaded.convert("RGB")) # These do optimize the palette check(128, 511, 128) @@ -106,79 +109,76 @@ def test_optimize_full_l(self): self.assertEqual(im.mode, "L") def test_roundtrip(self): - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im = hopper() im.save(out) reread = Image.open(out) - self.assert_image_similar(reread.convert('RGB'), im, 50) + self.assert_image_similar(reread.convert("RGB"), im, 50) def test_roundtrip2(self): # see https://github.com/python-pillow/Pillow/issues/403 - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im = Image.open(TEST_GIF) im2 = im.copy() im2.save(out) reread = Image.open(out) - self.assert_image_similar(reread.convert('RGB'), hopper(), 50) + self.assert_image_similar(reread.convert("RGB"), hopper(), 50) def test_roundtrip_save_all(self): # Single frame image - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im = hopper() im.save(out, save_all=True) reread = Image.open(out) - self.assert_image_similar(reread.convert('RGB'), im, 50) + self.assert_image_similar(reread.convert("RGB"), im, 50) # Multiframe image im = Image.open("Tests/images/dispose_bgnd.gif") - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im.save(out, save_all=True) reread = Image.open(out) self.assertEqual(reread.n_frames, 5) def test_headers_saving_for_animated_gifs(self): - important_headers = ['background', 'version', 'duration', 'loop'] + important_headers = ["background", "version", "duration", "loop"] # Multiframe image im = Image.open("Tests/images/dispose_bgnd.gif") info = im.info.copy() - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im.save(out, save_all=True) reread = Image.open(out) for header in important_headers: - self.assertEqual( - info[header], - reread.info[header] - ) + self.assertEqual(info[header], reread.info[header]) def test_palette_handling(self): # see https://github.com/python-pillow/Pillow/issues/513 im = Image.open(TEST_GIF) - im = im.convert('RGB') + im = im.convert("RGB") im = im.resize((100, 100), Image.LANCZOS) - im2 = im.convert('P', palette=Image.ADAPTIVE, colors=256) + im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256) - f = self.tempfile('temp.gif') + f = self.tempfile("temp.gif") im2.save(f, optimize=True) reloaded = Image.open(f) - self.assert_image_similar(im, reloaded.convert('RGB'), 10) + self.assert_image_similar(im, reloaded.convert("RGB"), 10) def test_palette_434(self): # see https://github.com/python-pillow/Pillow/issues/434 def roundtrip(im, *args, **kwargs): - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im.copy().save(out, *args, **kwargs) reloaded = Image.open(out) @@ -192,7 +192,7 @@ def roundtrip(im, *args, **kwargs): im = im.convert("RGB") # check automatic P conversion - reloaded = roundtrip(im).convert('RGB') + reloaded = roundtrip(im).convert("RGB") self.assert_image_equal(im, reloaded) @unittest.skipUnless(netpbm_available(), "netpbm not available") @@ -240,10 +240,7 @@ def test_seek_rewind(self): self.assert_image_equal(im, expected) 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 im = Image.open(path) self.assertEqual(im.is_animated, n_frames != 1) @@ -262,7 +259,7 @@ def test_eoferror(self): self.assertLess(im.tell(), n_frames) # Test that seeking to the last frame does not raise an error - im.seek(n_frames-1) + im.seek(n_frames - 1) def test_dispose_none(self): img = Image.open("Tests/images/dispose_none.gif") @@ -292,18 +289,15 @@ def test_dispose_previous(self): pass def test_save_dispose(self): - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im_list = [ - Image.new('L', (100, 100), '#000'), - Image.new('L', (100, 100), '#111'), - Image.new('L', (100, 100), '#222'), + Image.new("L", (100, 100), "#000"), + Image.new("L", (100, 100), "#111"), + Image.new("L", (100, 100), "#222"), ] for method in range(0, 4): 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) for _ in range(2): @@ -315,14 +309,14 @@ def test_save_dispose(self): out, save_all=True, append_images=im_list[1:], - disposal=tuple(range(len(im_list))) - ) + disposal=tuple(range(len(im_list))), + ) img = Image.open(out) for i in range(2): img.seek(img.tell() + 1) - self.assertEqual(img.disposal_method, i+1) + self.assertEqual(img.disposal_method, i + 1) def test_iss634(self): img = Image.open("Tests/images/iss634.gif") @@ -330,42 +324,39 @@ def test_iss634(self): img.seek(img.tell() + 1) # all transparent pixels should be replaced with the color from the # first frame - self.assertEqual(img.histogram()[img.info['transparency']], 0) + self.assertEqual(img.histogram()[img.info["transparency"]], 0) def test_duration(self): duration = 1000 - out = self.tempfile('temp.gif') - im = Image.new('L', (100, 100), '#000') + out = self.tempfile("temp.gif") + im = Image.new("L", (100, 100), "#000") # Check that the argument has priority over the info settings - im.info['duration'] = 100 + im.info["duration"] = 100 im.save(out, duration=duration) reread = Image.open(out) - self.assertEqual(reread.info['duration'], duration) + self.assertEqual(reread.info["duration"], duration) def test_multiple_duration(self): duration_list = [1000, 2000, 3000] - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im_list = [ - Image.new('L', (100, 100), '#000'), - Image.new('L', (100, 100), '#111'), - Image.new('L', (100, 100), '#222') + Image.new("L", (100, 100), "#000"), + Image.new("L", (100, 100), "#111"), + Image.new("L", (100, 100), "#222"), ] # duration as list 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) for duration in duration_list: - self.assertEqual(reread.info['duration'], duration) + self.assertEqual(reread.info["duration"], duration) try: reread.seek(reread.tell() + 1) except EOFError: @@ -373,15 +364,12 @@ def test_multiple_duration(self): # duration as tuple 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) for duration in duration_list: - self.assertEqual(reread.info['duration'], duration) + self.assertEqual(reread.info["duration"], duration) try: reread.seek(reread.tell() + 1) except EOFError: @@ -390,20 +378,17 @@ def test_multiple_duration(self): def test_identical_frames(self): duration_list = [1000, 1500, 2000, 4000] - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im_list = [ - Image.new('L', (100, 100), '#000'), - Image.new('L', (100, 100), '#000'), - Image.new('L', (100, 100), '#000'), - Image.new('L', (100, 100), '#111') + Image.new("L", (100, 100), "#000"), + Image.new("L", (100, 100), "#000"), + Image.new("L", (100, 100), "#000"), + Image.new("L", (100, 100), "#111"), ] # duration as list 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) @@ -411,64 +396,63 @@ def test_identical_frames(self): self.assertEqual(reread.n_frames, 2) # Assert that the new duration is the total of the identical frames - self.assertEqual(reread.info['duration'], 4500) + self.assertEqual(reread.info["duration"], 4500) def test_number_of_loops(self): number_of_loops = 2 - out = self.tempfile('temp.gif') - im = Image.new('L', (100, 100), '#000') + out = self.tempfile("temp.gif") + im = Image.new("L", (100, 100), "#000") im.save(out, loop=number_of_loops) reread = Image.open(out) - self.assertEqual(reread.info['loop'], number_of_loops) + self.assertEqual(reread.info["loop"], number_of_loops) def test_background(self): - out = self.tempfile('temp.gif') - im = Image.new('L', (100, 100), '#000') - im.info['background'] = 1 + out = self.tempfile("temp.gif") + im = Image.new("L", (100, 100), "#000") + im.info["background"] = 1 im.save(out) reread = Image.open(out) - self.assertEqual(reread.info['background'], im.info['background']) + self.assertEqual(reread.info["background"], im.info["background"]) if HAVE_WEBP and _webp.HAVE_WEBPANIM: im = Image.open("Tests/images/hopper.webp") - self.assertIsInstance(im.info['background'], tuple) + self.assertIsInstance(im.info["background"], tuple) im.save(out) def test_comment(self): im = Image.open(TEST_GIF) - self.assertEqual(im.info['comment'], - b"File written by Adobe Photoshop\xa8 4.0") + self.assertEqual(im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0") - out = self.tempfile('temp.gif') - im = Image.new('L', (100, 100), '#000') - im.info['comment'] = b"Test comment text" + out = self.tempfile("temp.gif") + im = Image.new("L", (100, 100), "#000") + im.info["comment"] = b"Test comment text" im.save(out) reread = Image.open(out) - self.assertEqual(reread.info['comment'], im.info['comment']) + self.assertEqual(reread.info["comment"], im.info["comment"]) def test_comment_over_255(self): - out = self.tempfile('temp.gif') - im = Image.new('L', (100, 100), '#000') + out = self.tempfile("temp.gif") + im = Image.new("L", (100, 100), "#000") comment = b"Test comment text" while len(comment) < 256: comment += comment - im.info['comment'] = comment + im.info["comment"] = comment im.save(out) reread = Image.open(out) - self.assertEqual(reread.info['comment'], comment) + self.assertEqual(reread.info["comment"], comment) def test_zero_comment_subblocks(self): - im = Image.open('Tests/images/hopper_zero_comment_subblocks.gif') + im = Image.open("Tests/images/hopper_zero_comment_subblocks.gif") expected = Image.open(TEST_GIF) self.assert_image_equal(im, expected) def test_version(self): - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") def assertVersionAfterSave(im, version): im.save(out) @@ -476,11 +460,11 @@ def assertVersionAfterSave(im, version): self.assertEqual(reread.info["version"], version) # Test that GIF87a is used by default - im = Image.new('L', (100, 100), '#000') + im = Image.new("L", (100, 100), "#000") assertVersionAfterSave(im, b"GIF87a") # Test setting the version to 89a - im = Image.new('L', (100, 100), '#000') + im = Image.new("L", (100, 100), "#000") im.info["version"] = b"89a" assertVersionAfterSave(im, b"GIF89a") @@ -497,12 +481,11 @@ def assertVersionAfterSave(im, version): assertVersionAfterSave(im, b"GIF87a") def test_append_images(self): - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") # Test appending single frame images - im = Image.new('RGB', (100, 100), '#f00') - ims = [Image.new('RGB', (100, 100), color) for color - in ['#0f0', '#00f']] + im = Image.new("RGB", (100, 100), "#f00") + ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]] im.copy().save(out, save_all=True, append_images=ims) reread = Image.open(out) @@ -512,6 +495,7 @@ def test_append_images(self): def imGenerator(ims): for im in ims: yield im + im.save(out, save_all=True, append_images=imGenerator(ims)) reread = Image.open(out) @@ -533,44 +517,43 @@ def test_transparent_optimize(self): # the top palette entry to trigger the bug. data = bytes(bytearray(range(1, 254))) - palette = ImagePalette.ImagePalette("RGB", list(range(256))*3) + palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) - im = Image.new('L', (253, 1)) + im = Image.new("L", (253, 1)) im.frombytes(data) im.putpalette(palette) - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im.save(out, transparency=253) reloaded = Image.open(out) - self.assertEqual(reloaded.info['transparency'], 253) + self.assertEqual(reloaded.info["transparency"], 253) def test_rgb_transparency(self): - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") # Single frame - im = Image.new('RGB', (1, 1)) - im.info['transparency'] = (255, 0, 0) + im = Image.new("RGB", (1, 1)) + im.info["transparency"] = (255, 0, 0) self.assert_warning(UserWarning, im.save, out) reloaded = Image.open(out) - self.assertNotIn('transparency', reloaded.info) + self.assertNotIn("transparency", reloaded.info) # Multiple frames - im = Image.new('RGB', (1, 1)) - im.info['transparency'] = b"" - ims = [Image.new('RGB', (1, 1))] - self.assert_warning(UserWarning, - im.save, out, save_all=True, append_images=ims) + im = Image.new("RGB", (1, 1)) + im.info["transparency"] = b"" + ims = [Image.new("RGB", (1, 1))] + self.assert_warning(UserWarning, im.save, out, save_all=True, append_images=ims) reloaded = Image.open(out) - self.assertNotIn('transparency', reloaded.info) + self.assertNotIn("transparency", reloaded.info) def test_bbox(self): - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") - im = Image.new('RGB', (100, 100), '#fff') - ims = [Image.new("RGB", (100, 100), '#000')] + im = Image.new("RGB", (100, 100), "#fff") + ims = [Image.new("RGB", (100, 100), "#000")] im.save(out, save_all=True, append_images=ims) reread = Image.open(out) @@ -579,26 +562,26 @@ def test_bbox(self): def test_palette_save_L(self): # generate an L mode image with a separate palette - im = hopper('P') - im_l = Image.frombytes('L', im.size, im.tobytes()) + im = hopper("P") + im_l = Image.frombytes("L", im.size, im.tobytes()) palette = bytes(bytearray(im.getpalette())) - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im_l.save(out, palette=palette) reloaded = Image.open(out) - self.assert_image_equal(reloaded.convert('RGB'), im.convert('RGB')) + self.assert_image_equal(reloaded.convert("RGB"), im.convert("RGB")) def test_palette_save_P(self): # pass in a different palette, then construct what the image # would look like. # Forcing a non-straight grayscale palette. - im = hopper('P') - palette = bytes(bytearray([255-i//3 for i in range(768)])) + im = hopper("P") + palette = bytes(bytearray([255 - i // 3 for i in range(768)])) - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im.save(out, palette=palette) reloaded = Image.open(out) @@ -609,10 +592,10 @@ def test_palette_save_ImagePalette(self): # pass in a different palette, as an ImagePalette.ImagePalette # effectively the same as test_palette_save_P - im = hopper('P') - palette = ImagePalette.ImagePalette('RGB', list(range(256))[::-1]*3) + im = hopper("P") + palette = ImagePalette.ImagePalette("RGB", list(range(256))[::-1] * 3) - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im.save(out, palette=palette) reloaded = Image.open(out) @@ -622,22 +605,22 @@ def test_palette_save_ImagePalette(self): def test_save_I(self): # Test saving something that would trigger the auto-convert to 'L' - im = hopper('I') + im = hopper("I") - out = self.tempfile('temp.gif') + out = self.tempfile("temp.gif") im.save(out) reloaded = Image.open(out) - self.assert_image_equal(reloaded.convert('L'), im.convert('L')) + self.assert_image_equal(reloaded.convert("L"), im.convert("L")) def test_getdata(self): # test getheader/getdata against legacy values # Create a 'P' image with holes in the palette im = Image._wedge().resize((16, 16)) - im.putpalette(ImagePalette.ImagePalette('RGB')) - im.info = {'background': 0} + im.putpalette(ImagePalette.ImagePalette("RGB")) + im.info = {"background": 0} - passed_palette = bytes(bytearray([255-i//3 for i in range(768)])) + passed_palette = bytes(bytearray([255 - i // 3 for i in range(768)])) GifImagePlugin._FORCE_OPTIMIZE = True try: @@ -645,10 +628,11 @@ def test_getdata(self): d = GifImagePlugin.getdata(im) import pickle + # Enable to get target values on pre-refactor version # with open('Tests/images/gif_header_data.pkl', 'wb') as f: # pickle.dump((h, d), f, 1) - with open('Tests/images/gif_header_data.pkl', 'rb') as f: + with open("Tests/images/gif_header_data.pkl", "rb") as f: (h_target, d_target) = pickle.load(f) self.assertEqual(h, h_target) @@ -658,14 +642,14 @@ def test_getdata(self): def test_lzw_bits(self): # see https://github.com/python-pillow/Pillow/issues/2811 - im = Image.open('Tests/images/issue_2811.gif') + im = Image.open("Tests/images/issue_2811.gif") self.assertEqual(im.tile[0][3][0], 11) # LZW bits # codec error prepatch im.load() def test_extents(self): - im = Image.open('Tests/images/test_extents.gif') + im = Image.open("Tests/images/test_extents.gif") self.assertEqual(im.size, (100, 100)) im.seek(1) self.assertEqual(im.size, (150, 150)) diff --git a/Tests/test_file_gimpgradient.py b/Tests/test_file_gimpgradient.py index c894402396f..14e0f3b38ee 100644 --- a/Tests/test_file_gimpgradient.py +++ b/Tests/test_file_gimpgradient.py @@ -4,7 +4,6 @@ class TestImage(PillowTestCase): - def test_linear_pos_le_middle(self): # Arrange middle = 0.5 @@ -96,6 +95,7 @@ def test_sphere_decreasing(self): def test_load_via_imagepalette(self): # Arrange from PIL import ImagePalette + test_file = "Tests/images/gimp_gradient.ggr" # Act @@ -109,6 +109,7 @@ def test_load_via_imagepalette(self): def test_load_1_3_via_imagepalette(self): # Arrange from PIL import ImagePalette + # GIMP 1.3 gradient files contain a name field test_file = "Tests/images/gimp_gradient_with_name.ggr" diff --git a/Tests/test_file_gimppalette.py b/Tests/test_file_gimppalette.py index 1ebac44e782..973dd3060fc 100644 --- a/Tests/test_file_gimppalette.py +++ b/Tests/test_file_gimppalette.py @@ -4,23 +4,22 @@ class TestImage(PillowTestCase): - def test_sanity(self): - with open('Tests/images/test.gpl', 'rb') as fp: + with open("Tests/images/test.gpl", "rb") as fp: GimpPaletteFile(fp) - with open('Tests/images/hopper.jpg', 'rb') as fp: + with open("Tests/images/hopper.jpg", "rb") as fp: self.assertRaises(SyntaxError, GimpPaletteFile, fp) - with open('Tests/images/bad_palette_file.gpl', 'rb') as fp: + with open("Tests/images/bad_palette_file.gpl", "rb") as fp: self.assertRaises(SyntaxError, GimpPaletteFile, fp) - with open('Tests/images/bad_palette_entry.gpl', 'rb') as fp: + with open("Tests/images/bad_palette_entry.gpl", "rb") as fp: self.assertRaises(ValueError, GimpPaletteFile, fp) def test_get_palette(self): # Arrange - with open('Tests/images/custom_gimp_palette.gpl', 'rb') as fp: + with open("Tests/images/custom_gimp_palette.gpl", "rb") as fp: palette_file = GimpPaletteFile(fp) # Act diff --git a/Tests/test_file_gribstub.py b/Tests/test_file_gribstub.py index 79e826945b6..0f371778ae4 100644 --- a/Tests/test_file_gribstub.py +++ b/Tests/test_file_gribstub.py @@ -6,7 +6,6 @@ class TestFileGribStub(PillowTestCase): - def test_open(self): # Act im = Image.open(TEST_FILE) @@ -23,8 +22,9 @@ def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" # Act / Assert - self.assertRaises(SyntaxError, - GribStubImagePlugin.GribStubImageFile, invalid_file) + self.assertRaises( + SyntaxError, GribStubImagePlugin.GribStubImageFile, invalid_file + ) def test_load(self): # Arrange diff --git a/Tests/test_file_hdf5stub.py b/Tests/test_file_hdf5stub.py index 598ef0c5c27..efb8de2387d 100644 --- a/Tests/test_file_hdf5stub.py +++ b/Tests/test_file_hdf5stub.py @@ -6,7 +6,6 @@ class TestFileHdf5Stub(PillowTestCase): - def test_open(self): # Act im = Image.open(TEST_FILE) @@ -23,8 +22,9 @@ def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" # Act / Assert - self.assertRaises(SyntaxError, - Hdf5StubImagePlugin.HDF5StubImageFile, invalid_file) + self.assertRaises( + SyntaxError, Hdf5StubImagePlugin.HDF5StubImageFile, invalid_file + ) def test_load(self): # Arrange @@ -42,5 +42,5 @@ def test_save(self): # Act / Assert: stub cannot save without an implemented handler self.assertRaises(IOError, im.save, dummy_filename) self.assertRaises( - IOError, - Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename) + IOError, Hdf5StubImagePlugin._save, im, dummy_fp, dummy_filename + ) diff --git a/Tests/test_file_icns.py b/Tests/test_file_icns.py index ac60731f957..01ad34e0484 100644 --- a/Tests/test_file_icns.py +++ b/Tests/test_file_icns.py @@ -8,11 +8,10 @@ # sample icon file TEST_FILE = "Tests/images/pillow.icns" -enable_jpeg2k = hasattr(Image.core, 'jp2klib_version') +enable_jpeg2k = hasattr(Image.core, "jp2klib_version") class TestFileIcns(PillowTestCase): - def test_sanity(self): # Loading this icon by default should result in the largest size # (512x512@2x) being loaded @@ -25,7 +24,7 @@ def test_sanity(self): self.assertEqual(im.size, (1024, 1024)) self.assertEqual(im.format, "ICNS") - @unittest.skipIf(sys.platform != 'darwin', "requires macOS") + @unittest.skipIf(sys.platform != "darwin", "requires macOS") def test_save(self): im = Image.open(TEST_FILE) @@ -38,12 +37,12 @@ def test_save(self): self.assertEqual(reread.size, (1024, 1024)) self.assertEqual(reread.format, "ICNS") - @unittest.skipIf(sys.platform != 'darwin', "requires macOS") + @unittest.skipIf(sys.platform != "darwin", "requires macOS") def test_save_append_images(self): im = Image.open(TEST_FILE) temp_file = self.tempfile("temp.icns") - provided_im = Image.new('RGBA', (32, 32), (255, 0, 0, 128)) + provided_im = Image.new("RGBA", (32, 32), (255, 0, 0, 128)) im.save(temp_file, append_images=[provided_im]) reread = Image.open(temp_file) @@ -58,14 +57,13 @@ def test_sizes(self): # Check that we can load all of the sizes, and that the final pixel # dimensions are as expected im = Image.open(TEST_FILE) - for w, h, r in im.info['sizes']: + for w, h, r in im.info["sizes"]: wr = w * r hr = h * r - im2 = Image.open(TEST_FILE) - im2.size = (w, h, r) - im2.load() - self.assertEqual(im2.mode, 'RGBA') - self.assertEqual(im2.size, (wr, hr)) + im.size = (w, h, r) + im.load() + self.assertEqual(im.mode, "RGBA") + self.assertEqual(im.size, (wr, hr)) # Check that we cannot load an incorrect size with self.assertRaises(ValueError): @@ -74,14 +72,14 @@ def test_sizes(self): def test_older_icon(self): # This icon was made with Icon Composer rather than iconutil; it still # uses PNG rather than JP2, however (since it was made on 10.9). - im = Image.open('Tests/images/pillow2.icns') - for w, h, r in im.info['sizes']: + im = Image.open("Tests/images/pillow2.icns") + for w, h, r in im.info["sizes"]: wr = w * r hr = h * r - im2 = Image.open('Tests/images/pillow2.icns') + im2 = Image.open("Tests/images/pillow2.icns") im2.size = (w, h, r) im2.load() - self.assertEqual(im2.mode, 'RGBA') + self.assertEqual(im2.mode, "RGBA") self.assertEqual(im2.size, (wr, hr)) def test_jp2_icon(self): @@ -95,18 +93,18 @@ def test_jp2_icon(self): if not enable_jpeg2k: return - im = Image.open('Tests/images/pillow3.icns') - for w, h, r in im.info['sizes']: + im = Image.open("Tests/images/pillow3.icns") + for w, h, r in im.info["sizes"]: wr = w * r hr = h * r - im2 = Image.open('Tests/images/pillow3.icns') + im2 = Image.open("Tests/images/pillow3.icns") im2.size = (w, h, r) im2.load() - self.assertEqual(im2.mode, 'RGBA') + self.assertEqual(im2.mode, "RGBA") self.assertEqual(im2.size, (wr, hr)) def test_getimage(self): - with open(TEST_FILE, 'rb') as fp: + with open(TEST_FILE, "rb") as fp: icns_file = IcnsImagePlugin.IcnsFile(fp) im = icns_file.getimage() @@ -118,6 +116,5 @@ def test_getimage(self): self.assertEqual(im.size, (512, 512)) def test_not_an_icns_file(self): - with io.BytesIO(b'invalid\n') as fp: - self.assertRaises(SyntaxError, - IcnsImagePlugin.IcnsFile, fp) + with io.BytesIO(b"invalid\n") as fp: + self.assertRaises(SyntaxError, IcnsImagePlugin.IcnsFile, fp) diff --git a/Tests/test_file_ico.py b/Tests/test_file_ico.py index 97fb781b35d..8427e2bd79c 100644 --- a/Tests/test_file_ico.py +++ b/Tests/test_file_ico.py @@ -1,13 +1,12 @@ from .helper import PillowTestCase, hopper import io -from PIL import Image, IcoImagePlugin +from PIL import Image, ImageDraw, IcoImagePlugin TEST_ICO_FILE = "Tests/images/hopper.ico" class TestFileIco(PillowTestCase): - def test_sanity(self): im = Image.open(TEST_ICO_FILE) im.load() @@ -18,8 +17,7 @@ def test_sanity(self): def test_invalid_file(self): with open("Tests/images/flower.jpg", "rb") as fp: - self.assertRaises(SyntaxError, - IcoImagePlugin.IcoImageFile, fp) + self.assertRaises(SyntaxError, IcoImagePlugin.IcoImageFile, fp) def test_save_to_bytes(self): output = io.BytesIO() @@ -29,13 +27,12 @@ def test_save_to_bytes(self): # the default image output.seek(0) reloaded = Image.open(output) - self.assertEqual(reloaded.info['sizes'], {(32, 32), (64, 64)}) + self.assertEqual(reloaded.info["sizes"], {(32, 32), (64, 64)}) self.assertEqual(im.mode, reloaded.mode) self.assertEqual((64, 64), reloaded.size) self.assertEqual(reloaded.format, "ICO") - self.assert_image_equal(reloaded, - hopper().resize((64, 64), Image.LANCZOS)) + self.assert_image_equal(reloaded, hopper().resize((64, 64), Image.LANCZOS)) # the other one output.seek(0) @@ -45,8 +42,7 @@ def test_save_to_bytes(self): self.assertEqual(im.mode, reloaded.mode) self.assertEqual((32, 32), reloaded.size) self.assertEqual(reloaded.format, "ICO") - 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): im = Image.open(TEST_ICO_FILE) @@ -81,12 +77,26 @@ def test_only_save_relevant_sizes(self): # Assert 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)} + ) def test_unexpected_size(self): # This image has been manually hexedited to state that it is 16x32 # while the image within is still 16x16 - im = self.assert_warning(UserWarning, - Image.open, "Tests/images/hopper_unexpected.ico") + im = self.assert_warning( + UserWarning, Image.open, "Tests/images/hopper_unexpected.ico" + ) self.assertEqual(im.size, (16, 16)) + + def test_draw_reloaded(self): + im = Image.open(TEST_ICO_FILE) + outfile = self.tempfile("temp_saved_hopper_draw.ico") + + draw = ImageDraw.Draw(im) + draw.line((0, 0) + im.size, "#f00") + im.save(outfile) + + im = Image.open(outfile) + im.save("Tests/images/hopper_draw.ico") + reloaded = Image.open("Tests/images/hopper_draw.ico") + self.assert_image_equal(im, reloaded) diff --git a/Tests/test_file_im.py b/Tests/test_file_im.py index 46858bb1f31..2a89e39dcb9 100644 --- a/Tests/test_file_im.py +++ b/Tests/test_file_im.py @@ -7,7 +7,6 @@ class TestFileIm(PillowTestCase): - def test_sanity(self): im = Image.open(TEST_IM) im.load() @@ -19,6 +18,7 @@ def test_unclosed_file(self): def open(): im = Image.open(TEST_IM) im.load() + self.assert_warning(None, open) def test_tell(self): @@ -45,11 +45,11 @@ def test_eoferror(self): self.assertLess(im.tell(), n_frames) # Test that seeking to the last frame does not raise an error - im.seek(n_frames-1) + im.seek(n_frames - 1) def test_roundtrip(self): for mode in ["RGB", "P", "PA"]: - out = self.tempfile('temp.im') + out = self.tempfile("temp.im") im = hopper(mode) im.save(out) reread = Image.open(out) @@ -57,15 +57,14 @@ def test_roundtrip(self): self.assert_image_equal(reread, im) def test_save_unsupported_mode(self): - out = self.tempfile('temp.im') + out = self.tempfile("temp.im") im = hopper("HSV") self.assertRaises(ValueError, im.save, out) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - ImImagePlugin.ImImageFile, invalid_file) + self.assertRaises(SyntaxError, ImImagePlugin.ImImageFile, invalid_file) def test_number(self): self.assertEqual(1.2, ImImagePlugin.number("1.2")) diff --git a/Tests/test_file_iptc.py b/Tests/test_file_iptc.py index 83b73546421..9f48633e179 100644 --- a/Tests/test_file_iptc.py +++ b/Tests/test_file_iptc.py @@ -6,7 +6,6 @@ class TestFileIptc(PillowTestCase): - def test_getiptcinfo_jpg_none(self): # Arrange im = hopper() @@ -58,6 +57,7 @@ def test_dump(self): except ImportError: from io import StringIO import sys + old_stdout = sys.stdout sys.stdout = mystdout = StringIO() diff --git a/Tests/test_file_jpeg.py b/Tests/test_file_jpeg.py index 134d6e4df82..4ade11a2966 100644 --- a/Tests/test_file_jpeg.py +++ b/Tests/test_file_jpeg.py @@ -15,7 +15,6 @@ class TestFileJpeg(PillowTestCase): - def setUp(self): if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: self.skipTest("jpeg support not available") @@ -29,14 +28,13 @@ def roundtrip(self, im, **options): im.bytes = test_bytes # for testing only return im - def gen_random_image(self, size, mode='RGB'): + def gen_random_image(self, size, mode="RGB"): """ Generates a very hard to compress file :param size: tuple :param mode: optional image mode """ - return Image.frombytes(mode, size, - os.urandom(size[0]*size[1]*len(mode))) + return Image.frombytes(mode, size, os.urandom(size[0] * size[1] * len(mode))) def test_sanity(self): @@ -54,10 +52,11 @@ def test_app(self): # Test APP/COM reader (@PIL135) im = Image.open(TEST_FILE) self.assertEqual( - im.applist[0], - ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00")) - self.assertEqual(im.applist[1], ( - "COM", b"File written by Adobe Photoshop\xa8 4.0\x00")) + im.applist[0], ("APP0", b"JFIF\x00\x01\x01\x01\x00`\x00`\x00\x00") + ) + self.assertEqual( + im.applist[1], ("COM", b"File written by Adobe Photoshop\xa8 4.0\x00") + ) self.assertEqual(len(im.applist), 2) def test_cmyk(self): @@ -72,8 +71,7 @@ def test_cmyk(self): self.assertGreater(y, 0.8) self.assertEqual(k, 0.0) # the opposite corner is black - c, m, y, k = [x / 255.0 for x in im.getpixel(( - im.size[0]-1, im.size[1]-1))] + c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))] self.assertGreater(k, 0.9) # roundtrip, and check again im = self.roundtrip(im) @@ -82,8 +80,7 @@ def test_cmyk(self): self.assertGreater(m, 0.8) self.assertGreater(y, 0.8) self.assertEqual(k, 0.0) - c, m, y, k = [x / 255.0 for x in im.getpixel(( - im.size[0]-1, im.size[1]-1))] + c, m, y, k = [x / 255.0 for x in im.getpixel((im.size[0] - 1, im.size[1] - 1))] self.assertGreater(k, 0.9) def test_dpi(self): @@ -91,6 +88,7 @@ def test(xdpi, ydpi=None): im = Image.open(TEST_FILE) im = self.roundtrip(im, dpi=(xdpi, ydpi or xdpi)) return im.info.get("dpi") + self.assertEqual(test(72), (72, 72)) self.assertEqual(test(300), (300, 300)) self.assertEqual(test(100, 200), (100, 200)) @@ -119,31 +117,38 @@ def test(n): # The ICC APP marker can store 65519 bytes per marker, so # using a 4-byte test code should allow us to detect out of # order issues. - icc_profile = (b"Test"*int(n/4+1))[:n] + icc_profile = (b"Test" * int(n / 4 + 1))[:n] self.assertEqual(len(icc_profile), n) # sanity im1 = self.roundtrip(hopper(), icc_profile=icc_profile) self.assertEqual(im1.info.get("icc_profile"), icc_profile or None) + test(0) test(1) test(3) test(4) test(5) - test(65533-14) # full JPEG marker block - test(65533-14+1) # full block plus one byte + test(65533 - 14) # full JPEG marker block + test(65533 - 14 + 1) # full block plus one byte test(ImageFile.MAXBLOCK) # full buffer block - test(ImageFile.MAXBLOCK+1) # full buffer block plus one byte - test(ImageFile.MAXBLOCK*4+3) # large block + test(ImageFile.MAXBLOCK + 1) # full buffer block plus one byte + test(ImageFile.MAXBLOCK * 4 + 3) # large block def test_large_icc_meta(self): # https://github.com/python-pillow/Pillow/issues/148 # Sometimes the meta data on the icc_profile block is bigger than # Image.MAXBLOCK or the image size. - im = Image.open('Tests/images/icc_profile_big.jpg') + im = Image.open("Tests/images/icc_profile_big.jpg") f = self.tempfile("temp.jpg") icc_profile = im.info["icc_profile"] # Should not raise IOError for image with icc larger than image size. - im.save(f, format='JPEG', progressive=True, quality=95, - icc_profile=icc_profile, optimize=True) + im.save( + f, + format="JPEG", + progressive=True, + quality=95, + icc_profile=icc_profile, + optimize=True, + ) def test_optimize(self): im1 = self.roundtrip(hopper()) @@ -156,9 +161,9 @@ def test_optimize(self): def test_optimize_large_buffer(self): # https://github.com/python-pillow/Pillow/issues/148 - f = self.tempfile('temp.jpg') + f = self.tempfile("temp.jpg") # this requires ~ 1.5x Image.MAXBLOCK - im = Image.new("RGB", (4096, 4096), 0xff3333) + im = Image.new("RGB", (4096, 4096), 0xFF3333) im.save(f, format="JPEG", optimize=True) def test_progressive(self): @@ -173,13 +178,13 @@ def test_progressive(self): self.assertGreaterEqual(im1.bytes, im3.bytes) def test_progressive_large_buffer(self): - f = self.tempfile('temp.jpg') + f = self.tempfile("temp.jpg") # this requires ~ 1.5x Image.MAXBLOCK - im = Image.new("RGB", (4096, 4096), 0xff3333) + im = Image.new("RGB", (4096, 4096), 0xFF3333) im.save(f, format="JPEG", progressive=True) def test_progressive_large_buffer_highest_quality(self): - f = self.tempfile('temp.jpg') + f = self.tempfile("temp.jpg") im = self.gen_random_image((255, 255)) # this requires more bytes than pixels in the image im.save(f, format="JPEG", progressive=True, quality=100) @@ -187,30 +192,31 @@ def test_progressive_large_buffer_highest_quality(self): def test_progressive_cmyk_buffer(self): # Issue 2272, quality 90 cmyk image is tripping the large buffer bug. f = BytesIO() - im = self.gen_random_image((256, 256), 'CMYK') - im.save(f, format='JPEG', progressive=True, quality=94) + im = self.gen_random_image((256, 256), "CMYK") + im.save(f, format="JPEG", progressive=True, quality=94) def test_large_exif(self): # https://github.com/python-pillow/Pillow/issues/148 - f = self.tempfile('temp.jpg') + f = self.tempfile("temp.jpg") im = hopper() - 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): - im = Image.open('Tests/images/exif_typeerror.jpg') + im = Image.open("Tests/images/exif_typeerror.jpg") # Should not raise a TypeError im._getexif() def test_exif_gps(self): # Arrange - im = Image.open('Tests/images/exif_gps.jpg') + im = Image.open("Tests/images/exif_gps.jpg") gps_index = 34853 expected_exif_gps = { - 0: b'\x00\x00\x00\x01', + 0: b"\x00\x00\x00\x01", 2: (4294967295, 1), - 5: b'\x01', + 5: b"\x01", 30: 65535, - 29: '1999:99:99 99:99:99'} + 29: "1999:99:99 99:99:99", + } # Act exif = im._getexif() @@ -222,35 +228,39 @@ def test_exif_rollback(self): # rolling back exif support in 3.1 to pre-3.0 formatting. # expected from 2.9, with b/u qualifiers switched for 3.2 compatibility # this test passes on 2.9 and 3.1, but not 3.0 - expected_exif = {34867: 4294967295, - 258: (24, 24, 24), - 36867: '2099:09:29 10:10:10', - 34853: {0: b'\x00\x00\x00\x01', - 2: (4294967295, 1), - 5: b'\x01', - 30: 65535, - 29: '1999:99:99 99:99:99'}, - 296: 65535, - 34665: 185, - 41994: 65535, - 514: 4294967295, - 271: 'Make', - 272: 'XXX-XXX', - 305: 'PIL', - 42034: ((1, 1), (1, 1), (1, 1), (1, 1)), - 42035: 'LensMake', - 34856: b'\xaa\xaa\xaa\xaa\xaa\xaa', - 282: (4294967295, 1), - 33434: (4294967295, 1)} - - im = Image.open('Tests/images/exif_gps.jpg') + expected_exif = { + 34867: 4294967295, + 258: (24, 24, 24), + 36867: "2099:09:29 10:10:10", + 34853: { + 0: b"\x00\x00\x00\x01", + 2: (4294967295, 1), + 5: b"\x01", + 30: 65535, + 29: "1999:99:99 99:99:99", + }, + 296: 65535, + 34665: 185, + 41994: 65535, + 514: 4294967295, + 271: "Make", + 272: "XXX-XXX", + 305: "PIL", + 42034: ((1, 1), (1, 1), (1, 1), (1, 1)), + 42035: "LensMake", + 34856: b"\xaa\xaa\xaa\xaa\xaa\xaa", + 282: (4294967295, 1), + 33434: (4294967295, 1), + } + + im = Image.open("Tests/images/exif_gps.jpg") exif = im._getexif() for tag, value in expected_exif.items(): self.assertEqual(value, exif[tag]) def test_exif_gps_typeerror(self): - im = Image.open('Tests/images/exif_gps_typeerror.jpg') + im = Image.open("Tests/images/exif_gps_typeerror.jpg") # Should not raise a TypeError im._getexif() @@ -291,6 +301,7 @@ def test_subsampling(self): def getsampling(im): layer = im.layer return layer[0][1:3] + layer[1][1:3] + layer[2][1:3] + # experimental API im = self.roundtrip(hopper(), subsampling=-1) # default self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1)) @@ -312,13 +323,12 @@ def getsampling(im): im = self.roundtrip(hopper(), subsampling="4:1:1") self.assertEqual(getsampling(im), (2, 2, 1, 1, 1, 1)) - self.assertRaises( - TypeError, self.roundtrip, hopper(), subsampling="1:1:1") + self.assertRaises(TypeError, self.roundtrip, hopper(), subsampling="1:1:1") def test_exif(self): im = Image.open("Tests/images/pil_sample_rgb.jpg") info = im._getexif() - self.assertEqual(info[305], 'Adobe Photoshop CS Macintosh') + self.assertEqual(info[305], "Adobe Photoshop CS Macintosh") def test_mp(self): im = Image.open("Tests/images/pil_sample_rgb.jpg") @@ -327,16 +337,16 @@ def test_mp(self): def test_quality_keep(self): # RGB im = Image.open("Tests/images/hopper.jpg") - f = self.tempfile('temp.jpg') - im.save(f, quality='keep') + f = self.tempfile("temp.jpg") + im.save(f, quality="keep") # Grayscale im = Image.open("Tests/images/hopper_gray.jpg") - f = self.tempfile('temp.jpg') - im.save(f, quality='keep') + f = self.tempfile("temp.jpg") + im.save(f, quality="keep") # CMYK im = Image.open("Tests/images/pil_sample_cmyk.jpg") - f = self.tempfile('temp.jpg') - im.save(f, quality='keep') + f = self.tempfile("temp.jpg") + im.save(f, quality="keep") def test_junk_jpeg_header(self): # https://github.com/python-pillow/Pillow/issues/630 @@ -364,8 +374,8 @@ def test_truncated_jpeg_throws_IOError(self): def _n_qtables_helper(self, n, test_file): im = Image.open(test_file) - f = self.tempfile('temp.jpg') - im.save(f, qtables=[[n]*64]*n) + f = self.tempfile("temp.jpg") + im.save(f, qtables=[[n] * 64] * n) im = Image.open(f) self.assertEqual(len(im.quantization), n) reloaded = self.roundtrip(im, qtables="keep") @@ -376,18 +386,18 @@ def test_qtables(self): qtables = im.quantization reloaded = self.roundtrip(im, qtables=qtables, subsampling=0) self.assertEqual(im.quantization, reloaded.quantization) - self.assert_image_similar(im, self.roundtrip(im, qtables='web_low'), - 30) - self.assert_image_similar(im, self.roundtrip(im, qtables='web_high'), - 30) - self.assert_image_similar(im, self.roundtrip(im, qtables='keep'), 30) + self.assert_image_similar(im, self.roundtrip(im, qtables="web_low"), 30) + self.assert_image_similar(im, self.roundtrip(im, qtables="web_high"), 30) + self.assert_image_similar(im, self.roundtrip(im, qtables="keep"), 30) # valid bounds for baseline qtable bounds_qtable = [int(s) for s in ("255 1 " * 32).split(None)] self.roundtrip(im, qtables=[bounds_qtable]) # values from wizard.txt in jpeg9-a src package. - standard_l_qtable = [int(s) for s in """ + standard_l_qtable = [ + int(s) + for s in """ 16 11 10 16 24 40 51 61 12 12 14 19 26 58 60 55 14 13 16 24 40 57 69 56 @@ -396,9 +406,14 @@ def test_qtables(self): 24 35 55 64 81 104 113 92 49 64 78 87 103 121 120 101 72 92 95 98 112 100 103 99 - """.split(None)] - - standard_chrominance_qtable = [int(s) for s in """ + """.split( + None + ) + ] + + standard_chrominance_qtable = [ + int(s) + for s in """ 17 18 24 47 99 99 99 99 18 21 26 66 99 99 99 99 24 26 56 99 99 99 99 99 @@ -407,25 +422,36 @@ def test_qtables(self): 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 - """.split(None)] + """.split( + None + ) + ] # list of qtable lists self.assert_image_similar( - im, self.roundtrip( - im, qtables=[standard_l_qtable, standard_chrominance_qtable]), - 30) + im, + self.roundtrip( + im, qtables=[standard_l_qtable, standard_chrominance_qtable] + ), + 30, + ) # tuple of qtable lists self.assert_image_similar( - im, self.roundtrip( - im, qtables=(standard_l_qtable, standard_chrominance_qtable)), - 30) + im, + self.roundtrip( + im, qtables=(standard_l_qtable, standard_chrominance_qtable) + ), + 30, + ) # dict of qtable lists - self.assert_image_similar(im, - self.roundtrip(im, qtables={ - 0: standard_l_qtable, - 1: standard_chrominance_qtable - }), 30) + self.assert_image_similar( + im, + self.roundtrip( + im, qtables={0: standard_l_qtable, 1: standard_chrominance_qtable} + ), + 30, + ) self._n_qtables_helper(1, "Tests/images/hopper_gray.jpg") self._n_qtables_helper(1, "Tests/images/pil_sample_rgb.jpg") @@ -437,18 +463,16 @@ def test_qtables(self): self._n_qtables_helper(4, "Tests/images/pil_sample_cmyk.jpg") # not a sequence - self.assertRaises(ValueError, self.roundtrip, im, qtables='a') + self.assertRaises(ValueError, self.roundtrip, im, qtables="a") # sequence wrong length self.assertRaises(ValueError, self.roundtrip, im, qtables=[]) # sequence wrong length - self.assertRaises(ValueError, - self.roundtrip, im, qtables=[1, 2, 3, 4, 5]) + self.assertRaises(ValueError, self.roundtrip, im, qtables=[1, 2, 3, 4, 5]) # qtable entry not a sequence self.assertRaises(ValueError, self.roundtrip, im, qtables=[1]) # qtable entry has wrong number of items - self.assertRaises(ValueError, - self.roundtrip, im, qtables=[[1, 2, 3, 4]]) + self.assertRaises(ValueError, self.roundtrip, im, qtables=[[1, 2, 3, 4]]) @unittest.skipUnless(djpeg_available(), "djpeg not available") def test_load_djpeg(self): @@ -468,11 +492,12 @@ def test_save_cjpeg(self): def test_no_duplicate_0x1001_tag(self): # Arrange from PIL import ExifTags + tag_ids = {v: k for k, v in ExifTags.TAGS.items()} # Assert - self.assertEqual(tag_ids['RelatedImageWidth'], 0x1001) - self.assertEqual(tag_ids['RelatedImageLength'], 0x1002) + self.assertEqual(tag_ids["RelatedImageWidth"], 0x1001) + self.assertEqual(tag_ids["RelatedImageLength"], 0x1002) def test_MAXBLOCK_scaling(self): im = self.gen_random_image((512, 512)) @@ -482,9 +507,9 @@ def test_MAXBLOCK_scaling(self): reloaded = Image.open(f) # none of these should crash - reloaded.save(f, quality='keep') - reloaded.save(f, quality='keep', progressive=True) - reloaded.save(f, quality='keep', optimize=True) + reloaded.save(f, quality="keep") + reloaded.save(f, quality="keep", progressive=True) + reloaded.save(f, quality="keep", optimize=True) def test_bad_mpo_header(self): """ Treat unknown MPO as JPEG """ @@ -500,14 +525,14 @@ def test_bad_mpo_header(self): def test_save_correct_modes(self): out = BytesIO() - for mode in ['1', 'L', 'RGB', 'RGBX', 'CMYK', 'YCbCr']: + for mode in ["1", "L", "RGB", "RGBX", "CMYK", "YCbCr"]: img = Image.new(mode, (20, 20)) img.save(out, "JPEG") def test_save_wrong_modes(self): # ref https://github.com/python-pillow/Pillow/issues/2005 out = BytesIO() - for mode in ['LA', 'La', 'RGBA', 'RGBa', 'P']: + for mode in ["LA", "La", "RGBA", "RGBa", "P"]: img = Image.new(mode, (20, 20)) self.assertRaises(IOError, img.save, out, "JPEG") @@ -517,25 +542,25 @@ def test_save_tiff_with_dpi(self): im = Image.open("Tests/images/hopper.tif") # Act - im.save(outfile, 'JPEG', dpi=im.info['dpi']) + im.save(outfile, "JPEG", dpi=im.info["dpi"]) # Assert reloaded = Image.open(outfile) reloaded.load() - self.assertEqual(im.info['dpi'], reloaded.info['dpi']) + self.assertEqual(im.info["dpi"], reloaded.info["dpi"]) def test_load_dpi_rounding(self): # Round up - im = Image.open('Tests/images/iptc_roundUp.jpg') + im = Image.open("Tests/images/iptc_roundUp.jpg") self.assertEqual(im.info["dpi"], (44, 44)) # Round down - im = Image.open('Tests/images/iptc_roundDown.jpg') + im = Image.open("Tests/images/iptc_roundDown.jpg") self.assertEqual(im.info["dpi"], (2, 2)) def test_save_dpi_rounding(self): outfile = self.tempfile("temp.jpg") - im = Image.open('Tests/images/hopper.jpg') + im = Image.open("Tests/images/hopper.jpg") im.save(outfile, dpi=(72.2, 72.2)) reloaded = Image.open(outfile) @@ -609,23 +634,26 @@ def test_ifd_offset_exif(self): im = Image.open("Tests/images/exif-ifd-offset.jpg") # 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): im = Image.open("Tests/images/photoshop-200dpi.jpg") - self.assertEqual(im.info["photoshop"][0x03ed], { - 'XResolution': 200.0, - 'DisplayedUnitsX': 1, - 'YResolution': 200.0, - 'DisplayedUnitsY': 1, - }) + self.assertEqual( + im.info["photoshop"][0x03ED], + { + "XResolution": 200.0, + "DisplayedUnitsX": 1, + "YResolution": 200.0, + "DisplayedUnitsY": 1, + }, + ) # This image does not contain a Photoshop header string im = Image.open("Tests/images/app13.jpg") self.assertNotIn("photoshop", im.info) -@unittest.skipUnless(sys.platform.startswith('win32'), "Windows only") +@unittest.skipUnless(sys.platform.startswith("win32"), "Windows only") class TestFileCloseW32(PillowTestCase): def setUp(self): if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: diff --git a/Tests/test_file_jpeg2k.py b/Tests/test_file_jpeg2k.py index 4b34354e2e4..a2483fade2f 100644 --- a/Tests/test_file_jpeg2k.py +++ b/Tests/test_file_jpeg2k.py @@ -5,7 +5,7 @@ codecs = dir(Image.core) -test_card = Image.open('Tests/images/test-card.png') +test_card = Image.open("Tests/images/test-card.png") test_card.load() # OpenJPEG 2.0.0 outputs this debugging message sometimes; we should @@ -14,10 +14,9 @@ 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') + self.skipTest("JPEG 2000 support not available") def roundtrip(self, im, **options): out = BytesIO() @@ -31,29 +30,28 @@ def roundtrip(self, im, **options): def test_sanity(self): # Internal version number - self.assertRegex(Image.core.jp2klib_version, r'\d+\.\d+\.\d+$') + self.assertRegex(Image.core.jp2klib_version, r"\d+\.\d+\.\d+$") - im = Image.open('Tests/images/test-card-lossless.jp2') + im = Image.open("Tests/images/test-card-lossless.jp2") px = im.load() self.assertEqual(px[0, 0], (0, 0, 0)) - self.assertEqual(im.mode, 'RGB') + self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (640, 480)) - self.assertEqual(im.format, 'JPEG2000') - self.assertEqual(im.get_format_mimetype(), 'image/jp2') + self.assertEqual(im.format, "JPEG2000") + self.assertEqual(im.get_format_mimetype(), "image/jp2") def test_jpf(self): - im = Image.open('Tests/images/balloon.jpf') - self.assertEqual(im.format, 'JPEG2000') - self.assertEqual(im.get_format_mimetype(), 'image/jpx') + im = Image.open("Tests/images/balloon.jpf") + self.assertEqual(im.format, "JPEG2000") + self.assertEqual(im.get_format_mimetype(), "image/jpx") def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - Jpeg2KImagePlugin.Jpeg2KImageFile, invalid_file) + self.assertRaises(SyntaxError, Jpeg2KImagePlugin.Jpeg2KImageFile, invalid_file) def test_bytesio(self): - with open('Tests/images/test-card-lossless.jp2', 'rb') as f: + with open("Tests/images/test-card-lossless.jp2", "rb") as f: data = BytesIO(f.read()) im = Image.open(data) im.load() @@ -63,14 +61,14 @@ def test_bytesio(self): # PIL (they were made using Adobe Photoshop) def test_lossless(self): - im = Image.open('Tests/images/test-card-lossless.jp2') + im = Image.open("Tests/images/test-card-lossless.jp2") im.load() - outfile = self.tempfile('temp_test-card.png') + outfile = self.tempfile("temp_test-card.png") im.save(outfile) self.assert_image_similar(im, test_card, 1.0e-3) def test_lossy_tiled(self): - im = Image.open('Tests/images/test-card-lossy-tiled.jp2') + im = Image.open("Tests/images/test-card-lossy-tiled.jp2") im.load() self.assert_image_similar(im, test_card, 2.0) @@ -88,8 +86,8 @@ def test_tiled_rt(self): def test_tiled_offset_rt(self): im = self.roundtrip( - test_card, tile_size=(128, 128), - tile_offset=(0, 0), offset=(32, 32)) + test_card, tile_size=(128, 128), tile_offset=(0, 0), offset=(32, 32) + ) self.assert_image_equal(im, test_card) def test_irreversible_rt(self): @@ -97,40 +95,34 @@ def test_irreversible_rt(self): self.assert_image_similar(im, test_card, 2.0) def test_prog_qual_rt(self): - im = self.roundtrip( - test_card, quality_layers=[60, 40, 20], progression='LRCP') + im = self.roundtrip(test_card, quality_layers=[60, 40, 20], progression="LRCP") self.assert_image_similar(im, test_card, 2.0) def test_prog_res_rt(self): - im = self.roundtrip(test_card, num_resolutions=8, progression='RLCP') + im = self.roundtrip(test_card, num_resolutions=8, progression="RLCP") self.assert_image_equal(im, test_card) def test_reduce(self): - im = Image.open('Tests/images/test-card-lossless.jp2') + im = Image.open("Tests/images/test-card-lossless.jp2") im.reduce = 2 im.load() self.assertEqual(im.size, (160, 120)) def test_layers_type(self): - outfile = self.tempfile('temp_layers.jp2') - for quality_layers in [ - [100, 50, 10], - (100, 50, 10), - None - ]: + outfile = self.tempfile("temp_layers.jp2") + for quality_layers in [[100, 50, 10], (100, 50, 10), None]: test_card.save(outfile, quality_layers=quality_layers) - for quality_layers in [ - 'quality_layers', - ('100', '50', '10') - ]: - self.assertRaises(ValueError, test_card.save, outfile, - quality_layers=quality_layers) + for quality_layers in ["quality_layers", ("100", "50", "10")]: + self.assertRaises( + ValueError, test_card.save, outfile, quality_layers=quality_layers + ) def test_layers(self): out = BytesIO() - test_card.save(out, 'JPEG2000', quality_layers=[100, 50, 10], - progression='LRCP') + test_card.save( + out, "JPEG2000", quality_layers=[100, 50, 10], progression="LRCP" + ) out.seek(0) im = Image.open(out) @@ -146,49 +138,49 @@ def test_layers(self): def test_rgba(self): # Arrange - j2k = Image.open('Tests/images/rgb_trns_ycbc.j2k') - jp2 = Image.open('Tests/images/rgb_trns_ycbc.jp2') + j2k = Image.open("Tests/images/rgb_trns_ycbc.j2k") + jp2 = Image.open("Tests/images/rgb_trns_ycbc.jp2") # Act j2k.load() jp2.load() # Assert - self.assertEqual(j2k.mode, 'RGBA') - self.assertEqual(jp2.mode, 'RGBA') + self.assertEqual(j2k.mode, "RGBA") + self.assertEqual(jp2.mode, "RGBA") def test_16bit_monochrome_has_correct_mode(self): - j2k = Image.open('Tests/images/16bit.cropped.j2k') - jp2 = Image.open('Tests/images/16bit.cropped.jp2') + j2k = Image.open("Tests/images/16bit.cropped.j2k") + jp2 = Image.open("Tests/images/16bit.cropped.jp2") j2k.load() jp2.load() - self.assertEqual(j2k.mode, 'I;16') - self.assertEqual(jp2.mode, 'I;16') + self.assertEqual(j2k.mode, "I;16") + self.assertEqual(jp2.mode, "I;16") def test_16bit_monochrome_jp2_like_tiff(self): - tiff_16bit = Image.open('Tests/images/16bit.cropped.tif') - jp2 = Image.open('Tests/images/16bit.cropped.jp2') + tiff_16bit = Image.open("Tests/images/16bit.cropped.tif") + jp2 = Image.open("Tests/images/16bit.cropped.jp2") self.assert_image_similar(jp2, tiff_16bit, 1e-3) def test_16bit_monochrome_j2k_like_tiff(self): - tiff_16bit = Image.open('Tests/images/16bit.cropped.tif') - j2k = Image.open('Tests/images/16bit.cropped.j2k') + tiff_16bit = Image.open("Tests/images/16bit.cropped.tif") + j2k = Image.open("Tests/images/16bit.cropped.j2k") self.assert_image_similar(j2k, tiff_16bit, 1e-3) def test_16bit_j2k_roundtrips(self): - j2k = Image.open('Tests/images/16bit.cropped.j2k') + j2k = Image.open("Tests/images/16bit.cropped.j2k") im = self.roundtrip(j2k) self.assert_image_equal(im, j2k) def test_16bit_jp2_roundtrips(self): - jp2 = Image.open('Tests/images/16bit.cropped.jp2') + jp2 = Image.open("Tests/images/16bit.cropped.jp2") im = self.roundtrip(jp2) self.assert_image_equal(im, jp2) @@ -196,12 +188,13 @@ def test_unbound_local(self): # prepatch, a malformed jp2 file could cause an UnboundLocalError # exception. with self.assertRaises(IOError): - Image.open('Tests/images/unbound_variable.jp2') + Image.open("Tests/images/unbound_variable.jp2") def test_parser_feed(self): # Arrange from PIL import ImageFile - with open('Tests/images/test-card-lossless.jp2', 'rb') as f: + + with open("Tests/images/test-card-lossless.jp2", "rb") as f: data = f.read() # Act diff --git a/Tests/test_file_libtiff.py b/Tests/test_file_libtiff.py index 9170aabb1d8..f02970b6770 100644 --- a/Tests/test_file_libtiff.py +++ b/Tests/test_file_libtiff.py @@ -17,9 +17,8 @@ class LibTiffTestCase(PillowTestCase): - def setUp(self): - if not features.check('libtiff'): + if not features.check("libtiff"): self.skipTest("tiff support not available") def _assert_noerr(self, im): @@ -32,7 +31,7 @@ def _assert_noerr(self, im): im.getdata() try: - self.assertEqual(im._compression, 'group4') + self.assertEqual(im._compression, "group4") except AttributeError: print("No _compression") print(dir(im)) @@ -42,11 +41,10 @@ def _assert_noerr(self, im): im.save(out) out_bytes = io.BytesIO() - im.save(out_bytes, format='tiff', compression='group4') + im.save(out_bytes, format="tiff", compression="group4") class TestFileLibTiff(LibTiffTestCase): - def test_g4_tiff(self): """Test the ordinary file path load path""" @@ -65,7 +63,7 @@ def test_g4_tiff_file(self): """Testing the string load path""" test_file = "Tests/images/hopper_g4_500.tif" - with open(test_file, 'rb') as f: + with open(test_file, "rb") as f: im = Image.open(f) self.assertEqual(im.size, (500, 500)) @@ -75,7 +73,7 @@ def test_g4_tiff_bytesio(self): """Testing the stringio loading code path""" test_file = "Tests/images/hopper_g4_500.tif" s = io.BytesIO() - with open(test_file, 'rb') as f: + with open(test_file, "rb") as f: s.write(f.read()) s.seek(0) im = Image.open(s) @@ -85,16 +83,16 @@ def test_g4_tiff_bytesio(self): def test_g4_eq_png(self): """ Checking that we're actually getting the data that we expect""" - png = Image.open('Tests/images/hopper_bw_500.png') - g4 = Image.open('Tests/images/hopper_g4_500.tif') + png = Image.open("Tests/images/hopper_bw_500.png") + g4 = Image.open("Tests/images/hopper_g4_500.tif") self.assert_image_equal(g4, png) # see https://github.com/python-pillow/Pillow/issues/279 def test_g4_fillorder_eq_png(self): """ Checking that we're actually getting the data that we expect""" - png = Image.open('Tests/images/g4-fillorder-test.png') - g4 = Image.open('Tests/images/g4-fillorder-test.tif') + png = Image.open("Tests/images/g4-fillorder-test.png") + g4 = Image.open("Tests/images/g4-fillorder-test.tif") self.assert_image_equal(g4, png) @@ -112,9 +110,9 @@ def test_g4_write(self): self.assertEqual(reread.size, (500, 500)) self._assert_noerr(reread) self.assert_image_equal(reread, rot) - self.assertEqual(reread.info['compression'], 'group4') + self.assertEqual(reread.info["compression"], "group4") - self.assertEqual(reread.info['compression'], orig.info['compression']) + self.assertEqual(reread.info["compression"], orig.info["compression"]) self.assertNotEqual(orig.tobytes(), reread.tobytes()) @@ -124,18 +122,16 @@ def test_adobe_deflate_tiff(self): self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (278, 374)) - self.assertEqual( - im.tile[0][:3], ('tiff_adobe_deflate', (0, 0, 278, 374), 0)) + self.assertEqual(im.tile[0][:3], ("tiff_adobe_deflate", (0, 0, 278, 374), 0)) im.load() - 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_write_metadata(self): """ Test metadata writing through libtiff """ for legacy_api in [False, True]: - img = Image.open('Tests/images/hopper_g4.tif') - f = self.tempfile('temp.tiff') + img = Image.open("Tests/images/hopper_g4.tif") + f = self.tempfile("temp.tiff") img.save(f, tiffinfo=img.tag) @@ -146,8 +142,12 @@ def test_write_metadata(self): # PhotometricInterpretation is set from SAVE_INFO, # not the original image. - ignored = ['StripByteCounts', 'RowsPerStrip', 'PageNumber', - 'PhotometricInterpretation'] + ignored = [ + "StripByteCounts", + "RowsPerStrip", + "PageNumber", + "PhotometricInterpretation", + ] loaded = Image.open(f) if legacy_api: @@ -155,28 +155,27 @@ def test_write_metadata(self): else: reloaded = loaded.tag_v2.named() - for tag, value in itertools.chain(reloaded.items(), - original.items()): + for tag, value in itertools.chain(reloaded.items(), original.items()): if tag not in ignored: val = original[tag] - if tag.endswith('Resolution'): + if tag.endswith("Resolution"): if legacy_api: self.assertEqual( c_float(val[0][0] / val[0][1]).value, c_float(value[0][0] / value[0][1]).value, - msg="%s didn't roundtrip" % tag) + msg="%s didn't roundtrip" % tag, + ) else: self.assertEqual( - c_float(val).value, c_float(value).value, - msg="%s didn't roundtrip" % tag) + c_float(val).value, + c_float(value).value, + msg="%s didn't roundtrip" % tag, + ) else: - self.assertEqual( - val, value, msg="%s didn't roundtrip" % tag) + self.assertEqual(val, value, msg="%s didn't roundtrip" % tag) # https://github.com/python-pillow/Pillow/issues/1561 - requested_fields = ['StripByteCounts', - 'RowsPerStrip', - 'StripOffsets'] + requested_fields = ["StripByteCounts", "RowsPerStrip", "StripOffsets"] for field in requested_fields: self.assertIn(field, reloaded, "%s not in metadata" % field) @@ -187,13 +186,15 @@ def test_additional_metadata(self): # Get the list of the ones that we should be able to write - core_items = {tag: info for tag, info in ((s, TiffTags.lookup(s)) for s - in TiffTags.LIBTIFF_CORE) - if info.type is not None} + core_items = { + tag: info + for tag, info in ((s, TiffTags.lookup(s)) for s in TiffTags.LIBTIFF_CORE) + if info.type is not None + } # Exclude ones that have special meaning # that we're already testing them - im = Image.open('Tests/images/hopper_g4.tif') + im = Image.open("Tests/images/hopper_g4.tif") for tag in im.tag_v2: try: del core_items[tag] @@ -207,11 +208,13 @@ def test_additional_metadata(self): # 5: "rational", # 12: "double", # Type: dummy value - values = {2: 'test', - 3: 1, - 4: 2**20, - 5: TiffImagePlugin.IFDRational(100, 1), - 12: 1.05} + values = { + 2: "test", + 3: 1, + 4: 2 ** 20, + 5: TiffImagePlugin.IFDRational(100, 1), + 12: 1.05, + } new_ifd = TiffImagePlugin.ImageFileDirectory_v2() for tag, info in core_items.items(): @@ -220,8 +223,7 @@ def test_additional_metadata(self): if info.length == 0: new_ifd[tag] = tuple(values[info.type] for _ in range(3)) else: - new_ifd[tag] = tuple(values[info.type] - for _ in range(info.length)) + new_ifd[tag] = tuple(values[info.type] for _ in range(info.length)) # Extra samples really doesn't make sense in this application. del new_ifd[338] @@ -234,7 +236,7 @@ def test_additional_metadata(self): TiffImagePlugin.WRITE_LIBTIFF = False def test_custom_metadata(self): - tc = namedtuple('test_case', 'value,type,supported_by_default') + tc = namedtuple("test_case", "value,type,supported_by_default") custom = {37000+k: v for k, v in enumerate([ tc(4, TiffTags.SHORT, True), tc(123456789, TiffTags.LONG, True), @@ -244,9 +246,9 @@ def test_custom_metadata(self): tc(TiffImagePlugin.IFDRational(4, 7), TiffTags.RATIONAL, True), tc(4.25, TiffTags.FLOAT, True), tc(4.25, TiffTags.DOUBLE, True), - tc('custom tag value', TiffTags.ASCII, True), - tc(u'custom tag value', TiffTags.ASCII, True), - tc(b'custom tag value', TiffTags.BYTE, True), + tc("custom tag value", TiffTags.ASCII, True), + tc(u"custom tag value", TiffTags.ASCII, True), + tc(b"custom tag value", TiffTags.BYTE, True), tc((4, 5, 6), TiffTags.SHORT, True), tc((123456789, 9, 34, 234, 219387, 92432323), TiffTags.LONG, True), tc((-4, 9, 10), TiffTags.SIGNED_BYTE, False), @@ -264,8 +266,9 @@ def test_custom_metadata(self): libtiff_version = TiffImagePlugin._libtiff_version() libtiffs = [False] - if distutils.version.StrictVersion(libtiff_version) >= \ - distutils.version.StrictVersion("4.0"): + if distutils.version.StrictVersion( + libtiff_version + ) >= distutils.version.StrictVersion("4.0"): libtiffs.append(True) for libtiff in libtiffs: @@ -308,68 +311,68 @@ def check_tags(tiffinfo): def test_int_dpi(self): # issue #1765 - im = hopper('RGB') - out = self.tempfile('temp.tif') + im = hopper("RGB") + out = self.tempfile("temp.tif") TiffImagePlugin.WRITE_LIBTIFF = True im.save(out, dpi=(72, 72)) TiffImagePlugin.WRITE_LIBTIFF = False reloaded = Image.open(out) - self.assertEqual(reloaded.info['dpi'], (72.0, 72.0)) + self.assertEqual(reloaded.info["dpi"], (72.0, 72.0)) def test_g3_compression(self): - i = Image.open('Tests/images/hopper_g4_500.tif') + i = Image.open("Tests/images/hopper_g4_500.tif") out = self.tempfile("temp.tif") - i.save(out, compression='group3') + i.save(out, compression="group3") reread = Image.open(out) - self.assertEqual(reread.info['compression'], 'group3') + self.assertEqual(reread.info["compression"], "group3") self.assert_image_equal(reread, i) def test_little_endian(self): - im = Image.open('Tests/images/16bit.deflate.tif') + im = Image.open("Tests/images/16bit.deflate.tif") self.assertEqual(im.getpixel((0, 0)), 480) - self.assertEqual(im.mode, 'I;16') + self.assertEqual(im.mode, "I;16") b = im.tobytes() # Bytes are in image native order (little endian) if py3: - self.assertEqual(b[0], ord(b'\xe0')) - self.assertEqual(b[1], ord(b'\x01')) + self.assertEqual(b[0], ord(b"\xe0")) + self.assertEqual(b[1], ord(b"\x01")) else: - self.assertEqual(b[0], b'\xe0') - self.assertEqual(b[1], b'\x01') + self.assertEqual(b[0], b"\xe0") + self.assertEqual(b[1], b"\x01") out = self.tempfile("temp.tif") # out = "temp.le.tif" im.save(out) reread = Image.open(out) - self.assertEqual(reread.info['compression'], im.info['compression']) + self.assertEqual(reread.info["compression"], im.info["compression"]) self.assertEqual(reread.getpixel((0, 0)), 480) # UNDONE - libtiff defaults to writing in native endian, so # on big endian, we'll get back mode = 'I;16B' here. def test_big_endian(self): - im = Image.open('Tests/images/16bit.MM.deflate.tif') + im = Image.open("Tests/images/16bit.MM.deflate.tif") self.assertEqual(im.getpixel((0, 0)), 480) - self.assertEqual(im.mode, 'I;16B') + self.assertEqual(im.mode, "I;16B") b = im.tobytes() # Bytes are in image native order (big endian) if py3: - self.assertEqual(b[0], ord(b'\x01')) - self.assertEqual(b[1], ord(b'\xe0')) + self.assertEqual(b[0], ord(b"\x01")) + self.assertEqual(b[1], ord(b"\xe0")) else: - self.assertEqual(b[0], b'\x01') - self.assertEqual(b[1], b'\xe0') + self.assertEqual(b[0], b"\x01") + self.assertEqual(b[1], b"\xe0") out = self.tempfile("temp.tif") im.save(out) reread = Image.open(out) - self.assertEqual(reread.info['compression'], im.info['compression']) + self.assertEqual(reread.info["compression"], im.info["compression"]) self.assertEqual(reread.getpixel((0, 0)), 480) def test_g4_string_info(self): @@ -379,18 +382,18 @@ def test_g4_string_info(self): out = self.tempfile("temp.tif") - orig.tag[269] = 'temp.tif' + orig.tag[269] = "temp.tif" orig.save(out) reread = Image.open(out) - self.assertEqual('temp.tif', reread.tag_v2[269]) - self.assertEqual('temp.tif', reread.tag[269][0]) + self.assertEqual("temp.tif", reread.tag_v2[269]) + self.assertEqual("temp.tif", reread.tag[269][0]) def test_12bit_rawmode(self): """ Are we generating the same interpretation of the image as Imagemagick is? """ TiffImagePlugin.READ_LIBTIFF = True - im = Image.open('Tests/images/12bit.cropped.tif') + im = Image.open("Tests/images/12bit.cropped.tif") im.load() TiffImagePlugin.READ_LIBTIFF = False # to make the target -- @@ -399,18 +402,19 @@ def test_12bit_rawmode(self): # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, # so we need to unshift so that the integer values are the same. - self.assert_image_equal_tofile(im, 'Tests/images/12in16bit.tif') + self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif") def test_blur(self): # test case from irc, how to do blur on b/w image # and save to compressed tif. from PIL import ImageFilter - out = self.tempfile('temp.tif') - im = Image.open('Tests/images/pport_g4.tif') - im = im.convert('L') + + out = self.tempfile("temp.tif") + im = Image.open("Tests/images/pport_g4.tif") + im = im.convert("L") im = im.filter(ImageFilter.GaussianBlur(4)) - im.save(out, compression='tiff_adobe_deflate') + im.save(out, compression="tiff_adobe_deflate") im2 = Image.open(out) im2.load() @@ -420,18 +424,18 @@ def test_blur(self): def test_compressions(self): # Test various tiff compressions and assert similar image content but reduced # file sizes. - im = hopper('RGB') - out = self.tempfile('temp.tif') + im = hopper("RGB") + out = self.tempfile("temp.tif") im.save(out) size_raw = os.path.getsize(out) - for compression in ('packbits', 'tiff_lzw'): + for compression in ("packbits", "tiff_lzw"): im.save(out, compression=compression) size_compressed = os.path.getsize(out) im2 = Image.open(out) self.assert_image_equal(im, im2) - im.save(out, compression='jpeg') + im.save(out, compression="jpeg") size_jpeg = os.path.getsize(out) im2 = Image.open(out) self.assert_image_similar(im, im2, 30) @@ -457,10 +461,10 @@ def test_quality(self): im.save(out, compression='jpeg', quality=100) def test_cmyk_save(self): - im = hopper('CMYK') - out = self.tempfile('temp.tif') + im = hopper("CMYK") + out = self.tempfile("temp.tif") - im.save(out, compression='tiff_adobe_deflate') + im.save(out, compression="tiff_adobe_deflate") im2 = Image.open(out) self.assert_image_equal(im, im2) @@ -469,12 +473,12 @@ def xtest_bw_compression_w_rgb(self): to output on stderr from the error thrown by libtiff. We need to capture that but not now""" - im = hopper('RGB') - out = self.tempfile('temp.tif') + im = hopper("RGB") + out = self.tempfile("temp.tif") - self.assertRaises(IOError, im.save, out, compression='tiff_ccitt') - self.assertRaises(IOError, im.save, out, compression='group3') - self.assertRaises(IOError, im.save, out, compression='group4') + self.assertRaises(IOError, im.save, out, compression="tiff_ccitt") + self.assertRaises(IOError, im.save, out, compression="group3") + self.assertRaises(IOError, im.save, out, compression="group4") def test_fp_leak(self): im = Image.open("Tests/images/hopper_g4_500.tif") @@ -490,30 +494,30 @@ def test_fp_leak(self): def test_multipage(self): # issue #862 TiffImagePlugin.READ_LIBTIFF = True - im = Image.open('Tests/images/multipage.tiff') + im = Image.open("Tests/images/multipage.tiff") # file is a multipage tiff, 10x10 green, 10x10 red, 20x20 blue im.seek(0) self.assertEqual(im.size, (10, 10)) - self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 128, 0)) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0)) self.assertTrue(im.tag.next) im.seek(1) self.assertEqual(im.size, (10, 10)) - self.assertEqual(im.convert('RGB').getpixel((0, 0)), (255, 0, 0)) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0)) self.assertTrue(im.tag.next) im.seek(2) self.assertFalse(im.tag.next) self.assertEqual(im.size, (20, 20)) - self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 0, 255)) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255)) TiffImagePlugin.READ_LIBTIFF = False def test_multipage_nframes(self): # issue #862 TiffImagePlugin.READ_LIBTIFF = True - im = Image.open('Tests/images/multipage.tiff') + im = Image.open("Tests/images/multipage.tiff") frames = im.n_frames self.assertEqual(frames, 3) for _ in range(frames): @@ -525,7 +529,7 @@ def test_multipage_nframes(self): def test__next(self): TiffImagePlugin.READ_LIBTIFF = True - im = Image.open('Tests/images/hopper.tif') + im = Image.open("Tests/images/hopper.tif") self.assertFalse(im.tag.next) im.load() self.assertFalse(im.tag.next) @@ -554,7 +558,7 @@ def test_gray_semibyte_per_pixel(self): "Tests/images/tiff_gray_2_4_bpp/hopper2I.tif", "Tests/images/tiff_gray_2_4_bpp/hopper2R.tif", "Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif", - ) + ), ), ( 7.3, # epsilon @@ -563,7 +567,7 @@ def test_gray_semibyte_per_pixel(self): "Tests/images/tiff_gray_2_4_bpp/hopper4I.tif", "Tests/images/tiff_gray_2_4_bpp/hopper4R.tif", "Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif", - ) + ), ), ) original = hopper("L") @@ -598,7 +602,7 @@ def save_bytesio(compression=None): self.assert_image_similar(pilim, pilim_load, 0) save_bytesio() - save_bytesio('raw') + save_bytesio("raw") save_bytesio("packbits") save_bytesio("tiff_lzw") @@ -607,12 +611,12 @@ def save_bytesio(compression=None): def test_crashing_metadata(self): # issue 1597 - im = Image.open('Tests/images/rdf.tif') - out = self.tempfile('temp.tif') + im = Image.open("Tests/images/rdf.tif") + out = self.tempfile("temp.tif") TiffImagePlugin.WRITE_LIBTIFF = True # this shouldn't crash - im.save(out, format='TIFF') + im.save(out, format="TIFF") TiffImagePlugin.WRITE_LIBTIFF = False def test_page_number_x_0(self): @@ -633,8 +637,8 @@ def test_fd_duplication(self): # https://github.com/python-pillow/Pillow/issues/1651 tmpfile = self.tempfile("temp.tif") - with open(tmpfile, 'wb') as f: - with open("Tests/images/g4-multi.tiff", 'rb') as src: + with open(tmpfile, "wb") as f: + with open("Tests/images/g4-multi.tiff", "rb") as src: f.write(src.read()) im = Image.open(tmpfile) @@ -645,29 +649,29 @@ def test_fd_duplication(self): def test_read_icc(self): with Image.open("Tests/images/hopper.iccprofile.tif") as img: - icc = img.info.get('icc_profile') + icc = img.info.get("icc_profile") self.assertIsNotNone(icc) TiffImagePlugin.READ_LIBTIFF = True with Image.open("Tests/images/hopper.iccprofile.tif") as img: - icc_libtiff = img.info.get('icc_profile') + icc_libtiff = img.info.get("icc_profile") self.assertIsNotNone(icc_libtiff) TiffImagePlugin.READ_LIBTIFF = False self.assertEqual(icc, icc_libtiff) def test_multipage_compression(self): - im = Image.open('Tests/images/compression.tif') + im = Image.open("Tests/images/compression.tif") im.seek(0) - self.assertEqual(im._compression, 'tiff_ccitt') + self.assertEqual(im._compression, "tiff_ccitt") self.assertEqual(im.size, (10, 10)) im.seek(1) - self.assertEqual(im._compression, 'packbits') + self.assertEqual(im._compression, "packbits") self.assertEqual(im.size, (10, 10)) im.load() im.seek(0) - self.assertEqual(im._compression, 'tiff_ccitt') + self.assertEqual(im._compression, "tiff_ccitt") self.assertEqual(im.size, (10, 10)) im.load() @@ -691,13 +695,18 @@ def test_16bit_RGB_tiff(self): self.assertEqual(im.size, (100, 40)) self.assertEqual( im.tile, - [('tiff_adobe_deflate', (0, 0, 100, 40), 0, - ('RGB;16N', 'tiff_adobe_deflate', False))] + [ + ( + "tiff_adobe_deflate", + (0, 0, 100, 40), + 0, + ("RGB;16N", "tiff_adobe_deflate", False), + ) + ], ) im.load() - self.assert_image_equal_tofile( - im, "Tests/images/tiff_16bit_RGB_target.png") + self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGB_target.png") def test_16bit_RGBa_tiff(self): im = Image.open("Tests/images/tiff_16bit_RGBa.tiff") @@ -705,13 +714,11 @@ def test_16bit_RGBa_tiff(self): self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (100, 40)) self.assertEqual( - im.tile, - [('tiff_lzw', (0, 0, 100, 40), 0, ('RGBa;16N', 'tiff_lzw', False))] + im.tile, [("tiff_lzw", (0, 0, 100, 40), 0, ("RGBa;16N", "tiff_lzw", False))] ) im.load() - self.assert_image_equal_tofile( - im, "Tests/images/tiff_16bit_RGBa_target.png") + self.assert_image_equal_tofile(im, "Tests/images/tiff_16bit_RGBa_target.png") def test_gimp_tiff(self): # Read TIFF JPEG images from GIMP [@PIL168] @@ -726,7 +733,7 @@ def test_gimp_tiff(self): self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (256, 256)) self.assertEqual( - im.tile, [('jpeg', (0, 0, 256, 256), 0, ('RGB', 'jpeg', False))] + im.tile, [("jpeg", (0, 0, 256, 256), 0, ("RGB", "jpeg", False))] ) im.load() @@ -735,15 +742,14 @@ def test_gimp_tiff(self): def test_sampleformat(self): # https://github.com/python-pillow/Pillow/issues/1466 im = Image.open("Tests/images/copyleft.tiff") - self.assertEqual(im.mode, 'RGB') + self.assertEqual(im.mode, "RGB") - self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", - mode='RGB') + self.assert_image_equal_tofile(im, "Tests/images/copyleft.png", mode="RGB") def test_lzw(self): im = Image.open("Tests/images/hopper_lzw.tif") - self.assertEqual(im.mode, 'RGB') + self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "TIFF") im2 = hopper() @@ -795,5 +801,6 @@ def test_old_style_jpeg(self): infile = "Tests/images/old-style-jpeg-compression.tif" im = Image.open(infile) - self.assert_image_equal_tofile(im, - "Tests/images/old-style-jpeg-compression.png") + self.assert_image_equal_tofile( + im, "Tests/images/old-style-jpeg-compression.png" + ) diff --git a/Tests/test_file_libtiff_small.py b/Tests/test_file_libtiff_small.py index 6b379718e8f..0db37c7ea95 100644 --- a/Tests/test_file_libtiff_small.py +++ b/Tests/test_file_libtiff_small.py @@ -17,7 +17,7 @@ def test_g4_hopper_file(self): """Testing the open file load path""" test_file = "Tests/images/hopper_g4.tif" - with open(test_file, 'rb') as f: + with open(test_file, "rb") as f: im = Image.open(f) self.assertEqual(im.size, (128, 128)) @@ -26,9 +26,10 @@ def test_g4_hopper_file(self): def test_g4_hopper_bytesio(self): """Testing the bytesio loading code path""" from io import BytesIO + test_file = "Tests/images/hopper_g4.tif" s = BytesIO() - with open(test_file, 'rb') as f: + with open(test_file, "rb") as f: s.write(f.read()) s.seek(0) im = Image.open(s) diff --git a/Tests/test_file_mcidas.py b/Tests/test_file_mcidas.py index e273faad977..5b8f4d592d7 100644 --- a/Tests/test_file_mcidas.py +++ b/Tests/test_file_mcidas.py @@ -4,12 +4,10 @@ class TestFileMcIdas(PillowTestCase): - def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - McIdasImagePlugin.McIdasImageFile, invalid_file) + self.assertRaises(SyntaxError, McIdasImagePlugin.McIdasImageFile, invalid_file) def test_valid_file(self): # Arrange diff --git a/Tests/test_file_mic.py b/Tests/test_file_mic.py index 3a7daa45925..390f56af17f 100644 --- a/Tests/test_file_mic.py +++ b/Tests/test_file_mic.py @@ -13,9 +13,8 @@ @unittest.skipUnless(olefile_installed, "olefile package not installed") -@unittest.skipUnless(features.check('libtiff'), "libtiff not installed") +@unittest.skipUnless(features.check("libtiff"), "libtiff not installed") class TestFileMic(PillowTestCase): - def test_sanity(self): im = Image.open(TEST_FILE) im.load() @@ -24,8 +23,8 @@ def test_sanity(self): self.assertEqual(im.format, "MIC") # Adjust for the gamma of 2.2 encoded into the file - lut = ImagePalette.make_gamma_lut(1/2.2) - im = Image.merge('RGBA', [chan.point(lut) for chan in im.split()]) + lut = ImagePalette.make_gamma_lut(1 / 2.2) + im = Image.merge("RGBA", [chan.point(lut) for chan in im.split()]) im2 = hopper("RGBA") self.assert_image_similar(im, im2, 10) @@ -57,10 +56,8 @@ def test_seek(self): def test_invalid_file(self): # Test an invalid OLE file invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - MicImagePlugin.MicImageFile, invalid_file) + self.assertRaises(SyntaxError, MicImagePlugin.MicImageFile, invalid_file) # Test a valid OLE file, but not a MIC file ole_file = "Tests/images/test-ole-file.doc" - self.assertRaises(SyntaxError, - MicImagePlugin.MicImageFile, ole_file) + self.assertRaises(SyntaxError, MicImagePlugin.MicImageFile, ole_file) diff --git a/Tests/test_file_mpo.py b/Tests/test_file_mpo.py index ce1ca96b672..45f88ca61b3 100644 --- a/Tests/test_file_mpo.py +++ b/Tests/test_file_mpo.py @@ -7,7 +7,6 @@ class TestFileMpo(PillowTestCase): - def setUp(self): codecs = dir(Image.core) if "jpeg_encoder" not in codecs or "jpeg_decoder" not in codecs: @@ -35,23 +34,25 @@ def test_unclosed_file(self): def open(): im = Image.open(test_files[0]) im.load() + self.assert_warning(None, open) def test_app(self): for test_file in test_files: # Test APP/COM reader (@PIL135) im = Image.open(test_file) - self.assertEqual(im.applist[0][0], 'APP1') - self.assertEqual(im.applist[1][0], 'APP2') - self.assertEqual(im.applist[1][1][:16], - b'MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00') + self.assertEqual(im.applist[0][0], "APP1") + self.assertEqual(im.applist[1][0], "APP2") + self.assertEqual( + im.applist[1][1][:16], b"MPF\x00MM\x00*\x00\x00\x00\x08\x00\x03\xb0\x00" + ) self.assertEqual(len(im.applist), 2) def test_exif(self): for test_file in test_files: im = Image.open(test_file) info = im._getexif() - self.assertEqual(info[272], 'Nintendo 3DS') + self.assertEqual(info[272], "Nintendo 3DS") self.assertEqual(info[296], 2) self.assertEqual(info[34665], 188) @@ -68,19 +69,19 @@ def test_parallax(self): # Nintendo im = Image.open("Tests/images/sugarshack.mpo") exif = im.getexif() - self.assertEqual(exif.get_ifd(0x927c)[0x1101]["Parallax"], -44.798187255859375) + self.assertEqual(exif.get_ifd(0x927C)[0x1101]["Parallax"], -44.798187255859375) # Fujifilm im = Image.open("Tests/images/fujifilm.mpo") im.seek(1) exif = im.getexif() - self.assertEqual(exif.get_ifd(0x927c)[0xb211], -3.125) + self.assertEqual(exif.get_ifd(0x927C)[0xB211], -3.125) def test_mp(self): for test_file in test_files: im = Image.open(test_file) mpinfo = im._getmp() - self.assertEqual(mpinfo[45056], b'0100') + self.assertEqual(mpinfo[45056], b"0100") self.assertEqual(mpinfo[45057], 2) def test_mp_offset(self): @@ -88,7 +89,7 @@ def test_mp_offset(self): # in APP2 data, in contrast to normal 8 im = Image.open("Tests/images/sugarshack_ifd_offset.mpo") mpinfo = im._getmp() - self.assertEqual(mpinfo[45056], b'0100') + self.assertEqual(mpinfo[45056], b"0100") self.assertEqual(mpinfo[45057], 2) def test_mp_attribute(self): @@ -97,17 +98,16 @@ def test_mp_attribute(self): mpinfo = im._getmp() frameNumber = 0 for mpentry in mpinfo[45058]: - mpattr = mpentry['Attribute'] + mpattr = mpentry["Attribute"] if frameNumber: - self.assertFalse(mpattr['RepresentativeImageFlag']) + self.assertFalse(mpattr["RepresentativeImageFlag"]) else: - self.assertTrue(mpattr['RepresentativeImageFlag']) - self.assertFalse(mpattr['DependentParentImageFlag']) - self.assertFalse(mpattr['DependentChildImageFlag']) - self.assertEqual(mpattr['ImageDataFormat'], 'JPEG') - self.assertEqual(mpattr['MPType'], - 'Multi-Frame Image: (Disparity)') - self.assertEqual(mpattr['Reserved'], 0) + self.assertTrue(mpattr["RepresentativeImageFlag"]) + self.assertFalse(mpattr["DependentParentImageFlag"]) + self.assertFalse(mpattr["DependentChildImageFlag"]) + self.assertEqual(mpattr["ImageDataFormat"], "JPEG") + self.assertEqual(mpattr["MPType"], "Multi-Frame Image: (Disparity)") + self.assertEqual(mpattr["Reserved"], 0) frameNumber += 1 def test_seek(self): @@ -144,7 +144,7 @@ def test_eoferror(self): self.assertLess(im.tell(), n_frames) # Test that seeking to the last frame does not raise an error - im.seek(n_frames-1) + im.seek(n_frames - 1) def test_image_grab(self): for test_file in test_files: diff --git a/Tests/test_file_msp.py b/Tests/test_file_msp.py index 724fc78b161..c60138ebd55 100644 --- a/Tests/test_file_msp.py +++ b/Tests/test_file_msp.py @@ -10,7 +10,6 @@ class TestFileMsp(PillowTestCase): - def test_sanity(self): test_file = self.tempfile("temp.msp") @@ -25,8 +24,7 @@ def test_sanity(self): def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - MspImagePlugin.MspImageFile, invalid_file) + self.assertRaises(SyntaxError, MspImagePlugin.MspImageFile, invalid_file) def test_bad_checksum(self): # Arrange @@ -34,8 +32,7 @@ def test_bad_checksum(self): bad_checksum = "Tests/images/hopper_bad_checksum.msp" # Act / Assert - self.assertRaises(SyntaxError, - MspImagePlugin.MspImageFile, bad_checksum) + self.assertRaises(SyntaxError, MspImagePlugin.MspImageFile, bad_checksum) def test_open_windows_v1(self): # Arrange @@ -51,25 +48,26 @@ def _assert_file_image_equal(self, source_path, target_path): target = Image.open(target_path) self.assert_image_equal(im, target) - @unittest.skipIf(not os.path.exists(EXTRA_DIR), - "Extra image files not installed") + @unittest.skipIf(not os.path.exists(EXTRA_DIR), "Extra image files not installed") def test_open_windows_v2(self): - files = (os.path.join(EXTRA_DIR, f) for f in os.listdir(EXTRA_DIR) - if os.path.splitext(f)[1] == '.msp') + files = ( + os.path.join(EXTRA_DIR, f) + for f in os.listdir(EXTRA_DIR) + if os.path.splitext(f)[1] == ".msp" + ) for path in files: - self._assert_file_image_equal(path, - path.replace('.msp', '.png')) + self._assert_file_image_equal(path, path.replace(".msp", ".png")) - @unittest.skipIf(not os.path.exists(YA_EXTRA_DIR), - "Even More Extra image files not installed") + @unittest.skipIf( + not os.path.exists(YA_EXTRA_DIR), "Even More Extra image files not installed" + ) def test_msp_v2(self): for f in os.listdir(YA_EXTRA_DIR): - if '.MSP' not in f: + if ".MSP" not in f: continue path = os.path.join(YA_EXTRA_DIR, f) - self._assert_file_image_equal(path, - path.replace('.MSP', '.png')) + self._assert_file_image_equal(path, path.replace(".MSP", ".png")) def test_cannot_save_wrong_mode(self): # Arrange diff --git a/Tests/test_file_pcd.py b/Tests/test_file_pcd.py index 7296a303fc8..1bd66783ad5 100644 --- a/Tests/test_file_pcd.py +++ b/Tests/test_file_pcd.py @@ -3,9 +3,8 @@ class TestFilePcd(PillowTestCase): - def test_load_raw(self): - im = Image.open('Tests/images/hopper.pcd') + im = Image.open("Tests/images/hopper.pcd") im.load() # should not segfault. # Note that this image was created with a resized hopper diff --git a/Tests/test_file_pcx.py b/Tests/test_file_pcx.py index 7608db47cf5..ae8f0f2ee7c 100644 --- a/Tests/test_file_pcx.py +++ b/Tests/test_file_pcx.py @@ -4,7 +4,6 @@ class TestFilePcx(PillowTestCase): - def _roundtrip(self, im): f = self.tempfile("temp.pcx") im.save(f) @@ -17,7 +16,7 @@ def _roundtrip(self, im): self.assert_image_equal(im2, im) def test_sanity(self): - for mode in ('1', 'L', 'P', 'RGB'): + for mode in ("1", "L", "P", "RGB"): self._roundtrip(hopper(mode)) # Test an unsupported mode @@ -28,14 +27,13 @@ def test_sanity(self): def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - PcxImagePlugin.PcxImageFile, invalid_file) + self.assertRaises(SyntaxError, PcxImagePlugin.PcxImageFile, invalid_file) def test_odd(self): # see issue #523, odd sized images should have a stride that's even. # not that imagemagick or gimp write pcx that way. # we were not handling properly. - for mode in ('1', 'L', 'P', 'RGB'): + for mode in ("1", "L", "P", "RGB"): # larger, odd sized images are better here to ensure that # we handle interrupted scan lines properly. self._roundtrip(hopper(mode).resize((511, 511))) @@ -50,17 +48,17 @@ def test_pil184(self): self.assertEqual(im.tile[0][1], (0, 0, 447, 144)) # Make sure all pixels are either 0 or 255. - self.assertEqual(im.histogram()[0] + im.histogram()[255], 447*144) + self.assertEqual(im.histogram()[0] + im.histogram()[255], 447 * 144) def test_1px_width(self): - im = Image.new('L', (1, 256)) + im = Image.new("L", (1, 256)) px = im.load() for y in range(256): px[0, y] = y self._roundtrip(im) def test_large_count(self): - im = Image.new('L', (256, 1)) + im = Image.new("L", (256, 1)) px = im.load() for x in range(256): px[x, 0] = x // 67 * 67 @@ -75,7 +73,7 @@ def _test_buffer_overflow(self, im, size=1024): ImageFile.MAXBLOCK = _last def test_break_in_count_overflow(self): - im = Image.new('L', (256, 5)) + im = Image.new("L", (256, 5)) px = im.load() for y in range(4): for x in range(256): @@ -83,7 +81,7 @@ def test_break_in_count_overflow(self): self._test_buffer_overflow(im) def test_break_one_in_loop(self): - im = Image.new('L', (256, 5)) + im = Image.new("L", (256, 5)) px = im.load() for y in range(5): for x in range(256): @@ -91,7 +89,7 @@ def test_break_one_in_loop(self): self._test_buffer_overflow(im) def test_break_many_in_loop(self): - im = Image.new('L', (256, 5)) + im = Image.new("L", (256, 5)) px = im.load() for y in range(4): for x in range(256): @@ -101,7 +99,7 @@ def test_break_many_in_loop(self): self._test_buffer_overflow(im) def test_break_one_at_end(self): - im = Image.new('L', (256, 5)) + im = Image.new("L", (256, 5)) px = im.load() for y in range(5): for x in range(256): @@ -110,7 +108,7 @@ def test_break_one_at_end(self): self._test_buffer_overflow(im) def test_break_many_at_end(self): - im = Image.new('L', (256, 5)) + im = Image.new("L", (256, 5)) px = im.load() for y in range(5): for x in range(256): @@ -121,7 +119,7 @@ def test_break_many_at_end(self): self._test_buffer_overflow(im) def test_break_padding(self): - im = Image.new('L', (257, 5)) + im = Image.new("L", (257, 5)) px = im.load() for y in range(5): for x in range(257): diff --git a/Tests/test_file_pdf.py b/Tests/test_file_pdf.py index 983bfffe7a1..b7ee700340e 100644 --- a/Tests/test_file_pdf.py +++ b/Tests/test_file_pdf.py @@ -8,7 +8,6 @@ class TestFilePdf(PillowTestCase): - def helper_save_as_pdf(self, mode, **kwargs): # Arrange im = hopper(mode) @@ -21,15 +20,16 @@ def helper_save_as_pdf(self, mode, **kwargs): self.assertTrue(os.path.isfile(outfile)) self.assertGreater(os.path.getsize(outfile), 0) with PdfParser.PdfParser(outfile) as pdf: - if kwargs.get("append_images", False) or \ - kwargs.get("append", False): + if kwargs.get("append_images", False) or kwargs.get("append", False): self.assertGreater(len(pdf.pages), 1) else: self.assertGreater(len(pdf.pages), 0) - with open(outfile, 'rb') as fp: + with open(outfile, "rb") as fp: contents = fp.read() - size = tuple(int(d) for d in - contents.split(b'/MediaBox [ 0 0 ')[1].split(b']')[0].split()) + size = tuple( + int(d) + for d in contents.split(b"/MediaBox [ 0 0 ")[1].split(b"]")[0].split() + ) self.assertEqual(im.size, size) return outfile @@ -82,7 +82,7 @@ def test_save_all(self): # Multiframe image im = Image.open("Tests/images/dispose_bgnd.gif") - outfile = self.tempfile('temp.pdf') + outfile = self.tempfile("temp.pdf") im.save(outfile, save_all=True) self.assertTrue(os.path.isfile(outfile)) @@ -99,6 +99,7 @@ def test_save_all(self): def imGenerator(ims): for im in ims: yield im + im.save(outfile, save_all=True, append_images=imGenerator(ims)) self.assertTrue(os.path.isfile(outfile)) @@ -115,7 +116,7 @@ def test_multiframe_normal_save(self): # Test saving a multiframe image without save_all im = Image.open("Tests/images/dispose_bgnd.gif") - outfile = self.tempfile('temp.pdf') + outfile = self.tempfile("temp.pdf") im.save(outfile) self.assertTrue(os.path.isfile(outfile)) @@ -124,8 +125,8 @@ def test_multiframe_normal_save(self): def test_pdf_open(self): # fail on a buffer full of null bytes self.assertRaises( - PdfParser.PdfFormatError, - PdfParser.PdfParser, buf=bytearray(65536)) + PdfParser.PdfFormatError, PdfParser.PdfParser, buf=bytearray(65536) + ) # make an empty PDF object with PdfParser.PdfParser() as empty_pdf: @@ -162,10 +163,9 @@ def test_pdf_append_fails_on_nonexistent_file(self): im = hopper("RGB") temp_dir = tempfile.mkdtemp() try: - self.assertRaises(IOError, - im.save, - os.path.join(temp_dir, "nonexistent.pdf"), - append=True) + self.assertRaises( + IOError, im.save, os.path.join(temp_dir, "nonexistent.pdf"), append=True + ) finally: os.rmdir(temp_dir) @@ -194,9 +194,9 @@ def test_pdf_append(self): with PdfParser.PdfParser(pdf_filename, mode="r+b") as pdf: self.assertEqual(len(pdf.pages), 1) self.assertEqual(len(pdf.info), 4) - self.assertEqual(pdf.info.Title, os.path.splitext( - os.path.basename(pdf_filename) - )[0]) + self.assertEqual( + pdf.info.Title, os.path.splitext(os.path.basename(pdf_filename))[0] + ) self.assertEqual(pdf.info.Producer, "PdfParser") self.assertIn(b"CreationDate", pdf.info) self.assertIn(b"ModDate", pdf.info) @@ -223,8 +223,7 @@ def test_pdf_append(self): # append two images mode_CMYK = hopper("CMYK") mode_P = hopper("P") - mode_CMYK.save(pdf_filename, - append=True, save_all=True, append_images=[mode_P]) + mode_CMYK.save(pdf_filename, append=True, save_all=True, append_images=[mode_P]) # open the PDF again, check pages and info again with PdfParser.PdfParser(pdf_filename) as pdf: @@ -242,10 +241,16 @@ def test_pdf_append(self): def test_pdf_info(self): # make a PDF file pdf_filename = self.helper_save_as_pdf( - "RGB", title="title", author="author", subject="subject", - keywords="keywords", creator="creator", producer="producer", + "RGB", + title="title", + author="author", + subject="subject", + keywords="keywords", + creator="creator", + producer="producer", creationDate=time.strptime("2000", "%Y"), - modDate=time.strptime("2001", "%Y")) + modDate=time.strptime("2001", "%Y"), + ) # open it, check pages and info with PdfParser.PdfParser(pdf_filename) as pdf: @@ -256,8 +261,7 @@ def test_pdf_info(self): self.assertEqual(pdf.info.Keywords, "keywords") self.assertEqual(pdf.info.Creator, "creator") self.assertEqual(pdf.info.Producer, "producer") - self.assertEqual(pdf.info.CreationDate, - time.strptime("2000", "%Y")) + self.assertEqual(pdf.info.CreationDate, time.strptime("2000", "%Y")) self.assertEqual(pdf.info.ModDate, time.strptime("2001", "%Y")) self.check_pdf_pages_consistency(pdf) diff --git a/Tests/test_file_pixar.py b/Tests/test_file_pixar.py index 3b998c0b5a8..df5b22d75e5 100644 --- a/Tests/test_file_pixar.py +++ b/Tests/test_file_pixar.py @@ -6,7 +6,6 @@ class TestFilePixar(PillowTestCase): - def test_sanity(self): im = Image.open(TEST_FILE) im.load() @@ -21,6 +20,4 @@ def test_sanity(self): def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises( - SyntaxError, - PixarImagePlugin.PixarImageFile, invalid_file) + self.assertRaises(SyntaxError, PixarImagePlugin.PixarImageFile, invalid_file) diff --git a/Tests/test_file_png.py b/Tests/test_file_png.py index 6a061fe6a5d..4824f122b09 100644 --- a/Tests/test_file_png.py +++ b/Tests/test_file_png.py @@ -8,6 +8,7 @@ try: from PIL import _webp + HAVE_WEBP = True except ImportError: HAVE_WEBP = False @@ -32,7 +33,7 @@ def chunk(cid, *data): o32 = PngImagePlugin.o32 -IHDR = chunk(b"IHDR", o32(1), o32(1), b'\x08\x02', b'\0\0\0') +IHDR = chunk(b"IHDR", o32(1), o32(1), b"\x08\x02", b"\0\0\0") IDAT = chunk(b"IDAT") IEND = chunk(b"IEND") @@ -52,7 +53,6 @@ def roundtrip(im, **options): 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") @@ -86,7 +86,7 @@ def test_sanity(self): self.assertEqual(im.mode, "RGB") self.assertEqual(im.size, (128, 128)) self.assertEqual(im.format, "PNG") - self.assertEqual(im.get_format_mimetype(), 'image/png') + self.assertEqual(im.get_format_mimetype(), "image/png") for mode in ["1", "L", "P", "RGB", "I", "I;16"]: im = hopper(mode) @@ -99,8 +99,7 @@ def test_sanity(self): def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - PngImagePlugin.PngImageFile, invalid_file) + self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, invalid_file) def test_broken(self): # Check reading of totally broken files. In this case, the test @@ -112,76 +111,83 @@ def test_broken(self): def test_bad_text(self): # Make sure PIL can read malformed tEXt chunks (@PIL152) - im = load(HEAD + chunk(b'tEXt') + TAIL) + im = load(HEAD + chunk(b"tEXt") + TAIL) self.assertEqual(im.info, {}) - im = load(HEAD + chunk(b'tEXt', b'spam') + TAIL) - self.assertEqual(im.info, {'spam': ''}) + im = load(HEAD + chunk(b"tEXt", b"spam") + TAIL) + self.assertEqual(im.info, {"spam": ""}) - im = load(HEAD + chunk(b'tEXt', b'spam\0') + TAIL) - self.assertEqual(im.info, {'spam': ''}) + im = load(HEAD + chunk(b"tEXt", b"spam\0") + TAIL) + self.assertEqual(im.info, {"spam": ""}) - im = load(HEAD + chunk(b'tEXt', b'spam\0egg') + TAIL) - self.assertEqual(im.info, {'spam': 'egg'}) + im = load(HEAD + chunk(b"tEXt", b"spam\0egg") + TAIL) + self.assertEqual(im.info, {"spam": "egg"}) - im = load(HEAD + chunk(b'tEXt', b'spam\0egg\0') + TAIL) - self.assertEqual(im.info, {'spam': 'egg\x00'}) + im = load(HEAD + chunk(b"tEXt", b"spam\0egg\0") + TAIL) + self.assertEqual(im.info, {"spam": "egg\x00"}) def test_bad_ztxt(self): # Test reading malformed zTXt chunks (python-pillow/Pillow#318) - im = load(HEAD + chunk(b'zTXt') + TAIL) + im = load(HEAD + chunk(b"zTXt") + TAIL) self.assertEqual(im.info, {}) - im = load(HEAD + chunk(b'zTXt', b'spam') + TAIL) - self.assertEqual(im.info, {'spam': ''}) + im = load(HEAD + chunk(b"zTXt", b"spam") + TAIL) + self.assertEqual(im.info, {"spam": ""}) - im = load(HEAD + chunk(b'zTXt', b'spam\0') + TAIL) - self.assertEqual(im.info, {'spam': ''}) + im = load(HEAD + chunk(b"zTXt", b"spam\0") + TAIL) + self.assertEqual(im.info, {"spam": ""}) - im = load(HEAD + chunk(b'zTXt', b'spam\0\0') + TAIL) - self.assertEqual(im.info, {'spam': ''}) + im = load(HEAD + chunk(b"zTXt", b"spam\0\0") + TAIL) + self.assertEqual(im.info, {"spam": ""}) - im = load(HEAD + chunk( - b'zTXt', b'spam\0\0' + zlib.compress(b'egg')[:1]) + TAIL) - self.assertEqual(im.info, {'spam': ''}) + im = load(HEAD + chunk(b"zTXt", b"spam\0\0" + zlib.compress(b"egg")[:1]) + TAIL) + self.assertEqual(im.info, {"spam": ""}) - im = load( - HEAD + chunk(b'zTXt', b'spam\0\0' + zlib.compress(b'egg')) + TAIL) - self.assertEqual(im.info, {'spam': 'egg'}) + im = load(HEAD + chunk(b"zTXt", b"spam\0\0" + zlib.compress(b"egg")) + TAIL) + self.assertEqual(im.info, {"spam": "egg"}) def test_bad_itxt(self): - im = load(HEAD + chunk(b'iTXt') + TAIL) + im = load(HEAD + chunk(b"iTXt") + TAIL) self.assertEqual(im.info, {}) - im = load(HEAD + chunk(b'iTXt', b'spam') + TAIL) + im = load(HEAD + chunk(b"iTXt", b"spam") + TAIL) self.assertEqual(im.info, {}) - im = load(HEAD + chunk(b'iTXt', b'spam\0') + TAIL) + im = load(HEAD + chunk(b"iTXt", b"spam\0") + TAIL) self.assertEqual(im.info, {}) - im = load(HEAD + chunk(b'iTXt', b'spam\0\x02') + TAIL) + im = load(HEAD + chunk(b"iTXt", b"spam\0\x02") + TAIL) self.assertEqual(im.info, {}) - im = load(HEAD + chunk(b'iTXt', b'spam\0\0\0foo\0') + TAIL) + im = load(HEAD + chunk(b"iTXt", b"spam\0\0\0foo\0") + TAIL) self.assertEqual(im.info, {}) - im = load(HEAD + chunk(b'iTXt', b'spam\0\0\0en\0Spam\0egg') + TAIL) + im = load(HEAD + chunk(b"iTXt", b"spam\0\0\0en\0Spam\0egg") + TAIL) self.assertEqual(im.info, {"spam": "egg"}) self.assertEqual(im.info["spam"].lang, "en") self.assertEqual(im.info["spam"].tkey, "Spam") - im = load(HEAD + chunk(b'iTXt', b'spam\0\1\0en\0Spam\0' + - zlib.compress(b"egg")[:1]) + TAIL) - self.assertEqual(im.info, {'spam': ''}) + im = load( + HEAD + + chunk(b"iTXt", b"spam\0\1\0en\0Spam\0" + zlib.compress(b"egg")[:1]) + + TAIL + ) + self.assertEqual(im.info, {"spam": ""}) - im = load(HEAD + chunk(b'iTXt', b'spam\0\1\1en\0Spam\0' + - zlib.compress(b"egg")) + TAIL) + im = load( + HEAD + + chunk(b"iTXt", b"spam\0\1\1en\0Spam\0" + zlib.compress(b"egg")) + + TAIL + ) self.assertEqual(im.info, {}) - im = load(HEAD + chunk(b'iTXt', b'spam\0\1\0en\0Spam\0' + - zlib.compress(b"egg")) + TAIL) + im = load( + HEAD + + chunk(b"iTXt", b"spam\0\1\0en\0Spam\0" + zlib.compress(b"egg")) + + TAIL + ) self.assertEqual(im.info, {"spam": "egg"}) self.assertEqual(im.info["spam"].lang, "en") self.assertEqual(im.info["spam"].tkey, "Spam") @@ -213,7 +219,7 @@ def test_load_transparent_p(self): self.assert_image(im, "RGBA", (162, 150)) # image has 124 unique alpha values - self.assertEqual(len(im.getchannel('A').getcolors()), 124) + self.assertEqual(len(im.getchannel("A").getcolors()), 124) def test_load_transparent_rgb(self): test_file = "Tests/images/rgb_trns.png" @@ -225,7 +231,7 @@ def test_load_transparent_rgb(self): self.assert_image(im, "RGBA", (64, 64)) # image has 876 transparent pixels - self.assertEqual(im.getchannel('A').getcolors()[0][0], 876) + self.assertEqual(im.getchannel("A").getcolors()[0][0], 876) def test_save_p_transparent_palette(self): in_file = "Tests/images/pil123p.png" @@ -247,7 +253,7 @@ def test_save_p_transparent_palette(self): self.assert_image(im, "RGBA", (162, 150)) # image has 124 unique alpha values - self.assertEqual(len(im.getchannel('A').getcolors()), 124) + self.assertEqual(len(im.getchannel("A").getcolors()), 124) def test_save_p_single_transparency(self): in_file = "Tests/images/p_trns_single.png" @@ -271,7 +277,7 @@ def test_save_p_single_transparency(self): self.assertEqual(im.getpixel((31, 31)), (0, 255, 52, 0)) # image has 876 transparent pixels - self.assertEqual(im.getchannel('A').getcolors()[0][0], 876) + self.assertEqual(im.getchannel("A").getcolors()[0][0], 876) def test_save_p_transparent_black(self): # check if solid black image with full transparency @@ -292,19 +298,14 @@ def test_save_p_transparent_black(self): self.assertEqual(im.getcolors(), [(100, (0, 0, 0, 0))]) def test_save_greyscale_transparency(self): - for mode, num_transparent in { - "1": 1994, - "L": 559, - "I": 559, - }.items(): - in_file = "Tests/images/"+mode.lower()+"_trns.png" + for mode, num_transparent in {"1": 1994, "L": 559, "I": 559}.items(): + in_file = "Tests/images/" + mode.lower() + "_trns.png" im = Image.open(in_file) self.assertEqual(im.mode, mode) self.assertEqual(im.info["transparency"], 255) - im_rgba = im.convert('RGBA') - self.assertEqual( - im_rgba.getchannel("A").getcolors()[0][0], num_transparent) + im_rgba = im.convert("RGBA") + self.assertEqual(im_rgba.getchannel("A").getcolors()[0][0], num_transparent) test_file = self.tempfile("temp.png") im.save(test_file) @@ -314,9 +315,10 @@ def test_save_greyscale_transparency(self): self.assertEqual(test_im.info["transparency"], 255) self.assert_image_equal(im, test_im) - test_im_rgba = test_im.convert('RGBA') + test_im_rgba = test_im.convert("RGBA") self.assertEqual( - test_im_rgba.getchannel('A').getcolors()[0][0], num_transparent) + test_im_rgba.getchannel("A").getcolors()[0][0], num_transparent + ) def test_save_rgb_single_transparency(self): in_file = "Tests/images/caption_6_33_22.png" @@ -345,7 +347,7 @@ def test_verify_struct_error(self): # -14: malformed chunk for offset in (-10, -13, -14): - with open(TEST_PNG_FILE, 'rb') as f: + with open(TEST_PNG_FILE, "rb") as f: test_file = f.read()[:offset] im = Image.open(BytesIO(test_file)) @@ -355,12 +357,11 @@ def test_verify_struct_error(self): def test_verify_ignores_crc_error(self): # check ignores crc errors in ancillary chunks - chunk_data = chunk(b'tEXt', b'spam') - broken_crc_chunk_data = chunk_data[:-1] + b'q' # break CRC + chunk_data = chunk(b"tEXt", b"spam") + broken_crc_chunk_data = chunk_data[:-1] + b"q" # break CRC image_data = HEAD + broken_crc_chunk_data + TAIL - self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, - BytesIO(image_data)) + self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, BytesIO(image_data)) ImageFile.LOAD_TRUNCATED_IMAGES = True try: @@ -372,12 +373,13 @@ def test_verify_ignores_crc_error(self): def test_verify_not_ignores_crc_error_in_required_chunk(self): # check does not ignore crc errors in required chunks - image_data = MAGIC + IHDR[:-1] + b'q' + TAIL + image_data = MAGIC + IHDR[:-1] + b"q" + TAIL ImageFile.LOAD_TRUNCATED_IMAGES = True try: - self.assertRaises(SyntaxError, PngImagePlugin.PngImageFile, - BytesIO(image_data)) + self.assertRaises( + SyntaxError, PngImagePlugin.PngImageFile, BytesIO(image_data) + ) finally: ImageFile.LOAD_TRUNCATED_IMAGES = False @@ -417,8 +419,8 @@ def test_roundtrip_text(self): info.add_text("ZIP", "VALUE", zip=True) im = roundtrip(im, pnginfo=info) - self.assertEqual(im.info, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) - self.assertEqual(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) + self.assertEqual(im.info, {"TXT": "VALUE", "ZIP": "VALUE"}) + self.assertEqual(im.text, {"TXT": "VALUE", "ZIP": "VALUE"}) def test_roundtrip_itxt(self): # Check iTXt roundtripping @@ -426,8 +428,7 @@ def test_roundtrip_itxt(self): im = Image.new("RGB", (32, 32)) info = PngImagePlugin.PngInfo() info.add_itxt("spam", "Eggs", "en", "Spam") - info.add_text("eggs", PngImagePlugin.iTXt("Spam", "en", "Eggs"), - zip=True) + info.add_text("eggs", PngImagePlugin.iTXt("Spam", "en", "Eggs"), zip=True) im = roundtrip(im, pnginfo=info) self.assertEqual(im.info, {"spam": "Eggs", "eggs": "Spam"}) @@ -459,11 +460,11 @@ def rt_text(value): self.assertEqual(im.info, {"Text": value}) if py3: - rt_text(" Aa" + chr(0xa0) + chr(0xc4) + chr(0xff)) # Latin1 - rt_text(chr(0x400) + chr(0x472) + chr(0x4ff)) # Cyrillic - rt_text(chr(0x4e00) + chr(0x66f0) + # CJK - chr(0x9fba) + chr(0x3042) + chr(0xac00)) - rt_text("A" + chr(0xc4) + chr(0x472) + chr(0x3042)) # Combined + rt_text(" Aa" + chr(0xA0) + chr(0xC4) + chr(0xFF)) # Latin1 + rt_text(chr(0x400) + chr(0x472) + chr(0x4FF)) # Cyrillic + # CJK: + rt_text(chr(0x4E00) + chr(0x66F0) + chr(0x9FBA) + chr(0x3042) + chr(0xAC00)) + rt_text("A" + chr(0xC4) + chr(0x472) + chr(0x3042)) # Combined def test_scary(self): # Check reading of evil PNG file. For information, see: @@ -471,8 +472,8 @@ def test_scary(self): # The first byte is removed from pngtest_bad.png # to avoid classification as malware. - with open("Tests/images/pngtest_bad.png.bin", 'rb') as fd: - data = b'\x89' + fd.read() + with open("Tests/images/pngtest_bad.png.bin", "rb") as fd: + data = b"\x89" + fd.read() pngfile = BytesIO(data) self.assertRaises(IOError, Image.open, pngfile) @@ -494,17 +495,16 @@ def test_trns_rgb(self): def test_trns_p(self): # Check writing a transparency of 0, issue #528 - im = hopper('P') - im.info['transparency'] = 0 + im = hopper("P") + im.info["transparency"] = 0 f = self.tempfile("temp.png") im.save(f) im2 = Image.open(f) - self.assertIn('transparency', im2.info) + self.assertIn("transparency", im2.info) - self.assert_image_equal(im2.convert('RGBA'), - im.convert('RGBA')) + self.assert_image_equal(im2.convert("RGBA"), im.convert("RGBA")) def test_trns_null(self): # Check reading images with null tRNS value, issue #1239 @@ -515,39 +515,39 @@ def test_trns_null(self): def test_save_icc_profile(self): im = Image.open("Tests/images/icc_profile_none.png") - self.assertIsNone(im.info['icc_profile']) + self.assertIsNone(im.info["icc_profile"]) with_icc = Image.open("Tests/images/icc_profile.png") - expected_icc = with_icc.info['icc_profile'] + expected_icc = with_icc.info["icc_profile"] im = roundtrip(im, icc_profile=expected_icc) - self.assertEqual(im.info['icc_profile'], expected_icc) + self.assertEqual(im.info["icc_profile"], expected_icc) def test_discard_icc_profile(self): - im = Image.open('Tests/images/icc_profile.png') + im = Image.open("Tests/images/icc_profile.png") im = roundtrip(im, icc_profile=None) - self.assertNotIn('icc_profile', im.info) + self.assertNotIn("icc_profile", im.info) def test_roundtrip_icc_profile(self): - im = Image.open('Tests/images/icc_profile.png') - expected_icc = im.info['icc_profile'] + im = Image.open("Tests/images/icc_profile.png") + expected_icc = im.info["icc_profile"] im = roundtrip(im) - self.assertEqual(im.info['icc_profile'], expected_icc) + self.assertEqual(im.info["icc_profile"], expected_icc) def test_roundtrip_no_icc_profile(self): im = Image.open("Tests/images/icc_profile_none.png") - self.assertIsNone(im.info['icc_profile']) + self.assertIsNone(im.info["icc_profile"]) im = roundtrip(im) - self.assertNotIn('icc_profile', im.info) + self.assertNotIn("icc_profile", im.info) def test_repr_png(self): im = hopper() repr_png = Image.open(BytesIO(im._repr_png_())) - self.assertEqual(repr_png.format, 'PNG') + self.assertEqual(repr_png.format, "PNG") self.assert_image_equal(im, repr_png) def test_chunk_order(self): @@ -579,10 +579,10 @@ def test_getchunks(self): def test_textual_chunks_after_idat(self): im = Image.open("Tests/images/hopper.png") - self.assertIn('comment', im.text.keys()) + self.assertIn("comment", im.text.keys()) for k, v in { - 'date:create': '2014-09-04T09:37:08+03:00', - 'date:modify': '2014-09-04T09:37:08+03:00', + "date:create": "2014-09-04T09:37:08+03:00", + "date:modify": "2014-09-04T09:37:08+03:00", }.items(): self.assertEqual(im.text[k], v) @@ -601,7 +601,7 @@ def test_textual_chunks_after_idat(self): # Raises an EOFError in load_end im = Image.open("Tests/images/hopper_idat_after_image_end.png") - self.assertEqual(im.text, {'TXT': 'VALUE', 'ZIP': 'VALUE'}) + self.assertEqual(im.text, {"TXT": "VALUE", "ZIP": "VALUE"}) def test_exif(self): im = Image.open("Tests/images/exif.png") @@ -637,20 +637,21 @@ def test_exif_argument(self): reloaded = Image.open(test_file) self.assertEqual(reloaded.info["exif"], b"Exif\x00\x00exifstring") - @unittest.skipUnless(HAVE_WEBP and _webp.HAVE_WEBPANIM, - "WebP support not installed with animation") + @unittest.skipUnless( + HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP support not installed with animation" + ) def test_apng(self): im = Image.open("Tests/images/iss634.apng") - self.assertEqual(im.get_format_mimetype(), 'image/apng') + self.assertEqual(im.get_format_mimetype(), "image/apng") # This also tests reading unknown PNG chunks (fcTL and fdAT) in load_end expected = Image.open("Tests/images/iss634.webp") self.assert_image_similar(im, expected, 0.23) -@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") +@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") class TestTruncatedPngPLeaks(PillowLeakTestCase): - mem_limit = 2*1024 # max increase in K + mem_limit = 2 * 1024 # max increase in K iterations = 100 # Leak is 56k/iteration, this will leak 5.6megs def setUp(self): @@ -658,7 +659,7 @@ def setUp(self): self.skipTest("zip/deflate support not available") def test_leak_load(self): - with open('Tests/images/hopper.png', 'rb') as f: + with open("Tests/images/hopper.png", "rb") as f: DATA = BytesIO(f.read(16 * 1024)) ImageFile.LOAD_TRUNCATED_IMAGES = True diff --git a/Tests/test_file_ppm.py b/Tests/test_file_ppm.py index fa024f2cd4f..74406612f95 100644 --- a/Tests/test_file_ppm.py +++ b/Tests/test_file_ppm.py @@ -7,7 +7,6 @@ class TestFilePpm(PillowTestCase): - def test_sanity(self): im = Image.open(test_file) im.load() @@ -17,39 +16,39 @@ def test_sanity(self): self.assertEqual(im.get_format_mimetype(), "image/x-portable-pixmap") def test_16bit_pgm(self): - im = Image.open('Tests/images/16_bit_binary.pgm') + im = Image.open("Tests/images/16_bit_binary.pgm") im.load() - self.assertEqual(im.mode, 'I') + self.assertEqual(im.mode, "I") self.assertEqual(im.size, (20, 100)) self.assertEqual(im.get_format_mimetype(), "image/x-portable-graymap") - tgt = Image.open('Tests/images/16_bit_binary_pgm.png') + tgt = Image.open("Tests/images/16_bit_binary_pgm.png") self.assert_image_equal(im, tgt) def test_16bit_pgm_write(self): - im = Image.open('Tests/images/16_bit_binary.pgm') + im = Image.open("Tests/images/16_bit_binary.pgm") im.load() - f = self.tempfile('temp.pgm') - im.save(f, 'PPM') + f = self.tempfile("temp.pgm") + im.save(f, "PPM") reloaded = Image.open(f) self.assert_image_equal(im, reloaded) def test_pnm(self): - im = Image.open('Tests/images/hopper.pnm') + im = Image.open("Tests/images/hopper.pnm") self.assert_image_similar(im, hopper(), 0.0001) - f = self.tempfile('temp.pnm') + f = self.tempfile("temp.pnm") im.save(f) reloaded = Image.open(f) self.assert_image_equal(im, reloaded) def test_truncated_file(self): - path = self.tempfile('temp.pgm') - with open(path, 'w') as f: - f.write('P6') + path = self.tempfile("temp.pgm") + with open(path, "w") as f: + f.write("P6") self.assertRaises(ValueError, Image.open, path) @@ -60,17 +59,17 @@ def test_neg_ppm(self): # sizes. with self.assertRaises(IOError): - Image.open('Tests/images/negative_size.ppm') + Image.open("Tests/images/negative_size.ppm") def test_mimetypes(self): - path = self.tempfile('temp.pgm') + path = self.tempfile("temp.pgm") - with open(path, 'w') as f: + with open(path, "w") as f: f.write("P4\n128 128\n255") im = Image.open(path) self.assertEqual(im.get_format_mimetype(), "image/x-portable-bitmap") - with open(path, 'w') as f: + with open(path, "w") as f: f.write("PyCMYK\n128 128\n255") im = Image.open(path) self.assertEqual(im.get_format_mimetype(), "image/x-portable-anymap") diff --git a/Tests/test_file_psd.py b/Tests/test_file_psd.py index 3b8a7add689..45fc0ef710b 100644 --- a/Tests/test_file_psd.py +++ b/Tests/test_file_psd.py @@ -6,7 +6,6 @@ class TestImagePsd(PillowTestCase): - def test_sanity(self): im = Image.open(test_file) im.load() @@ -17,11 +16,17 @@ def test_sanity(self): im2 = hopper() self.assert_image_similar(im, im2, 4.8) + def test_unclosed_file(self): + def open(): + im = Image.open(test_file) + im.load() + + self.assert_warning(None, open) + def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - PsdImagePlugin.PsdImageFile, invalid_file) + self.assertRaises(SyntaxError, PsdImagePlugin.PsdImageFile, invalid_file) def test_n_frames(self): im = Image.open("Tests/images/hopper_merged.psd") @@ -35,14 +40,14 @@ def test_n_frames(self): def test_eoferror(self): im = Image.open(test_file) # PSD seek index starts at 1 rather than 0 - n_frames = im.n_frames+1 + n_frames = im.n_frames + 1 # Test seeking past the last frame self.assertRaises(EOFError, im.seek, n_frames) self.assertLess(im.tell(), n_frames) # Test that seeking to the last frame does not raise an error - im.seek(n_frames-1) + im.seek(n_frames - 1) def test_seek_tell(self): im = Image.open(test_file) @@ -65,6 +70,12 @@ def test_seek_eoferror(self): self.assertRaises(EOFError, im.seek, -1) + def test_open_after_exclusive_load(self): + im = Image.open(test_file) + im.load() + im.seek(im.tell() + 1) + im.load() + def test_icc_profile(self): im = Image.open(test_file) self.assertIn("icc_profile", im.info) diff --git a/Tests/test_file_sgi.py b/Tests/test_file_sgi.py index 1ad70e24fd8..bf0af50661d 100644 --- a/Tests/test_file_sgi.py +++ b/Tests/test_file_sgi.py @@ -4,7 +4,6 @@ class TestFileSgi(PillowTestCase): - def test_rgb(self): # Created with ImageMagick then renamed: # convert hopper.ppm -compress None sgi:hopper.rgb @@ -12,7 +11,7 @@ def test_rgb(self): im = Image.open(test_file) self.assert_image_equal(im, hopper()) - self.assertEqual(im.get_format_mimetype(), 'image/rgb') + self.assertEqual(im.get_format_mimetype(), "image/rgb") def test_rgb16(self): test_file = "Tests/images/hopper16.rgb" @@ -26,8 +25,8 @@ def test_l(self): test_file = "Tests/images/hopper.bw" im = Image.open(test_file) - self.assert_image_similar(im, hopper('L'), 2) - self.assertEqual(im.get_format_mimetype(), 'image/sgi') + self.assert_image_similar(im, hopper("L"), 2) + self.assertEqual(im.get_format_mimetype(), "image/sgi") def test_rgba(self): # Created with ImageMagick: @@ -35,9 +34,9 @@ def test_rgba(self): test_file = "Tests/images/transparent.sgi" im = Image.open(test_file) - target = Image.open('Tests/images/transparent.png') + target = Image.open("Tests/images/transparent.png") self.assert_image_equal(im, target) - self.assertEqual(im.get_format_mimetype(), 'image/sgi') + self.assertEqual(im.get_format_mimetype(), "image/sgi") def test_rle(self): # Created with ImageMagick: @@ -45,47 +44,46 @@ def test_rle(self): test_file = "Tests/images/hopper.sgi" im = Image.open(test_file) - target = Image.open('Tests/images/hopper.rgb') + target = Image.open("Tests/images/hopper.rgb") self.assert_image_equal(im, target) def test_rle16(self): test_file = "Tests/images/tv16.sgi" im = Image.open(test_file) - target = Image.open('Tests/images/tv.rgb') + target = Image.open("Tests/images/tv.rgb") self.assert_image_equal(im, target) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(ValueError, - SgiImagePlugin.SgiImageFile, invalid_file) + self.assertRaises(ValueError, SgiImagePlugin.SgiImageFile, invalid_file) def test_write(self): def roundtrip(img): - out = self.tempfile('temp.sgi') - img.save(out, format='sgi') + out = self.tempfile("temp.sgi") + img.save(out, format="sgi") reloaded = Image.open(out) self.assert_image_equal(img, reloaded) - for mode in ('L', 'RGB', 'RGBA'): + for mode in ("L", "RGB", "RGBA"): roundtrip(hopper(mode)) # Test 1 dimension for an L mode image - roundtrip(Image.new('L', (10, 1))) + roundtrip(Image.new("L", (10, 1))) def test_write16(self): test_file = "Tests/images/hopper16.rgb" im = Image.open(test_file) - out = self.tempfile('temp.sgi') - im.save(out, format='sgi', bpc=2) + out = self.tempfile("temp.sgi") + im.save(out, format="sgi", bpc=2) reloaded = Image.open(out) self.assert_image_equal(im, reloaded) def test_unsupported_mode(self): - im = hopper('LA') - out = self.tempfile('temp.sgi') + im = hopper("LA") + out = self.tempfile("temp.sgi") - self.assertRaises(ValueError, im.save, out, format='sgi') + self.assertRaises(ValueError, im.save, out, format="sgi") diff --git a/Tests/test_file_spider.py b/Tests/test_file_spider.py index f160272fda1..7dec1505816 100644 --- a/Tests/test_file_spider.py +++ b/Tests/test_file_spider.py @@ -10,7 +10,6 @@ class TestImageSpider(PillowTestCase): - def test_sanity(self): im = Image.open(TEST_FILE) im.load() @@ -22,11 +21,12 @@ def test_unclosed_file(self): def open(): im = Image.open(TEST_FILE) im.load() + self.assert_warning(None, open) def test_save(self): # Arrange - temp = self.tempfile('temp.spider') + temp = self.tempfile("temp.spider") im = hopper() # Act diff --git a/Tests/test_file_sun.py b/Tests/test_file_sun.py index 65c00eea278..7270665c354 100644 --- a/Tests/test_file_sun.py +++ b/Tests/test_file_sun.py @@ -4,11 +4,10 @@ import os -EXTRA_DIR = 'Tests/images/sunraster' +EXTRA_DIR = "Tests/images/sunraster" class TestFileSun(PillowTestCase): - def test_sanity(self): # Arrange # Created with ImageMagick: convert hopper.jpg hopper.ras @@ -23,20 +22,20 @@ def test_sanity(self): self.assert_image_similar(im, hopper(), 5) # visually verified invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - SunImagePlugin.SunImageFile, invalid_file) + self.assertRaises(SyntaxError, SunImagePlugin.SunImageFile, invalid_file) def test_im1(self): - im = Image.open('Tests/images/sunraster.im1') - target = Image.open('Tests/images/sunraster.im1.png') + im = Image.open("Tests/images/sunraster.im1") + target = Image.open("Tests/images/sunraster.im1.png") self.assert_image_equal(im, target) - @unittest.skipIf(not os.path.exists(EXTRA_DIR), - "Extra image files not installed") + @unittest.skipIf(not os.path.exists(EXTRA_DIR), "Extra image files not installed") def test_others(self): - files = (os.path.join(EXTRA_DIR, f) for f in - os.listdir(EXTRA_DIR) if os.path.splitext(f)[1] - in ('.sun', '.SUN', '.ras')) + files = ( + os.path.join(EXTRA_DIR, f) + for f in os.listdir(EXTRA_DIR) + if os.path.splitext(f)[1] in (".sun", ".SUN", ".ras") + ) for path in files: with Image.open(path) as im: im.load() diff --git a/Tests/test_file_tar.py b/Tests/test_file_tar.py index cd2b95778a9..cd123e1128b 100644 --- a/Tests/test_file_tar.py +++ b/Tests/test_file_tar.py @@ -9,15 +9,14 @@ class TestFileTar(PillowTestCase): - def setUp(self): if "zip_decoder" not in codecs and "jpeg_decoder" not in codecs: self.skipTest("neither jpeg nor zip support available") def test_sanity(self): for codec, test_path, format in [ - ['zip_decoder', 'hopper.png', 'PNG'], - ['jpeg_decoder', 'hopper.jpg', 'JPEG'] + ["zip_decoder", "hopper.png", "PNG"], + ["jpeg_decoder", "hopper.jpg", "JPEG"], ]: if codec in codecs: tar = TarIO.TarIO(TEST_TAR_FILE, test_path) @@ -28,9 +27,9 @@ def test_sanity(self): self.assertEqual(im.format, format) def test_close(self): - tar = TarIO.TarIO(TEST_TAR_FILE, 'hopper.jpg') + tar = TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg") tar.close() def test_contextmanager(self): - with TarIO.TarIO(TEST_TAR_FILE, 'hopper.jpg'): + with TarIO.TarIO(TEST_TAR_FILE, "hopper.jpg"): pass diff --git a/Tests/test_file_tga.py b/Tests/test_file_tga.py index a8ab00d7f7f..6bc37f258f8 100644 --- a/Tests/test_file_tga.py +++ b/Tests/test_file_tga.py @@ -16,16 +16,13 @@ class TestFileTga(PillowTestCase): _MODES = ("L", "LA", "P", "RGB", "RGBA") _ORIGINS = ("tl", "bl") - _ORIGIN_TO_ORIENTATION = { - "tl": 1, - "bl": -1 - } + _ORIGIN_TO_ORIENTATION = {"tl": 1, "bl": -1} def test_sanity(self): for mode in self._MODES: png_paths = glob( - os.path.join( - _TGA_DIR_COMMON, "*x*_{}.png".format(mode.lower()))) + os.path.join(_TGA_DIR_COMMON, "*x*_{}.png".format(mode.lower())) + ) for png_path in png_paths: reference_im = Image.open(png_path) @@ -34,21 +31,22 @@ def test_sanity(self): path_no_ext = os.path.splitext(png_path)[0] for origin, rle in product(self._ORIGINS, (True, False)): tga_path = "{}_{}_{}.tga".format( - path_no_ext, origin, "rle" if rle else "raw") + path_no_ext, origin, "rle" if rle else "raw" + ) original_im = Image.open(tga_path) self.assertEqual(original_im.format, "TGA") self.assertEqual(original_im.get_format_mimetype(), "image/x-tga") if rle: - self.assertEqual( - original_im.info["compression"], "tga_rle") + self.assertEqual(original_im.info["compression"], "tga_rle") self.assertEqual( original_im.info["orientation"], - self._ORIGIN_TO_ORIENTATION[origin]) + self._ORIGIN_TO_ORIENTATION[origin], + ) if mode == "P": self.assertEqual( - original_im.getpalette(), - reference_im.getpalette()) + original_im.getpalette(), reference_im.getpalette() + ) self.assert_image_equal(original_im, reference_im) @@ -62,14 +60,15 @@ def test_sanity(self): if rle: self.assertEqual( saved_im.info["compression"], - original_im.info["compression"]) + original_im.info["compression"], + ) self.assertEqual( - saved_im.info["orientation"], - original_im.info["orientation"]) + saved_im.info["orientation"], original_im.info["orientation"] + ) if mode == "P": self.assertEqual( - saved_im.getpalette(), - original_im.getpalette()) + saved_im.getpalette(), original_im.getpalette() + ) self.assert_image_equal(saved_im, original_im) @@ -128,8 +127,7 @@ def test_save_id_section(self): # Save with custom id section greater than 255 characters id_section = b"Test content" * 25 - self.assert_warning(UserWarning, - lambda: im.save(out, id_section=id_section)) + self.assert_warning(UserWarning, lambda: im.save(out, id_section=id_section)) test_im = Image.open(out) self.assertEqual(test_im.info["id_section"], id_section[:255]) @@ -191,15 +189,13 @@ def test_save_l_transparency(self): in_file = "Tests/images/la.tga" im = Image.open(in_file) self.assertEqual(im.mode, "LA") - self.assertEqual( - im.getchannel("A").getcolors()[0][0], num_transparent) + self.assertEqual(im.getchannel("A").getcolors()[0][0], num_transparent) out = self.tempfile("temp.tga") im.save(out) test_im = Image.open(out) self.assertEqual(test_im.mode, "LA") - self.assertEqual( - test_im.getchannel("A").getcolors()[0][0], num_transparent) + self.assertEqual(test_im.getchannel("A").getcolors()[0][0], num_transparent) self.assert_image_equal(im, test_im) diff --git a/Tests/test_file_tiff.py b/Tests/test_file_tiff.py index 64d03361b69..945372f8667 100644 --- a/Tests/test_file_tiff.py +++ b/Tests/test_file_tiff.py @@ -12,7 +12,6 @@ class TestFileTiff(PillowTestCase): - def test_sanity(self): filename = self.tempfile("temp.tif") @@ -44,6 +43,7 @@ def test_unclosed_file(self): def open(): im = Image.open("Tests/images/multipage.tiff") im.load() + self.assert_warning(None, open) def test_mac_tiff(self): @@ -54,7 +54,7 @@ def test_mac_tiff(self): self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (55, 43)) - self.assertEqual(im.tile, [('raw', (0, 0, 55, 43), 8, ('RGBa', 0, 1))]) + self.assertEqual(im.tile, [("raw", (0, 0, 55, 43), 8, ("RGBa", 0, 1))]) im.load() self.assert_image_similar_tofile(im, "Tests/images/pil136.png", 1) @@ -64,16 +64,14 @@ def test_wrong_bits_per_sample(self): self.assertEqual(im.mode, "RGBA") self.assertEqual(im.size, (52, 53)) - self.assertEqual(im.tile, - [('raw', (0, 0, 52, 53), 160, ('RGBA', 0, 1))]) + self.assertEqual(im.tile, [("raw", (0, 0, 52, 53), 160, ("RGBA", 0, 1))]) im.load() def test_set_legacy_api(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() with self.assertRaises(Exception) as e: ifd.legacy_api = None - self.assertEqual(str(e.exception), - "Not allowing setting of legacy api") + self.assertEqual(str(e.exception), "Not allowing setting of legacy api") def test_size(self): filename = "Tests/images/pil168.tif" @@ -81,6 +79,7 @@ def test_size(self): def set_size(): im.size = (256, 256) + self.assert_warning(DeprecationWarning, set_size) def test_xyres_tiff(self): @@ -92,29 +91,24 @@ def test_xyres_tiff(self): self.assertIsInstance(im.tag[Y_RESOLUTION][0], tuple) # v2 api - self.assertIsInstance(im.tag_v2[X_RESOLUTION], - TiffImagePlugin.IFDRational) - self.assertIsInstance(im.tag_v2[Y_RESOLUTION], - TiffImagePlugin.IFDRational) + self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational) + self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational) - self.assertEqual(im.info['dpi'], (72., 72.)) + self.assertEqual(im.info["dpi"], (72.0, 72.0)) def test_xyres_fallback_tiff(self): filename = "Tests/images/compression.tif" im = Image.open(filename) # v2 api - self.assertIsInstance(im.tag_v2[X_RESOLUTION], - TiffImagePlugin.IFDRational) - self.assertIsInstance(im.tag_v2[Y_RESOLUTION], - TiffImagePlugin.IFDRational) - self.assertRaises(KeyError, - lambda: im.tag_v2[RESOLUTION_UNIT]) + self.assertIsInstance(im.tag_v2[X_RESOLUTION], TiffImagePlugin.IFDRational) + self.assertIsInstance(im.tag_v2[Y_RESOLUTION], TiffImagePlugin.IFDRational) + self.assertRaises(KeyError, lambda: im.tag_v2[RESOLUTION_UNIT]) # Legacy. - self.assertEqual(im.info['resolution'], (100., 100.)) + self.assertEqual(im.info["resolution"], (100.0, 100.0)) # Fallback "inch". - self.assertEqual(im.info['dpi'], (100., 100.)) + self.assertEqual(im.info["dpi"], (100.0, 100.0)) def test_int_resolution(self): filename = "Tests/images/pil168.tif" @@ -124,20 +118,21 @@ def test_int_resolution(self): im.tag_v2[X_RESOLUTION] = 71 im.tag_v2[Y_RESOLUTION] = 71 im._setup() - self.assertEqual(im.info['dpi'], (71., 71.)) + self.assertEqual(im.info["dpi"], (71.0, 71.0)) def test_load_dpi_rounding(self): - for resolutionUnit, dpi in ((None, (72, 73)), - (2, (72, 73)), - (3, (183, 185))): + for resolutionUnit, dpi in ((None, (72, 73)), (2, (72, 73)), (3, (183, 185))): im = Image.open( - "Tests/images/hopper_roundDown_"+str(resolutionUnit)+".tif") + "Tests/images/hopper_roundDown_" + str(resolutionUnit) + ".tif" + ) self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit) - self.assertEqual(im.info['dpi'], (dpi[0], dpi[0])) + self.assertEqual(im.info["dpi"], (dpi[0], dpi[0])) - im = Image.open("Tests/images/hopper_roundUp_"+str(resolutionUnit)+".tif") + im = Image.open( + "Tests/images/hopper_roundUp_" + str(resolutionUnit) + ".tif" + ) self.assertEqual(im.tag_v2.get(RESOLUTION_UNIT), resolutionUnit) - self.assertEqual(im.info['dpi'], (dpi[1], dpi[1])) + self.assertEqual(im.info["dpi"], (dpi[1], dpi[1])) def test_save_dpi_rounding(self): outfile = self.tempfile("temp.tif") @@ -148,12 +143,13 @@ def test_save_dpi_rounding(self): reloaded = Image.open(outfile) reloaded.load() - self.assertEqual((round(dpi), round(dpi)), reloaded.info['dpi']) + self.assertEqual((round(dpi), round(dpi)), reloaded.info["dpi"]) def test_save_setting_missing_resolution(self): b = BytesIO() Image.open("Tests/images/10ct_32bit_128.tiff").save( - b, format="tiff", resolution=123.45) + b, format="tiff", resolution=123.45 + ) im = Image.open(b) self.assertEqual(float(im.tag_v2[X_RESOLUTION]), 123.45) self.assertEqual(float(im.tag_v2[Y_RESOLUTION]), 123.45) @@ -161,16 +157,14 @@ def test_save_setting_missing_resolution(self): def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - TiffImagePlugin.TiffImageFile, invalid_file) + self.assertRaises(SyntaxError, TiffImagePlugin.TiffImageFile, invalid_file) TiffImagePlugin.PREFIXES.append(b"\xff\xd8\xff\xe0") - self.assertRaises(SyntaxError, - TiffImagePlugin.TiffImageFile, invalid_file) + self.assertRaises(SyntaxError, TiffImagePlugin.TiffImageFile, invalid_file) TiffImagePlugin.PREFIXES.pop() def test_bad_exif(self): - i = Image.open('Tests/images/hopper_bad_exif.jpg') + i = Image.open("Tests/images/hopper_bad_exif.jpg") # Should not raise struct.error. self.assert_warning(UserWarning, i._getexif) @@ -185,38 +179,38 @@ def test_save_unsupported_mode(self): self.assertRaises(IOError, im.save, outfile) def test_little_endian(self): - im = Image.open('Tests/images/16bit.cropped.tif') + im = Image.open("Tests/images/16bit.cropped.tif") self.assertEqual(im.getpixel((0, 0)), 480) - self.assertEqual(im.mode, 'I;16') + self.assertEqual(im.mode, "I;16") b = im.tobytes() # Bytes are in image native order (little endian) if py3: - self.assertEqual(b[0], ord(b'\xe0')) - self.assertEqual(b[1], ord(b'\x01')) + self.assertEqual(b[0], ord(b"\xe0")) + self.assertEqual(b[1], ord(b"\x01")) else: - self.assertEqual(b[0], b'\xe0') - self.assertEqual(b[1], b'\x01') + self.assertEqual(b[0], b"\xe0") + self.assertEqual(b[1], b"\x01") def test_big_endian(self): - im = Image.open('Tests/images/16bit.MM.cropped.tif') + im = Image.open("Tests/images/16bit.MM.cropped.tif") self.assertEqual(im.getpixel((0, 0)), 480) - self.assertEqual(im.mode, 'I;16B') + self.assertEqual(im.mode, "I;16B") b = im.tobytes() # Bytes are in image native order (big endian) if py3: - self.assertEqual(b[0], ord(b'\x01')) - self.assertEqual(b[1], ord(b'\xe0')) + self.assertEqual(b[0], ord(b"\x01")) + self.assertEqual(b[1], ord(b"\xe0")) else: - self.assertEqual(b[0], b'\x01') - self.assertEqual(b[1], b'\xe0') + self.assertEqual(b[0], b"\x01") + self.assertEqual(b[1], b"\xe0") def test_16bit_s(self): - im = Image.open('Tests/images/16bit.s.tif') + im = Image.open("Tests/images/16bit.s.tif") im.load() - self.assertEqual(im.mode, 'I') + self.assertEqual(im.mode, "I") self.assertEqual(im.getpixel((0, 0)), 32767) self.assertEqual(im.getpixel((0, 1)), 0) @@ -224,7 +218,7 @@ def test_12bit_rawmode(self): """ Are we generating the same interpretation of the image as Imagemagick is? """ - im = Image.open('Tests/images/12bit.cropped.tif') + im = Image.open("Tests/images/12bit.cropped.tif") # to make the target -- # convert 12bit.cropped.tif -depth 16 tmp.tif @@ -232,33 +226,33 @@ def test_12bit_rawmode(self): # imagemagick will auto scale so that a 12bit FFF is 16bit FFF0, # so we need to unshift so that the integer values are the same. - self.assert_image_equal_tofile(im, 'Tests/images/12in16bit.tif') + self.assert_image_equal_tofile(im, "Tests/images/12in16bit.tif") def test_32bit_float(self): # Issue 614, specific 32-bit float format - path = 'Tests/images/10ct_32bit_128.tiff' + path = "Tests/images/10ct_32bit_128.tiff" im = Image.open(path) im.load() self.assertEqual(im.getpixel((0, 0)), -0.4526388943195343) - self.assertEqual( - im.getextrema(), (-3.140936851501465, 3.140684127807617)) + self.assertEqual(im.getextrema(), (-3.140936851501465, 3.140684127807617)) def test_unknown_pixel_mode(self): self.assertRaises( - IOError, Image.open, 'Tests/images/hopper_unknown_pixel_mode.tif') + IOError, Image.open, "Tests/images/hopper_unknown_pixel_mode.tif" + ) def test_n_frames(self): for path, n_frames in [ - ['Tests/images/multipage-lastframe.tif', 1], - ['Tests/images/multipage.tiff', 3] + ["Tests/images/multipage-lastframe.tif", 1], + ["Tests/images/multipage.tiff", 3], ]: im = Image.open(path) self.assertEqual(im.n_frames, n_frames) self.assertEqual(im.is_animated, n_frames != 1) def test_eoferror(self): - im = Image.open('Tests/images/multipage-lastframe.tif') + im = Image.open("Tests/images/multipage-lastframe.tif") n_frames = im.n_frames # Test seeking past the last frame @@ -266,37 +260,37 @@ def test_eoferror(self): self.assertLess(im.tell(), n_frames) # Test that seeking to the last frame does not raise an error - im.seek(n_frames-1) + im.seek(n_frames - 1) def test_multipage(self): # issue #862 - im = Image.open('Tests/images/multipage.tiff') + im = Image.open("Tests/images/multipage.tiff") # file is a multipage tiff: 10x10 green, 10x10 red, 20x20 blue im.seek(0) self.assertEqual(im.size, (10, 10)) - self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 128, 0)) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0)) im.seek(1) im.load() self.assertEqual(im.size, (10, 10)) - self.assertEqual(im.convert('RGB').getpixel((0, 0)), (255, 0, 0)) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (255, 0, 0)) im.seek(0) im.load() self.assertEqual(im.size, (10, 10)) - self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 128, 0)) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 128, 0)) im.seek(2) im.load() self.assertEqual(im.size, (20, 20)) - self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 0, 255)) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255)) def test_multipage_last_frame(self): - im = Image.open('Tests/images/multipage-lastframe.tif') + im = Image.open("Tests/images/multipage-lastframe.tif") im.load() self.assertEqual(im.size, (20, 20)) - self.assertEqual(im.convert('RGB').getpixel((0, 0)), (0, 0, 255)) + self.assertEqual(im.convert("RGB").getpixel((0, 0)), (0, 0, 255)) def test___str__(self): filename = "Tests/images/pil136.tiff" @@ -314,16 +308,39 @@ def test_dict(self): im = Image.open(filename) # v2 interface - v2_tags = {256: 55, 257: 43, 258: (8, 8, 8, 8), 259: 1, - 262: 2, 296: 2, 273: (8,), 338: (1,), 277: 4, - 279: (9460,), 282: 72.0, 283: 72.0, 284: 1} + v2_tags = { + 256: 55, + 257: 43, + 258: (8, 8, 8, 8), + 259: 1, + 262: 2, + 296: 2, + 273: (8,), + 338: (1,), + 277: 4, + 279: (9460,), + 282: 72.0, + 283: 72.0, + 284: 1, + } self.assertEqual(dict(im.tag_v2), v2_tags) # legacy interface - legacy_tags = {256: (55,), 257: (43,), 258: (8, 8, 8, 8), 259: (1,), - 262: (2,), 296: (2,), 273: (8,), 338: (1,), 277: (4,), - 279: (9460,), 282: ((720000, 10000),), - 283: ((720000, 10000),), 284: (1,)} + legacy_tags = { + 256: (55,), + 257: (43,), + 258: (8, 8, 8, 8), + 259: (1,), + 262: (2,), + 296: (2,), + 273: (8,), + 338: (1,), + 277: (4,), + 279: (9460,), + 282: ((720000, 10000),), + 283: ((720000, 10000),), + 284: (1,), + } self.assertEqual(dict(im.tag), legacy_tags) def test__delitem__(self): @@ -351,13 +368,13 @@ def test_load_float(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abcdabcd" ret = ifd.load_float(data, False) - self.assertEqual(ret, (1.6777999408082104e+22, 1.6777999408082104e+22)) + self.assertEqual(ret, (1.6777999408082104e22, 1.6777999408082104e22)) def test_load_double(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() data = b"abcdefghabcdefgh" ret = ifd.load_double(data, False) - self.assertEqual(ret, (8.540883223036124e+194, 8.540883223036124e+194)) + self.assertEqual(ret, (8.540883223036124e194, 8.540883223036124e194)) def test_seek(self): filename = "Tests/images/pil136.tiff" @@ -374,12 +391,14 @@ def test_seek_eof(self): def test__limit_rational_int(self): from PIL.TiffImagePlugin import _limit_rational + value = 34 ret = _limit_rational(value, 65536) self.assertEqual(ret, (34, 1)) def test__limit_rational_float(self): from PIL.TiffImagePlugin import _limit_rational + value = 22.3 ret = _limit_rational(value, 65536) self.assertEqual(ret, (223, 10)) @@ -401,7 +420,7 @@ def test_gray_semibyte_per_pixel(self): "Tests/images/tiff_gray_2_4_bpp/hopper2I.tif", "Tests/images/tiff_gray_2_4_bpp/hopper2R.tif", "Tests/images/tiff_gray_2_4_bpp/hopper2IR.tif", - ) + ), ), ( 7.3, # epsilon @@ -410,7 +429,7 @@ def test_gray_semibyte_per_pixel(self): "Tests/images/tiff_gray_2_4_bpp/hopper4I.tif", "Tests/images/tiff_gray_2_4_bpp/hopper4R.tif", "Tests/images/tiff_gray_2_4_bpp/hopper4IR.tif", - ) + ), ), ) original = hopper("L") @@ -426,9 +445,7 @@ def test_gray_semibyte_per_pixel(self): self.assert_image_equal(im, im2) def test_with_underscores(self): - 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") hopper("RGB").save(filename, **kwargs) im = Image.open(filename) @@ -459,8 +476,7 @@ def test_strip_raw(self): infile = "Tests/images/tiff_strip_raw.tif" im = Image.open(infile) - 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_strip_planar_raw(self): # gdal_translate -of GTiff -co INTERLEAVE=BAND \ @@ -468,16 +484,14 @@ def test_strip_planar_raw(self): infile = "Tests/images/tiff_strip_planar_raw.tif" im = Image.open(infile) - 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_strip_planar_raw_with_overviews(self): # gdaladdo tiff_strip_planar_raw2.tif 2 4 8 16 infile = "Tests/images/tiff_strip_planar_raw_with_overviews.tif" im = Image.open(infile) - self.assert_image_equal_tofile(im, - "Tests/images/tiff_adobe_deflate.png") + self.assert_image_equal_tofile(im, "Tests/images/tiff_adobe_deflate.png") def test_tiled_planar_raw(self): # gdal_translate -of GTiff -co TILED=YES -co BLOCKXSIZE=32 \ @@ -486,8 +500,7 @@ def test_tiled_planar_raw(self): infile = "Tests/images/tiff_tiled_planar_raw.tif" im = Image.open(infile) - 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_palette(self): for mode in ["P", "PA"]: @@ -513,9 +526,8 @@ def test_tiff_save_all(self): # Test appending images mp = io.BytesIO() - im = Image.new('RGB', (100, 100), '#f00') - ims = [Image.new('RGB', (100, 100), color) for color - in ['#0f0', '#00f']] + im = Image.new("RGB", (100, 100), "#f00") + ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]] im.copy().save(mp, format="TIFF", save_all=True, append_images=ims) mp.seek(0, os.SEEK_SET) @@ -526,9 +538,9 @@ def test_tiff_save_all(self): def imGenerator(ims): for im in ims: yield im + mp = io.BytesIO() - im.save(mp, format="TIFF", save_all=True, - append_images=imGenerator(ims)) + im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims)) mp.seek(0, os.SEEK_SET) reread = Image.open(mp) @@ -539,15 +551,15 @@ def test_saving_icc_profile(self): # At the time of writing this will only work for non-compressed tiffs # as libtiff does not support embedded ICC profiles, # ImageFile._save(..) however does. - im = Image.new('RGB', (1, 1)) - im.info['icc_profile'] = 'Dummy value' + im = Image.new("RGB", (1, 1)) + im.info["icc_profile"] = "Dummy value" # Try save-load round trip to make sure both handle icc_profile. - tmpfile = self.tempfile('temp.tif') - im.save(tmpfile, 'TIFF', compression='raw') + tmpfile = self.tempfile("temp.tif") + im.save(tmpfile, "TIFF", compression="raw") reloaded = Image.open(tmpfile) - 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): # similar to test_fd_leak, but runs on unixlike os @@ -568,7 +580,7 @@ def test_close_on_load_nonexclusive(self): with Image.open("Tests/images/uint16_1_4660.tif") as im: im.save(tmpfile) - with open(tmpfile, 'rb') as f: + with open(tmpfile, "rb") as f: im = Image.open(f) fp = im.fp self.assertFalse(fp.closed) @@ -576,7 +588,7 @@ def test_close_on_load_nonexclusive(self): self.assertFalse(fp.closed) -@unittest.skipUnless(sys.platform.startswith('win32'), "Windows only") +@unittest.skipUnless(sys.platform.startswith("win32"), "Windows only") class TestFileTiffW32(PillowTestCase): def test_fd_leak(self): tmpfile = self.tempfile("temp.tif") diff --git a/Tests/test_file_tiff_metadata.py b/Tests/test_file_tiff_metadata.py index 14ec3ab6c82..f9b197a569d 100644 --- a/Tests/test_file_tiff_metadata.py +++ b/Tests/test_file_tiff_metadata.py @@ -10,7 +10,6 @@ class TestFileTiffMetadata(PillowTestCase): - def test_rt_metadata(self): """ Test writing arbitrary metadata into the tiff image directory Use case is ImageJ private tags, one numeric, one arbitrary @@ -29,23 +28,23 @@ def test_rt_metadata(self): # the tiff file format can't take 8 bit bytes in that field. basetextdata = "This is some arbitrary metadata for a text field" - bindata = basetextdata.encode('ascii') + b" \xff" + bindata = basetextdata.encode("ascii") + b" \xff" textdata = basetextdata + " " + chr(255) reloaded_textdata = basetextdata + " ?" floatdata = 12.345 doubledata = 67.89 info = TiffImagePlugin.ImageFileDirectory() - ImageJMetaData = tag_ids['ImageJMetaData'] - ImageJMetaDataByteCounts = tag_ids['ImageJMetaDataByteCounts'] - ImageDescription = tag_ids['ImageDescription'] + ImageJMetaData = tag_ids["ImageJMetaData"] + ImageJMetaDataByteCounts = tag_ids["ImageJMetaDataByteCounts"] + ImageDescription = tag_ids["ImageDescription"] info[ImageJMetaDataByteCounts] = len(bindata) info[ImageJMetaData] = bindata - info[tag_ids['RollAngle']] = floatdata - info.tagtype[tag_ids['RollAngle']] = 11 - info[tag_ids['YawAngle']] = doubledata - info.tagtype[tag_ids['YawAngle']] = 12 + info[tag_ids["RollAngle"]] = floatdata + info.tagtype[tag_ids["RollAngle"]] = 11 + info[tag_ids["YawAngle"]] = doubledata + info.tagtype[tag_ids["YawAngle"]] = 12 info[ImageDescription] = textdata @@ -56,8 +55,7 @@ def test_rt_metadata(self): loaded = Image.open(f) self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (len(bindata),)) - self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], - (len(bindata),)) + self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (len(bindata),)) self.assertEqual(loaded.tag[ImageJMetaData], bindata) self.assertEqual(loaded.tag_v2[ImageJMetaData], bindata) @@ -65,9 +63,9 @@ def test_rt_metadata(self): self.assertEqual(loaded.tag[ImageDescription], (reloaded_textdata,)) self.assertEqual(loaded.tag_v2[ImageDescription], reloaded_textdata) - loaded_float = loaded.tag[tag_ids['RollAngle']][0] + loaded_float = loaded.tag[tag_ids["RollAngle"]][0] self.assertAlmostEqual(loaded_float, floatdata, places=5) - loaded_double = loaded.tag[tag_ids['YawAngle']][0] + loaded_double = loaded.tag[tag_ids["YawAngle"]][0] self.assertAlmostEqual(loaded_double, doubledata) # check with 2 element ImageJMetaDataByteCounts, issue #2006 @@ -76,55 +74,61 @@ def test_rt_metadata(self): img.save(f, tiffinfo=info) loaded = Image.open(f) - self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], - (8, len(bindata) - 8)) - self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], - (8, len(bindata) - 8)) + self.assertEqual(loaded.tag[ImageJMetaDataByteCounts], (8, len(bindata) - 8)) + self.assertEqual(loaded.tag_v2[ImageJMetaDataByteCounts], (8, len(bindata) - 8)) def test_read_metadata(self): - img = Image.open('Tests/images/hopper_g4.tif') - - self.assertEqual({'YResolution': IFDRational(4294967295, 113653537), - 'PlanarConfiguration': 1, - 'BitsPerSample': (1,), - 'ImageLength': 128, - 'Compression': 4, - 'FillOrder': 1, - 'RowsPerStrip': 128, - 'ResolutionUnit': 3, - 'PhotometricInterpretation': 0, - 'PageNumber': (0, 1), - 'XResolution': IFDRational(4294967295, 113653537), - 'ImageWidth': 128, - 'Orientation': 1, - 'StripByteCounts': (1968,), - 'SamplesPerPixel': 1, - 'StripOffsets': (8,) - }, img.tag_v2.named()) - - self.assertEqual({'YResolution': ((4294967295, 113653537),), - 'PlanarConfiguration': (1,), - 'BitsPerSample': (1,), - 'ImageLength': (128,), - 'Compression': (4,), - 'FillOrder': (1,), - 'RowsPerStrip': (128,), - 'ResolutionUnit': (3,), - 'PhotometricInterpretation': (0,), - 'PageNumber': (0, 1), - 'XResolution': ((4294967295, 113653537),), - 'ImageWidth': (128,), - 'Orientation': (1,), - 'StripByteCounts': (1968,), - 'SamplesPerPixel': (1,), - 'StripOffsets': (8,) - }, img.tag.named()) + img = Image.open("Tests/images/hopper_g4.tif") + + self.assertEqual( + { + "YResolution": IFDRational(4294967295, 113653537), + "PlanarConfiguration": 1, + "BitsPerSample": (1,), + "ImageLength": 128, + "Compression": 4, + "FillOrder": 1, + "RowsPerStrip": 128, + "ResolutionUnit": 3, + "PhotometricInterpretation": 0, + "PageNumber": (0, 1), + "XResolution": IFDRational(4294967295, 113653537), + "ImageWidth": 128, + "Orientation": 1, + "StripByteCounts": (1968,), + "SamplesPerPixel": 1, + "StripOffsets": (8,), + }, + img.tag_v2.named(), + ) + + self.assertEqual( + { + "YResolution": ((4294967295, 113653537),), + "PlanarConfiguration": (1,), + "BitsPerSample": (1,), + "ImageLength": (128,), + "Compression": (4,), + "FillOrder": (1,), + "RowsPerStrip": (128,), + "ResolutionUnit": (3,), + "PhotometricInterpretation": (0,), + "PageNumber": (0, 1), + "XResolution": ((4294967295, 113653537),), + "ImageWidth": (128,), + "Orientation": (1,), + "StripByteCounts": (1968,), + "SamplesPerPixel": (1,), + "StripOffsets": (8,), + }, + img.tag.named(), + ) def test_write_metadata(self): """ Test metadata writing through the python code """ - img = Image.open('Tests/images/hopper.tif') + img = Image.open("Tests/images/hopper.tif") - f = self.tempfile('temp.tiff') + f = self.tempfile("temp.tiff") img.save(f, tiffinfo=img.tag) loaded = Image.open(f) @@ -134,42 +138,44 @@ def test_write_metadata(self): for k, v in original.items(): if isinstance(v, IFDRational): - original[k] = IFDRational(*_limit_rational(v, 2**31)) + original[k] = IFDRational(*_limit_rational(v, 2 ** 31)) elif isinstance(v, tuple) and isinstance(v[0], IFDRational): - original[k] = tuple(IFDRational(*_limit_rational(elt, 2**31)) - for elt in v) + original[k] = tuple( + IFDRational(*_limit_rational(elt, 2 ** 31)) for elt in v + ) - ignored = ['StripByteCounts', 'RowsPerStrip', - 'PageNumber', 'StripOffsets'] + ignored = ["StripByteCounts", "RowsPerStrip", "PageNumber", "StripOffsets"] for tag, value in reloaded.items(): if tag in ignored: continue - if (isinstance(original[tag], tuple) - and isinstance(original[tag][0], IFDRational)): + if isinstance(original[tag], tuple) and isinstance( + original[tag][0], IFDRational + ): # Need to compare element by element in the tuple, # not comparing tuples of object references - self.assert_deep_equal(original[tag], - value, - "%s didn't roundtrip, %s, %s" % - (tag, original[tag], value)) + self.assert_deep_equal( + original[tag], + value, + "%s didn't roundtrip, %s, %s" % (tag, original[tag], value), + ) else: - self.assertEqual(original[tag], - value, - "%s didn't roundtrip, %s, %s" % - (tag, original[tag], value)) + self.assertEqual( + original[tag], + value, + "%s didn't roundtrip, %s, %s" % (tag, original[tag], value), + ) for tag, value in original.items(): if tag not in ignored: - self.assertEqual( - value, reloaded[tag], "%s didn't roundtrip" % tag) + self.assertEqual(value, reloaded[tag], "%s didn't roundtrip" % tag) def test_no_duplicate_50741_tag(self): - self.assertEqual(tag_ids['MakerNoteSafety'], 50741) - self.assertEqual(tag_ids['BestQualityScale'], 50780) + self.assertEqual(tag_ids["MakerNoteSafety"], 50741) + self.assertEqual(tag_ids["BestQualityScale"], 50780) def test_empty_metadata(self): - f = io.BytesIO(b'II*\x00\x08\x00\x00\x00') + f = io.BytesIO(b"II*\x00\x08\x00\x00\x00") head = f.read(8) info = TiffImagePlugin.ImageFileDirectory(head) # Should not raise struct.error. @@ -177,31 +183,31 @@ def test_empty_metadata(self): def test_iccprofile(self): # https://github.com/python-pillow/Pillow/issues/1462 - im = Image.open('Tests/images/hopper.iccprofile.tif') - out = self.tempfile('temp.tiff') + im = Image.open("Tests/images/hopper.iccprofile.tif") + out = self.tempfile("temp.tiff") im.save(out) reloaded = Image.open(out) - self.assertNotIsInstance(im.info['icc_profile'], tuple) - self.assertEqual(im.info['icc_profile'], reloaded.info['icc_profile']) + self.assertNotIsInstance(im.info["icc_profile"], tuple) + self.assertEqual(im.info["icc_profile"], reloaded.info["icc_profile"]) def test_iccprofile_binary(self): # https://github.com/python-pillow/Pillow/issues/1526 # We should be able to load this, # but probably won't be able to save it. - im = Image.open('Tests/images/hopper.iccprofile_binary.tif') + im = Image.open("Tests/images/hopper.iccprofile_binary.tif") self.assertEqual(im.tag_v2.tagtype[34675], 1) - self.assertTrue(im.info['icc_profile']) + self.assertTrue(im.info["icc_profile"]) def test_iccprofile_save_png(self): - im = Image.open('Tests/images/hopper.iccprofile.tif') - outfile = self.tempfile('temp.png') + im = Image.open("Tests/images/hopper.iccprofile.tif") + outfile = self.tempfile("temp.png") im.save(outfile) def test_iccprofile_binary_save_png(self): - im = Image.open('Tests/images/hopper.iccprofile_binary.tif') - outfile = self.tempfile('temp.png') + im = Image.open("Tests/images/hopper.iccprofile_binary.tif") + outfile = self.tempfile("temp.png") im.save(outfile) def test_exif_div_zero(self): @@ -209,8 +215,8 @@ def test_exif_div_zero(self): info = TiffImagePlugin.ImageFileDirectory_v2() info[41988] = TiffImagePlugin.IFDRational(0, 0) - out = self.tempfile('temp.tiff') - im.save(out, tiffinfo=info, compression='raw') + out = self.tempfile("temp.tiff") + im.save(out, tiffinfo=info, compression="raw") reloaded = Image.open(out) self.assertEqual(0, reloaded.tag_v2[41988].numerator) @@ -218,10 +224,11 @@ def test_exif_div_zero(self): def test_expty_values(self): data = io.BytesIO( - b'II*\x00\x08\x00\x00\x00\x03\x00\x1a\x01\x05\x00\x00\x00\x00\x00' - b'\x00\x00\x00\x00\x1b\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00' - b'\x98\x82\x02\x00\x07\x00\x00\x002\x00\x00\x00\x00\x00\x00\x00a ' - b'text\x00\x00') + b"II*\x00\x08\x00\x00\x00\x03\x00\x1a\x01\x05\x00\x00\x00\x00\x00" + b"\x00\x00\x00\x00\x1b\x01\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00" + b"\x98\x82\x02\x00\x07\x00\x00\x002\x00\x00\x00\x00\x00\x00\x00a " + b"text\x00\x00" + ) head = data.read(8) info = TiffImagePlugin.ImageFileDirectory_v2(head) info.load(data) @@ -230,10 +237,10 @@ def test_expty_values(self): self.assertIn(33432, info) def test_PhotoshopInfo(self): - im = Image.open('Tests/images/issue_2278.tif') + im = Image.open("Tests/images/issue_2278.tif") self.assertIsInstance(im.tag_v2[34377], bytes) - out = self.tempfile('temp.tiff') + out = self.tempfile("temp.tiff") im.save(out) reloaded = Image.open(out) self.assertIsInstance(reloaded.tag_v2[34377], bytes) @@ -242,7 +249,7 @@ def test_too_many_entries(self): ifd = TiffImagePlugin.ImageFileDirectory_v2() # 277: ("SamplesPerPixel", SHORT, 1), - ifd._tagdata[277] = struct.pack('hh', 4, 4) + ifd._tagdata[277] = struct.pack("hh", 4, 4) ifd.tagtype[277] = TiffTags.SHORT # Should not raise ValueError. diff --git a/Tests/test_file_wal.py b/Tests/test_file_wal.py index 1e0a835d229..8b85aaecdea 100644 --- a/Tests/test_file_wal.py +++ b/Tests/test_file_wal.py @@ -4,7 +4,6 @@ class TestFileWal(PillowTestCase): - def test_open(self): # Arrange TEST_FILE = "Tests/images/hopper.wal" diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 7e6fad93cfb..659f2baf1ef 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -4,6 +4,7 @@ try: from PIL import _webp + HAVE_WEBP = True except ImportError: HAVE_WEBP = False @@ -16,8 +17,7 @@ def test_unsupported(self): file_path = "Tests/images/hopper.webp" self.assert_warning( - UserWarning, - lambda: self.assertRaises(IOError, Image.open, file_path) + UserWarning, lambda: self.assertRaises(IOError, Image.open, file_path) ) if HAVE_WEBP: @@ -26,7 +26,6 @@ def test_unsupported(self): @unittest.skipIf(not HAVE_WEBP, "WebP support not installed") class TestFileWebp(PillowTestCase): - def setUp(self): self.rgb_mode = "RGB" @@ -51,7 +50,8 @@ def test_read_rgb(self): # generated with: # dwebp -ppm ../../Tests/images/hopper.webp -o hopper_webp_bits.ppm self.assert_image_similar_tofile( - image, 'Tests/images/hopper_webp_bits.ppm', 1.0) + image, "Tests/images/hopper_webp_bits.ppm", 1.0 + ) def test_write_rgb(self): """ @@ -72,7 +72,8 @@ def test_write_rgb(self): # generated with: dwebp -ppm temp.webp -o hopper_webp_write.ppm self.assert_image_similar_tofile( - image, 'Tests/images/hopper_webp_write.ppm', 12.0) + image, "Tests/images/hopper_webp_write.ppm", 12.0 + ) # This test asserts that the images are similar. If the average pixel # difference between the two images is less than the epsilon value, @@ -149,12 +150,13 @@ def test_no_resource_warning(self): def test_file_pointer_could_be_reused(self): file_path = "Tests/images/hopper.webp" - with open(file_path, 'rb') as blob: + with open(file_path, "rb") as blob: Image.open(blob).load() Image.open(blob).load() - @unittest.skipUnless(HAVE_WEBP and _webp.HAVE_WEBPANIM, - "WebP save all not available") + @unittest.skipUnless( + HAVE_WEBP and _webp.HAVE_WEBPANIM, "WebP save all not available" + ) def test_background_from_gif(self): im = Image.open("Tests/images/chi.gif") original_value = im.convert("RGB").getpixel((1, 1)) @@ -169,6 +171,7 @@ def test_background_from_gif(self): reread = Image.open(out_gif) reread_value = reread.convert("RGB").getpixel((1, 1)) - difference = sum([abs(original_value[i] - reread_value[i]) - for i in range(0, 3)]) + difference = sum( + [abs(original_value[i] - reread_value[i]) for i in range(0, 3)] + ) self.assertLess(difference, 5) diff --git a/Tests/test_file_webp_alpha.py b/Tests/test_file_webp_alpha.py index c868fa1dfdf..255fa6e22b2 100644 --- a/Tests/test_file_webp_alpha.py +++ b/Tests/test_file_webp_alpha.py @@ -10,11 +10,11 @@ @unittest.skipIf(_webp is None, "WebP support not installed") class TestFileWebpAlpha(PillowTestCase): - def setUp(self): if _webp.WebPDecoderBuggyAlpha(self): - self.skipTest("Buggy early version of WebP installed, " - "not testing transparency") + self.skipTest( + "Buggy early version of WebP installed, not testing transparency" + ) def test_read_rgba(self): """ @@ -34,7 +34,7 @@ def test_read_rgba(self): image.tobytes() - target = Image.open('Tests/images/transparent.png') + target = Image.open("Tests/images/transparent.png") self.assert_image_similar(image, target, 20.0) def test_write_lossless_rgb(self): @@ -46,7 +46,7 @@ def test_write_lossless_rgb(self): temp_file = self.tempfile("temp.webp") # temp_file = "temp.webp" - pil_image = hopper('RGBA') + pil_image = hopper("RGBA") mask = Image.new("RGBA", (64, 64), (128, 128, 128, 128)) # Add some partially transparent bits: diff --git a/Tests/test_file_webp_animated.py b/Tests/test_file_webp_animated.py index c751545c7a8..6a73fccdf72 100644 --- a/Tests/test_file_webp_animated.py +++ b/Tests/test_file_webp_animated.py @@ -4,21 +4,23 @@ try: from PIL import _webp + HAVE_WEBP = True except ImportError: HAVE_WEBP = False class TestFileWebpAnimation(PillowTestCase): - def setUp(self): if not HAVE_WEBP: - self.skipTest('WebP support not installed') + self.skipTest("WebP support not installed") return if not _webp.HAVE_WEBPANIM: - self.skipTest("WebP library does not contain animation support, " - "not testing animation") + self.skipTest( + "WebP library does not contain animation support, " + "not testing animation" + ) def test_n_frames(self): """ @@ -53,8 +55,8 @@ def test_write_animation_L(self): orig.load() im.load() self.assert_image_similar(im, orig.convert("RGBA"), 25.0) - orig.seek(orig.n_frames-1) - im.seek(im.n_frames-1) + orig.seek(orig.n_frames - 1) + im.seek(im.n_frames - 1) orig.load() im.load() self.assert_image_similar(im, orig.convert("RGBA"), 25.0) @@ -78,23 +80,27 @@ def check(temp_file): im.load() self.assert_image_equal(im, frame2.convert("RGBA")) - frame1 = Image.open('Tests/images/anim_frame1.webp') - frame2 = Image.open('Tests/images/anim_frame2.webp') + frame1 = Image.open("Tests/images/anim_frame1.webp") + frame2 = Image.open("Tests/images/anim_frame2.webp") temp_file1 = self.tempfile("temp.webp") - frame1.copy().save(temp_file1, - save_all=True, append_images=[frame2], - lossless=True) + frame1.copy().save( + temp_file1, save_all=True, append_images=[frame2], lossless=True + ) check(temp_file1) # Tests appending using a generator def imGenerator(ims): for im in ims: yield im + temp_file2 = self.tempfile("temp_generator.webp") - frame1.copy().save(temp_file2, - save_all=True, append_images=imGenerator([frame2]), - lossless=True) + frame1.copy().save( + temp_file2, + save_all=True, + append_images=imGenerator([frame2]), + lossless=True, + ) check(temp_file2) def test_timestamp_and_duration(self): @@ -105,11 +111,14 @@ def test_timestamp_and_duration(self): durations = [0, 10, 20, 30, 40] temp_file = self.tempfile("temp.webp") - frame1 = Image.open('Tests/images/anim_frame1.webp') - frame2 = Image.open('Tests/images/anim_frame2.webp') - frame1.save(temp_file, save_all=True, - append_images=[frame2, frame1, frame2, frame1], - duration=durations) + frame1 = Image.open("Tests/images/anim_frame1.webp") + frame2 = Image.open("Tests/images/anim_frame2.webp") + frame1.save( + temp_file, + save_all=True, + append_images=[frame2, frame1, frame2, frame1], + duration=durations, + ) im = Image.open(temp_file) self.assertEqual(im.n_frames, 5) @@ -133,18 +142,21 @@ def test_seeking(self): dur = 33 temp_file = self.tempfile("temp.webp") - frame1 = Image.open('Tests/images/anim_frame1.webp') - frame2 = Image.open('Tests/images/anim_frame2.webp') - frame1.save(temp_file, save_all=True, - append_images=[frame2, frame1, frame2, frame1], - duration=dur) + frame1 = Image.open("Tests/images/anim_frame1.webp") + frame2 = Image.open("Tests/images/anim_frame2.webp") + frame1.save( + temp_file, + save_all=True, + append_images=[frame2, frame1, frame2, frame1], + duration=dur, + ) im = Image.open(temp_file) self.assertEqual(im.n_frames, 5) self.assertTrue(im.is_animated) # Traverse frames in reverse, checking timestamps and durations - ts = dur * (im.n_frames-1) + ts = dur * (im.n_frames - 1) for frame in reversed(range(im.n_frames)): im.seek(frame) im.load() diff --git a/Tests/test_file_webp_lossless.py b/Tests/test_file_webp_lossless.py index 528c9177d26..c9dddfcd46d 100644 --- a/Tests/test_file_webp_lossless.py +++ b/Tests/test_file_webp_lossless.py @@ -4,20 +4,20 @@ try: from PIL import _webp + HAVE_WEBP = True except ImportError: HAVE_WEBP = False class TestFileWebpLossless(PillowTestCase): - def setUp(self): if not HAVE_WEBP: - self.skipTest('WebP support not installed') + self.skipTest("WebP support not installed") return if _webp.WebPDecoderVersion() < 0x0200: - self.skipTest('lossless not included') + self.skipTest("lossless not included") self.rgb_mode = "RGB" diff --git a/Tests/test_file_webp_metadata.py b/Tests/test_file_webp_metadata.py index 402b6ce42cd..7f4b457edff 100644 --- a/Tests/test_file_webp_metadata.py +++ b/Tests/test_file_webp_metadata.py @@ -4,20 +4,20 @@ try: from PIL import _webp + HAVE_WEBP = True except ImportError: HAVE_WEBP = False class TestFileWebpMetadata(PillowTestCase): - def setUp(self): if not HAVE_WEBP: - self.skipTest('WebP support not installed') + self.skipTest("WebP support not installed") return if not _webp.HAVE_WEBPMUX: - self.skipTest('WebPMux support not installed') + self.skipTest("WebPMux support not installed") def test_read_exif_metadata(self): @@ -33,8 +33,8 @@ def test_read_exif_metadata(self): # camera make self.assertEqual(exif[271], "Canon") - jpeg_image = Image.open('Tests/images/flower.jpg') - expected_exif = jpeg_image.info['exif'] + jpeg_image = Image.open("Tests/images/flower.jpg") + expected_exif = jpeg_image.info["exif"] self.assertEqual(exif_data, expected_exif) @@ -43,7 +43,7 @@ def test_write_exif_metadata(self): file_path = "Tests/images/flower.jpg" image = Image.open(file_path) - expected_exif = image.info['exif'] + expected_exif = image.info["exif"] test_buffer = BytesIO() @@ -52,11 +52,10 @@ def test_write_exif_metadata(self): test_buffer.seek(0) webp_image = Image.open(test_buffer) - webp_exif = webp_image.info.get('exif', None) + webp_exif = webp_image.info.get("exif", None) self.assertTrue(webp_exif) if webp_exif: - self.assertEqual( - webp_exif, expected_exif, "WebP EXIF didn't match") + self.assertEqual(webp_exif, expected_exif, "WebP EXIF didn't match") def test_read_icc_profile(self): @@ -66,10 +65,10 @@ def test_read_icc_profile(self): self.assertEqual(image.format, "WEBP") self.assertTrue(image.info.get("icc_profile", None)) - icc = image.info['icc_profile'] + icc = image.info["icc_profile"] - jpeg_image = Image.open('Tests/images/flower2.jpg') - expected_icc = jpeg_image.info['icc_profile'] + jpeg_image = Image.open("Tests/images/flower2.jpg") + expected_icc = jpeg_image.info["icc_profile"] self.assertEqual(icc, expected_icc) @@ -78,7 +77,7 @@ def test_write_icc_metadata(self): file_path = "Tests/images/flower2.jpg" image = Image.open(file_path) - expected_icc_profile = image.info['icc_profile'] + expected_icc_profile = image.info["icc_profile"] test_buffer = BytesIO() @@ -87,20 +86,20 @@ def test_write_icc_metadata(self): test_buffer.seek(0) webp_image = Image.open(test_buffer) - webp_icc_profile = webp_image.info.get('icc_profile', None) + webp_icc_profile = webp_image.info.get("icc_profile", None) self.assertTrue(webp_icc_profile) if webp_icc_profile: self.assertEqual( - webp_icc_profile, expected_icc_profile, - "Webp ICC didn't match") + webp_icc_profile, expected_icc_profile, "Webp ICC didn't match" + ) def test_read_no_exif(self): from io import BytesIO file_path = "Tests/images/flower.jpg" image = Image.open(file_path) - self.assertIn('exif', image.info) + self.assertIn("exif", image.info) test_buffer = BytesIO() @@ -113,23 +112,28 @@ def test_read_no_exif(self): def test_write_animated_metadata(self): if not _webp.HAVE_WEBPANIM: - self.skipTest('WebP animation support not available') + self.skipTest("WebP animation support not available") - iccp_data = ''.encode('utf-8') - exif_data = ''.encode('utf-8') - xmp_data = ''.encode('utf-8') + iccp_data = "".encode("utf-8") + exif_data = "".encode("utf-8") + xmp_data = "".encode("utf-8") temp_file = self.tempfile("temp.webp") - frame1 = Image.open('Tests/images/anim_frame1.webp') - frame2 = Image.open('Tests/images/anim_frame2.webp') - frame1.save(temp_file, save_all=True, - append_images=[frame2, frame1, frame2], - icc_profile=iccp_data, exif=exif_data, xmp=xmp_data) + frame1 = Image.open("Tests/images/anim_frame1.webp") + frame2 = Image.open("Tests/images/anim_frame2.webp") + frame1.save( + temp_file, + save_all=True, + append_images=[frame2, frame1, frame2], + icc_profile=iccp_data, + exif=exif_data, + xmp=xmp_data, + ) image = Image.open(temp_file) - self.assertIn('icc_profile', image.info) - self.assertIn('exif', image.info) - self.assertIn('xmp', image.info) - self.assertEqual(iccp_data, image.info.get('icc_profile', None)) - self.assertEqual(exif_data, image.info.get('exif', None)) - self.assertEqual(xmp_data, image.info.get('xmp', None)) + self.assertIn("icc_profile", image.info) + self.assertIn("exif", image.info) + self.assertIn("xmp", image.info) + self.assertEqual(iccp_data, image.info.get("icc_profile", None)) + self.assertEqual(exif_data, image.info.get("exif", None)) + self.assertEqual(xmp_data, image.info.get("xmp", None)) diff --git a/Tests/test_file_wmf.py b/Tests/test_file_wmf.py index e3c6063f2fa..cf104cfe31b 100644 --- a/Tests/test_file_wmf.py +++ b/Tests/test_file_wmf.py @@ -5,26 +5,25 @@ class TestFileWmf(PillowTestCase): - def test_load_raw(self): # Test basic EMF open and rendering - im = Image.open('Tests/images/drawing.emf') + im = Image.open("Tests/images/drawing.emf") if hasattr(Image.core, "drawwmf"): # Currently, support for WMF/EMF is Windows-only im.load() # Compare to reference rendering - imref = Image.open('Tests/images/drawing_emf_ref.png') + imref = Image.open("Tests/images/drawing_emf_ref.png") imref.load() self.assert_image_similar(im, imref, 0) # Test basic WMF open and rendering - im = Image.open('Tests/images/drawing.wmf') + im = Image.open("Tests/images/drawing.wmf") if hasattr(Image.core, "drawwmf"): # Currently, support for WMF/EMF is Windows-only im.load() # Compare to reference rendering - imref = Image.open('Tests/images/drawing_wmf_ref.png') + imref = Image.open("Tests/images/drawing_wmf_ref.png") imref.load() self.assert_image_similar(im, imref, 2.0) @@ -34,6 +33,7 @@ class TestHandler: def save(self, im, fp, filename): self.methodCalled = True + handler = TestHandler() WmfImagePlugin.register_handler(handler) @@ -47,16 +47,16 @@ def save(self, im, fp, filename): def test_load_dpi_rounding(self): # Round up - im = Image.open('Tests/images/drawing.emf') + im = Image.open("Tests/images/drawing.emf") self.assertEqual(im.info["dpi"], 1424) # Round down - im = Image.open('Tests/images/drawing_roundDown.emf') + im = Image.open("Tests/images/drawing_roundDown.emf") self.assertEqual(im.info["dpi"], 1426) def test_save(self): im = hopper() for ext in [".wmf", ".emf"]: - tmpfile = self.tempfile("temp"+ext) + tmpfile = self.tempfile("temp" + ext) self.assertRaises(IOError, im.save, tmpfile) diff --git a/Tests/test_file_xbm.py b/Tests/test_file_xbm.py index fbcb30e90a0..3ebadf03084 100644 --- a/Tests/test_file_xbm.py +++ b/Tests/test_file_xbm.py @@ -27,14 +27,13 @@ class TestFileXbm(PillowTestCase): - def test_pil151(self): from io import BytesIO im = Image.open(BytesIO(PIL151)) im.load() - self.assertEqual(im.mode, '1') + self.assertEqual(im.mode, "1") self.assertEqual(im.size, (32, 32)) def test_open(self): @@ -46,7 +45,7 @@ def test_open(self): im = Image.open(filename) # Assert - self.assertEqual(im.mode, '1') + self.assertEqual(im.mode, "1") self.assertEqual(im.size, (128, 128)) def test_open_filename_with_underscore(self): @@ -58,5 +57,5 @@ def test_open_filename_with_underscore(self): im = Image.open(filename) # Assert - self.assertEqual(im.mode, '1') + self.assertEqual(im.mode, "1") self.assertEqual(im.size, (128, 128)) diff --git a/Tests/test_file_xpm.py b/Tests/test_file_xpm.py index b57cda2e3f5..4133b00960e 100644 --- a/Tests/test_file_xpm.py +++ b/Tests/test_file_xpm.py @@ -6,7 +6,6 @@ class TestFileXpm(PillowTestCase): - def test_sanity(self): im = Image.open(TEST_FILE) im.load() @@ -15,13 +14,12 @@ def test_sanity(self): self.assertEqual(im.format, "XPM") # large error due to quantization->44 colors. - self.assert_image_similar(im.convert('RGB'), hopper('RGB'), 60) + self.assert_image_similar(im.convert("RGB"), hopper("RGB"), 60) def test_invalid_file(self): invalid_file = "Tests/images/flower.jpg" - self.assertRaises(SyntaxError, - XpmImagePlugin.XpmImageFile, invalid_file) + self.assertRaises(SyntaxError, XpmImagePlugin.XpmImageFile, invalid_file) def test_load_read(self): # Arrange diff --git a/Tests/test_file_xvthumb.py b/Tests/test_file_xvthumb.py index 11672ebae94..bcced28530a 100644 --- a/Tests/test_file_xvthumb.py +++ b/Tests/test_file_xvthumb.py @@ -6,7 +6,6 @@ class TestFileXVThumb(PillowTestCase): - def test_open(self): # Act im = Image.open(TEST_FILE) @@ -24,13 +23,13 @@ def test_unexpected_eof(self): bad_file = "Tests/images/hopper_bad.p7" # Act / Assert - self.assertRaises(SyntaxError, - XVThumbImagePlugin.XVThumbImageFile, bad_file) + self.assertRaises(SyntaxError, XVThumbImagePlugin.XVThumbImageFile, bad_file) def test_invalid_file(self): # Arrange invalid_file = "Tests/images/flower.jpg" # Act / Assert - self.assertRaises(SyntaxError, - XVThumbImagePlugin.XVThumbImageFile, invalid_file) + self.assertRaises( + SyntaxError, XVThumbImagePlugin.XVThumbImageFile, invalid_file + ) diff --git a/Tests/test_font_bdf.py b/Tests/test_font_bdf.py index be49e818e24..69badc37630 100644 --- a/Tests/test_font_bdf.py +++ b/Tests/test_font_bdf.py @@ -6,7 +6,6 @@ class TestFontBdf(PillowTestCase): - def test_sanity(self): with open(filename, "rb") as test_file: diff --git a/Tests/test_font_leaks.py b/Tests/test_font_leaks.py index 119012bc512..84f2a434f5b 100644 --- a/Tests/test_font_leaks.py +++ b/Tests/test_font_leaks.py @@ -4,22 +4,24 @@ from PIL import Image, features, ImageDraw, ImageFont -@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") +@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") class TestTTypeFontLeak(PillowLeakTestCase): # fails at iteration 3 in master iterations = 10 mem_limit = 4096 # k def _test_font(self, font): - im = Image.new('RGB', (255, 255), 'white') + im = Image.new("RGB", (255, 255), "white") draw = ImageDraw.ImageDraw(im) - self._test_leak(lambda: draw.text((0, 0), "some text "*1024, # ~10k - font=font, fill="black")) + self._test_leak( + lambda: draw.text( + (0, 0), "some text " * 1024, font=font, fill="black" # ~10k + ) + ) - @unittest.skipIf(not features.check('freetype2'), - "Test requires freetype2") + @unittest.skipIf(not features.check("freetype2"), "Test requires freetype2") def test_leak(self): - ttype = ImageFont.truetype('Tests/fonts/FreeMono.ttf', 20) + 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 d092634f331..5db389d37d4 100644 --- a/Tests/test_font_pcf.py +++ b/Tests/test_font_pcf.py @@ -12,7 +12,6 @@ class TestFontPcf(PillowTestCase): - def setUp(self): if "zip_encoder" not in codecs or "zip_decoder" not in codecs: self.skipTest("zlib support not available") @@ -25,15 +24,15 @@ def save_font(self): self.assertEqual(len([_f for _f in font.glyph if _f]), 223) tempname = self.tempfile("temp.pil") - self.addCleanup(self.delete_tempfile, tempname[:-4]+'.pbm') + self.addCleanup(self.delete_tempfile, tempname[:-4] + ".pbm") font.save(tempname) - with Image.open(tempname.replace('.pil', '.pbm')) as loaded: - with Image.open('Tests/fonts/10x20.pbm') as target: + with Image.open(tempname.replace(".pil", ".pbm")) as loaded: + with Image.open("Tests/fonts/10x20.pbm") as target: self.assert_image_equal(loaded, target) - with open(tempname, 'rb') as f_loaded: - with open('Tests/fonts/10x20.pil', 'rb') as f_target: + with open(tempname, "rb") as f_loaded: + with open("Tests/fonts/10x20.pil", "rb") as f_target: self.assertEqual(f_loaded.read(), f_target.read()) return tempname @@ -49,8 +48,8 @@ def test_draw(self): font = ImageFont.load(tempname) im = Image.new("L", (130, 30), "white") draw = ImageDraw.Draw(im) - draw.text((0, 0), message, 'black', font=font) - with Image.open('Tests/images/test_draw_pbm_target.png') as target: + draw.text((0, 0), message, "black", font=font) + with Image.open("Tests/images/test_draw_pbm_target.png") as target: self.assert_image_similar(im, target, 0) def test_textsize(self): @@ -61,8 +60,8 @@ def test_textsize(self): self.assertEqual(dy, 20) self.assertIn(dx, (0, 10)) for l in range(len(message)): - msg = message[:l+1] - self.assertEqual(font.getsize(msg), (len(msg)*10, 20)) + msg = message[: l + 1] + self.assertEqual(font.getsize(msg), (len(msg) * 10, 20)) def _test_high_characters(self, message): tempname = self.save_font() @@ -70,12 +69,12 @@ def _test_high_characters(self, message): im = Image.new("L", (750, 30), "white") draw = ImageDraw.Draw(im) draw.text((0, 0), message, "black", font=font) - with Image.open('Tests/images/high_ascii_chars.png') as target: + with Image.open("Tests/images/high_ascii_chars.png") as target: self.assert_image_similar(im, target, 0) def test_high_characters(self): - message = "".join(chr(i+1) for i in range(140, 232)) + message = "".join(chr(i + 1) for i in range(140, 232)) self._test_high_characters(message) # accept bytes instances in Py3. if py3: - self._test_high_characters(message.encode('latin1')) + self._test_high_characters(message.encode("latin1")) diff --git a/Tests/test_format_hsv.py b/Tests/test_format_hsv.py index 3e1c00c9e18..781bfc80141 100644 --- a/Tests/test_format_hsv.py +++ b/Tests/test_format_hsv.py @@ -8,19 +8,18 @@ class TestFormatHSV(PillowTestCase): - def int_to_float(self, i): - return float(i)/255.0 + return float(i) / 255.0 def str_to_float(self, i): - return float(ord(i))/255.0 + return float(ord(i)) / 255.0 def tuple_to_ints(self, tp): x, y, z = tp - return int(x*255.0), int(y*255.0), int(z*255.0) + return int(x * 255.0), int(y * 255.0), int(z * 255.0) def test_sanity(self): - Image.new('HSV', (100, 100)) + Image.new("HSV", (100, 100)) def wedge(self): w = Image._wedge() @@ -28,7 +27,7 @@ def wedge(self): (px, h) = w.size - r = Image.new('L', (px*3, h)) + r = Image.new("L", (px * 3, h)) g = r.copy() b = r.copy() @@ -36,12 +35,12 @@ def wedge(self): r.paste(w90, (px, 0)) g.paste(w90, (0, 0)) - g.paste(w, (2*px, 0)) + g.paste(w, (2 * px, 0)) b.paste(w, (px, 0)) - b.paste(w90, (2*px, 0)) + b.paste(w90, (2 * px, 0)) - img = Image.merge('RGB', (r, g, b)) + img = Image.merge("RGB", (r, g, b)) return img @@ -55,77 +54,101 @@ def to_xxx_colorsys(self, im, func, mode): else: conv_func = self.str_to_float - if hasattr(itertools, 'izip'): + if hasattr(itertools, "izip"): iter_helper = itertools.izip else: iter_helper = itertools.zip_longest - converted = [self.tuple_to_ints(func(conv_func(_r), conv_func(_g), - conv_func(_b))) - for (_r, _g, _b) in iter_helper(r.tobytes(), g.tobytes(), - b.tobytes())] + converted = [ + self.tuple_to_ints(func(conv_func(_r), conv_func(_g), conv_func(_b))) + for (_r, _g, _b) in iter_helper(r.tobytes(), g.tobytes(), b.tobytes()) + ] if py3: - new_bytes = b''.join(bytes(chr(h)+chr(s)+chr(v), 'latin-1') for ( - h, s, v) in converted) + new_bytes = b"".join( + bytes(chr(h) + chr(s) + chr(v), "latin-1") for (h, s, v) in converted + ) else: - new_bytes = b''.join(chr(h)+chr(s)+chr(v) for ( - h, s, v) in converted) + new_bytes = b"".join(chr(h) + chr(s) + chr(v) for (h, s, v) in converted) hsv = Image.frombytes(mode, r.size, new_bytes) return hsv def to_hsv_colorsys(self, im): - return self.to_xxx_colorsys(im, colorsys.rgb_to_hsv, 'HSV') + return self.to_xxx_colorsys(im, colorsys.rgb_to_hsv, "HSV") def to_rgb_colorsys(self, im): - return self.to_xxx_colorsys(im, colorsys.hsv_to_rgb, 'RGB') + return self.to_xxx_colorsys(im, colorsys.hsv_to_rgb, "RGB") def test_wedge(self): - src = self.wedge().resize((3*32, 32), Image.BILINEAR) - im = src.convert('HSV') + src = self.wedge().resize((3 * 32, 32), Image.BILINEAR) + im = src.convert("HSV") comparable = self.to_hsv_colorsys(src) - self.assert_image_similar(im.getchannel(0), comparable.getchannel(0), - 1, "Hue conversion is wrong") - self.assert_image_similar(im.getchannel(1), comparable.getchannel(1), - 1, "Saturation conversion is wrong") - self.assert_image_similar(im.getchannel(2), comparable.getchannel(2), - 1, "Value conversion is wrong") + self.assert_image_similar( + im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong" + ) + self.assert_image_similar( + im.getchannel(1), + comparable.getchannel(1), + 1, + "Saturation conversion is wrong", + ) + self.assert_image_similar( + im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong" + ) comparable = src - im = im.convert('RGB') - - self.assert_image_similar(im.getchannel(0), comparable.getchannel(0), - 3, "R conversion is wrong") - self.assert_image_similar(im.getchannel(1), comparable.getchannel(1), - 3, "G conversion is wrong") - self.assert_image_similar(im.getchannel(2), comparable.getchannel(2), - 3, "B conversion is wrong") + im = im.convert("RGB") + + self.assert_image_similar( + im.getchannel(0), comparable.getchannel(0), 3, "R conversion is wrong" + ) + self.assert_image_similar( + im.getchannel(1), comparable.getchannel(1), 3, "G conversion is wrong" + ) + self.assert_image_similar( + im.getchannel(2), comparable.getchannel(2), 3, "B conversion is wrong" + ) def test_convert(self): - im = hopper('RGB').convert('HSV') - comparable = self.to_hsv_colorsys(hopper('RGB')) - - self.assert_image_similar(im.getchannel(0), comparable.getchannel(0), - 1, "Hue conversion is wrong") - self.assert_image_similar(im.getchannel(1), comparable.getchannel(1), - 1, "Saturation conversion is wrong") - self.assert_image_similar(im.getchannel(2), comparable.getchannel(2), - 1, "Value conversion is wrong") + im = hopper("RGB").convert("HSV") + comparable = self.to_hsv_colorsys(hopper("RGB")) + + self.assert_image_similar( + im.getchannel(0), comparable.getchannel(0), 1, "Hue conversion is wrong" + ) + self.assert_image_similar( + im.getchannel(1), + comparable.getchannel(1), + 1, + "Saturation conversion is wrong", + ) + self.assert_image_similar( + im.getchannel(2), comparable.getchannel(2), 1, "Value conversion is wrong" + ) def test_hsv_to_rgb(self): - comparable = self.to_hsv_colorsys(hopper('RGB')) - converted = comparable.convert('RGB') + comparable = self.to_hsv_colorsys(hopper("RGB")) + converted = comparable.convert("RGB") comparable = self.to_rgb_colorsys(comparable) - self.assert_image_similar(converted.getchannel(0), - comparable.getchannel(0), - 3, "R conversion is wrong") - self.assert_image_similar(converted.getchannel(1), - comparable.getchannel(1), - 3, "G conversion is wrong") - self.assert_image_similar(converted.getchannel(2), - comparable.getchannel(2), - 3, "B conversion is wrong") + self.assert_image_similar( + converted.getchannel(0), + comparable.getchannel(0), + 3, + "R conversion is wrong", + ) + self.assert_image_similar( + converted.getchannel(1), + comparable.getchannel(1), + 3, + "G conversion is wrong", + ) + self.assert_image_similar( + converted.getchannel(2), + comparable.getchannel(2), + 3, + "B conversion is wrong", + ) diff --git a/Tests/test_format_lab.py b/Tests/test_format_lab.py index 8a096e672f9..87e9858323a 100644 --- a/Tests/test_format_lab.py +++ b/Tests/test_format_lab.py @@ -4,15 +4,14 @@ class TestFormatLab(PillowTestCase): - def test_white(self): - i = Image.open('Tests/images/lab.tif') + i = Image.open("Tests/images/lab.tif") i.load() - self.assertEqual(i.mode, 'LAB') + self.assertEqual(i.mode, "LAB") - self.assertEqual(i.getbands(), ('L', 'A', 'B')) + self.assertEqual(i.getbands(), ("L", "A", "B")) k = i.getpixel((0, 0)) self.assertEqual(k, (255, 128, 128)) @@ -21,14 +20,14 @@ def test_white(self): a = i.getdata(1) b = i.getdata(2) - self.assertEqual(list(L), [255]*100) - self.assertEqual(list(a), [128]*100) - self.assertEqual(list(b), [128]*100) + self.assertEqual(list(L), [255] * 100) + self.assertEqual(list(a), [128] * 100) + self.assertEqual(list(b), [128] * 100) def test_green(self): # l= 50 (/100), a = -100 (-128 .. 128) b=0 in PS # == RGB: 0, 152, 117 - i = Image.open('Tests/images/lab-green.tif') + i = Image.open("Tests/images/lab-green.tif") k = i.getpixel((0, 0)) self.assertEqual(k, (128, 28, 128)) @@ -36,7 +35,7 @@ def test_green(self): def test_red(self): # l= 50 (/100), a = 100 (-128 .. 128) b=0 in PS # == RGB: 255, 0, 124 - i = Image.open('Tests/images/lab-red.tif') + i = Image.open("Tests/images/lab-red.tif") k = i.getpixel((0, 0)) self.assertEqual(k, (128, 228, 128)) diff --git a/Tests/test_image.py b/Tests/test_image.py index a89abf3d41f..5afbbcc3c04 100644 --- a/Tests/test_image.py +++ b/Tests/test_image.py @@ -8,37 +8,54 @@ class TestImage(PillowTestCase): - def test_image_modes_success(self): for mode in [ - '1', 'P', 'PA', - 'L', 'LA', 'La', - 'F', 'I', 'I;16', 'I;16L', 'I;16B', 'I;16N', - 'RGB', 'RGBX', 'RGBA', 'RGBa', - 'CMYK', 'YCbCr', 'LAB', 'HSV', + "1", + "P", + "PA", + "L", + "LA", + "La", + "F", + "I", + "I;16", + "I;16L", + "I;16B", + "I;16N", + "RGB", + "RGBX", + "RGBA", + "RGBa", + "CMYK", + "YCbCr", + "LAB", + "HSV", ]: Image.new(mode, (1, 1)) def test_image_modes_fail(self): for mode in [ - '', 'bad', 'very very long', - 'BGR;15', 'BGR;16', 'BGR;24', 'BGR;32' + "", + "bad", + "very very long", + "BGR;15", + "BGR;16", + "BGR;24", + "BGR;32", ]: with self.assertRaises(ValueError) as e: Image.new(mode, (1, 1)) - self.assertEqual(str(e.exception), 'unrecognized image mode') + self.assertEqual(str(e.exception), "unrecognized image mode") def test_sanity(self): im = Image.new("L", (100, 100)) - self.assertEqual( - repr(im)[:45], " 8 bit lut for converting I->L images see https://github.com/python-pillow/Pillow/issues/440 """ im = hopper("I") - im.point(list(range(256))*256, 'L') + im.point(list(range(256)) * 256, "L") def test_f_lut(self): """ Tests for floating point lut of 8bit gray image """ - im = hopper('L') + im = hopper("L") lut = [0.5 * float(x) for x in range(256)] - out = im.point(lut, 'F') + out = im.point(lut, "F") - int_lut = [x//2 for x in range(256)] - self.assert_image_equal(out.convert('L'), im.point(int_lut, 'L')) + int_lut = [x // 2 for x in range(256)] + self.assert_image_equal(out.convert("L"), im.point(int_lut, "L")) def test_f_mode(self): - im = hopper('F') + im = hopper("F") self.assertRaises(ValueError, im.point, None) diff --git a/Tests/test_image_putalpha.py b/Tests/test_image_putalpha.py index 702afb995d4..859c391156a 100644 --- a/Tests/test_image_putalpha.py +++ b/Tests/test_image_putalpha.py @@ -4,7 +4,6 @@ class TestImagePutAlpha(PillowTestCase): - def test_interface(self): im = Image.new("RGBA", (1, 1), (1, 2, 3, 0)) @@ -25,21 +24,21 @@ def test_promote(self): self.assertEqual(im.getpixel((0, 0)), 1) im.putalpha(2) - self.assertEqual(im.mode, 'LA') + self.assertEqual(im.mode, "LA") self.assertEqual(im.getpixel((0, 0)), (1, 2)) im = Image.new("P", (1, 1), 1) self.assertEqual(im.getpixel((0, 0)), 1) im.putalpha(2) - self.assertEqual(im.mode, 'PA') + self.assertEqual(im.mode, "PA") self.assertEqual(im.getpixel((0, 0)), (1, 2)) im = Image.new("RGB", (1, 1), (1, 2, 3)) self.assertEqual(im.getpixel((0, 0)), (1, 2, 3)) im.putalpha(4) - self.assertEqual(im.mode, 'RGBA') + self.assertEqual(im.mode, "RGBA") self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) def test_readonly(self): @@ -49,5 +48,5 @@ def test_readonly(self): im.putalpha(4) self.assertFalse(im.readonly) - self.assertEqual(im.mode, 'RGBA') + self.assertEqual(im.mode, "RGBA") self.assertEqual(im.getpixel((0, 0)), (1, 2, 3, 4)) diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index 1b57e38b733..3d309eef26f 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -7,7 +7,6 @@ class TestImagePutData(PillowTestCase): - def test_sanity(self): im1 = hopper() @@ -33,32 +32,33 @@ def put(value): im = Image.new("RGBA", (1, 1)) im.putdata([value]) return im.getpixel((0, 0)) + self.assertEqual(put(0xFFFFFFFF), (255, 255, 255, 255)) self.assertEqual(put(0xFFFFFFFF), (255, 255, 255, 255)) self.assertEqual(put(-1), (255, 255, 255, 255)) self.assertEqual(put(-1), (255, 255, 255, 255)) - if sys.maxsize > 2**32: + if sys.maxsize > 2 ** 32: self.assertEqual(put(sys.maxsize), (255, 255, 255, 255)) else: self.assertEqual(put(sys.maxsize), (255, 255, 255, 127)) def test_pypy_performance(self): - im = Image.new('L', (256, 256)) - im.putdata(list(range(256))*256) + im = Image.new("L", (256, 256)) + im.putdata(list(range(256)) * 256) def test_mode_i(self): - src = hopper('L') + src = hopper("L") data = list(src.getdata()) - im = Image.new('I', src.size, 0) + im = Image.new("I", src.size, 0) im.putdata(data, 2, 256) target = [2 * elt + 256 for elt in data] self.assertEqual(list(im.getdata()), target) def test_mode_F(self): - src = hopper('L') + src = hopper("L") data = list(src.getdata()) - im = Image.new('F', src.size, 0) + im = Image.new("F", src.size, 0) im.putdata(data, 2.0, 256.0) target = [2.0 * float(elt) + 256.0 for elt in data] @@ -68,8 +68,8 @@ def test_array_B(self): # shouldn't segfault # see https://github.com/python-pillow/Pillow/issues/1008 - arr = array('B', [0])*15000 - im = Image.new('L', (150, 100)) + arr = array("B", [0]) * 15000 + im = Image.new("L", (150, 100)) im.putdata(arr) self.assertEqual(len(im.getdata()), len(arr)) @@ -78,8 +78,8 @@ def test_array_F(self): # shouldn't segfault # see https://github.com/python-pillow/Pillow/issues/1008 - im = Image.new('F', (150, 100)) - arr = array('f', [0.0])*15000 + im = Image.new("F", (150, 100)) + arr = array("f", [0.0]) * 15000 im.putdata(arr) self.assertEqual(len(im.getdata()), len(arr)) diff --git a/Tests/test_image_putpalette.py b/Tests/test_image_putpalette.py index 4bac448fd55..55fa71fa555 100644 --- a/Tests/test_image_putpalette.py +++ b/Tests/test_image_putpalette.py @@ -4,20 +4,21 @@ class TestImagePutPalette(PillowTestCase): - def test_putpalette(self): def palette(mode): im = hopper(mode).copy() - im.putpalette(list(range(256))*3) + im.putpalette(list(range(256)) * 3) p = im.getpalette() if p: return im.mode, p[:10] return im.mode + self.assertRaises(ValueError, palette, "1") for mode in ["L", "LA", "P", "PA"]: - self.assertEqual(palette(mode), - ("PA" if "A" in mode else "P", - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])) + self.assertEqual( + palette(mode), + ("PA" if "A" in mode else "P", [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), + ) self.assertRaises(ValueError, palette, "I") self.assertRaises(ValueError, palette, "F") self.assertRaises(ValueError, palette, "RGB") diff --git a/Tests/test_image_quantize.py b/Tests/test_image_quantize.py index 5f2318574d8..0c56609e2d3 100644 --- a/Tests/test_image_quantize.py +++ b/Tests/test_image_quantize.py @@ -4,60 +4,59 @@ class TestImageQuantize(PillowTestCase): - def test_sanity(self): image = hopper() converted = image.quantize() - self.assert_image(converted, 'P', converted.size) - self.assert_image_similar(converted.convert('RGB'), image, 10) + self.assert_image(converted, "P", converted.size) + self.assert_image_similar(converted.convert("RGB"), image, 10) image = hopper() - converted = image.quantize(palette=hopper('P')) - self.assert_image(converted, 'P', converted.size) - self.assert_image_similar(converted.convert('RGB'), image, 60) + converted = image.quantize(palette=hopper("P")) + self.assert_image(converted, "P", converted.size) + self.assert_image_similar(converted.convert("RGB"), image, 60) def test_libimagequant_quantize(self): image = hopper() try: converted = image.quantize(100, Image.LIBIMAGEQUANT) except ValueError as ex: - if 'dependency' in str(ex).lower(): - self.skipTest('libimagequant support not available') + if "dependency" in str(ex).lower(): + self.skipTest("libimagequant support not available") else: raise - self.assert_image(converted, 'P', converted.size) - self.assert_image_similar(converted.convert('RGB'), image, 15) + self.assert_image(converted, "P", converted.size) + self.assert_image_similar(converted.convert("RGB"), image, 15) self.assertEqual(len(converted.getcolors()), 100) def test_octree_quantize(self): image = hopper() converted = image.quantize(100, Image.FASTOCTREE) - self.assert_image(converted, 'P', converted.size) - self.assert_image_similar(converted.convert('RGB'), image, 20) + self.assert_image(converted, "P", converted.size) + self.assert_image_similar(converted.convert("RGB"), image, 20) self.assertEqual(len(converted.getcolors()), 100) def test_rgba_quantize(self): - image = hopper('RGBA') + image = hopper("RGBA") self.assertRaises(ValueError, image.quantize, method=0) self.assertEqual(image.quantize().convert().mode, "RGBA") def test_quantize(self): - image = Image.open('Tests/images/caption_6_33_22.png').convert('RGB') + image = Image.open("Tests/images/caption_6_33_22.png").convert("RGB") converted = image.quantize() - self.assert_image(converted, 'P', converted.size) - self.assert_image_similar(converted.convert('RGB'), image, 1) + self.assert_image(converted, "P", converted.size) + self.assert_image_similar(converted.convert("RGB"), image, 1) def test_quantize_no_dither(self): image = hopper() - palette = Image.open('Tests/images/caption_6_33_22.png').convert('P') + palette = Image.open("Tests/images/caption_6_33_22.png").convert("P") converted = image.quantize(dither=0, palette=palette) - self.assert_image(converted, 'P', converted.size) + self.assert_image(converted, "P", converted.size) def test_quantize_dither_diff(self): image = hopper() - palette = Image.open('Tests/images/caption_6_33_22.png').convert('P') + palette = Image.open("Tests/images/caption_6_33_22.png").convert("P") dither = image.quantize(dither=1, palette=palette) nodither = image.quantize(dither=0, palette=palette) diff --git a/Tests/test_image_resample.py b/Tests/test_image_resample.py index 9ab8280ed5e..8f666b5730f 100644 --- a/Tests/test_image_resample.py +++ b/Tests/test_image_resample.py @@ -9,7 +9,7 @@ class TestImagingResampleVulnerability(PillowTestCase): # see https://github.com/python-pillow/Pillow/issues/1710 def test_overflow(self): - im = hopper('L') + im = hopper("L") xsize = 0x100000008 // 4 ysize = 1000 # unimportant with self.assertRaises(MemoryError): @@ -29,11 +29,11 @@ def test_invalid_size(self): im.resize((100, -100)) def test_modify_after_resizing(self): - im = hopper('RGB') + im = hopper("RGB") # get copy with same size copy = im.resize(im.size) # some in-place operation - copy.paste('black', (0, 0, im.width // 2, im.height // 2)) + copy.paste("black", (0, 0, im.width // 2, im.height // 2)) # image should be different self.assertNotEqual(im.tobytes(), copy.tobytes()) @@ -47,7 +47,7 @@ def make_case(self, mode, size, color): 1f 1f e0 e0 1f 1f e0 e0 """ - case = Image.new('L', size, 255 - color) + case = Image.new("L", size, 255 - color) rectangle = ImageDraw.Draw(case).rectangle rectangle((0, 0, size[0] // 2 - 1, size[1] // 2 - 1), color) rectangle((size[0] // 2, size[1] // 2, size[0], size[1]), color) @@ -58,13 +58,13 @@ def make_sample(self, data, size): """Restores a sample image from given data string which contains hex-encoded pixels from the top left fourth of a sample. """ - data = data.replace(' ', '') - sample = Image.new('L', size) + data = data.replace(" ", "") + sample = Image.new("L", size) s_px = sample.load() w, h = size[0] // 2, size[1] // 2 for y in range(h): for x in range(w): - val = int(data[(y * w + x) * 2:(y * w + x + 1) * 2], 16) + val = int(data[(y * w + x) * 2 : (y * w + x + 1) * 2], 16) s_px[x, y] = val s_px[size[0] - x - 1, size[1] - y - 1] = val s_px[x, size[1] - y - 1] = 255 - val @@ -77,118 +77,134 @@ def check_case(self, case, sample): for y in range(case.size[1]): for x in range(case.size[0]): if c_px[x, y] != s_px[x, y]: - message = '\nHave: \n{}\n\nExpected: \n{}'.format( - self.serialize_image(case), - self.serialize_image(sample), + message = "\nHave: \n{}\n\nExpected: \n{}".format( + self.serialize_image(case), self.serialize_image(sample) ) self.assertEqual(s_px[x, y], c_px[x, y], message) def serialize_image(self, image): s_px = image.load() - return '\n'.join( - ' '.join( - '{:02x}'.format(s_px[x, y]) - for x in range(image.size[0]) - ) + return "\n".join( + " ".join("{:02x}".format(s_px[x, y]) for x in range(image.size[0])) for y in range(image.size[1]) ) def test_reduce_box(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (8, 8), 0xe1) + for mode in ["RGBX", "RGB", "La", "L"]: + case = self.make_case(mode, (8, 8), 0xE1) case = case.resize((4, 4), Image.BOX) - data = ('e1 e1' - 'e1 e1') + # fmt: off + data = ("e1 e1" + "e1 e1") + # fmt: on for channel in case.split(): self.check_case(channel, self.make_sample(data, (4, 4))) def test_reduce_bilinear(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (8, 8), 0xe1) + for mode in ["RGBX", "RGB", "La", "L"]: + case = self.make_case(mode, (8, 8), 0xE1) case = case.resize((4, 4), Image.BILINEAR) - data = ('e1 c9' - 'c9 b7') + # fmt: off + data = ("e1 c9" + "c9 b7") + # fmt: on for channel in case.split(): self.check_case(channel, self.make_sample(data, (4, 4))) def test_reduce_hamming(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (8, 8), 0xe1) + for mode in ["RGBX", "RGB", "La", "L"]: + case = self.make_case(mode, (8, 8), 0xE1) case = case.resize((4, 4), Image.HAMMING) - data = ('e1 da' - 'da d3') + # fmt: off + data = ("e1 da" + "da d3") + # fmt: on for channel in case.split(): self.check_case(channel, self.make_sample(data, (4, 4))) def test_reduce_bicubic(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (12, 12), 0xe1) + for mode in ["RGBX", "RGB", "La", "L"]: + case = self.make_case(mode, (12, 12), 0xE1) case = case.resize((6, 6), Image.BICUBIC) - data = ('e1 e3 d4' - 'e3 e5 d6' - 'd4 d6 c9') + # fmt: off + data = ("e1 e3 d4" + "e3 e5 d6" + "d4 d6 c9") + # fmt: on for channel in case.split(): self.check_case(channel, self.make_sample(data, (6, 6))) def test_reduce_lanczos(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (16, 16), 0xe1) + for mode in ["RGBX", "RGB", "La", "L"]: + case = self.make_case(mode, (16, 16), 0xE1) case = case.resize((8, 8), Image.LANCZOS) - data = ('e1 e0 e4 d7' - 'e0 df e3 d6' - 'e4 e3 e7 da' - 'd7 d6 d9 ce') + # fmt: off + data = ("e1 e0 e4 d7" + "e0 df e3 d6" + "e4 e3 e7 da" + "d7 d6 d9 ce") + # fmt: on for channel in case.split(): self.check_case(channel, self.make_sample(data, (8, 8))) def test_enlarge_box(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (2, 2), 0xe1) + for mode in ["RGBX", "RGB", "La", "L"]: + case = self.make_case(mode, (2, 2), 0xE1) case = case.resize((4, 4), Image.BOX) - data = ('e1 e1' - 'e1 e1') + # fmt: off + data = ("e1 e1" + "e1 e1") + # fmt: on for channel in case.split(): self.check_case(channel, self.make_sample(data, (4, 4))) def test_enlarge_bilinear(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (2, 2), 0xe1) + for mode in ["RGBX", "RGB", "La", "L"]: + case = self.make_case(mode, (2, 2), 0xE1) case = case.resize((4, 4), Image.BILINEAR) - data = ('e1 b0' - 'b0 98') + # fmt: off + data = ("e1 b0" + "b0 98") + # fmt: on for channel in case.split(): self.check_case(channel, self.make_sample(data, (4, 4))) def test_enlarge_hamming(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (2, 2), 0xe1) + for mode in ["RGBX", "RGB", "La", "L"]: + case = self.make_case(mode, (2, 2), 0xE1) case = case.resize((4, 4), Image.HAMMING) - data = ('e1 d2' - 'd2 c5') + # fmt: off + data = ("e1 d2" + "d2 c5") + # fmt: on for channel in case.split(): self.check_case(channel, self.make_sample(data, (4, 4))) def test_enlarge_bicubic(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (4, 4), 0xe1) + for mode in ["RGBX", "RGB", "La", "L"]: + case = self.make_case(mode, (4, 4), 0xE1) case = case.resize((8, 8), Image.BICUBIC) - data = ('e1 e5 ee b9' - 'e5 e9 f3 bc' - 'ee f3 fd c1' - 'b9 bc c1 a2') + # fmt: off + data = ("e1 e5 ee b9" + "e5 e9 f3 bc" + "ee f3 fd c1" + "b9 bc c1 a2") + # fmt: on for channel in case.split(): self.check_case(channel, self.make_sample(data, (8, 8))) def test_enlarge_lanczos(self): - for mode in ['RGBX', 'RGB', 'La', 'L']: - case = self.make_case(mode, (6, 6), 0xe1) + for mode in ["RGBX", "RGB", "La", "L"]: + case = self.make_case(mode, (6, 6), 0xE1) case = case.resize((12, 12), Image.LANCZOS) - data = ('e1 e0 db ed f5 b8' - 'e0 df da ec f3 b7' - 'db db d6 e7 ee b5' - 'ed ec e6 fb ff bf' - 'f5 f4 ee ff ff c4' - 'b8 b7 b4 bf c4 a0') + data = ( + "e1 e0 db ed f5 b8" + "e0 df da ec f3 b7" + "db db d6 e7 ee b5" + "ed ec e6 fb ff bf" + "f5 f4 ee ff ff c4" + "b8 b7 b4 bf c4 a0" + ) for channel in case.split(): self.check_case(channel, self.make_sample(data, (12, 12))) @@ -204,29 +220,28 @@ def run_case(self, case): for x in range(channel.size[0]): for y in range(channel.size[1]): if px[x, y] != color: - message = "{} != {} for pixel {}".format( - px[x, y], color, (x, y)) + message = "{} != {} for pixel {}".format(px[x, y], color, (x, y)) self.assertEqual(px[x, y], color, message) def test_8u(self): - im, color = self.make_case('RGB', (0, 64, 255)) + im, color = self.make_case("RGB", (0, 64, 255)) r, g, b = im.split() self.run_case((r, color[0])) self.run_case((g, color[1])) self.run_case((b, color[2])) - self.run_case(self.make_case('L', 12)) + self.run_case(self.make_case("L", 12)) def test_32i(self): - self.run_case(self.make_case('I', 12)) - self.run_case(self.make_case('I', 0x7fffffff)) - self.run_case(self.make_case('I', -12)) - self.run_case(self.make_case('I', -1 << 31)) + self.run_case(self.make_case("I", 12)) + self.run_case(self.make_case("I", 0x7FFFFFFF)) + self.run_case(self.make_case("I", -12)) + self.run_case(self.make_case("I", -1 << 31)) def test_32f(self): - self.run_case(self.make_case('F', 1)) - self.run_case(self.make_case('F', 3.40282306074e+38)) - self.run_case(self.make_case('F', 1.175494e-38)) - self.run_case(self.make_case('F', 1.192093e-07)) + self.run_case(self.make_case("F", 1)) + self.run_case(self.make_case("F", 3.40282306074e38)) + self.run_case(self.make_case("F", 1.175494e-38)) + self.run_case(self.make_case("F", 1.192093e-07)) class CoreResampleAlphaCorrectTest(PillowTestCase): @@ -244,13 +259,16 @@ def run_levels_case(self, i): px = i.load() for y in range(i.size[1]): used_colors = {px[x, y][0] for x in range(i.size[0])} - self.assertEqual(256, len(used_colors), - 'All colors should present in resized image. ' - 'Only {} on {} line.'.format(len(used_colors), y)) + self.assertEqual( + 256, + len(used_colors), + "All colors should present in resized image. " + "Only {} on {} line.".format(len(used_colors), y), + ) @unittest.skip("current implementation isn't precise enough") def test_levels_rgba(self): - case = self.make_levels_case('RGBA') + case = self.make_levels_case("RGBA") self.run_levels_case(case.resize((512, 32), Image.BOX)) self.run_levels_case(case.resize((512, 32), Image.BILINEAR)) self.run_levels_case(case.resize((512, 32), Image.HAMMING)) @@ -259,7 +277,7 @@ def test_levels_rgba(self): @unittest.skip("current implementation isn't precise enough") def test_levels_la(self): - case = self.make_levels_case('LA') + case = self.make_levels_case("LA") self.run_levels_case(case.resize((512, 32), Image.BOX)) self.run_levels_case(case.resize((512, 32), Image.BILINEAR)) self.run_levels_case(case.resize((512, 32), Image.HAMMING)) @@ -281,24 +299,21 @@ def run_dirty_case(self, i, clean_pixel): for y in range(i.size[1]): for x in range(i.size[0]): if px[x, y][-1] != 0 and px[x, y][:-1] != clean_pixel: - message = 'pixel at ({}, {}) is differ:\n{}\n{}'\ - .format(x, y, px[x, y], clean_pixel) + message = "pixel at ({}, {}) is differ:\n{}\n{}".format( + x, y, px[x, y], clean_pixel + ) self.assertEqual(px[x, y][:3], clean_pixel, message) def test_dirty_pixels_rgba(self): - case = self.make_dirty_case('RGBA', (255, 255, 0, 128), (0, 0, 255, 0)) + case = self.make_dirty_case("RGBA", (255, 255, 0, 128), (0, 0, 255, 0)) self.run_dirty_case(case.resize((20, 20), Image.BOX), (255, 255, 0)) - self.run_dirty_case(case.resize((20, 20), Image.BILINEAR), - (255, 255, 0)) - self.run_dirty_case(case.resize((20, 20), Image.HAMMING), - (255, 255, 0)) - self.run_dirty_case(case.resize((20, 20), Image.BICUBIC), - (255, 255, 0)) - self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), - (255, 255, 0)) + self.run_dirty_case(case.resize((20, 20), Image.BILINEAR), (255, 255, 0)) + self.run_dirty_case(case.resize((20, 20), Image.HAMMING), (255, 255, 0)) + self.run_dirty_case(case.resize((20, 20), Image.BICUBIC), (255, 255, 0)) + self.run_dirty_case(case.resize((20, 20), Image.LANCZOS), (255, 255, 0)) def test_dirty_pixels_la(self): - case = self.make_dirty_case('LA', (255, 128), (0, 0)) + case = self.make_dirty_case("LA", (255, 128), (0, 0)) self.run_dirty_case(case.resize((20, 20), Image.BOX), (255,)) self.run_dirty_case(case.resize((20, 20), Image.BILINEAR), (255,)) self.run_dirty_case(case.resize((20, 20), Image.HAMMING), (255,)) @@ -309,27 +324,27 @@ def test_dirty_pixels_la(self): class CoreResamplePassesTest(PillowTestCase): @contextmanager def count(self, diff): - count = Image.core.get_stats()['new_count'] + count = Image.core.get_stats()["new_count"] yield - self.assertEqual(Image.core.get_stats()['new_count'] - count, diff) + self.assertEqual(Image.core.get_stats()["new_count"] - count, diff) def test_horizontal(self): - im = hopper('L') + im = hopper("L") with self.count(1): im.resize((im.size[0] - 10, im.size[1]), Image.BILINEAR) def test_vertical(self): - im = hopper('L') + im = hopper("L") with self.count(1): im.resize((im.size[0], im.size[1] - 10), Image.BILINEAR) def test_both(self): - im = hopper('L') + im = hopper("L") with self.count(2): im.resize((im.size[0] - 10, im.size[1] - 10), Image.BILINEAR) def test_box_horizontal(self): - im = hopper('L') + im = hopper("L") box = (20, 0, im.size[0] - 20, im.size[1]) with self.count(1): # the same size, but different box @@ -339,7 +354,7 @@ def test_box_horizontal(self): self.assert_image_similar(with_box, cropped, 0.1) def test_box_vertical(self): - im = hopper('L') + im = hopper("L") box = (0, 20, im.size[0], im.size[1] - 20) with self.count(1): # the same size, but different box @@ -354,7 +369,7 @@ def test_reduce(self): test_color = 254 for size in range(400000, 400010, 2): - i = Image.new('L', (size, 1), 0) + i = Image.new("L", (size, 1), 0) draw = ImageDraw.Draw(i) draw.rectangle((0, 0, i.size[0] // 2 - 1, 0), test_color) @@ -365,7 +380,7 @@ def test_reduce(self): def test_nonzero_coefficients(self): # regression test for the wrong coefficients calculation # due to bug https://github.com/python-pillow/Pillow/issues/2161 - im = Image.new('RGBA', (1280, 1280), (0x20, 0x40, 0x60, 0xff)) + im = Image.new("RGBA", (1280, 1280), (0x20, 0x40, 0x60, 0xFF)) histogram = im.resize((256, 256), Image.BICUBIC).histogram() # first channel @@ -375,21 +390,26 @@ def test_nonzero_coefficients(self): # third channel self.assertEqual(histogram[0x100 * 2 + 0x60], 0x10000) # fourth channel - self.assertEqual(histogram[0x100 * 3 + 0xff], 0x10000) + self.assertEqual(histogram[0x100 * 3 + 0xFF], 0x10000) class CoreResampleBoxTest(PillowTestCase): def test_wrong_arguments(self): im = hopper() - for resample in (Image.NEAREST, Image.BOX, Image.BILINEAR, - Image.HAMMING, Image.BICUBIC, Image.LANCZOS): + for resample in ( + Image.NEAREST, + Image.BOX, + Image.BILINEAR, + Image.HAMMING, + Image.BICUBIC, + Image.LANCZOS, + ): im.resize((32, 32), resample, (0, 0, im.width, im.height)) im.resize((32, 32), resample, (20, 20, im.width, im.height)) im.resize((32, 32), resample, (20, 20, 20, 100)) im.resize((32, 32), resample, (20, 20, 100, 20)) - with self.assertRaisesRegex(TypeError, - "must be sequence of length 4"): + with self.assertRaisesRegex(TypeError, "must be sequence of length 4"): im.resize((32, 32), resample, (im.width, im.height)) with self.assertRaisesRegex(ValueError, "can't be negative"): @@ -420,8 +440,7 @@ def split_range(size, tiles): for y0, y1 in split_range(dst_size[1], ytiles): for x0, x1 in split_range(dst_size[0], xtiles): - box = (x0 * scale[0], y0 * scale[1], - x1 * scale[0], y1 * scale[1]) + box = (x0 * scale[0], y0 * scale[1], x1 * scale[0], y1 * scale[1]) tile = im.resize((x1 - x0, y1 - y0), Image.BICUBIC, box) tiled.paste(tile, (x0, y0)) return tiled @@ -447,8 +466,7 @@ def test_subsample(self): # Image.BOX emulates supersampling (480 / 8 = 60, 360 / 8 = 45) supersampled = im.resize((60, 45), Image.BOX) - with_box = supersampled.resize(dst_size, Image.BICUBIC, - (0, 0, 59.125, 44.125)) + with_box = supersampled.resize(dst_size, Image.BICUBIC, (0, 0, 59.125, 44.125)) without_box = supersampled.resize(dst_size, Image.BICUBIC) # error with box should be much smaller than without @@ -458,7 +476,7 @@ def test_subsample(self): def test_formats(self): for resample in [Image.NEAREST, Image.BILINEAR]: - for mode in ['RGB', 'L', 'RGBA', 'LA', 'I', '']: + for mode in ["RGB", "L", "RGBA", "LA", "I", ""]: im = hopper(mode) box = (20, 20, im.size[0] - 20, im.size[1] - 20) with_box = im.resize((32, 32), resample, box) @@ -480,7 +498,7 @@ def test_passthrough(self): self.assertEqual(res.size, size) self.assert_image_equal(res, im.crop(box)) except AssertionError: - print('>>>', size, box) + print(">>>", size, box) raise def test_no_passthrough(self): @@ -500,7 +518,7 @@ def test_no_passthrough(self): # check that the difference at least that much self.assert_image_similar(res, im.crop(box), 20) except AssertionError: - print('>>>', size, box) + print(">>>", size, box) raise def test_skip_horizontal(self): @@ -518,10 +536,9 @@ def test_skip_horizontal(self): res = im.resize(size, flt, box) self.assertEqual(res.size, size) # Borders should be slightly different - self.assert_image_similar( - res, im.crop(box).resize(size, flt), 0.4) + self.assert_image_similar(res, im.crop(box).resize(size, flt), 0.4) except AssertionError: - print('>>>', size, box, flt) + print(">>>", size, box, flt) raise def test_skip_vertical(self): @@ -539,8 +556,7 @@ def test_skip_vertical(self): res = im.resize(size, flt, box) self.assertEqual(res.size, size) # Borders should be slightly different - self.assert_image_similar( - res, im.crop(box).resize(size, flt), 0.4) + self.assert_image_similar(res, im.crop(box).resize(size, flt), 0.4) except AssertionError: - print('>>>', size, box, flt) + print(">>>", size, box, flt) raise diff --git a/Tests/test_image_resize.py b/Tests/test_image_resize.py index c47c7317bb7..aa831faa811 100644 --- a/Tests/test_image_resize.py +++ b/Tests/test_image_resize.py @@ -9,15 +9,24 @@ class TestImagingCoreResize(PillowTestCase): - def resize(self, im, size, f): # Image class independent version of resize. im.load() return im._new(im.im.resize(size, f)) def test_nearest_mode(self): - for mode in ["1", "P", "L", "I", "F", "RGB", "RGBA", "CMYK", "YCbCr", - "I;16"]: # exotic mode + for mode in [ + "1", + "P", + "L", + "I", + "F", + "RGB", + "RGBA", + "CMYK", + "YCbCr", + "I;16", + ]: # exotic mode im = hopper(mode) r = self.resize(im, (15, 12), Image.NEAREST) self.assertEqual(r.mode, mode) @@ -25,12 +34,15 @@ def test_nearest_mode(self): self.assertEqual(r.im.bands, im.im.bands) def test_convolution_modes(self): - self.assertRaises(ValueError, self.resize, hopper("1"), - (15, 12), Image.BILINEAR) - self.assertRaises(ValueError, self.resize, hopper("P"), - (15, 12), Image.BILINEAR) - self.assertRaises(ValueError, self.resize, hopper("I;16"), - (15, 12), Image.BILINEAR) + self.assertRaises( + ValueError, self.resize, hopper("1"), (15, 12), Image.BILINEAR + ) + self.assertRaises( + ValueError, self.resize, hopper("P"), (15, 12), Image.BILINEAR + ) + self.assertRaises( + ValueError, self.resize, hopper("I;16"), (15, 12), Image.BILINEAR + ) for mode in ["L", "I", "F", "RGB", "RGBA", "CMYK", "YCbCr"]: im = hopper(mode) r = self.resize(im, (15, 12), Image.BILINEAR) @@ -39,15 +51,27 @@ def test_convolution_modes(self): self.assertEqual(r.im.bands, im.im.bands) def test_reduce_filters(self): - for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, - Image.HAMMING, Image.BICUBIC, Image.LANCZOS]: + for f in [ + Image.NEAREST, + Image.BOX, + Image.BILINEAR, + Image.HAMMING, + Image.BICUBIC, + Image.LANCZOS, + ]: r = self.resize(hopper("RGB"), (15, 12), f) self.assertEqual(r.mode, "RGB") self.assertEqual(r.size, (15, 12)) def test_enlarge_filters(self): - for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, - Image.HAMMING, Image.BICUBIC, Image.LANCZOS]: + for f in [ + Image.NEAREST, + Image.BOX, + Image.BILINEAR, + Image.HAMMING, + Image.BICUBIC, + Image.LANCZOS, + ]: r = self.resize(hopper("RGB"), (212, 195), f) self.assertEqual(r.mode, "RGB") self.assertEqual(r.size, (212, 195)) @@ -60,24 +84,29 @@ def test_endianness(self): # an endianness issues. samples = { - 'blank': Image.new('L', (2, 2), 0), - 'filled': Image.new('L', (2, 2), 255), - 'dirty': Image.new('L', (2, 2), 0), + "blank": Image.new("L", (2, 2), 0), + "filled": Image.new("L", (2, 2), 255), + "dirty": Image.new("L", (2, 2), 0), } - samples['dirty'].putpixel((1, 1), 128) - - for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, - Image.HAMMING, Image.BICUBIC, Image.LANCZOS]: + samples["dirty"].putpixel((1, 1), 128) + + for f in [ + Image.NEAREST, + Image.BOX, + Image.BILINEAR, + Image.HAMMING, + Image.BICUBIC, + Image.LANCZOS, + ]: # samples resized with current filter references = { - name: self.resize(ch, (4, 4), f) - for name, ch in samples.items() + name: self.resize(ch, (4, 4), f) for name, ch in samples.items() } for mode, channels_set in [ - ('RGB', ('blank', 'filled', 'dirty')), - ('RGBA', ('blank', 'blank', 'filled', 'dirty')), - ('LA', ('filled', 'dirty')), + ("RGB", ("blank", "filled", "dirty")), + ("RGBA", ("blank", "blank", "filled", "dirty")), + ("LA", ("filled", "dirty")), ]: for channels in set(permutations(channels_set)): # compile image from different channels permutations @@ -90,9 +119,15 @@ def test_endianness(self): self.assert_image_equal(ch, references[channels[i]]) def test_enlarge_zero(self): - for f in [Image.NEAREST, Image.BOX, Image.BILINEAR, - Image.HAMMING, Image.BICUBIC, Image.LANCZOS]: - r = self.resize(Image.new('RGB', (0, 0), "white"), (212, 195), f) + for f in [ + Image.NEAREST, + Image.BOX, + Image.BILINEAR, + Image.HAMMING, + Image.BICUBIC, + Image.LANCZOS, + ]: + r = self.resize(Image.new("RGB", (0, 0), "white"), (212, 195), f) self.assertEqual(r.mode, "RGB") self.assertEqual(r.size, (212, 195)) self.assertEqual(r.getdata()[0], (0, 0, 0)) @@ -102,12 +137,12 @@ def test_unknown_filter(self): class TestImageResize(PillowTestCase): - def test_resize(self): def resize(mode, size): out = hopper(mode).resize(size) self.assertEqual(out.mode, mode) self.assertEqual(out.size, size) + for mode in "1", "P", "L", "RGB", "I", "F": resize(mode, (112, 103)) resize(mode, (188, 214)) diff --git a/Tests/test_image_rotate.py b/Tests/test_image_rotate.py index 05043cd0684..a2363b6bc95 100644 --- a/Tests/test_image_rotate.py +++ b/Tests/test_image_rotate.py @@ -3,7 +3,6 @@ class TestImageRotate(PillowTestCase): - def rotate(self, im, mode, angle, center=None, translate=None): out = im.rotate(angle, center=center, translate=translate) self.assertEqual(out.mode, mode) @@ -24,12 +23,12 @@ def test_mode(self): def test_angle(self): for angle in (0, 90, 180, 270): - im = Image.open('Tests/images/test-card.png') + im = Image.open("Tests/images/test-card.png") self.rotate(im, im.mode, angle) def test_zero(self): for angle in (0, 45, 90, 180, 270): - im = Image.new('RGB', (0, 0)) + im = Image.new("RGB", (0, 0)) self.rotate(im, im.mode, angle) def test_resample(self): @@ -38,18 +37,20 @@ def test_resample(self): # >>> im = im.rotate(45, resample=Image.BICUBIC, expand=True) # >>> im.save('Tests/images/hopper_45.png') - target = Image.open('Tests/images/hopper_45.png') - for (resample, epsilon) in ((Image.NEAREST, 10), - (Image.BILINEAR, 5), - (Image.BICUBIC, 0)): + target = Image.open("Tests/images/hopper_45.png") + for (resample, epsilon) in ( + (Image.NEAREST, 10), + (Image.BILINEAR, 5), + (Image.BICUBIC, 0), + ): im = hopper() im = im.rotate(45, resample=resample, expand=True) self.assert_image_similar(im, target, epsilon) def test_center_0(self): im = hopper() - target = Image.open('Tests/images/hopper_45.png') - target_origin = target.size[1]/2 + target = Image.open("Tests/images/hopper_45.png") + target_origin = target.size[1] / 2 target = target.crop((0, target_origin, 128, target_origin + 128)) im = im.rotate(45, center=(0, 0), resample=Image.BICUBIC) @@ -58,7 +59,7 @@ def test_center_0(self): def test_center_14(self): im = hopper() - target = Image.open('Tests/images/hopper_45.png') + target = Image.open("Tests/images/hopper_45.png") target_origin = target.size[1] / 2 - 14 target = target.crop((6, target_origin, 128 + 6, target_origin + 128)) @@ -68,10 +69,11 @@ def test_center_14(self): def test_translate(self): im = hopper() - target = Image.open('Tests/images/hopper_45.png') + target = Image.open("Tests/images/hopper_45.png") target_origin = (target.size[1] / 2 - 64) - 5 - target = target.crop((target_origin, target_origin, - target_origin + 128, target_origin + 128)) + target = target.crop( + (target_origin, target_origin, target_origin + 128, target_origin + 128) + ) im = im.rotate(45, translate=(5, 5), resample=Image.BICUBIC) @@ -82,44 +84,43 @@ def test_fastpath_center(self): # resulting image should be black for angle in (90, 180, 270): im = hopper().rotate(angle, center=(-1, -1)) - self.assert_image_equal(im, Image.new('RGB', im.size, 'black')) + self.assert_image_equal(im, Image.new("RGB", im.size, "black")) def test_fastpath_translate(self): # if we post-translate by -128 # resulting image should be black for angle in (0, 90, 180, 270): im = hopper().rotate(angle, translate=(-128, -128)) - self.assert_image_equal(im, Image.new('RGB', im.size, 'black')) + self.assert_image_equal(im, Image.new("RGB", im.size, "black")) def test_center(self): im = hopper() self.rotate(im, im.mode, 45, center=(0, 0)) - self.rotate(im, im.mode, 45, translate=(im.size[0]/2, 0)) - self.rotate(im, im.mode, 45, center=(0, 0), - translate=(im.size[0]/2, 0)) + self.rotate(im, im.mode, 45, translate=(im.size[0] / 2, 0)) + self.rotate(im, im.mode, 45, center=(0, 0), translate=(im.size[0] / 2, 0)) def test_rotate_no_fill(self): - im = Image.new('RGB', (100, 100), 'green') - target = Image.open('Tests/images/rotate_45_no_fill.png') + im = Image.new("RGB", (100, 100), "green") + target = Image.open("Tests/images/rotate_45_no_fill.png") im = im.rotate(45) self.assert_image_equal(im, target) def test_rotate_with_fill(self): - im = Image.new('RGB', (100, 100), 'green') - target = Image.open('Tests/images/rotate_45_with_fill.png') - im = im.rotate(45, fillcolor='white') + im = Image.new("RGB", (100, 100), "green") + target = Image.open("Tests/images/rotate_45_with_fill.png") + im = im.rotate(45, fillcolor="white") self.assert_image_equal(im, target) def test_alpha_rotate_no_fill(self): # Alpha images are handled differently internally - im = Image.new('RGBA', (10, 10), 'green') + im = Image.new("RGBA", (10, 10), "green") im = im.rotate(45, expand=1) corner = im.getpixel((0, 0)) self.assertEqual(corner, (0, 0, 0, 0)) def test_alpha_rotate_with_fill(self): # Alpha images are handled differently internally - im = Image.new('RGBA', (10, 10), 'green') + im = Image.new("RGBA", (10, 10), "green") im = im.rotate(45, expand=1, fillcolor=(255, 0, 0, 255)) corner = im.getpixel((0, 0)) self.assertEqual(corner, (255, 0, 0, 255)) diff --git a/Tests/test_image_split.py b/Tests/test_image_split.py index 2d97dabc2e1..e1bc17be270 100644 --- a/Tests/test_image_split.py +++ b/Tests/test_image_split.py @@ -4,33 +4,35 @@ class TestImageSplit(PillowTestCase): - def test_split(self): def split(mode): layers = hopper(mode).split() return [(i.mode, i.size[0], i.size[1]) for i in layers] - self.assertEqual(split("1"), [('1', 128, 128)]) - self.assertEqual(split("L"), [('L', 128, 128)]) - self.assertEqual(split("I"), [('I', 128, 128)]) - self.assertEqual(split("F"), [('F', 128, 128)]) - self.assertEqual(split("P"), [('P', 128, 128)]) + + self.assertEqual(split("1"), [("1", 128, 128)]) + self.assertEqual(split("L"), [("L", 128, 128)]) + self.assertEqual(split("I"), [("I", 128, 128)]) + self.assertEqual(split("F"), [("F", 128, 128)]) + self.assertEqual(split("P"), [("P", 128, 128)]) self.assertEqual( - split("RGB"), [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + split("RGB"), [("L", 128, 128), ("L", 128, 128), ("L", 128, 128)] + ) self.assertEqual( split("RGBA"), - [('L', 128, 128), ('L', 128, 128), - ('L', 128, 128), ('L', 128, 128)]) + [("L", 128, 128), ("L", 128, 128), ("L", 128, 128), ("L", 128, 128)], + ) self.assertEqual( split("CMYK"), - [('L', 128, 128), ('L', 128, 128), - ('L', 128, 128), ('L', 128, 128)]) + [("L", 128, 128), ("L", 128, 128), ("L", 128, 128), ("L", 128, 128)], + ) self.assertEqual( - split("YCbCr"), - [('L', 128, 128), ('L', 128, 128), ('L', 128, 128)]) + split("YCbCr"), [("L", 128, 128), ("L", 128, 128), ("L", 128, 128)] + ) def test_split_merge(self): def split_merge(mode): return Image.merge(mode, hopper(mode).split()) + self.assert_image_equal(hopper("1"), split_merge("1")) self.assert_image_equal(hopper("L"), split_merge("L")) self.assert_image_equal(hopper("I"), split_merge("I")) @@ -44,7 +46,7 @@ def split_merge(mode): def test_split_open(self): codecs = dir(Image.core) - if 'zip_encoder' in codecs: + if "zip_encoder" in codecs: test_file = self.tempfile("temp.png") else: test_file = self.tempfile("temp.pcx") @@ -53,9 +55,10 @@ def split_open(mode): hopper(mode).save(test_file) im = Image.open(test_file) return len(im.split()) + self.assertEqual(split_open("1"), 1) self.assertEqual(split_open("L"), 1) self.assertEqual(split_open("P"), 1) self.assertEqual(split_open("RGB"), 3) - if 'zip_encoder' in codecs: + if "zip_encoder" in codecs: self.assertEqual(split_open("RGBA"), 4) diff --git a/Tests/test_image_thumbnail.py b/Tests/test_image_thumbnail.py index fbadf50cf4b..a53f3146123 100644 --- a/Tests/test_image_thumbnail.py +++ b/Tests/test_image_thumbnail.py @@ -3,7 +3,6 @@ class TestImageThumbnail(PillowTestCase): - def test_sanity(self): im = hopper() diff --git a/Tests/test_image_tobitmap.py b/Tests/test_image_tobitmap.py index c5c06ba013a..8af565f5e1f 100644 --- a/Tests/test_image_tobitmap.py +++ b/Tests/test_image_tobitmap.py @@ -2,7 +2,6 @@ class TestImageToBitmap(PillowTestCase): - def test_sanity(self): self.assertRaises(ValueError, lambda: hopper().tobitmap()) diff --git a/Tests/test_image_tobytes.py b/Tests/test_image_tobytes.py index 2bfee2da3f5..d21ef7f6f98 100644 --- a/Tests/test_image_tobytes.py +++ b/Tests/test_image_tobytes.py @@ -2,7 +2,6 @@ class TestImageToBytes(PillowTestCase): - def test_sanity(self): data = hopper().tobytes() self.assertIsInstance(data, bytes) diff --git a/Tests/test_image_transform.py b/Tests/test_image_transform.py index 384657d9849..feedf366e62 100644 --- a/Tests/test_image_transform.py +++ b/Tests/test_image_transform.py @@ -6,7 +6,6 @@ class TestImageTransform(PillowTestCase): - def test_sanity(self): from PIL import ImageTransform @@ -24,54 +23,61 @@ def test_sanity(self): im.transform((100, 100), transform) def test_extent(self): - im = hopper('RGB') + im = hopper("RGB") (w, h) = im.size + # fmt: off transformed = im.transform(im.size, Image.EXTENT, (0, 0, w//2, h//2), # ul -> lr Image.BILINEAR) + # fmt: on - scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0, 0, w, h)) + scaled = im.resize((w * 2, h * 2), Image.BILINEAR).crop((0, 0, w, h)) # undone -- precision? self.assert_image_similar(transformed, scaled, 23) def test_quad(self): # one simple quad transform, equivalent to scale & crop upper left quad - im = hopper('RGB') + im = hopper("RGB") (w, h) = im.size + # fmt: off transformed = im.transform(im.size, Image.QUAD, (0, 0, 0, h//2, # ul -> ccw around quad: w//2, h//2, w//2, 0), Image.BILINEAR) + # fmt: on - scaled = im.transform((w, h), Image.AFFINE, - (.5, 0, 0, 0, .5, 0), - Image.BILINEAR) + scaled = im.transform( + (w, h), Image.AFFINE, (0.5, 0, 0, 0, 0.5, 0), Image.BILINEAR + ) self.assert_image_equal(transformed, scaled) def test_fill(self): for mode, pixel in [ - ['RGB', (255, 0, 0)], - ['RGBA', (255, 0, 0, 255)], - ['LA', (76, 0)] + ["RGB", (255, 0, 0)], + ["RGBA", (255, 0, 0, 255)], + ["LA", (76, 0)], ]: im = hopper(mode) (w, h) = im.size - transformed = im.transform(im.size, Image.EXTENT, - (0, 0, - w*2, h*2), - Image.BILINEAR, - fillcolor='red') + transformed = im.transform( + im.size, + Image.EXTENT, + (0, 0, w * 2, h * 2), + Image.BILINEAR, + fillcolor="red", + ) - self.assertEqual(transformed.getpixel((w-1, h-1)), pixel) + self.assertEqual(transformed.getpixel((w - 1, h - 1)), pixel) def test_mesh(self): # this should be a checkerboard of halfsized hoppers in ul, lr - im = hopper('RGBA') + im = hopper("RGBA") (w, h) = im.size + # fmt: off transformed = im.transform(im.size, Image.MESH, [((0, 0, w//2, h//2), # box (0, 0, 0, h, @@ -80,54 +86,50 @@ def test_mesh(self): (0, 0, 0, h, w, h, w, 0))], # ul -> ccw around quad Image.BILINEAR) + # fmt: on - scaled = im.transform((w//2, h//2), Image.AFFINE, - (2, 0, 0, 0, 2, 0), - Image.BILINEAR) + scaled = im.transform( + (w // 2, h // 2), Image.AFFINE, (2, 0, 0, 0, 2, 0), Image.BILINEAR + ) - checker = Image.new('RGBA', im.size) + checker = Image.new("RGBA", im.size) checker.paste(scaled, (0, 0)) - checker.paste(scaled, (w//2, h//2)) + checker.paste(scaled, (w // 2, h // 2)) self.assert_image_equal(transformed, checker) # now, check to see that the extra area is (0, 0, 0, 0) - blank = Image.new('RGBA', (w//2, h//2), (0, 0, 0, 0)) + blank = Image.new("RGBA", (w // 2, h // 2), (0, 0, 0, 0)) - self.assert_image_equal(blank, transformed.crop((w//2, 0, w, h//2))) - self.assert_image_equal(blank, transformed.crop((0, h//2, w//2, h))) + self.assert_image_equal(blank, transformed.crop((w // 2, 0, w, h // 2))) + self.assert_image_equal(blank, transformed.crop((0, h // 2, w // 2, h))) def _test_alpha_premult(self, op): # create image with half white, half black, # with the black half transparent. # do op, # there should be no darkness in the white section. - im = Image.new('RGBA', (10, 10), (0, 0, 0, 0)) - im2 = Image.new('RGBA', (5, 10), (255, 255, 255, 255)) + im = Image.new("RGBA", (10, 10), (0, 0, 0, 0)) + im2 = Image.new("RGBA", (5, 10), (255, 255, 255, 255)) im.paste(im2, (0, 0)) im = op(im, (40, 10)) - im_background = Image.new('RGB', (40, 10), (255, 255, 255)) + im_background = Image.new("RGB", (40, 10), (255, 255, 255)) im_background.paste(im, (0, 0), im) hist = im_background.histogram() - self.assertEqual(40*10, hist[-1]) + self.assertEqual(40 * 10, hist[-1]) def test_alpha_premult_resize(self): - def op(im, sz): return im.resize(sz, Image.BILINEAR) self._test_alpha_premult(op) def test_alpha_premult_transform(self): - def op(im, sz): (w, h) = im.size - return im.transform(sz, Image.EXTENT, - (0, 0, - w, h), - Image.BILINEAR) + return im.transform(sz, Image.EXTENT, (0, 0, w, h), Image.BILINEAR) self._test_alpha_premult(op) @@ -146,10 +148,7 @@ def test_blank_fill(self): # Running by default, but I'd totally understand not doing it in # the future - pattern = [ - Image.new('RGBA', (1024, 1024), (a, a, a, a)) - for a in range(1, 65) - ] + pattern = [Image.new("RGBA", (1024, 1024), (a, a, a, a)) for a in range(1, 65)] # Yeah. Watch some JIT optimize this out. pattern = None # noqa: F841 @@ -164,27 +163,37 @@ def test_unknown_resampling_filter(self): im = hopper() (w, h) = im.size for resample in (Image.BOX, "unknown"): - self.assertRaises(ValueError, im.transform, (100, 100), Image.EXTENT, - (0, 0, - w, h), - resample) + self.assertRaises( + ValueError, + im.transform, + (100, 100), + Image.EXTENT, + (0, 0, w, h), + resample, + ) class TestImageTransformAffine(PillowTestCase): transform = Image.AFFINE def _test_image(self): - im = hopper('RGB') + im = hopper("RGB") return im.crop((10, 20, im.width - 10, im.height - 20)) def _test_rotate(self, deg, transpose): im = self._test_image() - angle = - math.radians(deg) + angle = -math.radians(deg) matrix = [ - round(math.cos(angle), 15), round(math.sin(angle), 15), 0.0, - round(-math.sin(angle), 15), round(math.cos(angle), 15), 0.0, - 0, 0] + round(math.cos(angle), 15), + round(math.sin(angle), 15), + 0.0, + round(-math.sin(angle), 15), + round(math.cos(angle), 15), + 0.0, + 0, + 0, + ] matrix[2] = (1 - matrix[0] - matrix[1]) * im.width / 2 matrix[5] = (1 - matrix[3] - matrix[4]) * im.height / 2 @@ -194,8 +203,9 @@ def _test_rotate(self, deg, transpose): transposed = im for resample in [Image.NEAREST, Image.BILINEAR, Image.BICUBIC]: - transformed = im.transform(transposed.size, self.transform, - matrix, resample) + transformed = im.transform( + transposed.size, self.transform, matrix, resample + ) self.assert_image_equal(transposed, transformed) def test_rotate_0_deg(self): @@ -214,21 +224,18 @@ def _test_resize(self, scale, epsilonscale): im = self._test_image() size_up = int(round(im.width * scale)), int(round(im.height * scale)) - matrix_up = [ - 1 / scale, 0, 0, - 0, 1 / scale, 0, - 0, 0] - matrix_down = [ - scale, 0, 0, - 0, scale, 0, - 0, 0] - - for resample, epsilon in [(Image.NEAREST, 0), - (Image.BILINEAR, 2), (Image.BICUBIC, 1)]: - transformed = im.transform( - size_up, self.transform, matrix_up, resample) + matrix_up = [1 / scale, 0, 0, 0, 1 / scale, 0, 0, 0] + matrix_down = [scale, 0, 0, 0, scale, 0, 0, 0] + + for resample, epsilon in [ + (Image.NEAREST, 0), + (Image.BILINEAR, 2), + (Image.BICUBIC, 1), + ]: + transformed = im.transform(size_up, self.transform, matrix_up, resample) transformed = transformed.transform( - im.size, self.transform, matrix_down, resample) + im.size, self.transform, matrix_down, resample + ) self.assert_image_similar(transformed, im, epsilon * epsilonscale) def test_resize_1_1x(self): @@ -250,28 +257,25 @@ def _test_translate(self, x, y, epsilonscale): im = self._test_image() size_up = int(round(im.width + x)), int(round(im.height + y)) - matrix_up = [ - 1, 0, -x, - 0, 1, -y, - 0, 0] - matrix_down = [ - 1, 0, x, - 0, 1, y, - 0, 0] - - for resample, epsilon in [(Image.NEAREST, 0), - (Image.BILINEAR, 1.5), (Image.BICUBIC, 1)]: - transformed = im.transform( - size_up, self.transform, matrix_up, resample) + matrix_up = [1, 0, -x, 0, 1, -y, 0, 0] + matrix_down = [1, 0, x, 0, 1, y, 0, 0] + + for resample, epsilon in [ + (Image.NEAREST, 0), + (Image.BILINEAR, 1.5), + (Image.BICUBIC, 1), + ]: + transformed = im.transform(size_up, self.transform, matrix_up, resample) transformed = transformed.transform( - im.size, self.transform, matrix_down, resample) + im.size, self.transform, matrix_down, resample + ) self.assert_image_similar(transformed, im, epsilon * epsilonscale) def test_translate_0_1(self): - self._test_translate(.1, 0, 3.7) + self._test_translate(0.1, 0, 3.7) def test_translate_0_6(self): - self._test_translate(.6, 0, 9.1) + self._test_translate(0.6, 0, 9.1) def test_translate_50(self): self._test_translate(50, 50, 0) diff --git a/Tests/test_image_transpose.py b/Tests/test_image_transpose.py index 1cce5e98642..021e174f264 100644 --- a/Tests/test_image_transpose.py +++ b/Tests/test_image_transpose.py @@ -1,15 +1,23 @@ from . import helper from .helper import PillowTestCase -from PIL.Image import (FLIP_LEFT_RIGHT, FLIP_TOP_BOTTOM, ROTATE_90, ROTATE_180, - ROTATE_270, TRANSPOSE, TRANSVERSE) +from PIL.Image import ( + FLIP_LEFT_RIGHT, + FLIP_TOP_BOTTOM, + ROTATE_90, + ROTATE_180, + ROTATE_270, + TRANSPOSE, + TRANSVERSE, +) class TestImageTranspose(PillowTestCase): - hopper = {mode: helper.hopper(mode).crop((0, 0, 121, 127)).copy() for mode in [ - 'L', 'RGB', 'I;16', 'I;16L', 'I;16B' - ]} + hopper = { + mode: helper.hopper(mode).crop((0, 0, 121, 127)).copy() + for mode in ["L", "RGB", "I;16", "I;16L", "I;16B"] + } def test_flip_left_right(self): def transpose(mode): @@ -19,10 +27,10 @@ def transpose(mode): self.assertEqual(out.size, im.size) x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((x-2, 1))) - self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, y-2))) - self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, y-2))) + self.assertEqual(im.getpixel((1, 1)), out.getpixel((x - 2, 1))) + self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((x - 2, y - 2))) + self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((1, y - 2))) for mode in self.hopper: transpose(mode) @@ -35,10 +43,10 @@ def transpose(mode): self.assertEqual(out.size, im.size) x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, y-2))) - self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((x-2, y-2))) - self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((x-2, 1))) + self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, y - 2))) + self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((x - 2, y - 2))) + self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((x - 2, 1))) for mode in self.hopper: transpose(mode) @@ -51,10 +59,10 @@ def transpose(mode): self.assertEqual(out.size, im.size[::-1]) x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, x-2))) - self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((1, y-2)), out.getpixel((y-2, x-2))) - self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((y-2, 1))) + self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, x - 2))) + self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((y - 2, x - 2))) + self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((y - 2, 1))) for mode in self.hopper: transpose(mode) @@ -67,10 +75,10 @@ def transpose(mode): self.assertEqual(out.size, im.size) x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((x-2, y-2))) - self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, y-2))) - self.assertEqual(im.getpixel((1, y-2)), out.getpixel((x-2, 1))) - self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((1, 1)), out.getpixel((x - 2, y - 2))) + self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((1, y - 2))) + self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((x - 2, 1))) + self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((1, 1))) for mode in self.hopper: transpose(mode) @@ -83,10 +91,10 @@ def transpose(mode): self.assertEqual(out.size, im.size[::-1]) x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((y-2, 1))) - self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((y-2, x-2))) - self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, x-2))) + self.assertEqual(im.getpixel((1, 1)), out.getpixel((y - 2, 1))) + self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((y - 2, x - 2))) + self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((1, x - 2))) for mode in self.hopper: transpose(mode) @@ -100,9 +108,9 @@ def transpose(mode): x, y = im.size self.assertEqual(im.getpixel((1, 1)), out.getpixel((1, 1))) - self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((1, x-2))) - self.assertEqual(im.getpixel((1, y-2)), out.getpixel((y-2, 1))) - self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((y-2, x-2))) + self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((1, x - 2))) + self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((y - 2, 1))) + self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((y - 2, x - 2))) for mode in self.hopper: transpose(mode) @@ -115,10 +123,10 @@ def transpose(mode): self.assertEqual(out.size, im.size[::-1]) x, y = im.size - self.assertEqual(im.getpixel((1, 1)), out.getpixel((y-2, x-2))) - self.assertEqual(im.getpixel((x-2, 1)), out.getpixel((y-2, 1))) - self.assertEqual(im.getpixel((1, y-2)), out.getpixel((1, x-2))) - self.assertEqual(im.getpixel((x-2, y-2)), out.getpixel((1, 1))) + self.assertEqual(im.getpixel((1, 1)), out.getpixel((y - 2, x - 2))) + self.assertEqual(im.getpixel((x - 2, 1)), out.getpixel((y - 2, 1))) + self.assertEqual(im.getpixel((1, y - 2)), out.getpixel((1, x - 2))) + self.assertEqual(im.getpixel((x - 2, y - 2)), out.getpixel((1, 1))) for mode in self.hopper: transpose(mode) @@ -130,19 +138,22 @@ def test_roundtrip(self): def transpose(first, second): return im.transpose(first).transpose(second) - self.assert_image_equal( - im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) - self.assert_image_equal( - im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) + self.assert_image_equal(im, transpose(FLIP_LEFT_RIGHT, FLIP_LEFT_RIGHT)) + self.assert_image_equal(im, transpose(FLIP_TOP_BOTTOM, FLIP_TOP_BOTTOM)) self.assert_image_equal(im, transpose(ROTATE_90, ROTATE_270)) self.assert_image_equal(im, transpose(ROTATE_180, ROTATE_180)) self.assert_image_equal( - im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM)) + im.transpose(TRANSPOSE), transpose(ROTATE_90, FLIP_TOP_BOTTOM) + ) self.assert_image_equal( - im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT)) + im.transpose(TRANSPOSE), transpose(ROTATE_270, FLIP_LEFT_RIGHT) + ) self.assert_image_equal( - im.transpose(TRANSVERSE), transpose(ROTATE_90, FLIP_LEFT_RIGHT)) + im.transpose(TRANSVERSE), transpose(ROTATE_90, FLIP_LEFT_RIGHT) + ) self.assert_image_equal( - im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM)) + im.transpose(TRANSVERSE), transpose(ROTATE_270, FLIP_TOP_BOTTOM) + ) self.assert_image_equal( - im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE)) + im.transpose(TRANSVERSE), transpose(ROTATE_180, TRANSPOSE) + ) diff --git a/Tests/test_imagechops.py b/Tests/test_imagechops.py index 8aa784cdd53..76ca397df10 100644 --- a/Tests/test_imagechops.py +++ b/Tests/test_imagechops.py @@ -15,7 +15,6 @@ class TestImageChops(PillowTestCase): - def test_sanity(self): im = hopper("L") @@ -264,11 +263,12 @@ def test_offset(self): # Assert self.assertEqual(new.getbbox(), (0, 45, 100, 96)) self.assertEqual(new.getpixel((50, 50)), BLACK) - self.assertEqual(new.getpixel((50+xoffset, 50+yoffset)), DARK_GREEN) + self.assertEqual(new.getpixel((50 + xoffset, 50 + yoffset)), DARK_GREEN) # Test no yoffset - self.assertEqual(ImageChops.offset(im, xoffset), - ImageChops.offset(im, xoffset, xoffset)) + self.assertEqual( + ImageChops.offset(im, xoffset), ImageChops.offset(im, xoffset, xoffset) + ) def test_screen(self): # Arrange @@ -343,7 +343,6 @@ def test_subtract_modulo_no_clip(self): self.assertEqual(new.getpixel((50, 50)), (241, 167, 127)) def test_logical(self): - def table(op, a, b): out = [] for x in (a, b): @@ -353,23 +352,14 @@ def table(op, a, b): out.append(op(imx, imy).getpixel((0, 0))) return tuple(out) - self.assertEqual( - table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) - self.assertEqual( - table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) - self.assertEqual( - table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) + self.assertEqual(table(ImageChops.logical_and, 0, 1), (0, 0, 0, 255)) + self.assertEqual(table(ImageChops.logical_or, 0, 1), (0, 255, 255, 255)) + self.assertEqual(table(ImageChops.logical_xor, 0, 1), (0, 255, 255, 0)) - self.assertEqual( - table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) - self.assertEqual( - table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) - self.assertEqual( - table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) + self.assertEqual(table(ImageChops.logical_and, 0, 128), (0, 0, 0, 255)) + self.assertEqual(table(ImageChops.logical_or, 0, 128), (0, 255, 255, 255)) + self.assertEqual(table(ImageChops.logical_xor, 0, 128), (0, 255, 255, 0)) - self.assertEqual( - table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) - self.assertEqual( - table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) - self.assertEqual( - table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) + self.assertEqual(table(ImageChops.logical_and, 0, 255), (0, 0, 0, 255)) + self.assertEqual(table(ImageChops.logical_or, 0, 255), (0, 255, 255, 255)) + self.assertEqual(table(ImageChops.logical_xor, 0, 255), (0, 255, 255, 0)) diff --git a/Tests/test_imagecms.py b/Tests/test_imagecms.py index de140710f31..678af449425 100644 --- a/Tests/test_imagecms.py +++ b/Tests/test_imagecms.py @@ -9,6 +9,7 @@ try: from PIL import ImageCms from PIL.ImageCms import ImageCmsProfile + ImageCms.core.profile_open except ImportError: # Skipped via setUp() @@ -20,10 +21,10 @@ class TestImageCms(PillowTestCase): - def setUp(self): try: from PIL import ImageCms + # need to hit getattr to trigger the delayed import error ImageCms.core.profile_open except ImportError as v: @@ -39,7 +40,7 @@ def test_sanity(self): # this mostly follows the cms_test outline. v = ImageCms.versions() # should return four strings - self.assertEqual(v[0], '1.0.0 pil') + self.assertEqual(v[0], "1.0.0 pil") self.assertEqual(list(map(type, v)), [str, str, str, str]) # internal version number @@ -82,57 +83,70 @@ def test_name(self): # get profile information for file self.assertEqual( ImageCms.getProfileName(SRGB).strip(), - 'IEC 61966-2-1 Default RGB Colour Space - sRGB') + "IEC 61966-2-1 Default RGB Colour Space - sRGB", + ) def test_info(self): self.skip_missing() self.assertEqual( - ImageCms.getProfileInfo(SRGB).splitlines(), [ - 'sRGB IEC61966-2-1 black scaled', '', - 'Copyright International Color Consortium, 2009', '']) + ImageCms.getProfileInfo(SRGB).splitlines(), + [ + "sRGB IEC61966-2-1 black scaled", + "", + "Copyright International Color Consortium, 2009", + "", + ], + ) def test_copyright(self): self.skip_missing() self.assertEqual( ImageCms.getProfileCopyright(SRGB).strip(), - 'Copyright International Color Consortium, 2009') + "Copyright International Color Consortium, 2009", + ) def test_manufacturer(self): self.skip_missing() - self.assertEqual( - ImageCms.getProfileManufacturer(SRGB).strip(), - '') + self.assertEqual(ImageCms.getProfileManufacturer(SRGB).strip(), "") def test_model(self): self.skip_missing() self.assertEqual( ImageCms.getProfileModel(SRGB).strip(), - 'IEC 61966-2-1 Default RGB Colour Space - sRGB') + "IEC 61966-2-1 Default RGB Colour Space - sRGB", + ) def test_description(self): self.skip_missing() self.assertEqual( ImageCms.getProfileDescription(SRGB).strip(), - 'sRGB IEC61966-2-1 black scaled') + "sRGB IEC61966-2-1 black scaled", + ) def test_intent(self): self.skip_missing() self.assertEqual(ImageCms.getDefaultIntent(SRGB), 0) - self.assertEqual(ImageCms.isIntentSupported( - SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, - ImageCms.DIRECTION_INPUT), 1) + self.assertEqual( + ImageCms.isIntentSupported( + SRGB, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT + ), + 1, + ) def test_profile_object(self): # same, using profile object p = ImageCms.createProfile("sRGB") - # self.assertEqual(ImageCms.getProfileName(p).strip(), - # 'sRGB built-in - (lcms internal)') - # self.assertEqual(ImageCms.getProfileInfo(p).splitlines(), - # ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) + # self.assertEqual(ImageCms.getProfileName(p).strip(), + # 'sRGB built-in - (lcms internal)') + # self.assertEqual(ImageCms.getProfileInfo(p).splitlines(), + # ['sRGB built-in', '', 'WhitePoint : D65 (daylight)', '', '']) self.assertEqual(ImageCms.getDefaultIntent(p), 0) - self.assertEqual(ImageCms.isIntentSupported( - p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, - ImageCms.DIRECTION_INPUT), 1) + self.assertEqual( + ImageCms.isIntentSupported( + p, ImageCms.INTENT_ABSOLUTE_COLORIMETRIC, ImageCms.DIRECTION_INPUT + ), + 1, + ) def test_extensions(self): # extensions @@ -141,7 +155,8 @@ def test_extensions(self): p = ImageCms.getOpenProfile(BytesIO(i.info["icc_profile"])) self.assertEqual( ImageCms.getProfileName(p).strip(), - 'IEC 61966-2.1 Default RGB colour space - sRGB') + "IEC 61966-2.1 Default RGB colour space - sRGB", + ) def test_exceptions(self): # Test mode mismatch @@ -152,18 +167,16 @@ def test_exceptions(self): # the procedural pyCMS API uses PyCMSError for all sorts of errors self.assertRaises( - ImageCms.PyCMSError, - ImageCms.profileToProfile, hopper(), "foo", "bar") - self.assertRaises( - ImageCms.PyCMSError, - ImageCms.buildTransform, "foo", "bar", "RGB", "RGB") + ImageCms.PyCMSError, ImageCms.profileToProfile, hopper(), "foo", "bar" + ) self.assertRaises( - ImageCms.PyCMSError, - ImageCms.getProfileName, None) + ImageCms.PyCMSError, ImageCms.buildTransform, "foo", "bar", "RGB", "RGB" + ) + self.assertRaises(ImageCms.PyCMSError, ImageCms.getProfileName, None) self.skip_missing() self.assertRaises( - ImageCms.PyCMSError, - ImageCms.isIntentSupported, SRGB, None, None) + ImageCms.PyCMSError, ImageCms.isIntentSupported, SRGB, None, None + ) def test_display_profile(self): # try fetching the profile for the current display device @@ -174,15 +187,13 @@ def test_lab_color_profile(self): ImageCms.createProfile("LAB", 6500) def test_unsupported_color_space(self): - self.assertRaises(ImageCms.PyCMSError, - ImageCms.createProfile, "unsupported") + self.assertRaises(ImageCms.PyCMSError, ImageCms.createProfile, "unsupported") def test_invalid_color_temperature(self): - self.assertRaises(ImageCms.PyCMSError, - ImageCms.createProfile, "LAB", "invalid") + self.assertRaises(ImageCms.PyCMSError, ImageCms.createProfile, "LAB", "invalid") def test_simple_lab(self): - i = Image.new('RGB', (10, 10), (128, 128, 128)) + i = Image.new("RGB", (10, 10), (128, 128, 128)) psRGB = ImageCms.createProfile("sRGB") pLab = ImageCms.createProfile("LAB") @@ -190,7 +201,7 @@ def test_simple_lab(self): i_lab = ImageCms.applyTransform(i, t) - self.assertEqual(i_lab.mode, 'LAB') + self.assertEqual(i_lab.mode, "LAB") k = i_lab.getpixel((0, 0)) # not a linear luminance map. so L != 128: @@ -217,7 +228,7 @@ def test_lab_color(self): # i.save('temp.lab.tif') # visually verified vs PS. - target = Image.open('Tests/images/hopper.Lab.tif') + target = Image.open("Tests/images/hopper.Lab.tif") self.assert_image_similar(i, target, 3.5) @@ -226,17 +237,17 @@ def test_lab_srgb(self): pLab = ImageCms.createProfile("LAB") t = ImageCms.buildTransform(pLab, psRGB, "LAB", "RGB") - img = Image.open('Tests/images/hopper.Lab.tif') + img = Image.open("Tests/images/hopper.Lab.tif") img_srgb = ImageCms.applyTransform(img, t) # img_srgb.save('temp.srgb.tif') # visually verified vs ps. self.assert_image_similar(hopper(), img_srgb, 30) - self.assertTrue(img_srgb.info['icc_profile']) + self.assertTrue(img_srgb.info["icc_profile"]) - profile = ImageCmsProfile(BytesIO(img_srgb.info['icc_profile'])) - self.assertIn('sRGB', ImageCms.getProfileDescription(profile)) + profile = ImageCmsProfile(BytesIO(img_srgb.info["icc_profile"])) + self.assertIn("sRGB", ImageCms.getProfileDescription(profile)) def test_lab_roundtrip(self): # check to see if we're at least internally consistent. @@ -248,8 +259,7 @@ def test_lab_roundtrip(self): i = ImageCms.applyTransform(hopper(), t) - self.assertEqual(i.info['icc_profile'], - ImageCmsProfile(pLab).tobytes()) + self.assertEqual(i.info["icc_profile"], ImageCmsProfile(pLab).tobytes()) out = ImageCms.applyTransform(i, t2) @@ -264,10 +274,10 @@ def test_profile_tobytes(self): # not the same bytes as the original icc_profile, # but it does roundtrip self.assertEqual(p.tobytes(), p2.tobytes()) - self.assertEqual(ImageCms.getProfileName(p), - ImageCms.getProfileName(p2)) - self.assertEqual(ImageCms.getProfileDescription(p), - ImageCms.getProfileDescription(p2)) + self.assertEqual(ImageCms.getProfileName(p), ImageCms.getProfileName(p2)) + self.assertEqual( + ImageCms.getProfileDescription(p), ImageCms.getProfileDescription(p2) + ) def test_extended_information(self): self.skip_missing() @@ -281,110 +291,150 @@ def assert_truncated_tuple_equal(tup1, tup2, digits=10): def truncate_tuple(tuple_or_float): return tuple( - truncate_tuple(val) if isinstance(val, tuple) - else int(val * power) / power for val in tuple_or_float) + truncate_tuple(val) + if isinstance(val, tuple) + else int(val * power) / power + for val in tuple_or_float + ) + self.assertEqual(truncate_tuple(tup1), truncate_tuple(tup2)) self.assertEqual(p.attributes, 4294967296) assert_truncated_tuple_equal( p.blue_colorant, - ((0.14306640625, 0.06060791015625, 0.7140960693359375), - (0.1558847490315394, 0.06603820639433387, 0.06060791015625))) + ( + (0.14306640625, 0.06060791015625, 0.7140960693359375), + (0.1558847490315394, 0.06603820639433387, 0.06060791015625), + ), + ) assert_truncated_tuple_equal( p.blue_primary, - ((0.14306641366715667, 0.06060790921083026, 0.7140960805782015), - (0.15588475410450106, 0.06603820408959558, 0.06060790921083026))) + ( + (0.14306641366715667, 0.06060790921083026, 0.7140960805782015), + (0.15588475410450106, 0.06603820408959558, 0.06060790921083026), + ), + ) assert_truncated_tuple_equal( p.chromatic_adaptation, - (((1.04791259765625, 0.0229339599609375, -0.050201416015625), - (0.02960205078125, 0.9904632568359375, -0.0170745849609375), - (-0.009246826171875, 0.0150604248046875, 0.7517852783203125)), - ((1.0267159024652783, 0.022470062342089134, 0.0229339599609375), - (0.02951378324103937, 0.9875098886387147, 0.9904632568359375), - (-0.012205438066465256, 0.01987915407854985, 0.0150604248046875)))) + ( + ( + (1.04791259765625, 0.0229339599609375, -0.050201416015625), + (0.02960205078125, 0.9904632568359375, -0.0170745849609375), + (-0.009246826171875, 0.0150604248046875, 0.7517852783203125), + ), + ( + (1.0267159024652783, 0.022470062342089134, 0.0229339599609375), + (0.02951378324103937, 0.9875098886387147, 0.9904632568359375), + (-0.012205438066465256, 0.01987915407854985, 0.0150604248046875), + ), + ), + ) self.assertIsNone(p.chromaticity) - self.assertEqual(p.clut, { - 0: (False, False, True), - 1: (False, False, True), - 2: (False, False, True), - 3: (False, False, True) - }) + self.assertEqual( + p.clut, + { + 0: (False, False, True), + 1: (False, False, True), + 2: (False, False, True), + 3: (False, False, True), + }, + ) self.assertIsNone(p.colorant_table) self.assertIsNone(p.colorant_table_out) self.assertIsNone(p.colorimetric_intent) - self.assertEqual(p.connection_space, 'XYZ ') - self.assertEqual(p.copyright, - 'Copyright International Color Consortium, 2009') - self.assertEqual(p.creation_date, - datetime.datetime(2009, 2, 27, 21, 36, 31)) - self.assertEqual(p.device_class, 'mntr') + self.assertEqual(p.connection_space, "XYZ ") + self.assertEqual(p.copyright, "Copyright International Color Consortium, 2009") + self.assertEqual(p.creation_date, datetime.datetime(2009, 2, 27, 21, 36, 31)) + self.assertEqual(p.device_class, "mntr") assert_truncated_tuple_equal( p.green_colorant, - ((0.3851470947265625, 0.7168731689453125, 0.097076416015625), - (0.32119769927720654, 0.5978443449048152, 0.7168731689453125))) + ( + (0.3851470947265625, 0.7168731689453125, 0.097076416015625), + (0.32119769927720654, 0.5978443449048152, 0.7168731689453125), + ), + ) assert_truncated_tuple_equal( p.green_primary, - ((0.3851470888162112, 0.7168731974161346, 0.09707641738998518), - (0.32119768793686687, 0.5978443567149709, 0.7168731974161346))) + ( + (0.3851470888162112, 0.7168731974161346, 0.09707641738998518), + (0.32119768793686687, 0.5978443567149709, 0.7168731974161346), + ), + ) self.assertEqual(p.header_flags, 0) - self.assertEqual(p.header_manufacturer, '\x00\x00\x00\x00') - self.assertEqual(p.header_model, '\x00\x00\x00\x00') - self.assertEqual(p.icc_measurement_condition, { - 'backing': (0.0, 0.0, 0.0), - 'flare': 0.0, - 'geo': 'unknown', - 'observer': 1, - 'illuminant_type': 'D65' - }) + self.assertEqual(p.header_manufacturer, "\x00\x00\x00\x00") + self.assertEqual(p.header_model, "\x00\x00\x00\x00") + self.assertEqual( + p.icc_measurement_condition, + { + "backing": (0.0, 0.0, 0.0), + "flare": 0.0, + "geo": "unknown", + "observer": 1, + "illuminant_type": "D65", + }, + ) self.assertEqual(p.icc_version, 33554432) self.assertIsNone(p.icc_viewing_condition) - self.assertEqual(p.intent_supported, { - 0: (True, True, True), - 1: (True, True, True), - 2: (True, True, True), - 3: (True, True, True) - }) + self.assertEqual( + p.intent_supported, + { + 0: (True, True, True), + 1: (True, True, True), + 2: (True, True, True), + 3: (True, True, True), + }, + ) self.assertTrue(p.is_matrix_shaper) self.assertEqual(p.luminance, ((0.0, 80.0, 0.0), (0.0, 1.0, 80.0))) self.assertIsNone(p.manufacturer) assert_truncated_tuple_equal( p.media_black_point, - ((0.012054443359375, 0.0124969482421875, 0.01031494140625), - (0.34573304157549234, 0.35842450765864337, 0.0124969482421875))) + ( + (0.012054443359375, 0.0124969482421875, 0.01031494140625), + (0.34573304157549234, 0.35842450765864337, 0.0124969482421875), + ), + ) assert_truncated_tuple_equal( p.media_white_point, - ((0.964202880859375, 1.0, 0.8249053955078125), - (0.3457029219802284, 0.3585375327567059, 1.0))) + ( + (0.964202880859375, 1.0, 0.8249053955078125), + (0.3457029219802284, 0.3585375327567059, 1.0), + ), + ) assert_truncated_tuple_equal( - (p.media_white_point_temperature,), - (5000.722328847392,)) - self.assertEqual(p.model, - 'IEC 61966-2-1 Default RGB Colour Space - sRGB') + (p.media_white_point_temperature,), (5000.722328847392,) + ) + self.assertEqual(p.model, "IEC 61966-2-1 Default RGB Colour Space - sRGB") self.assertIsNone(p.perceptual_rendering_intent_gamut) - self.assertEqual( - p.profile_description, 'sRGB IEC61966-2-1 black scaled') - self.assertEqual( - p.profile_id, b')\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r') + self.assertEqual(p.profile_description, "sRGB IEC61966-2-1 black scaled") + self.assertEqual(p.profile_id, b")\xf8=\xde\xaf\xf2U\xaexB\xfa\xe4\xca\x839\r") assert_truncated_tuple_equal( p.red_colorant, - ((0.436065673828125, 0.2224884033203125, 0.013916015625), - (0.6484536316398539, 0.3308524880306778, 0.2224884033203125))) + ( + (0.436065673828125, 0.2224884033203125, 0.013916015625), + (0.6484536316398539, 0.3308524880306778, 0.2224884033203125), + ), + ) assert_truncated_tuple_equal( p.red_primary, - ((0.43606566581047446, 0.22248840582960838, 0.013916015621759925), - (0.6484536250319214, 0.3308524944738204, 0.22248840582960838))) + ( + (0.43606566581047446, 0.22248840582960838, 0.013916015621759925), + (0.6484536250319214, 0.3308524944738204, 0.22248840582960838), + ), + ) self.assertEqual(p.rendering_intent, 0) self.assertIsNone(p.saturation_rendering_intent_gamut) self.assertIsNone(p.screening_description) self.assertIsNone(p.target) - self.assertEqual(p.technology, 'CRT ') + self.assertEqual(p.technology, "CRT ") self.assertEqual(p.version, 2.0) - self.assertEqual(p.viewing_condition, - 'Reference Viewing Condition in IEC 61966-2-1') - self.assertEqual(p.xcolor_space, 'RGB ') + self.assertEqual( + p.viewing_condition, "Reference Viewing Condition in IEC 61966-2-1" + ) + self.assertEqual(p.xcolor_space, "RGB ") def test_deprecations(self): self.skip_missing() @@ -431,34 +481,36 @@ def test_profile_typesafety(self): with self.assertRaises(TypeError): ImageCms.ImageCmsProfile(1).tobytes() - def assert_aux_channel_preserved(self, mode, - transform_in_place, preserved_channel): + def assert_aux_channel_preserved(self, mode, transform_in_place, preserved_channel): def create_test_image(): - # set up test image with something interesting in the tested aux - # channel. - nine_grid_deltas = [ # noqa: E128 + # set up test image with something interesting in the tested aux channel. + # fmt: off + nine_grid_deltas = [ # noqa: E131 (-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 0), (1, 1), ] + # fmt: on chans = [] bands = ImageMode.getmode(mode).bands for band_ndx in range(len(bands)): - channel_type = 'L' # 8-bit unorm + channel_type = "L" # 8-bit unorm channel_pattern = hopper(channel_type) # paste pattern with varying offsets to avoid correlation # potentially hiding some bugs (like channels getting mixed). paste_offset = ( int(band_ndx / float(len(bands)) * channel_pattern.size[0]), - int(band_ndx / float(len(bands) * 2) * channel_pattern.size[1]) + int(band_ndx / float(len(bands) * 2) * channel_pattern.size[1]), ) channel_data = Image.new(channel_type, channel_pattern.size) for delta in nine_grid_deltas: channel_data.paste( channel_pattern, - tuple(paste_offset[c] + delta[c] * channel_pattern.size[c] - for c in range(2)), + tuple( + paste_offset[c] + delta[c] * channel_pattern.size[c] + for c in range(2) + ), ) chans.append(channel_data) return Image.merge(mode, chans) @@ -470,42 +522,46 @@ def create_test_image(): source_profile = ImageCms.createProfile("sRGB") destination_profile = ImageCms.createProfile("sRGB") t = ImageCms.buildTransform( - source_profile, destination_profile, inMode=mode, outMode=mode) + source_profile, destination_profile, inMode=mode, outMode=mode + ) # apply transform if transform_in_place: ImageCms.applyTransform(source_image, t, inPlace=True) result_image = source_image else: - result_image = ImageCms.applyTransform( - source_image, t, inPlace=False) + result_image = ImageCms.applyTransform(source_image, t, inPlace=False) result_image_aux = result_image.getchannel(preserved_channel) self.assert_image_equal(source_image_aux, result_image_aux) def test_preserve_auxiliary_channels_rgba(self): self.assert_aux_channel_preserved( - mode='RGBA', transform_in_place=False, preserved_channel='A') + mode="RGBA", transform_in_place=False, preserved_channel="A" + ) def test_preserve_auxiliary_channels_rgba_in_place(self): self.assert_aux_channel_preserved( - mode='RGBA', transform_in_place=True, preserved_channel='A') + mode="RGBA", transform_in_place=True, preserved_channel="A" + ) def test_preserve_auxiliary_channels_rgbx(self): self.assert_aux_channel_preserved( - mode='RGBX', transform_in_place=False, preserved_channel='X') + mode="RGBX", transform_in_place=False, preserved_channel="X" + ) def test_preserve_auxiliary_channels_rgbx_in_place(self): self.assert_aux_channel_preserved( - mode='RGBX', transform_in_place=True, preserved_channel='X') + mode="RGBX", transform_in_place=True, preserved_channel="X" + ) def test_auxiliary_channels_isolated(self): # test data in aux channels does not affect non-aux channels aux_channel_formats = [ # format, profile, color-only format, source test image - ('RGBA', 'sRGB', 'RGB', hopper('RGBA')), - ('RGBX', 'sRGB', 'RGB', hopper('RGBX')), - ('LAB', 'LAB', 'LAB', Image.open('Tests/images/hopper.Lab.tif')), + ("RGBA", "sRGB", "RGB", hopper("RGBA")), + ("RGBX", "sRGB", "RGB", hopper("RGBX")), + ("LAB", "LAB", "LAB", Image.open("Tests/images/hopper.Lab.tif")), ] for src_format in aux_channel_formats: for dst_format in aux_channel_formats: @@ -519,25 +575,34 @@ def test_auxiliary_channels_isolated(self): destination_profile = ImageCms.createProfile(dst_format[1]) source_image = src_format[3] test_transform = ImageCms.buildTransform( - source_profile, destination_profile, - inMode=src_format[0], outMode=dst_format[0]) + source_profile, + destination_profile, + inMode=src_format[0], + outMode=dst_format[0], + ) # test conversion from aux-ful source if transform_in_place: test_image = source_image.copy() ImageCms.applyTransform( - test_image, test_transform, inPlace=True) + test_image, test_transform, inPlace=True + ) else: test_image = ImageCms.applyTransform( - source_image, test_transform, inPlace=False) + source_image, test_transform, inPlace=False + ) # reference conversion from aux-less source reference_transform = ImageCms.buildTransform( - source_profile, destination_profile, - inMode=src_format[2], outMode=dst_format[2]) + source_profile, + destination_profile, + inMode=src_format[2], + outMode=dst_format[2], + ) reference_image = ImageCms.applyTransform( - source_image.convert(src_format[2]), - reference_transform) + source_image.convert(src_format[2]), reference_transform + ) - self.assert_image_equal(test_image.convert(dst_format[2]), - reference_image) + self.assert_image_equal( + test_image.convert(dst_format[2]), reference_image + ) diff --git a/Tests/test_imagecolor.py b/Tests/test_imagecolor.py index cb9c9843c5d..c0983ec4cae 100644 --- a/Tests/test_imagecolor.py +++ b/Tests/test_imagecolor.py @@ -5,7 +5,6 @@ class TestImageColor(PillowTestCase): - def test_hash(self): # short 3 components self.assertEqual((255, 0, 0), ImageColor.getrgb("#f00")) @@ -31,12 +30,9 @@ def test_hash(self): # case insensitivity self.assertEqual(ImageColor.getrgb("#DEF"), ImageColor.getrgb("#def")) - self.assertEqual(ImageColor.getrgb("#CDEF"), - ImageColor.getrgb("#cdef")) - self.assertEqual(ImageColor.getrgb("#DEFDEF"), - ImageColor.getrgb("#defdef")) - self.assertEqual(ImageColor.getrgb("#CDEFCDEF"), - ImageColor.getrgb("#cdefcdef")) + self.assertEqual(ImageColor.getrgb("#CDEF"), ImageColor.getrgb("#cdef")) + self.assertEqual(ImageColor.getrgb("#DEFDEF"), ImageColor.getrgb("#defdef")) + self.assertEqual(ImageColor.getrgb("#CDEFCDEF"), ImageColor.getrgb("#cdefcdef")) # not a number self.assertRaises(ValueError, ImageColor.getrgb, "#fo0") @@ -81,49 +77,48 @@ def test_functions(self): self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(0,100%,100%)")) self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(360,100%,100%)")) - self.assertEqual((0, 255, 255), - ImageColor.getrgb("hsv(180,100%,100%)")) + self.assertEqual((0, 255, 255), ImageColor.getrgb("hsv(180,100%,100%)")) # alternate format - self.assertEqual(ImageColor.getrgb("hsb(0,100%,50%)"), - ImageColor.getrgb("hsv(0,100%,50%)")) + self.assertEqual( + ImageColor.getrgb("hsb(0,100%,50%)"), ImageColor.getrgb("hsv(0,100%,50%)") + ) # floats - self.assertEqual((254, 3, 3), - ImageColor.getrgb("hsl(0.1,99.2%,50.3%)")) - self.assertEqual((255, 0, 0), - ImageColor.getrgb("hsl(360.,100.0%,50%)")) + self.assertEqual((254, 3, 3), ImageColor.getrgb("hsl(0.1,99.2%,50.3%)")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl(360.,100.0%,50%)")) - self.assertEqual((253, 2, 2), - ImageColor.getrgb("hsv(0.1,99.2%,99.3%)")) - self.assertEqual((255, 0, 0), - ImageColor.getrgb("hsv(360.,100.0%,100%)")) + self.assertEqual((253, 2, 2), ImageColor.getrgb("hsv(0.1,99.2%,99.3%)")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv(360.,100.0%,100%)")) # case insensitivity - self.assertEqual(ImageColor.getrgb("RGB(255,0,0)"), - ImageColor.getrgb("rgb(255,0,0)")) - self.assertEqual(ImageColor.getrgb("RGB(100%,0%,0%)"), - ImageColor.getrgb("rgb(100%,0%,0%)")) - self.assertEqual(ImageColor.getrgb("RGBA(255,0,0,0)"), - ImageColor.getrgb("rgba(255,0,0,0)")) - self.assertEqual(ImageColor.getrgb("HSL(0,100%,50%)"), - ImageColor.getrgb("hsl(0,100%,50%)")) - self.assertEqual(ImageColor.getrgb("HSV(0,100%,50%)"), - ImageColor.getrgb("hsv(0,100%,50%)")) - self.assertEqual(ImageColor.getrgb("HSB(0,100%,50%)"), - ImageColor.getrgb("hsb(0,100%,50%)")) + self.assertEqual( + ImageColor.getrgb("RGB(255,0,0)"), ImageColor.getrgb("rgb(255,0,0)") + ) + self.assertEqual( + ImageColor.getrgb("RGB(100%,0%,0%)"), ImageColor.getrgb("rgb(100%,0%,0%)") + ) + self.assertEqual( + ImageColor.getrgb("RGBA(255,0,0,0)"), ImageColor.getrgb("rgba(255,0,0,0)") + ) + self.assertEqual( + ImageColor.getrgb("HSL(0,100%,50%)"), ImageColor.getrgb("hsl(0,100%,50%)") + ) + self.assertEqual( + ImageColor.getrgb("HSV(0,100%,50%)"), ImageColor.getrgb("hsv(0,100%,50%)") + ) + self.assertEqual( + ImageColor.getrgb("HSB(0,100%,50%)"), ImageColor.getrgb("hsb(0,100%,50%)") + ) # space agnosticism - self.assertEqual((255, 0, 0), - ImageColor.getrgb("rgb( 255 , 0 , 0 )")) - self.assertEqual((255, 0, 0), - ImageColor.getrgb("rgb( 100% , 0% , 0% )")) - self.assertEqual((255, 0, 0, 0), - ImageColor.getrgb("rgba( 255 , 0 , 0 , 0 )")) - self.assertEqual((255, 0, 0), - ImageColor.getrgb("hsl( 0 , 100% , 50% )")) - self.assertEqual((255, 0, 0), - ImageColor.getrgb("hsv( 0 , 100% , 100% )")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb( 255 , 0 , 0 )")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("rgb( 100% , 0% , 0% )")) + self.assertEqual( + (255, 0, 0, 0), ImageColor.getrgb("rgba( 255 , 0 , 0 , 0 )") + ) + self.assertEqual((255, 0, 0), ImageColor.getrgb("hsl( 0 , 100% , 50% )")) + self.assertEqual((255, 0, 0), ImageColor.getrgb("hsv( 0 , 100% , 100% )")) # wrong number of components self.assertRaises(ValueError, ImageColor.getrgb, "rgb(255,0)") @@ -153,35 +148,32 @@ def test_functions(self): def test_rounding_errors(self): for color in ImageColor.colormap: - expected = Image.new( - "RGB", (1, 1), color).convert("L").getpixel((0, 0)) - actual = ImageColor.getcolor(color, 'L') + expected = Image.new("RGB", (1, 1), color).convert("L").getpixel((0, 0)) + actual = ImageColor.getcolor(color, "L") self.assertEqual(expected, actual) self.assertEqual( - (0, 255, 115), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGB")) + (0, 255, 115), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGB") + ) Image.new("RGB", (1, 1), "white") self.assertEqual((0, 0, 0, 255), ImageColor.getcolor("black", "RGBA")) + self.assertEqual((255, 255, 255, 255), ImageColor.getcolor("white", "RGBA")) self.assertEqual( - (255, 255, 255, 255), ImageColor.getcolor("white", "RGBA")) - self.assertEqual( - (0, 255, 115, 33), - ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGBA")) + (0, 255, 115, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "RGBA") + ) Image.new("RGBA", (1, 1), "white") self.assertEqual(0, ImageColor.getcolor("black", "L")) self.assertEqual(255, ImageColor.getcolor("white", "L")) - self.assertEqual(162, - ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) + self.assertEqual(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "L")) Image.new("L", (1, 1), "white") self.assertEqual(0, ImageColor.getcolor("black", "1")) self.assertEqual(255, ImageColor.getcolor("white", "1")) # The following test is wrong, but is current behavior # The correct result should be 255 due to the mode 1 - self.assertEqual( - 162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) + self.assertEqual(162, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) # Correct behavior # self.assertEqual( # 255, ImageColor.getcolor("rgba(0, 255, 115, 33)", "1")) @@ -189,6 +181,5 @@ def test_rounding_errors(self): self.assertEqual((0, 255), ImageColor.getcolor("black", "LA")) self.assertEqual((255, 255), ImageColor.getcolor("white", "LA")) - self.assertEqual( - (162, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "LA")) + self.assertEqual((162, 33), ImageColor.getcolor("rgba(0, 255, 115, 33)", "LA")) Image.new("LA", (1, 1), "white") diff --git a/Tests/test_imagedraw.py b/Tests/test_imagedraw.py index cff08a39c4a..7abc34aab03 100644 --- a/Tests/test_imagedraw.py +++ b/Tests/test_imagedraw.py @@ -6,8 +6,8 @@ BLACK = (0, 0, 0) WHITE = (255, 255, 255) GRAY = (190, 190, 190) -DEFAULT_MODE = 'RGB' -IMAGES_PATH = os.path.join('Tests', 'images', 'imagedraw') +DEFAULT_MODE = "RGB" +IMAGES_PATH = os.path.join("Tests", "images", "imagedraw") # Image size W, H = 100, 100 @@ -30,7 +30,6 @@ class TestImageDraw(PillowTestCase): - def test_sanity(self): im = hopper("RGB").copy() @@ -62,8 +61,7 @@ def helper_arc(self, bbox, start, end): draw.arc(bbox, start, end) # Assert - self.assert_image_similar( - im, Image.open("Tests/images/imagedraw_arc.png"), 1) + self.assert_image_similar(im, Image.open("Tests/images/imagedraw_arc.png"), 1) def test_arc1(self): self.helper_arc(BBOX1, 0, 180) @@ -85,7 +83,8 @@ def test_arc_end_le_start(self): # Assert self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_arc_end_le_start.png")) + im, Image.open("Tests/images/imagedraw_arc_end_le_start.png") + ) def test_arc_no_loops(self): # No need to go in loops @@ -100,7 +99,8 @@ def test_arc_no_loops(self): # Assert self.assert_image_similar( - im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1) + im, Image.open("Tests/images/imagedraw_arc_no_loops.png"), 1 + ) def test_arc_width(self): # Arrange @@ -149,8 +149,7 @@ def test_bitmap(self): draw.bitmap((10, 10), small) # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_bitmap.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_bitmap.png")) def helper_chord(self, mode, bbox, start, end): # Arrange @@ -224,17 +223,15 @@ def test_ellipse_edge(self): draw = ImageDraw.Draw(im) # Act - draw.ellipse(((0, 0), (W-1, H)), fill="white") + draw.ellipse(((0, 0), (W - 1, H)), fill="white") # Assert self.assert_image_similar( - im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1) + im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1 + ) def test_ellipse_symmetric(self): - for bbox in [ - (25, 25, 76, 76), - (25, 25, 75, 75) - ]: + for bbox in [(25, 25, 76, 76), (25, 25, 75, 75)]: im = Image.new("RGB", (101, 101)) draw = ImageDraw.Draw(im) draw.ellipse(bbox, fill="green", outline="blue") @@ -285,8 +282,7 @@ def helper_line(self, points): draw.line(points, fill="yellow", width=2) # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_line.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) def test_line1(self): self.helper_line(POINTS1) @@ -312,8 +308,7 @@ def test_shape1(self): draw.shape(s, fill=1) # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_shape1.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_shape1.png")) def test_shape2(self): # Arrange @@ -333,8 +328,7 @@ def test_shape2(self): draw.shape(s, outline="blue") # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_shape2.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_shape2.png")) def helper_pieslice(self, bbox, start, end): # Arrange @@ -346,7 +340,8 @@ def helper_pieslice(self, bbox, start, end): # Assert self.assert_image_similar( - im, Image.open("Tests/images/imagedraw_pieslice.png"), 1) + im, Image.open("Tests/images/imagedraw_pieslice.png"), 1 + ) def test_pieslice1(self): self.helper_pieslice(BBOX1, -90, 45) @@ -389,8 +384,7 @@ def helper_point(self, points): draw.point(points, fill="yellow") # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_point.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_point.png")) def test_point1(self): self.helper_point(POINTS1) @@ -407,8 +401,7 @@ def helper_polygon(self, points): draw.polygon(points, fill="red", outline="blue") # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_polygon.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png")) def test_polygon1(self): self.helper_polygon(POINTS1) @@ -423,8 +416,7 @@ def test_polygon_kite(self): # Arrange im = Image.new(mode, (W, H)) draw = ImageDraw.Draw(im) - expected = "Tests/images/imagedraw_polygon_kite_{}.png".format( - mode) + expected = "Tests/images/imagedraw_polygon_kite_{}.png".format(mode) # Act draw.polygon(KITE_POINTS, fill="blue", outline="yellow") @@ -441,8 +433,7 @@ def helper_rectangle(self, bbox): draw.rectangle(bbox, fill="black", outline="green") # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_rectangle.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png")) def test_rectangle1(self): self.helper_rectangle(BBOX1) @@ -454,7 +445,7 @@ def test_big_rectangle(self): # Test drawing a rectangle bigger than the image # Arrange im = Image.new("RGB", (W, H)) - bbox = [(-1, -1), (W+1, H+1)] + bbox = [(-1, -1), (W + 1, H + 1)] draw = ImageDraw.Draw(im) expected = "Tests/images/imagedraw_big_rectangle.png" @@ -491,22 +482,18 @@ def test_rectangle_width_fill(self): def test_floodfill(self): red = ImageColor.getrgb("red") - for mode, value in [ - ("L", 1), - ("RGBA", (255, 0, 0, 0)), - ("RGB", red) - ]: + for mode, value in [("L", 1), ("RGBA", (255, 0, 0, 0)), ("RGB", red)]: # Arrange im = Image.new(mode, (W, H)) draw = ImageDraw.Draw(im) draw.rectangle(BBOX2, outline="yellow", fill="green") - centre_point = (int(W/2), int(H/2)) + centre_point = (int(W / 2), int(H / 2)) # Act ImageDraw.floodfill(im, centre_point, value) # Assert - expected = "Tests/images/imagedraw_floodfill_"+mode+".png" + expected = "Tests/images/imagedraw_floodfill_" + mode + ".png" im_floodfill = Image.open(expected) self.assert_image_equal(im, im_floodfill) @@ -530,16 +517,18 @@ def test_floodfill_border(self): im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) draw.rectangle(BBOX2, outline="yellow", fill="green") - centre_point = (int(W/2), int(H/2)) + centre_point = (int(W / 2), int(H / 2)) # Act ImageDraw.floodfill( - im, centre_point, ImageColor.getrgb("red"), - border=ImageColor.getrgb("black")) + im, + centre_point, + ImageColor.getrgb("red"), + border=ImageColor.getrgb("black"), + ) # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_floodfill2.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png")) def test_floodfill_thresh(self): # floodfill() is experimental @@ -548,21 +537,17 @@ def test_floodfill_thresh(self): im = Image.new("RGB", (W, H)) draw = ImageDraw.Draw(im) draw.rectangle(BBOX2, outline="darkgreen", fill="green") - centre_point = (int(W/2), int(H/2)) + centre_point = (int(W / 2), int(H / 2)) # Act - ImageDraw.floodfill( - im, centre_point, ImageColor.getrgb("red"), - thresh=30) + ImageDraw.floodfill(im, centre_point, ImageColor.getrgb("red"), thresh=30) # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_floodfill2.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_floodfill2.png")) - def create_base_image_draw(self, size, - mode=DEFAULT_MODE, - background1=WHITE, - background2=GRAY): + def create_base_image_draw( + self, size, mode=DEFAULT_MODE, background1=WHITE, background2=GRAY + ): img = Image.new(mode, size, background1) for x in range(0, size[0]): for y in range(0, size[1]): @@ -571,140 +556,152 @@ def create_base_image_draw(self, size, return img, ImageDraw.Draw(img) def test_square(self): - expected = Image.open(os.path.join(IMAGES_PATH, 'square.png')) + expected = Image.open(os.path.join(IMAGES_PATH, "square.png")) expected.load() img, draw = self.create_base_image_draw((10, 10)) draw.polygon([(2, 2), (2, 7), (7, 7), (7, 2)], BLACK) - self.assert_image_equal(img, expected, - 'square as normal polygon failed') + self.assert_image_equal(img, expected, "square as normal polygon failed") img, draw = self.create_base_image_draw((10, 10)) draw.polygon([(7, 7), (7, 2), (2, 2), (2, 7)], BLACK) - self.assert_image_equal(img, expected, - 'square as inverted polygon failed') + self.assert_image_equal(img, expected, "square as inverted polygon failed") img, draw = self.create_base_image_draw((10, 10)) draw.rectangle((2, 2, 7, 7), BLACK) - self.assert_image_equal(img, expected, - 'square as normal rectangle failed') + self.assert_image_equal(img, expected, "square as normal rectangle failed") img, draw = self.create_base_image_draw((10, 10)) draw.rectangle((7, 7, 2, 2), BLACK) - self.assert_image_equal( - img, expected, 'square as inverted rectangle failed') + self.assert_image_equal(img, expected, "square as inverted rectangle failed") def test_triangle_right(self): - expected = Image.open(os.path.join(IMAGES_PATH, 'triangle_right.png')) + expected = Image.open(os.path.join(IMAGES_PATH, "triangle_right.png")) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.polygon([(3, 5), (17, 5), (10, 12)], BLACK) - self.assert_image_equal(img, expected, 'triangle right failed') + self.assert_image_equal(img, expected, "triangle right failed") def test_line_horizontal(self): - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_horizontal_w2px_normal.png')) + expected = Image.open( + os.path.join(IMAGES_PATH, "line_horizontal_w2px_normal.png") + ) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 14, 5), BLACK, 2) self.assert_image_equal( - img, expected, 'line straight horizontal normal 2px wide failed') - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_horizontal_w2px_inverted.png')) + img, expected, "line straight horizontal normal 2px wide failed" + ) + expected = Image.open( + os.path.join(IMAGES_PATH, "line_horizontal_w2px_inverted.png") + ) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((14, 5, 5, 5), BLACK, 2) self.assert_image_equal( - img, expected, 'line straight horizontal inverted 2px wide failed') - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_horizontal_w3px.png')) + img, expected, "line straight horizontal inverted 2px wide failed" + ) + expected = Image.open(os.path.join(IMAGES_PATH, "line_horizontal_w3px.png")) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 14, 5), BLACK, 3) self.assert_image_equal( - img, expected, 'line straight horizontal normal 3px wide failed') + img, expected, "line straight horizontal normal 3px wide failed" + ) img, draw = self.create_base_image_draw((20, 20)) draw.line((14, 5, 5, 5), BLACK, 3) self.assert_image_equal( - img, expected, 'line straight horizontal inverted 3px wide failed') - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_horizontal_w101px.png')) + img, expected, "line straight horizontal inverted 3px wide failed" + ) + expected = Image.open(os.path.join(IMAGES_PATH, "line_horizontal_w101px.png")) expected.load() img, draw = self.create_base_image_draw((200, 110)) draw.line((5, 55, 195, 55), BLACK, 101) self.assert_image_equal( - img, expected, 'line straight horizontal 101px wide failed') + img, expected, "line straight horizontal 101px wide failed" + ) def test_line_h_s1_w2(self): - self.skipTest('failing') - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_horizontal_slope1px_w2px.png')) + self.skipTest("failing") + expected = Image.open( + os.path.join(IMAGES_PATH, "line_horizontal_slope1px_w2px.png") + ) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 14, 6), BLACK, 2) self.assert_image_equal( - img, expected, 'line horizontal 1px slope 2px wide failed') + img, expected, "line horizontal 1px slope 2px wide failed" + ) def test_line_vertical(self): - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_vertical_w2px_normal.png')) + expected = Image.open( + os.path.join(IMAGES_PATH, "line_vertical_w2px_normal.png") + ) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 5, 14), BLACK, 2) self.assert_image_equal( - img, expected, 'line straight vertical normal 2px wide failed') - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_vertical_w2px_inverted.png')) + img, expected, "line straight vertical normal 2px wide failed" + ) + expected = Image.open( + os.path.join(IMAGES_PATH, "line_vertical_w2px_inverted.png") + ) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 14, 5, 5), BLACK, 2) self.assert_image_equal( - img, expected, 'line straight vertical inverted 2px wide failed') - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_vertical_w3px.png')) + img, expected, "line straight vertical inverted 2px wide failed" + ) + expected = Image.open(os.path.join(IMAGES_PATH, "line_vertical_w3px.png")) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 5, 14), BLACK, 3) self.assert_image_equal( - img, expected, 'line straight vertical normal 3px wide failed') + img, expected, "line straight vertical normal 3px wide failed" + ) img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 14, 5, 5), BLACK, 3) self.assert_image_equal( - img, expected, 'line straight vertical inverted 3px wide failed') - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_vertical_w101px.png')) + img, expected, "line straight vertical inverted 3px wide failed" + ) + expected = Image.open(os.path.join(IMAGES_PATH, "line_vertical_w101px.png")) expected.load() img, draw = self.create_base_image_draw((110, 200)) draw.line((55, 5, 55, 195), BLACK, 101) - self.assert_image_equal(img, expected, - 'line straight vertical 101px wide failed') - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_vertical_slope1px_w2px.png')) + self.assert_image_equal( + img, expected, "line straight vertical 101px wide failed" + ) + expected = Image.open( + os.path.join(IMAGES_PATH, "line_vertical_slope1px_w2px.png") + ) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 6, 14), BLACK, 2) - self.assert_image_equal(img, expected, - 'line vertical 1px slope 2px wide failed') + self.assert_image_equal( + img, expected, "line vertical 1px slope 2px wide failed" + ) def test_line_oblique_45(self): - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_oblique_45_w3px_a.png')) + expected = Image.open(os.path.join(IMAGES_PATH, "line_oblique_45_w3px_a.png")) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 5, 14, 14), BLACK, 3) - self.assert_image_equal(img, expected, - 'line oblique 45 normal 3px wide A failed') + self.assert_image_equal( + img, expected, "line oblique 45 normal 3px wide A failed" + ) img, draw = self.create_base_image_draw((20, 20)) draw.line((14, 14, 5, 5), BLACK, 3) - self.assert_image_equal(img, expected, - 'line oblique 45 inverted 3px wide A failed') - expected = Image.open(os.path.join(IMAGES_PATH, - 'line_oblique_45_w3px_b.png')) + self.assert_image_equal( + img, expected, "line oblique 45 inverted 3px wide A failed" + ) + expected = Image.open(os.path.join(IMAGES_PATH, "line_oblique_45_w3px_b.png")) expected.load() img, draw = self.create_base_image_draw((20, 20)) draw.line((14, 5, 5, 14), BLACK, 3) - self.assert_image_equal(img, expected, - 'line oblique 45 normal 3px wide B failed') + self.assert_image_equal( + img, expected, "line oblique 45 normal 3px wide B failed" + ) img, draw = self.create_base_image_draw((20, 20)) draw.line((5, 14, 14, 5), BLACK, 3) - self.assert_image_equal(img, expected, - 'line oblique 45 inverted 3px wide B failed') + self.assert_image_equal( + img, expected, "line oblique 45 inverted 3px wide B failed" + ) def test_wide_line_dot(self): # Test drawing a wide "line" from one point to another just draws @@ -726,9 +723,22 @@ def test_line_joint(self): expected = "Tests/images/imagedraw_line_joint_curve.png" # Act - xy = [(400, 280), (380, 280), (450, 280), (440, 120), (350, 200), - (310, 280), (300, 280), (250, 280), (250, 200), (150, 200), - (150, 260), (50, 200), (150, 50), (250, 100)] + xy = [ + (400, 280), + (380, 280), + (450, 280), + (440, 120), + (350, 200), + (310, 280), + (300, 280), + (250, 280), + (250, 200), + (150, 200), + (150, 260), + (50, 200), + (150, 50), + (250, 100), + ] draw.line(xy, GRAY, 50, "curve") # Assert @@ -761,18 +771,14 @@ def test_same_color_outline(self): # Begin for mode in ["RGB", "L"]: - for fill, outline in [ - ["red", None], - ["red", "red"], - ["red", "#f00"] - ]: + for fill, outline in [["red", None], ["red", "red"], ["red", "#f00"]]: for operation, args in { - 'chord': [BBOX1, 0, 180], - 'ellipse': [BBOX1], - 'shape': [s], - 'pieslice': [BBOX1, -90, 45], - 'polygon': [[(18, 30), (85, 30), (60, 72)]], - 'rectangle': [BBOX1] + "chord": [BBOX1, 0, 180], + "ellipse": [BBOX1], + "shape": [s], + "pieslice": [BBOX1, -90, 45], + "polygon": [[(18, 30), (85, 30), (60, 72)]], + "rectangle": [BBOX1], }.items(): # Arrange im = Image.new(mode, (W, H)) @@ -784,6 +790,7 @@ def test_same_color_outline(self): draw_method(*args) # Assert - expected = ("Tests/images/imagedraw_outline" - "_{}_{}.png".format(operation, mode)) + expected = "Tests/images/imagedraw_outline_{}_{}.png".format( + operation, mode + ) self.assert_image_similar(im, Image.open(expected), 1) diff --git a/Tests/test_imagedraw2.py b/Tests/test_imagedraw2.py index 97033c8a765..5e75a08f76d 100644 --- a/Tests/test_imagedraw2.py +++ b/Tests/test_imagedraw2.py @@ -33,7 +33,6 @@ class TestImageDraw(PillowTestCase): - def test_sanity(self): im = hopper("RGB").copy() @@ -74,11 +73,12 @@ def test_ellipse_edge(self): brush = ImageDraw2.Brush("white") # Act - draw.ellipse(((0, 0), (W-1, H)), brush) + draw.ellipse(((0, 0), (W - 1, H)), brush) # Assert self.assert_image_similar( - im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1) + im, Image.open("Tests/images/imagedraw_ellipse_edge.png"), 1 + ) def helper_line(self, points): # Arrange @@ -90,8 +90,7 @@ def helper_line(self, points): draw.line(points, pen) # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_line.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) def test_line1_pen(self): self.helper_line(POINTS1) @@ -111,8 +110,7 @@ def test_line_pen_as_brush(self): draw.line(POINTS1, pen, brush) # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_line.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_line.png")) def helper_polygon(self, points): # Arrange @@ -125,8 +123,7 @@ def helper_polygon(self, points): draw.polygon(points, pen, brush) # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_polygon.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_polygon.png")) def test_polygon1(self): self.helper_polygon(POINTS1) @@ -145,8 +142,7 @@ def helper_rectangle(self, bbox): draw.rectangle(bbox, pen, brush) # Assert - self.assert_image_equal( - im, Image.open("Tests/images/imagedraw_rectangle.png")) + self.assert_image_equal(im, Image.open("Tests/images/imagedraw_rectangle.png")) def test_rectangle1(self): self.helper_rectangle(BBOX1) diff --git a/Tests/test_imageenhance.py b/Tests/test_imageenhance.py index 0e4e8c4f330..4f7c90559df 100644 --- a/Tests/test_imageenhance.py +++ b/Tests/test_imageenhance.py @@ -5,7 +5,6 @@ class TestImageEnhance(PillowTestCase): - def test_sanity(self): # FIXME: assert_image @@ -23,10 +22,10 @@ def test_crash(self): def _half_transparent_image(self): # returns an image, half transparent, half solid - im = hopper('RGB') + im = hopper("RGB") - transparent = Image.new('L', im.size, 0) - solid = Image.new('L', (im.size[0]//2, im.size[1]), 255) + transparent = Image.new("L", im.size, 0) + solid = Image.new("L", (im.size[0] // 2, im.size[1]), 255) transparent.paste(solid, (0, 0)) im.putalpha(transparent) @@ -34,8 +33,11 @@ def _half_transparent_image(self): def _check_alpha(self, im, original, op, amount): self.assertEqual(im.getbands(), original.getbands()) - self.assert_image_equal(im.getchannel('A'), original.getchannel('A'), - "Diff on %s: %s" % (op, amount)) + self.assert_image_equal( + im.getchannel("A"), + original.getchannel("A"), + "Diff on %s: %s" % (op, amount), + ) def test_alpha(self): # Issue https://github.com/python-pillow/Pillow/issues/899 @@ -43,8 +45,11 @@ def test_alpha(self): original = self._half_transparent_image() - for op in ['Color', 'Brightness', 'Contrast', 'Sharpness']: + for op in ["Color", "Brightness", "Contrast", "Sharpness"]: for amount in [0, 0.5, 1.0]: self._check_alpha( getattr(ImageEnhance, op)(original).enhance(amount), - original, op, amount) + original, + op, + amount, + ) diff --git a/Tests/test_imagefile.py b/Tests/test_imagefile.py index e2339d76de0..83170cb2ab5 100644 --- a/Tests/test_imagefile.py +++ b/Tests/test_imagefile.py @@ -8,6 +8,7 @@ try: from PIL import _webp + HAVE_WEBP = True except ImportError: HAVE_WEBP = False @@ -21,9 +22,7 @@ class TestImageFile(PillowTestCase): - def test_parser(self): - def roundtrip(format): im = hopper("L").resize((1000, 1000)) @@ -44,7 +43,7 @@ def roundtrip(format): self.assert_image_equal(*roundtrip("BMP")) im1, im2 = roundtrip("GIF") - self.assert_image_similar(im1.convert('P'), im2, 1) + self.assert_image_similar(im1.convert("P"), im2, 1) self.assert_image_equal(*roundtrip("IM")) self.assert_image_equal(*roundtrip("MSP")) if "zip_encoder" in codecs: @@ -69,7 +68,7 @@ def roundtrip(format): # md5sum: ba974835ff2d6f3f2fd0053a23521d4a # EPS comes back in RGB: - self.assert_image_similar(im1, im2.convert('L'), 20) + self.assert_image_similar(im1, im2.convert("L"), 20) if "jpeg_encoder" in codecs: im1, im2 = roundtrip("JPEG") # lossy compression @@ -78,7 +77,7 @@ def roundtrip(format): self.assertRaises(IOError, roundtrip, "PDF") def test_ico(self): - with open('Tests/images/python.ico', 'rb') as f: + with open("Tests/images/python.ico", "rb") as f: data = f.read() with ImageFile.Parser() as p: p.feed(data) @@ -158,14 +157,13 @@ def decode(self, buffer): class MockImageFile(ImageFile.ImageFile): def _open(self): - self.rawmode = 'RGBA' - self.mode = 'RGBA' + self.rawmode = "RGBA" + self.mode = "RGBA" self._size = (200, 200) - self.tile = [("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize), 32, None)] + self.tile = [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize), 32, None)] class TestPyDecoder(PillowTestCase): - def get_decoder(self): decoder = MockPyDecoder(None) @@ -173,11 +171,11 @@ def closure(mode, *args): decoder.__init__(mode, *args) return decoder - Image.register_decoder('MOCK', closure) + Image.register_decoder("MOCK", closure) return decoder def test_setimage(self): - buf = BytesIO(b'\x00'*255) + buf = BytesIO(b"\x00" * 255) im = MockImageFile(buf) d = self.get_decoder() @@ -189,10 +187,10 @@ def test_setimage(self): self.assertEqual(d.state.xsize, xsize) self.assertEqual(d.state.ysize, ysize) - self.assertRaises(ValueError, d.set_as_raw, b'\x00') + self.assertRaises(ValueError, d.set_as_raw, b"\x00") def test_extents_none(self): - buf = BytesIO(b'\x00'*255) + buf = BytesIO(b"\x00" * 255) im = MockImageFile(buf) im.tile = [("MOCK", None, 32, None)] @@ -206,35 +204,31 @@ def test_extents_none(self): self.assertEqual(d.state.ysize, 200) def test_negsize(self): - buf = BytesIO(b'\x00'*255) + buf = BytesIO(b"\x00" * 255) im = MockImageFile(buf) - im.tile = [("MOCK", (xoff, yoff, -10, yoff+ysize), 32, None)] + im.tile = [("MOCK", (xoff, yoff, -10, yoff + ysize), 32, None)] self.get_decoder() self.assertRaises(ValueError, im.load) - im.tile = [("MOCK", (xoff, yoff, xoff+xsize, -10), 32, None)] + im.tile = [("MOCK", (xoff, yoff, xoff + xsize, -10), 32, None)] self.assertRaises(ValueError, im.load) def test_oversize(self): - buf = BytesIO(b'\x00'*255) + buf = BytesIO(b"\x00" * 255) im = MockImageFile(buf) - im.tile = [ - ("MOCK", (xoff, yoff, xoff+xsize + 100, yoff+ysize), 32, None) - ] + im.tile = [("MOCK", (xoff, yoff, xoff + xsize + 100, yoff + ysize), 32, None)] self.get_decoder() self.assertRaises(ValueError, im.load) - im.tile = [ - ("MOCK", (xoff, yoff, xoff+xsize, yoff+ysize + 100), 32, None) - ] + im.tile = [("MOCK", (xoff, yoff, xoff + xsize, yoff + ysize + 100), 32, None)] self.assertRaises(ValueError, im.load) def test_no_format(self): - buf = BytesIO(b'\x00'*255) + buf = BytesIO(b"\x00" * 255) im = MockImageFile(buf) self.assertIsNone(im.format) @@ -248,7 +242,7 @@ def test_exif_jpeg(self): self.assertEqual(exif[40963], 450) self.assertEqual(exif[11], "gThumb 3.0.1") - out = self.tempfile('temp.jpg') + out = self.tempfile("temp.jpg") exif[258] = 8 del exif[40960] exif[40963] = 455 @@ -268,7 +262,7 @@ def test_exif_jpeg(self): self.assertEqual(exif[40963], 200) self.assertEqual(exif[305], "Adobe Photoshop CC 2017 (Macintosh)") - out = self.tempfile('temp.jpg') + out = self.tempfile("temp.jpg") exif[258] = 8 del exif[34665] exif[40963] = 455 @@ -281,14 +275,16 @@ def test_exif_jpeg(self): 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") + @unittest.skipIf( + not HAVE_WEBP or not _webp.HAVE_WEBPANIM, + "WebP support not installed with animation", + ) def test_exif_webp(self): im = Image.open("Tests/images/hopper.webp") exif = im.getexif() self.assertEqual(exif, {}) - out = self.tempfile('temp.webp') + out = self.tempfile("temp.webp") exif[258] = 8 exif[40963] = 455 exif[305] = "Pillow test" @@ -299,6 +295,7 @@ def check_exif(): self.assertEqual(reloaded_exif[258], 8) self.assertEqual(reloaded_exif[40963], 455) self.assertEqual(exif[305], "Pillow test") + im.save(out, exif=exif) check_exif() im.save(out, exif=exif, save_all=True) @@ -309,7 +306,7 @@ def test_exif_png(self): exif = im.getexif() self.assertEqual(exif, {274: 1}) - out = self.tempfile('temp.png') + out = self.tempfile("temp.png") exif[258] = 8 del exif[274] exif[40963] = 455 @@ -318,18 +315,11 @@ def test_exif_png(self): reloaded = Image.open(out) 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): im = Image.open("Tests/images/flower.jpg") exif = im.getexif() - self.assertEqual(exif.get_ifd(0xa005), { - 1: 'R98', - 2: b'0100', - 4097: 2272, - 4098: 1704, - }) + self.assertEqual( + exif.get_ifd(0xA005), {1: "R98", 2: b"0100", 4097: 2272, 4098: 1704} + ) diff --git a/Tests/test_imagefont.py b/Tests/test_imagefont.py index 9f1f10c953f..3388c205579 100644 --- a/Tests/test_imagefont.py +++ b/Tests/test_imagefont.py @@ -15,8 +15,8 @@ TEST_TEXT = "hey you\nyou are awesome\nthis looks awkward" -HAS_FREETYPE = features.check('freetype2') -HAS_RAQM = features.check('raqm') +HAS_FREETYPE = features.check("freetype2") +HAS_RAQM = features.check("raqm") class SimplePatcher(object): @@ -52,32 +52,24 @@ class TestImageFont(PillowTestCase): # Freetype has different metrics depending on the version. # (and, other things, but first things first) METRICS = { - ('>=2.3', '<2.4'): { - 'multiline': 30, - 'textsize': 12, - 'getters': (13, 16)}, - ('>=2.7',): { - 'multiline': 6.2, - 'textsize': 2.5, - 'getters': (12, 16)}, - 'Default': { - 'multiline': 0.5, - 'textsize': 0.5, - 'getters': (12, 16)}, - } + (">=2.3", "<2.4"): {"multiline": 30, "textsize": 12, "getters": (13, 16)}, + (">=2.7",): {"multiline": 6.2, "textsize": 2.5, "getters": (12, 16)}, + "Default": {"multiline": 0.5, "textsize": 0.5, "getters": (12, 16)}, + } def setUp(self): freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) - self.metrics = self.METRICS['Default'] + self.metrics = self.METRICS["Default"] for conditions, metrics in self.METRICS.items(): if not isinstance(conditions, tuple): continue for condition in conditions: - version = re.sub('[<=>]', '', condition) - if (condition.startswith('>=') and freetype >= version) or \ - (condition.startswith('<') and freetype < version): + version = re.sub("[<=>]", "", condition) + if (condition.startswith(">=") and freetype >= version) or ( + condition.startswith("<") and freetype < version + ): # Condition was met continue @@ -88,8 +80,9 @@ def setUp(self): self.metrics = metrics def get_font(self): - return ImageFont.truetype(FONT_PATH, FONT_SIZE, - layout_engine=self.LAYOUT_ENGINE) + return ImageFont.truetype( + FONT_PATH, FONT_SIZE, layout_engine=self.LAYOUT_ENGINE + ) def test_sanity(self): self.assertRegex(ImageFont.core.freetype2_version, r"\d+\.\d+\.\d+$") @@ -103,8 +96,8 @@ def test_font_properties(self): self.assertEqual(ttf_copy.path, FONT_PATH) self.assertEqual(ttf_copy.size, FONT_SIZE) - ttf_copy = ttf.font_variant(size=FONT_SIZE+1) - self.assertEqual(ttf_copy.size, FONT_SIZE+1) + ttf_copy = ttf.font_variant(size=FONT_SIZE + 1) + self.assertEqual(ttf_copy.size, FONT_SIZE + 1) second_font_path = "Tests/fonts/DejaVuSans.ttf" ttf_copy = ttf.font_variant(font=second_font_path) @@ -115,13 +108,14 @@ def test_font_with_name(self): self._render(FONT_PATH) def _font_as_bytes(self): - with open(FONT_PATH, 'rb') as f: + with open(FONT_PATH, "rb") as f: font_bytes = BytesIO(f.read()) return font_bytes def test_font_with_filelike(self): - ImageFont.truetype(self._font_as_bytes(), FONT_SIZE, - layout_engine=self.LAYOUT_ENGINE) + ImageFont.truetype( + self._font_as_bytes(), FONT_SIZE, layout_engine=self.LAYOUT_ENGINE + ) self._render(self._font_as_bytes()) # Usage note: making two fonts from the same buffer fails. # shared_bytes = self._font_as_bytes() @@ -129,12 +123,12 @@ def test_font_with_filelike(self): # self.assertRaises(Exception, _render, shared_bytes) def test_font_with_open_file(self): - with open(FONT_PATH, 'rb') as f: + with open(FONT_PATH, "rb") as f: self._render(f) def test_non_unicode_path(self): try: - tempfile = self.tempfile("temp_"+chr(128)+".ttf") + tempfile = self.tempfile("temp_" + chr(128) + ".ttf") except UnicodeEncodeError: self.skipTest("Unicode path could not be created") shutil.copy(FONT_PATH, tempfile) @@ -146,8 +140,9 @@ def test_unavailable_layout_engine(self): ImageFont.core.HAVE_RAQM = False try: - ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE, - layout_engine=ImageFont.LAYOUT_RAQM) + ttf = ImageFont.truetype( + FONT_PATH, FONT_SIZE, layout_engine=ImageFont.LAYOUT_RAQM + ) finally: ImageFont.core.HAVE_RAQM = have_raqm @@ -155,26 +150,25 @@ def test_unavailable_layout_engine(self): def _render(self, font): txt = "Hello World!" - ttf = ImageFont.truetype(font, FONT_SIZE, - layout_engine=self.LAYOUT_ENGINE) + ttf = ImageFont.truetype(font, FONT_SIZE, layout_engine=self.LAYOUT_ENGINE) ttf.getsize(txt) img = Image.new("RGB", (256, 64), "white") d = ImageDraw.Draw(img) - d.text((10, 10), txt, font=ttf, fill='black') + d.text((10, 10), txt, font=ttf, fill="black") return img def test_render_equal(self): img_path = self._render(FONT_PATH) - with open(FONT_PATH, 'rb') as f: + with open(FONT_PATH, "rb") as f: font_filelike = BytesIO(f.read()) img_filelike = self._render(font_filelike) self.assert_image_equal(img_path, img_filelike) def test_textsize_equal(self): - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) ttf = self.get_font() @@ -183,96 +177,101 @@ def test_textsize_equal(self): draw.text((10, 10), txt, font=ttf) draw.rectangle((10, 10, 10 + size[0], 10 + size[1])) - target = 'Tests/images/rectangle_surrounding_text.png' + target = "Tests/images/rectangle_surrounding_text.png" target_img = Image.open(target) # Epsilon ~.5 fails with FreeType 2.7 - self.assert_image_similar(im, target_img, self.metrics['textsize']) + self.assert_image_similar(im, target_img, self.metrics["textsize"]) def test_render_multiline(self): - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) ttf = self.get_font() - line_spacing = draw.textsize('A', font=ttf)[1] + 4 + line_spacing = draw.textsize("A", font=ttf)[1] + 4 lines = TEST_TEXT.split("\n") y = 0 for line in lines: draw.text((0, y), line, font=ttf) y += line_spacing - target = 'Tests/images/multiline_text.png' + target = "Tests/images/multiline_text.png" target_img = Image.open(target) # some versions of freetype have different horizontal spacing. # setting a tight epsilon, I'm showing the original test failure # at epsilon = ~38. - self.assert_image_similar(im, target_img, self.metrics['multiline']) + self.assert_image_similar(im, target_img, self.metrics["multiline"]) def test_render_multiline_text(self): ttf = self.get_font() # Test that text() correctly connects to multiline_text() # and that align defaults to left - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) draw.text((0, 0), TEST_TEXT, font=ttf) - target = 'Tests/images/multiline_text.png' + target = "Tests/images/multiline_text.png" target_img = Image.open(target) # Epsilon ~.5 fails with FreeType 2.7 - self.assert_image_similar(im, target_img, self.metrics['multiline']) + self.assert_image_similar(im, target_img, self.metrics["multiline"]) # Test that text() can pass on additional arguments # to multiline_text() - draw.text((0, 0), TEST_TEXT, fill=None, font=ttf, anchor=None, - spacing=4, align="left") + draw.text( + (0, 0), TEST_TEXT, fill=None, font=ttf, anchor=None, spacing=4, align="left" + ) draw.text((0, 0), TEST_TEXT, None, ttf, None, 4, "left") # Test align center and right - for align, ext in {"center": "_center", - "right": "_right"}.items(): - im = Image.new(mode='RGB', size=(300, 100)) + for align, ext in {"center": "_center", "right": "_right"}.items(): + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) draw.multiline_text((0, 0), TEST_TEXT, font=ttf, align=align) - target = 'Tests/images/multiline_text'+ext+'.png' + target = "Tests/images/multiline_text" + ext + ".png" target_img = Image.open(target) # Epsilon ~.5 fails with FreeType 2.7 - self.assert_image_similar(im, target_img, - self.metrics['multiline']) + self.assert_image_similar(im, target_img, self.metrics["multiline"]) def test_unknown_align(self): - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) ttf = self.get_font() # Act/Assert self.assertRaises( ValueError, - draw.multiline_text, (0, 0), TEST_TEXT, font=ttf, align="unknown") + draw.multiline_text, + (0, 0), + TEST_TEXT, + font=ttf, + align="unknown", + ) def test_draw_align(self): - im = Image.new('RGB', (300, 100), 'white') + im = Image.new("RGB", (300, 100), "white") draw = ImageDraw.Draw(im) ttf = self.get_font() line = "some text" - draw.text((100, 40), line, (0, 0, 0), font=ttf, align='left') + draw.text((100, 40), line, (0, 0, 0), font=ttf, align="left") def test_multiline_size(self): ttf = self.get_font() - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) # Test that textsize() correctly connects to multiline_textsize() - self.assertEqual(draw.textsize(TEST_TEXT, font=ttf), - draw.multiline_textsize(TEST_TEXT, font=ttf)) + self.assertEqual( + draw.textsize(TEST_TEXT, font=ttf), + draw.multiline_textsize(TEST_TEXT, font=ttf), + ) # Test that multiline_textsize corresponds to ImageFont.textsize() # for single line text - self.assertEqual(ttf.getsize('A'), - draw.multiline_textsize('A', font=ttf)) + self.assertEqual(ttf.getsize("A"), draw.multiline_textsize("A", font=ttf)) # Test that textsize() can pass on additional arguments # to multiline_textsize() @@ -281,25 +280,26 @@ def test_multiline_size(self): def test_multiline_width(self): ttf = self.get_font() - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - self.assertEqual(draw.textsize("longest line", font=ttf)[0], - draw.multiline_textsize("longest line\nline", - font=ttf)[0]) + self.assertEqual( + draw.textsize("longest line", font=ttf)[0], + draw.multiline_textsize("longest line\nline", font=ttf)[0], + ) def test_multiline_spacing(self): ttf = self.get_font() - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) draw.multiline_text((0, 0), TEST_TEXT, font=ttf, spacing=10) - target = 'Tests/images/multiline_text_spacing.png' + target = "Tests/images/multiline_text_spacing.png" target_img = Image.open(target) # Epsilon ~.5 fails with FreeType 2.7 - self.assert_image_similar(im, target_img, self.metrics['multiline']) + self.assert_image_similar(im, target_img, self.metrics["multiline"]) def test_rotated_transposed_font(self): img_grey = Image.new("L", (100, 100)) @@ -308,8 +308,7 @@ def test_rotated_transposed_font(self): font = self.get_font() orientation = Image.ROTATE_90 - transposed_font = ImageFont.TransposedFont( - font, orientation=orientation) + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) # Original font draw.font = font @@ -330,8 +329,7 @@ def test_unrotated_transposed_font(self): font = self.get_font() orientation = None - transposed_font = ImageFont.TransposedFont( - font, orientation=orientation) + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) # Original font draw.font = font @@ -349,8 +347,7 @@ def test_rotated_transposed_font_get_mask(self): text = "mask this" font = self.get_font() orientation = Image.ROTATE_90 - transposed_font = ImageFont.TransposedFont( - font, orientation=orientation) + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) # Act mask = transposed_font.getmask(text) @@ -363,8 +360,7 @@ def test_unrotated_transposed_font_get_mask(self): text = "mask this" font = self.get_font() orientation = None - transposed_font = ImageFont.TransposedFont( - font, orientation=orientation) + transposed_font = ImageFont.TransposedFont(font, orientation=orientation) # Act mask = transposed_font.getmask(text) @@ -380,7 +376,7 @@ def test_free_type_font_get_name(self): name = font.getname() # Assert - self.assertEqual(('FreeMono', 'Regular'), name) + self.assertEqual(("FreeMono", "Regular"), name) def test_free_type_font_get_metrics(self): # Arrange @@ -427,10 +423,10 @@ def test_load_path_not_found(self): def test_default_font(self): # Arrange txt = 'This is a "better than nothing" default font.' - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - target = 'Tests/images/default_font.png' + target = "Tests/images/default_font.png" target_img = Image.open(target) # Act @@ -444,16 +440,16 @@ def test_getsize_empty(self): # issue #2614 font = self.get_font() # should not crash. - self.assertEqual((0, 0), font.getsize('')) + self.assertEqual((0, 0), font.getsize("")) def test_render_empty(self): # issue 2666 font = self.get_font() - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) target = im.copy() draw = ImageDraw.Draw(im) # should not crash here. - draw.text((10, 10), '', font=font) + draw.text((10, 10), "", font=font) self.assert_image_equal(im, target) def test_unicode_pilfont(self): @@ -466,76 +462,99 @@ def test_unicode_pilfont(self): 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): - def loadable_font(filepath, size, index, encoding, - *args, **kwargs): + with SimplePatcher(ImageFont, "_FreeTypeFont", free_type_font): + + def loadable_font(filepath, size, index, encoding, *args, **kwargs): if filepath == path_to_fake: - return ImageFont._FreeTypeFont(FONT_PATH, size, index, - encoding, *args, **kwargs) - return ImageFont._FreeTypeFont(filepath, size, index, - encoding, *args, **kwargs) - with SimplePatcher(ImageFont, 'FreeTypeFont', loadable_font): + return ImageFont._FreeTypeFont( + FONT_PATH, size, index, encoding, *args, **kwargs + ) + return ImageFont._FreeTypeFont( + filepath, size, index, encoding, *args, **kwargs + ) + + with SimplePatcher(ImageFont, "FreeTypeFont", loadable_font): font = ImageFont.truetype(fontname) # Make sure it's loaded name = font.getname() - self.assertEqual(('FreeMono', 'Regular'), name) + self.assertEqual(("FreeMono", "Regular"), name) - @unittest.skipIf(sys.platform.startswith('win32'), - "requires Unix or macOS") + @unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") def test_find_linux_font(self): # 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'): + 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): + patched_env["XDG_DATA_DIRS"] = "/usr/share/:/usr/local/share/" + with SimplePatcher(os, "environ", patched_env): + def fake_walker(path): if path == font_directory: - return [(path, [], [ - 'Arial.ttf', 'Single.otf', 'Duplicate.otf', - 'Duplicate.ttf'], )] - return [(path, [], ['some_random_font.ttf'], )] - with SimplePatcher(os, 'walk', fake_walker): + return [ + ( + path, + [], + [ + "Arial.ttf", + "Single.otf", + "Duplicate.otf", + "Duplicate.ttf", + ], + ) + ] + return [(path, [], ["some_random_font.ttf"])] + + with SimplePatcher(os, "walk", fake_walker): # Test that the font loads both with and without the # extension self._test_fake_loading_font( - font_directory+'/Arial.ttf', 'Arial.ttf') - self._test_fake_loading_font( - font_directory+'/Arial.ttf', 'Arial') + font_directory + "/Arial.ttf", "Arial.ttf" + ) + self._test_fake_loading_font(font_directory + "/Arial.ttf", "Arial") # Test that non-ttf fonts can be found without the # extension self._test_fake_loading_font( - font_directory+'/Single.otf', 'Single') + font_directory + "/Single.otf", "Single" + ) # Test that ttf fonts are preferred if the extension is # not specified self._test_fake_loading_font( - font_directory+'/Duplicate.ttf', 'Duplicate') + font_directory + "/Duplicate.ttf", "Duplicate" + ) - @unittest.skipIf(sys.platform.startswith('win32'), - "requires Unix or macOS") + @unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") def test_find_macos_font(self): # Like the linux test, more cover hitting code rather than testing # correctness. - font_directory = '/System/Library/Fonts' - with SimplePatcher(sys, 'platform', 'darwin'): + font_directory = "/System/Library/Fonts" + with SimplePatcher(sys, "platform", "darwin"): + def fake_walker(path): if path == font_directory: - return [(path, [], - ['Arial.ttf', 'Single.otf', - 'Duplicate.otf', 'Duplicate.ttf'], )] - return [(path, [], ['some_random_font.ttf'], )] - with SimplePatcher(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') + return [ + ( + path, + [], + [ + "Arial.ttf", + "Single.otf", + "Duplicate.otf", + "Duplicate.ttf", + ], + ) + ] + return [(path, [], ["some_random_font.ttf"])] + + with SimplePatcher(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") self._test_fake_loading_font( - font_directory+'/Duplicate.ttf', 'Duplicate') + font_directory + "/Duplicate.ttf", "Duplicate" + ) def test_imagefont_getters(self): # Arrange @@ -549,26 +568,144 @@ def test_imagefont_getters(self): self.assertEqual(t.font.x_ppem, 20) self.assertEqual(t.font.y_ppem, 20) self.assertEqual(t.font.glyphs, 4177) - self.assertEqual(t.getsize('A'), (12, 16)) - self.assertEqual(t.getsize('AB'), (24, 16)) - self.assertEqual(t.getsize('M'), self.metrics['getters']) - self.assertEqual(t.getsize('y'), (12, 20)) - self.assertEqual(t.getsize('a'), (12, 16)) - self.assertEqual(t.getsize_multiline('A'), (12, 16)) - self.assertEqual(t.getsize_multiline('AB'), (24, 16)) - self.assertEqual(t.getsize_multiline('a'), (12, 16)) - self.assertEqual(t.getsize_multiline('ABC\n'), (36, 36)) - self.assertEqual(t.getsize_multiline('ABC\nA'), (36, 36)) - self.assertEqual(t.getsize_multiline('ABC\nAaaa'), (48, 36)) + self.assertEqual(t.getsize("A"), (12, 16)) + self.assertEqual(t.getsize("AB"), (24, 16)) + self.assertEqual(t.getsize("M"), self.metrics["getters"]) + self.assertEqual(t.getsize("y"), (12, 20)) + self.assertEqual(t.getsize("a"), (12, 16)) + self.assertEqual(t.getsize_multiline("A"), (12, 16)) + self.assertEqual(t.getsize_multiline("AB"), (24, 16)) + self.assertEqual(t.getsize_multiline("a"), (12, 16)) + self.assertEqual(t.getsize_multiline("ABC\n"), (36, 36)) + self.assertEqual(t.getsize_multiline("ABC\nA"), (36, 36)) + self.assertEqual(t.getsize_multiline("ABC\nAaaa"), (48, 36)) def test_complex_font_settings(self): # Arrange t = self.get_font() # Act / Assert if t.layout_engine == ImageFont.LAYOUT_BASIC: - self.assertRaises(KeyError, t.getmask, 'абвг', direction='rtl') - self.assertRaises(KeyError, t.getmask, 'абвг', features=['-kern']) - self.assertRaises(KeyError, t.getmask, 'абвг', language='sr') + self.assertRaises(KeyError, t.getmask, "абвг", direction="rtl") + self.assertRaises(KeyError, t.getmask, "абвг", features=["-kern"]) + self.assertRaises(KeyError, t.getmask, "абвг", language="sr") + + def test_variation_get(self): + font = self.get_font() + + freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) + if freetype < "2.9.1": + self.assertRaises(NotImplementedError, font.get_variation_names) + self.assertRaises(NotImplementedError, font.get_variation_axes) + return + + self.assertRaises(IOError, font.get_variation_names) + self.assertRaises(IOError, font.get_variation_axes) + + font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf") + self.assertEqual( + font.get_variation_names(), + [ + b"ExtraLight", + b"Light", + b"Regular", + b"Semibold", + b"Bold", + b"Black", + b"Black Medium Contrast", + b"Black High Contrast", + b"Default", + ], + ) + self.assertEqual( + font.get_variation_axes(), + [ + {"name": b"Weight", "minimum": 200, "maximum": 900, "default": 389}, + {"name": b"Contrast", "minimum": 0, "maximum": 100, "default": 0}, + ], + ) + + font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf") + self.assertEqual( + font.get_variation_names(), + [ + b"20", + b"40", + b"60", + b"80", + b"100", + b"120", + b"140", + b"160", + b"180", + b"200", + b"220", + b"240", + b"260", + b"280", + b"300", + b"Regular", + ], + ) + self.assertEqual( + font.get_variation_axes(), + [{"name": b"Size", "minimum": 0, "maximum": 300, "default": 0}], + ) + + def test_variation_set_by_name(self): + font = self.get_font() + + freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) + if freetype < "2.9.1": + self.assertRaises(NotImplementedError, font.set_variation_by_name, "Bold") + return + + self.assertRaises(IOError, font.set_variation_by_name, "Bold") + + def _check_text(font, path, epsilon): + im = Image.new("RGB", (100, 75), "white") + d = ImageDraw.Draw(im) + d.text((10, 10), "Text", font=font, fill="black") + + expected = Image.open(path) + self.assert_image_similar(im, expected, epsilon) + + font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf", 36) + _check_text(font, "Tests/images/variation_adobe.png", 11) + for name in ["Bold", b"Bold"]: + font.set_variation_by_name(name) + _check_text(font, "Tests/images/variation_adobe_name.png", 11) + + font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf", 36) + _check_text(font, "Tests/images/variation_tiny.png", 40) + for name in ["200", b"200"]: + font.set_variation_by_name(name) + _check_text(font, "Tests/images/variation_tiny_name.png", 40) + + def test_variation_set_by_axes(self): + font = self.get_font() + + freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version) + if freetype < "2.9.1": + self.assertRaises(NotImplementedError, font.set_variation_by_axes, [100]) + return + + self.assertRaises(IOError, font.set_variation_by_axes, [500, 50]) + + def _check_text(font, path, epsilon): + im = Image.new("RGB", (100, 75), "white") + d = ImageDraw.Draw(im) + d.text((10, 10), "Text", font=font, fill="black") + + expected = Image.open(path) + self.assert_image_similar(im, expected, epsilon) + + font = ImageFont.truetype("Tests/fonts/AdobeVFPrototype.ttf", 36) + font.set_variation_by_axes([500, 50]) + _check_text(font, "Tests/images/variation_adobe_axes.png", 5.1) + + font = ImageFont.truetype("Tests/fonts/TINY5x3GX.ttf", 36) + font.set_variation_by_axes([100]) + _check_text(font, "Tests/images/variation_tiny_axes.png", 32.5) @unittest.skipUnless(HAS_RAQM, "Raqm not Available") diff --git a/Tests/test_imagefont_bitmap.py b/Tests/test_imagefont_bitmap.py index eb44957e45e..5a8ee2e0810 100644 --- a/Tests/test_imagefont_bitmap.py +++ b/Tests/test_imagefont_bitmap.py @@ -12,16 +12,18 @@ @unittest.skipIf(not image_font_installed, "image font not installed") class TestImageFontBitmap(PillowTestCase): def test_similar(self): - text = 'EmbeddedBitmap' - font_outline = ImageFont.truetype( - font='Tests/fonts/DejaVuSans.ttf', size=24) + text = "EmbeddedBitmap" + font_outline = ImageFont.truetype(font="Tests/fonts/DejaVuSans.ttf", size=24) font_bitmap = ImageFont.truetype( - font='Tests/fonts/DejaVuSans-bitmap.ttf', size=24) + font="Tests/fonts/DejaVuSans-bitmap.ttf", size=24 + ) size_outline = font_outline.getsize(text) size_bitmap = font_bitmap.getsize(text) - size_final = (max(size_outline[0], size_bitmap[0]), - max(size_outline[1], size_bitmap[1])) - im_bitmap = Image.new('RGB', size_final, (255, 255, 255)) + size_final = ( + max(size_outline[0], size_bitmap[0]), + max(size_outline[1], size_bitmap[1]), + ) + im_bitmap = Image.new("RGB", size_final, (255, 255, 255)) im_outline = im_bitmap.copy() draw_bitmap = ImageDraw.Draw(im_bitmap) draw_outline = ImageDraw.Draw(im_outline) @@ -29,8 +31,13 @@ def test_similar(self): # Metrics are different on the bitmap and ttf fonts, # more so on some platforms and versions of freetype than others. # Mac has a 1px difference, linux doesn't. - draw_bitmap.text((0, size_final[1] - size_bitmap[1]), - text, fill=(0, 0, 0), font=font_bitmap) - draw_outline.text((0, size_final[1] - size_outline[1]), - text, fill=(0, 0, 0), font=font_outline) + draw_bitmap.text( + (0, size_final[1] - size_bitmap[1]), text, fill=(0, 0, 0), font=font_bitmap + ) + draw_outline.text( + (0, size_final[1] - size_outline[1]), + text, + fill=(0, 0, 0), + font=font_outline, + ) self.assert_image_similar(im_bitmap, im_outline, 20) diff --git a/Tests/test_imagefontctl.py b/Tests/test_imagefontctl.py index 3c498332c68..3ec5e905555 100644 --- a/Tests/test_imagefontctl.py +++ b/Tests/test_imagefontctl.py @@ -7,37 +7,35 @@ FONT_PATH = "Tests/fonts/DejaVuSans.ttf" -@unittest.skipUnless(features.check('raqm'), "Raqm Library is not installed.") +@unittest.skipUnless(features.check("raqm"), "Raqm Library is not installed.") class TestImagecomplextext(PillowTestCase): - def test_english(self): # smoke test, this should not fail ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'TEST', font=ttf, fill=500, direction='ltr') + draw.text((0, 0), "TEST", font=ttf, fill=500, direction="ltr") def test_complex_text(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'اهلا عمان', font=ttf, fill=500) + draw.text((0, 0), "اهلا عمان", font=ttf, fill=500) - target = 'Tests/images/test_text.png' + target = "Tests/images/test_text.png" target_img = Image.open(target) - self.assert_image_similar(im, target_img, .5) + self.assert_image_similar(im, target_img, 0.5) def test_y_offset(self): - ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", - FONT_SIZE) + ttf = ImageFont.truetype("Tests/fonts/NotoNastaliqUrdu-Regular.ttf", FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'العالم العربي', font=ttf, fill=500) + draw.text((0, 0), "العالم العربي", font=ttf, fill=500) - target = 'Tests/images/test_y_offset.png' + target = "Tests/images/test_y_offset.png" target_img = Image.open(target) self.assert_image_similar(im, target_img, 1.7) @@ -45,23 +43,22 @@ def test_y_offset(self): def test_complex_unicode_text(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'السلام عليكم', font=ttf, fill=500) + draw.text((0, 0), "السلام عليكم", font=ttf, fill=500) - target = 'Tests/images/test_complex_unicode_text.png' + target = "Tests/images/test_complex_unicode_text.png" target_img = Image.open(target) - self.assert_image_similar(im, target_img, .5) + self.assert_image_similar(im, target_img, 0.5) - ttf = ImageFont.truetype("Tests/fonts/KhmerOSBattambang-Regular.ttf", - FONT_SIZE) + ttf = ImageFont.truetype("Tests/fonts/KhmerOSBattambang-Regular.ttf", FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'លោកុប្បត្តិ', font=ttf, fill=500) + draw.text((0, 0), "លោកុប្បត្តិ", font=ttf, fill=500) - target = 'Tests/images/test_complex_unicode_text2.png' + target = "Tests/images/test_complex_unicode_text2.png" target_img = Image.open(target) self.assert_image_similar(im, target_img, 2.3) @@ -69,89 +66,119 @@ def test_complex_unicode_text(self): def test_text_direction_rtl(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'English عربي', font=ttf, fill=500, direction='rtl') + draw.text((0, 0), "English عربي", font=ttf, fill=500, direction="rtl") - target = 'Tests/images/test_direction_rtl.png' + target = "Tests/images/test_direction_rtl.png" target_img = Image.open(target) - self.assert_image_similar(im, target_img, .5) + self.assert_image_similar(im, target_img, 0.5) def test_text_direction_ltr(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'سلطنة عمان Oman', - font=ttf, fill=500, direction='ltr') + draw.text((0, 0), "سلطنة عمان Oman", font=ttf, fill=500, direction="ltr") - target = 'Tests/images/test_direction_ltr.png' + target = "Tests/images/test_direction_ltr.png" target_img = Image.open(target) - self.assert_image_similar(im, target_img, .5) + self.assert_image_similar(im, target_img, 0.5) def test_text_direction_rtl2(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) + draw = ImageDraw.Draw(im) + draw.text((0, 0), "Oman سلطنة عمان", font=ttf, fill=500, direction="rtl") + + target = "Tests/images/test_direction_ltr.png" + target_img = Image.open(target) + + self.assert_image_similar(im, target_img, 0.5) + + def test_text_direction_ttb(self): + ttf = ImageFont.truetype("Tests/fonts/NotoSansJP-Regular.otf", FONT_SIZE) + + im = Image.new(mode="RGB", size=(100, 300)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'Oman سلطنة عمان', - font=ttf, fill=500, direction='rtl') + try: + draw.text((0, 0), "English あい", font=ttf, fill=500, direction="ttb") + except ValueError as ex: + if str(ex) == "libraqm 0.7 or greater required for 'ttb' direction": + self.skipTest("libraqm 0.7 or greater not available") - target = 'Tests/images/test_direction_ltr.png' + target = "Tests/images/test_direction_ttb.png" target_img = Image.open(target) - self.assert_image_similar(im, target_img, .5) + self.assert_image_similar(im, target_img, 1.15) def test_ligature_features(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'filling', font=ttf, fill=500, features=['-liga']) - target = 'Tests/images/test_ligature_features.png' + draw.text((0, 0), "filling", font=ttf, fill=500, features=["-liga"]) + target = "Tests/images/test_ligature_features.png" target_img = Image.open(target) - self.assert_image_similar(im, target_img, .5) + self.assert_image_similar(im, target_img, 0.5) - liga_size = ttf.getsize('fi', features=['-liga']) + liga_size = ttf.getsize("fi", features=["-liga"]) self.assertEqual(liga_size, (13, 19)) def test_kerning_features(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'TeToAV', font=ttf, fill=500, features=['-kern']) + draw.text((0, 0), "TeToAV", font=ttf, fill=500, features=["-kern"]) - target = 'Tests/images/test_kerning_features.png' + target = "Tests/images/test_kerning_features.png" target_img = Image.open(target) - self.assert_image_similar(im, target_img, .5) + self.assert_image_similar(im, target_img, 0.5) def test_arabictext_features(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) + draw = ImageDraw.Draw(im) + draw.text( + (0, 0), + "اللغة العربية", + font=ttf, + fill=500, + features=["-fina", "-init", "-medi"], + ) + + target = "Tests/images/test_arabictext_features.png" + target_img = Image.open(target) + + self.assert_image_similar(im, target_img, 0.5) + + def test_x_max_and_y_offset(self): + ttf = ImageFont.truetype("Tests/fonts/ArefRuqaa-Regular.ttf", 40) + + im = Image.new(mode="RGB", size=(50, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'اللغة العربية', font=ttf, fill=500, - features=['-fina', '-init', '-medi']) + draw.text((0, 0), "لح", font=ttf, fill=500) - target = 'Tests/images/test_arabictext_features.png' + target = "Tests/images/test_x_max_and_y_offset.png" target_img = Image.open(target) - self.assert_image_similar(im, target_img, .5) + self.assert_image_similar(im, target_img, 0.5) def test_language(self): ttf = ImageFont.truetype(FONT_PATH, FONT_SIZE) - im = Image.new(mode='RGB', size=(300, 100)) + im = Image.new(mode="RGB", size=(300, 100)) draw = ImageDraw.Draw(im) - draw.text((0, 0), 'абвг', font=ttf, fill=500, - language='sr') + draw.text((0, 0), "абвг", font=ttf, fill=500, language="sr") - target = 'Tests/images/test_language.png' + target = "Tests/images/test_language.png" target_img = Image.open(target) - self.assert_image_similar(im, target_img, .5) + self.assert_image_similar(im, target_img, 0.5) diff --git a/Tests/test_imagegrab.py b/Tests/test_imagegrab.py index 97014cef8e3..9ead827e013 100644 --- a/Tests/test_imagegrab.py +++ b/Tests/test_imagegrab.py @@ -7,37 +7,37 @@ from PIL import ImageGrab class TestImageGrab(PillowTestCase): - def test_grab(self): - for im in [ - ImageGrab.grab(), - ImageGrab.grab(include_layered_windows=True) - ]: + for im in [ImageGrab.grab(), ImageGrab.grab(include_layered_windows=True)]: self.assert_image(im, im.mode, im.size) def test_grabclipboard(self): if sys.platform == "darwin": - subprocess.call(['screencapture', '-cx']) + subprocess.call(["screencapture", "-cx"]) else: - p = subprocess.Popen(['powershell', '-command', '-'], - stdin=subprocess.PIPE) - p.stdin.write(b'''[Reflection.Assembly]::LoadWithPartialName("System.Drawing") + p = subprocess.Popen( + ["powershell", "-command", "-"], stdin=subprocess.PIPE + ) + p.stdin.write( + b"""[Reflection.Assembly]::LoadWithPartialName("System.Drawing") [Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") $bmp = New-Object Drawing.Bitmap 200, 200 -[Windows.Forms.Clipboard]::SetImage($bmp)''') +[Windows.Forms.Clipboard]::SetImage($bmp)""" + ) p.communicate() im = ImageGrab.grabclipboard() self.assert_image(im, im.mode, im.size) + except ImportError: + class TestImageGrab(PillowTestCase): def test_skip(self): self.skipTest("ImportError") class TestImageGrabImport(PillowTestCase): - def test_import(self): # Arrange exception = None @@ -45,6 +45,7 @@ def test_import(self): # Act try: from PIL import ImageGrab + ImageGrab.__name__ # dummy to prevent Pyflakes warning except Exception as e: exception = e @@ -54,5 +55,4 @@ def test_import(self): self.assertIsNone(exception) else: self.assertIsInstance(exception, ImportError) - self.assertEqual(str(exception), - "ImageGrab is macOS and Windows only") + self.assertEqual(str(exception), "ImageGrab is macOS and Windows only") diff --git a/Tests/test_imagemath.py b/Tests/test_imagemath.py index 8273b63c14e..a1a9bad0ffa 100644 --- a/Tests/test_imagemath.py +++ b/Tests/test_imagemath.py @@ -27,15 +27,13 @@ def pixel(im): class TestImageMath(PillowTestCase): - def test_sanity(self): self.assertEqual(ImageMath.eval("1"), 1) self.assertEqual(ImageMath.eval("1+A", A=2), 3) self.assertEqual(pixel(ImageMath.eval("A+B", A=A, B=B)), "I 3") self.assertEqual(pixel(ImageMath.eval("A+B", images)), "I 3") self.assertEqual(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") - self.assertEqual(pixel( - ImageMath.eval("int(float(A)+B)", images)), "I 3") + self.assertEqual(pixel(ImageMath.eval("int(float(A)+B)", images)), "I 3") def test_ops(self): @@ -47,16 +45,16 @@ def test_ops(self): self.assertEqual(pixel(ImageMath.eval("A*B", images)), "I 2") self.assertEqual(pixel(ImageMath.eval("A/B", images)), "I 0") self.assertEqual(pixel(ImageMath.eval("B**2", images)), "I 4") - self.assertEqual(pixel( - ImageMath.eval("B**33", images)), "I 2147483647") + self.assertEqual(pixel(ImageMath.eval("B**33", images)), "I 2147483647") self.assertEqual(pixel(ImageMath.eval("float(A)+B", images)), "F 3.0") self.assertEqual(pixel(ImageMath.eval("float(A)-B", images)), "F -1.0") self.assertEqual(pixel(ImageMath.eval("float(A)*B", images)), "F 2.0") self.assertEqual(pixel(ImageMath.eval("float(A)/B", images)), "F 0.5") self.assertEqual(pixel(ImageMath.eval("float(B)**2", images)), "F 4.0") - self.assertEqual(pixel( - ImageMath.eval("float(B)**33", images)), "F 8589934592.0") + self.assertEqual( + pixel(ImageMath.eval("float(B)**33", images)), "F 8589934592.0" + ) def test_logical(self): self.assertEqual(pixel(ImageMath.eval("not A", images)), 0) @@ -64,12 +62,11 @@ def test_logical(self): self.assertEqual(pixel(ImageMath.eval("A or B", images)), "L 1") def test_convert(self): - self.assertEqual(pixel( - ImageMath.eval("convert(A+B, 'L')", images)), "L 3") - self.assertEqual(pixel( - ImageMath.eval("convert(A+B, '1')", images)), "1 0") - self.assertEqual(pixel( - ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)") + self.assertEqual(pixel(ImageMath.eval("convert(A+B, 'L')", images)), "L 3") + self.assertEqual(pixel(ImageMath.eval("convert(A+B, '1')", images)), "1 0") + self.assertEqual( + pixel(ImageMath.eval("convert(A+B, 'RGB')", images)), "RGB (3, 3, 3)" + ) def test_compare(self): self.assertEqual(pixel(ImageMath.eval("min(A, B)", images)), "I 1") @@ -176,9 +173,6 @@ def test_logical_not_equal(self): self.assertEqual(pixel(ImageMath.eval("notequal(A, A)", A=A)), "I 0") self.assertEqual(pixel(ImageMath.eval("notequal(B, B)", B=B)), "I 0") self.assertEqual(pixel(ImageMath.eval("notequal(Z, Z)", Z=Z)), "I 0") - self.assertEqual( - pixel(ImageMath.eval("notequal(A, B)", A=A, B=B)), "I 1") - self.assertEqual( - pixel(ImageMath.eval("notequal(B, A)", A=A, B=B)), "I 1") - self.assertEqual( - pixel(ImageMath.eval("notequal(A, Z)", A=A, Z=Z)), "I 1") + self.assertEqual(pixel(ImageMath.eval("notequal(A, B)", A=A, B=B)), "I 1") + self.assertEqual(pixel(ImageMath.eval("notequal(B, A)", A=A, B=B)), "I 1") + self.assertEqual(pixel(ImageMath.eval("notequal(A, Z)", A=A, Z=Z)), "I 1") diff --git a/Tests/test_imagemorph.py b/Tests/test_imagemorph.py index 0cf15bd6cab..8ecd170e873 100644 --- a/Tests/test_imagemorph.py +++ b/Tests/test_imagemorph.py @@ -5,7 +5,6 @@ class MorphTests(PillowTestCase): - def setUp(self): self.A = self.string_to_img( """ @@ -17,27 +16,27 @@ def setUp(self): ....... ....... """ - ) + ) def img_to_string(self, im): """Turn a (small) binary image into a string representation""" - chars = '.1' + chars = ".1" width, height = im.size - return '\n'.join( - ''.join(chars[im.getpixel((c, r)) > 0] for c in range(width)) - for r in range(height)) + return "\n".join( + "".join(chars[im.getpixel((c, r)) > 0] for c in range(width)) + for r in range(height) + ) def string_to_img(self, image_string): """Turn a string image representation into a binary image""" - rows = [s for s in image_string.replace(' ', '').split('\n') - if len(s)] + rows = [s for s in image_string.replace(" ", "").split("\n") if len(s)] height = len(rows) width = len(rows[0]) - im = Image.new('L', (width, height)) + im = Image.new("L", (width, height)) for i in range(width): for j in range(height): c = rows[j][i] - v = c in 'X1' + v = c in "X1" im.putpixel((i, j), v) return im @@ -49,55 +48,50 @@ def assert_img_equal(self, A, B): self.assertEqual(self.img_to_string(A), self.img_to_string(B)) def assert_img_equal_img_string(self, A, Bstring): - self.assertEqual( - self.img_to_string(A), - self.img_string_normalize(Bstring)) + self.assertEqual(self.img_to_string(A), self.img_string_normalize(Bstring)) def test_str_to_img(self): - im = Image.open('Tests/images/morph_a.png') + im = Image.open("Tests/images/morph_a.png") self.assert_image_equal(self.A, im) def create_lut(self): - for op in ( - 'corner', 'dilation4', 'dilation8', - 'erosion4', 'erosion8', 'edge'): + for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"): lb = ImageMorph.LutBuilder(op_name=op) lut = lb.build_lut() - with open('Tests/images/%s.lut' % op, 'wb') as f: + with open("Tests/images/%s.lut" % op, "wb") as f: f.write(lut) # create_lut() def test_lut(self): - for op in ( - 'corner', 'dilation4', 'dilation8', - 'erosion4', 'erosion8', 'edge'): + for op in ("corner", "dilation4", "dilation8", "erosion4", "erosion8", "edge"): lb = ImageMorph.LutBuilder(op_name=op) self.assertIsNone(lb.get_lut()) lut = lb.build_lut() - with open('Tests/images/%s.lut' % op, 'rb') as f: + with open("Tests/images/%s.lut" % op, "rb") as f: self.assertEqual(lut, bytearray(f.read())) def test_no_operator_loaded(self): mop = ImageMorph.MorphOp() with self.assertRaises(Exception) as e: mop.apply(None) - self.assertEqual(str(e.exception), 'No operator loaded') + self.assertEqual(str(e.exception), "No operator loaded") with self.assertRaises(Exception) as e: mop.match(None) - self.assertEqual(str(e.exception), 'No operator loaded') + self.assertEqual(str(e.exception), "No operator loaded") with self.assertRaises(Exception) as e: mop.save_lut(None) - self.assertEqual(str(e.exception), 'No operator loaded') + self.assertEqual(str(e.exception), "No operator loaded") # Test the named patterns def test_erosion8(self): # erosion8 - mop = ImageMorph.MorphOp(op_name='erosion8') + mop = ImageMorph.MorphOp(op_name="erosion8") count, Aout = mop.apply(self.A) self.assertEqual(count, 8) - self.assert_img_equal_img_string(Aout, - """ + self.assert_img_equal_img_string( + Aout, + """ ....... ....... ....... @@ -105,15 +99,17 @@ def test_erosion8(self): ....... ....... ....... - """) + """, + ) def test_dialation8(self): # dialation8 - mop = ImageMorph.MorphOp(op_name='dilation8') + mop = ImageMorph.MorphOp(op_name="dilation8") count, Aout = mop.apply(self.A) self.assertEqual(count, 16) - self.assert_img_equal_img_string(Aout, - """ + self.assert_img_equal_img_string( + Aout, + """ ....... .11111. .11111. @@ -121,15 +117,17 @@ def test_dialation8(self): .11111. .11111. ....... - """) + """, + ) def test_erosion4(self): # erosion4 - mop = ImageMorph.MorphOp(op_name='dilation4') + mop = ImageMorph.MorphOp(op_name="dilation4") count, Aout = mop.apply(self.A) self.assertEqual(count, 12) - self.assert_img_equal_img_string(Aout, - """ + self.assert_img_equal_img_string( + Aout, + """ ....... ..111.. .11111. @@ -137,15 +135,17 @@ def test_erosion4(self): .11111. ..111.. ....... - """) + """, + ) def test_edge(self): # edge - mop = ImageMorph.MorphOp(op_name='edge') + mop = ImageMorph.MorphOp(op_name="edge") count, Aout = mop.apply(self.A) self.assertEqual(count, 1) - self.assert_img_equal_img_string(Aout, - """ + self.assert_img_equal_img_string( + Aout, + """ ....... ....... ..111.. @@ -153,16 +153,17 @@ def test_edge(self): ..111.. ....... ....... - """) + """, + ) def test_corner(self): # Create a corner detector pattern - mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0', - '4:(00. 01. ...)->1']) + mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "4:(00. 01. ...)->1"]) count, Aout = mop.apply(self.A) self.assertEqual(count, 5) - self.assert_img_equal_img_string(Aout, - """ + self.assert_img_equal_img_string( + Aout, + """ ....... ....... ..1.1.. @@ -170,7 +171,8 @@ def test_corner(self): ..1.1.. ....... ....... - """) + """, + ) # Test the coordinate counting with the same operator coords = mop.match(self.A) @@ -183,12 +185,12 @@ def test_corner(self): def test_mirroring(self): # Test 'M' for mirroring - mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0', - 'M:(00. 01. ...)->1']) + mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "M:(00. 01. ...)->1"]) count, Aout = mop.apply(self.A) self.assertEqual(count, 7) - self.assert_img_equal_img_string(Aout, - """ + self.assert_img_equal_img_string( + Aout, + """ ....... ....... ..1.1.. @@ -196,16 +198,17 @@ def test_mirroring(self): ....... ....... ....... - """) + """, + ) def test_negate(self): # Test 'N' for negate - mop = ImageMorph.MorphOp(patterns=['1:(... ... ...)->0', - 'N:(00. 01. ...)->1']) + mop = ImageMorph.MorphOp(patterns=["1:(... ... ...)->0", "N:(00. 01. ...)->1"]) count, Aout = mop.apply(self.A) self.assertEqual(count, 8) - self.assert_img_equal_img_string(Aout, - """ + self.assert_img_equal_img_string( + Aout, + """ ....... ....... ..1.... @@ -213,32 +216,34 @@ def test_negate(self): ....... ....... ....... - """) + """, + ) def test_non_binary_images(self): - im = hopper('RGB') + im = hopper("RGB") mop = ImageMorph.MorphOp(op_name="erosion8") with self.assertRaises(Exception) as e: mop.apply(im) - self.assertEqual(str(e.exception), - 'Image must be binary, meaning it must use mode L') + self.assertEqual( + str(e.exception), "Image must be binary, meaning it must use mode L" + ) with self.assertRaises(Exception) as e: mop.match(im) - self.assertEqual(str(e.exception), - 'Image must be binary, meaning it must use mode L') + self.assertEqual( + str(e.exception), "Image must be binary, meaning it must use mode L" + ) with self.assertRaises(Exception) as e: mop.get_on_pixels(im) - self.assertEqual(str(e.exception), - 'Image must be binary, meaning it must use mode L') + self.assertEqual( + str(e.exception), "Image must be binary, meaning it must use mode L" + ) def test_add_patterns(self): # Arrange - lb = ImageMorph.LutBuilder(op_name='corner') - self.assertEqual(lb.patterns, ['1:(... ... ...)->0', - '4:(00. 01. ...)->1']) - new_patterns = ['M:(00. 01. ...)->1', - 'N:(00. 01. ...)->1'] + lb = ImageMorph.LutBuilder(op_name="corner") + self.assertEqual(lb.patterns, ["1:(... ... ...)->0", "4:(00. 01. ...)->1"]) + new_patterns = ["M:(00. 01. ...)->1", "N:(00. 01. ...)->1"] # Act lb.add_patterns(new_patterns) @@ -246,44 +251,44 @@ def test_add_patterns(self): # Assert self.assertEqual( lb.patterns, - ['1:(... ... ...)->0', - '4:(00. 01. ...)->1', - 'M:(00. 01. ...)->1', - 'N:(00. 01. ...)->1']) + [ + "1:(... ... ...)->0", + "4:(00. 01. ...)->1", + "M:(00. 01. ...)->1", + "N:(00. 01. ...)->1", + ], + ) def test_unknown_pattern(self): - self.assertRaises( - Exception, - ImageMorph.LutBuilder, op_name='unknown') + self.assertRaises(Exception, ImageMorph.LutBuilder, op_name="unknown") def test_pattern_syntax_error(self): # Arrange - lb = ImageMorph.LutBuilder(op_name='corner') - new_patterns = ['a pattern with a syntax error'] + lb = ImageMorph.LutBuilder(op_name="corner") + new_patterns = ["a pattern with a syntax error"] lb.add_patterns(new_patterns) # Act / Assert with self.assertRaises(Exception) as e: lb.build_lut() self.assertEqual( - str(e.exception), - 'Syntax error in pattern "a pattern with a syntax error"') + str(e.exception), 'Syntax error in pattern "a pattern with a syntax error"' + ) def test_load_invalid_mrl(self): # Arrange - invalid_mrl = 'Tests/images/hopper.png' + invalid_mrl = "Tests/images/hopper.png" mop = ImageMorph.MorphOp() # Act / Assert with self.assertRaises(Exception) as e: mop.load_lut(invalid_mrl) - self.assertEqual(str(e.exception), - 'Wrong size operator file!') + self.assertEqual(str(e.exception), "Wrong size operator file!") def test_roundtrip_mrl(self): # Arrange - tempfile = self.tempfile('temp.mrl') - mop = ImageMorph.MorphOp(op_name='corner') + tempfile = self.tempfile("temp.mrl") + mop = ImageMorph.MorphOp(op_name="corner") initial_lut = mop.lut # Act @@ -295,7 +300,7 @@ def test_roundtrip_mrl(self): def test_set_lut(self): # Arrange - lb = ImageMorph.LutBuilder(op_name='corner') + lb = ImageMorph.LutBuilder(op_name="corner") lut = lb.build_lut() mop = ImageMorph.MorphOp() @@ -306,9 +311,9 @@ def test_set_lut(self): self.assertEqual(mop.lut, lut) def test_wrong_mode(self): - lut = ImageMorph.LutBuilder(op_name='corner').build_lut() - imrgb = Image.new('RGB', (10, 10)) - iml = Image.new('L', (10, 10)) + lut = ImageMorph.LutBuilder(op_name="corner").build_lut() + imrgb = Image.new("RGB", (10, 10)) + iml = Image.new("L", (10, 10)) with self.assertRaises(RuntimeError): _imagingmorph.apply(bytes(lut), imrgb.im.id, iml.im.id) diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index eaa8fde3c28..006af903ed7 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -5,13 +5,13 @@ try: from PIL import _webp + HAVE_WEBP = True except ImportError: HAVE_WEBP = False class TestImageOps(PillowTestCase): - class Deformer(object): def getmesh(self, im): x, y = im.size @@ -91,15 +91,15 @@ def test_pad(self): for label, color, new_size in [ ("h", None, (im.width * 4, im.height * 2)), - ("v", "#f00", (im.width * 2, im.height * 4)) + ("v", "#f00", (im.width * 2, im.height * 4)), ]: for i, centering in enumerate([(0, 0), (0.5, 0.5), (1, 1)]): - new_im = ImageOps.pad(im, new_size, - color=color, centering=centering) + new_im = ImageOps.pad(im, new_size, color=color, centering=centering) self.assertEqual(new_im.size, new_size) target = Image.open( - "Tests/images/imageops_pad_"+label+"_"+str(i)+".jpg") + "Tests/images/imageops_pad_" + label + "_" + str(i) + ".jpg" + ) self.assert_image_similar(new_im, target, 6) def test_pil163(self): @@ -135,24 +135,30 @@ def test_colorize_2color(self): im = im.convert("L") # Create image with original 2-color functionality - im_test = ImageOps.colorize(im, 'red', 'green') + im_test = ImageOps.colorize(im, "red", "green") # Test output image (2-color) left = (0, 1) middle = (127, 1) right = (255, 1) - self.assert_tuple_approx_equal(im_test.getpixel(left), - (255, 0, 0), - threshold=1, - msg='black test pixel incorrect') - self.assert_tuple_approx_equal(im_test.getpixel(middle), - (127, 63, 0), - threshold=1, - msg='mid test pixel incorrect') - self.assert_tuple_approx_equal(im_test.getpixel(right), - (0, 127, 0), - threshold=1, - msg='white test pixel incorrect') + self.assert_tuple_approx_equal( + im_test.getpixel(left), + (255, 0, 0), + threshold=1, + msg="black test pixel incorrect", + ) + self.assert_tuple_approx_equal( + im_test.getpixel(middle), + (127, 63, 0), + threshold=1, + msg="mid test pixel incorrect", + ) + self.assert_tuple_approx_equal( + im_test.getpixel(right), + (0, 127, 0), + threshold=1, + msg="white test pixel incorrect", + ) def test_colorize_2color_offset(self): # Test the colorizing function with 2-color functionality and offset @@ -162,28 +168,32 @@ def test_colorize_2color_offset(self): im = im.convert("L") # Create image with original 2-color functionality with offsets - im_test = ImageOps.colorize(im, - black='red', - white='green', - blackpoint=50, - whitepoint=100) + im_test = ImageOps.colorize( + im, black="red", white="green", blackpoint=50, whitepoint=100 + ) # Test output image (2-color) with offsets left = (25, 1) middle = (75, 1) right = (125, 1) - self.assert_tuple_approx_equal(im_test.getpixel(left), - (255, 0, 0), - threshold=1, - msg='black test pixel incorrect') - self.assert_tuple_approx_equal(im_test.getpixel(middle), - (127, 63, 0), - threshold=1, - msg='mid test pixel incorrect') - self.assert_tuple_approx_equal(im_test.getpixel(right), - (0, 127, 0), - threshold=1, - msg='white test pixel incorrect') + self.assert_tuple_approx_equal( + im_test.getpixel(left), + (255, 0, 0), + threshold=1, + msg="black test pixel incorrect", + ) + self.assert_tuple_approx_equal( + im_test.getpixel(middle), + (127, 63, 0), + threshold=1, + msg="mid test pixel incorrect", + ) + self.assert_tuple_approx_equal( + im_test.getpixel(right), + (0, 127, 0), + threshold=1, + msg="white test pixel incorrect", + ) def test_colorize_3color_offset(self): # Test the colorizing function with 3-color functionality and offset @@ -193,13 +203,15 @@ def test_colorize_3color_offset(self): im = im.convert("L") # Create image with new three color functionality with offsets - im_test = ImageOps.colorize(im, - black='red', - white='green', - mid='blue', - blackpoint=50, - whitepoint=200, - midpoint=100) + im_test = ImageOps.colorize( + im, + black="red", + white="green", + mid="blue", + blackpoint=50, + whitepoint=200, + midpoint=100, + ) # Test output image (3-color) with offsets left = (25, 1) @@ -207,43 +219,47 @@ def test_colorize_3color_offset(self): middle = (100, 1) right_middle = (150, 1) right = (225, 1) - self.assert_tuple_approx_equal(im_test.getpixel(left), - (255, 0, 0), - threshold=1, - msg='black test pixel incorrect') - self.assert_tuple_approx_equal(im_test.getpixel(left_middle), - (127, 0, 127), - threshold=1, - msg='low-mid test pixel incorrect') - self.assert_tuple_approx_equal(im_test.getpixel(middle), - (0, 0, 255), - threshold=1, - msg='mid incorrect') - self.assert_tuple_approx_equal(im_test.getpixel(right_middle), - (0, 63, 127), - threshold=1, - msg='high-mid test pixel incorrect') - self.assert_tuple_approx_equal(im_test.getpixel(right), - (0, 127, 0), - threshold=1, - msg='white test pixel incorrect') + self.assert_tuple_approx_equal( + im_test.getpixel(left), + (255, 0, 0), + threshold=1, + msg="black test pixel incorrect", + ) + self.assert_tuple_approx_equal( + im_test.getpixel(left_middle), + (127, 0, 127), + threshold=1, + msg="low-mid test pixel incorrect", + ) + self.assert_tuple_approx_equal( + im_test.getpixel(middle), (0, 0, 255), threshold=1, msg="mid incorrect" + ) + self.assert_tuple_approx_equal( + im_test.getpixel(right_middle), + (0, 63, 127), + threshold=1, + msg="high-mid test pixel incorrect", + ) + self.assert_tuple_approx_equal( + im_test.getpixel(right), + (0, 127, 0), + threshold=1, + msg="white test pixel incorrect", + ) def test_exif_transpose(self): exts = [".jpg"] if HAVE_WEBP and _webp.HAVE_WEBPANIM: exts.append(".webp") for ext in exts: - base_im = Image.open("Tests/images/hopper"+ext) + base_im = Image.open("Tests/images/hopper" + ext) orientations = [base_im] for i in range(2, 9): - im = Image.open("Tests/images/hopper_orientation_"+str(i)+ext) + im = Image.open("Tests/images/hopper_orientation_" + str(i) + ext) orientations.append(im) for i, orientation_im in enumerate(orientations): - for im in [ - orientation_im, # ImageFile - orientation_im.copy() # Image - ]: + for im in [orientation_im, orientation_im.copy()]: # ImageFile # Image if i == 0: self.assertNotIn("exif", im.info) else: diff --git a/Tests/test_imageops_usm.py b/Tests/test_imageops_usm.py index a867e543022..fc49f4d2d51 100644 --- a/Tests/test_imageops_usm.py +++ b/Tests/test_imageops_usm.py @@ -8,7 +8,6 @@ class TestImageOpsUsm(PillowTestCase): - def test_filter_api(self): test_filter = ImageFilter.GaussianBlur(2.0) @@ -47,24 +46,36 @@ def test_blur_formats(self): def test_usm_accuracy(self): - src = snakes.convert('RGB') + src = snakes.convert("RGB") i = src.filter(ImageFilter.UnsharpMask(5, 1024, 0)) # Image should not be changed because it have only 0 and 255 levels. self.assertEqual(i.tobytes(), src.tobytes()) def test_blur_accuracy(self): - i = snakes.filter(ImageFilter.GaussianBlur(.4)) + i = snakes.filter(ImageFilter.GaussianBlur(0.4)) # These pixels surrounded with pixels with 255 intensity. # They must be very close to 255. - for x, y, c in [(1, 0, 1), (2, 0, 1), (7, 8, 1), (8, 8, 1), (2, 9, 1), - (7, 3, 0), (8, 3, 0), (5, 8, 0), (5, 9, 0), (1, 3, 0), - (4, 3, 2), (4, 2, 2)]: + for x, y, c in [ + (1, 0, 1), + (2, 0, 1), + (7, 8, 1), + (8, 8, 1), + (2, 9, 1), + (7, 3, 0), + (8, 3, 0), + (5, 8, 0), + (5, 9, 0), + (1, 3, 0), + (4, 3, 2), + (4, 2, 2), + ]: self.assertGreaterEqual(i.im.getpixel((x, y))[c], 250) # Fuzzy match. def gp(x, y): return i.im.getpixel((x, y)) + self.assertTrue(236 <= gp(7, 4)[0] <= 239) self.assertTrue(236 <= gp(7, 5)[2] <= 239) self.assertTrue(236 <= gp(7, 6)[2] <= 239) diff --git a/Tests/test_imagepalette.py b/Tests/test_imagepalette.py index e4b5b7f72ee..50670f26c42 100644 --- a/Tests/test_imagepalette.py +++ b/Tests/test_imagepalette.py @@ -4,12 +4,12 @@ class TestImagePalette(PillowTestCase): - def test_sanity(self): - ImagePalette.ImagePalette("RGB", list(range(256))*3) - self.assertRaises(ValueError, - ImagePalette.ImagePalette, "RGB", list(range(256))*2) + ImagePalette.ImagePalette("RGB", list(range(256)) * 3) + self.assertRaises( + ValueError, ImagePalette.ImagePalette, "RGB", list(range(256)) * 2 + ) def test_getcolor(self): @@ -27,7 +27,7 @@ def test_getcolor(self): def test_file(self): - palette = ImagePalette.ImagePalette("RGB", list(range(256))*3) + palette = ImagePalette.ImagePalette("RGB", list(range(256)) * 3) f = self.tempfile("temp.lut") @@ -65,8 +65,9 @@ def test_make_linear_lut_not_yet_implemented(self): white = 255 # Act - self.assertRaises(NotImplementedError, - ImagePalette.make_linear_lut, black, white) + self.assertRaises( + NotImplementedError, ImagePalette.make_linear_lut, black, white + ) def test_make_gamma_lut(self): # Arrange @@ -87,7 +88,7 @@ def test_make_gamma_lut(self): def test_rawmode_valueerrors(self): # Arrange - palette = ImagePalette.raw("RGB", list(range(256))*3) + palette = ImagePalette.raw("RGB", list(range(256)) * 3) # Act / Assert self.assertRaises(ValueError, palette.tobytes) @@ -97,7 +98,7 @@ def test_rawmode_valueerrors(self): def test_getdata(self): # Arrange - data_in = list(range(256))*3 + data_in = list(range(256)) * 3 palette = ImagePalette.ImagePalette("RGB", data_in) # Act @@ -108,7 +109,7 @@ def test_getdata(self): def test_rawmode_getdata(self): # Arrange - data_in = list(range(256))*3 + data_in = list(range(256)) * 3 palette = ImagePalette.raw("RGB", data_in) # Act @@ -120,17 +121,16 @@ def test_rawmode_getdata(self): def test_2bit_palette(self): # issue #2258, 2 bit palettes are corrupted. - outfile = self.tempfile('temp.png') + outfile = self.tempfile("temp.png") - rgb = b'\x00' * 2 + b'\x01' * 2 + b'\x02' * 2 - img = Image.frombytes('P', (6, 1), rgb) - img.putpalette(b'\xFF\x00\x00\x00\xFF\x00\x00\x00\xFF') # RGB - img.save(outfile, format='PNG') + rgb = b"\x00" * 2 + b"\x01" * 2 + b"\x02" * 2 + img = Image.frombytes("P", (6, 1), rgb) + img.putpalette(b"\xFF\x00\x00\x00\xFF\x00\x00\x00\xFF") # RGB + img.save(outfile, format="PNG") reloaded = Image.open(outfile) self.assert_image_equal(img, reloaded) def test_invalid_palette(self): - self.assertRaises(IOError, - ImagePalette.load, "Tests/images/hopper.jpg") + self.assertRaises(IOError, ImagePalette.load, "Tests/images/hopper.jpg") diff --git a/Tests/test_imagepath.py b/Tests/test_imagepath.py index 8cf88b7c139..f5309ee1458 100644 --- a/Tests/test_imagepath.py +++ b/Tests/test_imagepath.py @@ -8,7 +8,6 @@ class TestImagePath(PillowTestCase): - def test_path(self): p = ImagePath.Path(list(range(10))) @@ -19,21 +18,19 @@ def test_path(self): self.assertEqual(p[-1], (8.0, 9.0)) self.assertEqual(list(p[:1]), [(0.0, 1.0)]) with self.assertRaises(TypeError) as cm: - p['foo'] - self.assertEqual( - str(cm.exception), - "Path indices must be integers, not str") + p["foo"] + self.assertEqual(str(cm.exception), "Path indices must be integers, not str") self.assertEqual( - list(p), - [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) + list(p), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)] + ) # method sanity check self.assertEqual( - p.tolist(), - [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)]) + p.tolist(), [(0.0, 1.0), (2.0, 3.0), (4.0, 5.0), (6.0, 7.0), (8.0, 9.0)] + ) self.assertEqual( - p.tolist(1), - [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]) + p.tolist(1), [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0] + ) self.assertEqual(p.getbbox(), (0.0, 1.0, 8.0, 9.0)) @@ -62,7 +59,7 @@ def test_path(self): self.assertEqual(list(p), [(0.0, 1.0)]) arr = array.array("f", [0, 1]) - if hasattr(arr, 'tobytes'): + if hasattr(arr, "tobytes"): p = ImagePath.Path(arr.tobytes()) else: p = ImagePath.Path(arr.tostring()) @@ -79,9 +76,9 @@ def test_overflow_segfault(self): # and segfaults for i in range(200000): if py3: - x[i] = b'0'*16 + x[i] = b"0" * 16 else: - x[i] = "0"*16 + x[i] = "0" * 16 class evil: diff --git a/Tests/test_imageqt.py b/Tests/test_imageqt.py index bd93828ef15..696acdb85a7 100644 --- a/Tests/test_imageqt.py +++ b/Tests/test_imageqt.py @@ -17,13 +17,15 @@ def skip_if_qt_is_not_installed(_): pass + + else: + def skip_if_qt_is_not_installed(test_case): - test_case.skipTest('Qt bindings are not installed') + test_case.skipTest("Qt bindings are not installed") class PillowQtTestCase(object): - def setUp(self): skip_if_qt_is_not_installed(self) @@ -32,20 +34,19 @@ def tearDown(self): class PillowQPixmapTestCase(PillowQtTestCase): - def setUp(self): PillowQtTestCase.setUp(self) try: - if ImageQt.qt_version == '5': + if ImageQt.qt_version == "5": from PyQt5.QtGui import QGuiApplication - elif ImageQt.qt_version == '4': + elif ImageQt.qt_version == "4": from PyQt4.QtGui import QGuiApplication - elif ImageQt.qt_version == 'side': + elif ImageQt.qt_version == "side": from PySide.QtGui import QGuiApplication - elif ImageQt.qt_version == 'side2': + elif ImageQt.qt_version == "side2": from PySide2.QtGui import QGuiApplication except ImportError: - self.skipTest('QGuiApplication not installed') + self.skipTest("QGuiApplication not installed") self.app = QGuiApplication([]) @@ -55,29 +56,28 @@ def tearDown(self): class TestImageQt(PillowQtTestCase, PillowTestCase): - def test_rgb(self): # from https://doc.qt.io/archives/qt-4.8/qcolor.html # typedef QRgb # An ARGB quadruplet on the format #AARRGGBB, # equivalent to an unsigned int. - if ImageQt.qt_version == '5': + if ImageQt.qt_version == "5": from PyQt5.QtGui import qRgb - elif ImageQt.qt_version == '4': + elif ImageQt.qt_version == "4": from PyQt4.QtGui import qRgb - elif ImageQt.qt_version == 'side': + elif ImageQt.qt_version == "side": from PySide.QtGui import qRgb - elif ImageQt.qt_version == 'side2': + elif ImageQt.qt_version == "side2": from PySide2.QtGui import qRgb self.assertEqual(qRgb(0, 0, 0), qRgba(0, 0, 0, 255)) def checkrgb(r, g, b): val = ImageQt.rgb(r, g, b) - val = val % 2**24 # drop the alpha + val = val % 2 ** 24 # drop the alpha self.assertEqual(val >> 16, r) - self.assertEqual(((val >> 8) % 2**8), g) - self.assertEqual(val % 2**8, b) + self.assertEqual(((val >> 8) % 2 ** 8), g) + self.assertEqual(val % 2 ** 8, b) checkrgb(0, 0, 0) checkrgb(255, 0, 0) @@ -85,7 +85,7 @@ def checkrgb(r, g, b): checkrgb(0, 0, 255) def test_image(self): - for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): + for mode in ("1", "RGB", "RGBA", "L", "P"): ImageQt.ImageQt(hopper(mode)) def test_deprecated(self): diff --git a/Tests/test_imagesequence.py b/Tests/test_imagesequence.py index 9fbf3fed8a7..38645f13389 100644 --- a/Tests/test_imagesequence.py +++ b/Tests/test_imagesequence.py @@ -4,7 +4,6 @@ class TestImageSequence(PillowTestCase): - def test_sanity(self): test_file = self.tempfile("temp.im") @@ -25,19 +24,25 @@ def test_sanity(self): self.assertRaises(AttributeError, ImageSequence.Iterator, 0) def test_iterator(self): - im = Image.open('Tests/images/multipage.tiff') + im = Image.open("Tests/images/multipage.tiff") i = ImageSequence.Iterator(im) for index in range(0, im.n_frames): self.assertEqual(i[index], next(i)) - self.assertRaises(IndexError, lambda: i[index+1]) + self.assertRaises(IndexError, lambda: i[index + 1]) self.assertRaises(StopIteration, next, i) + def test_iterator_min_frame(self): + im = Image.open("Tests/images/hopper.psd") + i = ImageSequence.Iterator(im) + for index in range(1, im.n_frames): + self.assertEqual(i[index], next(i)) + def _test_multipage_tiff(self): - im = Image.open('Tests/images/multipage.tiff') + im = Image.open("Tests/images/multipage.tiff") for index, frame in enumerate(ImageSequence.Iterator(im)): frame.load() self.assertEqual(index, im.tell()) - frame.convert('RGB') + frame.convert("RGB") def test_tiff(self): self._test_multipage_tiff() @@ -53,7 +58,7 @@ def test_libtiff(self): TiffImagePlugin.READ_LIBTIFF = False def test_consecutive(self): - im = Image.open('Tests/images/multipage.tiff') + im = Image.open("Tests/images/multipage.tiff") firstFrame = None for frame in ImageSequence.Iterator(im): if firstFrame is None: @@ -64,7 +69,7 @@ def test_consecutive(self): def test_palette_mmap(self): # Using mmap in ImageFile can require to reload the palette. - im = Image.open('Tests/images/multipage-mmap.tiff') + im = Image.open("Tests/images/multipage-mmap.tiff") color1 = im.getpalette()[0:3] im.seek(0) color2 = im.getpalette()[0:3] diff --git a/Tests/test_imageshow.py b/Tests/test_imageshow.py index 899c057d6f7..a77acd3075a 100644 --- a/Tests/test_imageshow.py +++ b/Tests/test_imageshow.py @@ -5,7 +5,6 @@ class TestImageShow(PillowTestCase): - def test_sanity(self): dir(Image) dir(ImageShow) @@ -18,18 +17,20 @@ def test_register(self): ImageShow._viewers.pop() def test_show(self): - class TestViewer: + class TestViewer(ImageShow.Viewer): methodCalled = False - def show(self, image, title=None, **options): + def show_image(self, image, **options): self.methodCalled = True return True + viewer = TestViewer() ImageShow.register(viewer, -1) - im = hopper() - self.assertTrue(ImageShow.show(im)) - self.assertTrue(viewer.methodCalled) + for mode in ("1", "I;16", "LA", "RGB", "RGBA"): + im = hopper(mode) + self.assertTrue(ImageShow.show(im)) + self.assertTrue(viewer.methodCalled) # Restore original state ImageShow._viewers.pop(0) @@ -43,4 +44,4 @@ def test_viewer(self): def test_viewers(self): for viewer in ImageShow._viewers: - viewer.get_command('test.jpg') + viewer.get_command("test.jpg") diff --git a/Tests/test_imagestat.py b/Tests/test_imagestat.py index c2580a1b1e5..ef0f28c328f 100644 --- a/Tests/test_imagestat.py +++ b/Tests/test_imagestat.py @@ -5,7 +5,6 @@ class TestImageStat(PillowTestCase): - def test_sanity(self): im = hopper() @@ -48,8 +47,8 @@ def test_constant(self): st = ImageStat.Stat(im) self.assertEqual(st.extrema[0], (128, 128)) - self.assertEqual(st.sum[0], 128**3) - self.assertEqual(st.sum2[0], 128**4) + self.assertEqual(st.sum[0], 128 ** 3) + self.assertEqual(st.sum2[0], 128 ** 4) self.assertEqual(st.mean[0], 128) self.assertEqual(st.median[0], 128) self.assertEqual(st.rms[0], 128) diff --git a/Tests/test_imagetk.py b/Tests/test_imagetk.py index 162c0a93527..9bcb4954aac 100644 --- a/Tests/test_imagetk.py +++ b/Tests/test_imagetk.py @@ -5,6 +5,7 @@ try: from PIL import ImageTk + if py3: import tkinter as tk else: @@ -15,12 +16,11 @@ # Skipped via setUp() HAS_TK = False -TK_MODES = ('1', 'L', 'P', 'RGB', 'RGBA') +TK_MODES = ("1", "L", "P", "RGB", "RGBA") @unittest.skipIf(not HAS_TK, "Tk not installed") class TestImageTk(PillowTestCase): - def setUp(self): try: # setup tk @@ -34,7 +34,7 @@ def test_kw(self): TEST_PNG = "Tests/images/hopper.png" im1 = Image.open(TEST_JPG) im2 = Image.open(TEST_PNG) - with open(TEST_PNG, 'rb') as fp: + with open(TEST_PNG, "rb") as fp: data = fp.read() kw = {"file": TEST_JPG, "data": data} @@ -76,7 +76,7 @@ def test_photoimage_blank(self): # self.assert_image_equal(reloaded, im) def test_bitmapimage(self): - im = hopper('1') + im = hopper("1") # this should not crash im_tk = ImageTk.BitmapImage(im) diff --git a/Tests/test_imagewin.py b/Tests/test_imagewin.py index 16d681f2ca9..82f11a68d74 100644 --- a/Tests/test_imagewin.py +++ b/Tests/test_imagewin.py @@ -5,7 +5,6 @@ class TestImageWin(PillowTestCase): - def test_sanity(self): dir(ImageWin) @@ -32,9 +31,8 @@ def test_hwnd(self): self.assertEqual(wnd2, 50) -@unittest.skipUnless(sys.platform.startswith('win32'), "Windows only") +@unittest.skipUnless(sys.platform.startswith("win32"), "Windows only") class TestImageWinDib(PillowTestCase): - def test_dib_image(self): # Arrange im = hopper() diff --git a/Tests/test_imagewin_pointers.py b/Tests/test_imagewin_pointers.py index 64f92191624..9c5704dbe22 100644 --- a/Tests/test_imagewin_pointers.py +++ b/Tests/test_imagewin_pointers.py @@ -7,33 +7,33 @@ # see https://github.com/python-pillow/Pillow/pull/1431#issuecomment-144692652 -if sys.platform.startswith('win32'): +if sys.platform.startswith("win32"): import ctypes.wintypes class BITMAPFILEHEADER(ctypes.Structure): _pack_ = 2 _fields_ = [ - ('bfType', ctypes.wintypes.WORD), - ('bfSize', ctypes.wintypes.DWORD), - ('bfReserved1', ctypes.wintypes.WORD), - ('bfReserved2', ctypes.wintypes.WORD), - ('bfOffBits', ctypes.wintypes.DWORD), + ("bfType", ctypes.wintypes.WORD), + ("bfSize", ctypes.wintypes.DWORD), + ("bfReserved1", ctypes.wintypes.WORD), + ("bfReserved2", ctypes.wintypes.WORD), + ("bfOffBits", ctypes.wintypes.DWORD), ] class BITMAPINFOHEADER(ctypes.Structure): _pack_ = 2 _fields_ = [ - ('biSize', ctypes.wintypes.DWORD), - ('biWidth', ctypes.wintypes.LONG), - ('biHeight', ctypes.wintypes.LONG), - ('biPlanes', ctypes.wintypes.WORD), - ('biBitCount', ctypes.wintypes.WORD), - ('biCompression', ctypes.wintypes.DWORD), - ('biSizeImage', ctypes.wintypes.DWORD), - ('biXPelsPerMeter', ctypes.wintypes.LONG), - ('biYPelsPerMeter', ctypes.wintypes.LONG), - ('biClrUsed', ctypes.wintypes.DWORD), - ('biClrImportant', ctypes.wintypes.DWORD), + ("biSize", ctypes.wintypes.DWORD), + ("biWidth", ctypes.wintypes.LONG), + ("biHeight", ctypes.wintypes.LONG), + ("biPlanes", ctypes.wintypes.WORD), + ("biBitCount", ctypes.wintypes.WORD), + ("biCompression", ctypes.wintypes.DWORD), + ("biSizeImage", ctypes.wintypes.DWORD), + ("biXPelsPerMeter", ctypes.wintypes.LONG), + ("biYPelsPerMeter", ctypes.wintypes.LONG), + ("biClrUsed", ctypes.wintypes.DWORD), + ("biClrImportant", ctypes.wintypes.DWORD), ] BI_RGB = 0 @@ -57,15 +57,19 @@ class BITMAPINFOHEADER(ctypes.Structure): DeleteObject.argtypes = [ctypes.wintypes.HGDIOBJ] CreateDIBSection = ctypes.windll.gdi32.CreateDIBSection - CreateDIBSection.argtypes = [ctypes.wintypes.HDC, ctypes.c_void_p, - ctypes.c_uint, - ctypes.POINTER(ctypes.c_void_p), - ctypes.wintypes.HANDLE, ctypes.wintypes.DWORD] + CreateDIBSection.argtypes = [ + ctypes.wintypes.HDC, + ctypes.c_void_p, + ctypes.c_uint, + ctypes.POINTER(ctypes.c_void_p), + ctypes.wintypes.HANDLE, + ctypes.wintypes.DWORD, + ] CreateDIBSection.restype = ctypes.wintypes.HBITMAP def serialize_dib(bi, pixels): bf = BITMAPFILEHEADER() - bf.bfType = 0x4d42 + bf.bfType = 0x4D42 bf.bfOffBits = ctypes.sizeof(bf) + bi.biSize bf.bfSize = bf.bfOffBits + bi.biSizeImage bf.bfReserved1 = bf.bfReserved2 = 0 @@ -81,7 +85,7 @@ class TestImageWinPointers(PillowTestCase): def test_pointer(self): im = hopper() (width, height) = im.size - opath = self.tempfile('temp.png') + opath = self.tempfile("temp.png") imdib = ImageWin.Dib(im) hdr = BITMAPINFOHEADER() @@ -97,8 +101,9 @@ def test_pointer(self): hdc = CreateCompatibleDC(None) pixels = ctypes.c_void_p() - dib = CreateDIBSection(hdc, ctypes.byref(hdr), DIB_RGB_COLORS, - ctypes.byref(pixels), None, 0) + dib = CreateDIBSection( + hdc, ctypes.byref(hdr), DIB_RGB_COLORS, ctypes.byref(pixels), None, 0 + ) SelectObject(hdc, dib) imdib.expose(hdc) diff --git a/Tests/test_lib_image.py b/Tests/test_lib_image.py index 466c43f8827..36a1e97afd7 100644 --- a/Tests/test_lib_image.py +++ b/Tests/test_lib_image.py @@ -4,7 +4,6 @@ class TestLibImage(PillowTestCase): - def test_setmode(self): im = Image.new("L", (1, 1), 255) @@ -33,5 +32,5 @@ def test_setmode(self): self.assertRaises(ValueError, im.im.setmode, "RGBABCDE") -if __name__ == '__main__': +if __name__ == "__main__": unittest.main() diff --git a/Tests/test_lib_pack.py b/Tests/test_lib_pack.py index 543d151ac48..88ba3a30762 100644 --- a/Tests/test_lib_pack.py +++ b/Tests/test_lib_pack.py @@ -24,32 +24,31 @@ def assert_pack(self, mode, rawmode, data, *pixels): self.assertEqual(data, im.tobytes("raw", rawmode)) def test_1(self): - self.assert_pack("1", "1", b'\x01', 0, 0, 0, 0, 0, 0, 0, X) - self.assert_pack("1", "1;I", b'\x01', X, X, X, X, X, X, X, 0) - self.assert_pack("1", "1;R", b'\x01', X, 0, 0, 0, 0, 0, 0, 0) - self.assert_pack("1", "1;IR", b'\x01', 0, X, X, X, X, X, X, X) + self.assert_pack("1", "1", b"\x01", 0, 0, 0, 0, 0, 0, 0, X) + self.assert_pack("1", "1;I", b"\x01", X, X, X, X, X, X, X, 0) + self.assert_pack("1", "1;R", b"\x01", X, 0, 0, 0, 0, 0, 0, 0) + self.assert_pack("1", "1;IR", b"\x01", 0, X, X, X, X, X, X, X) - self.assert_pack("1", "1", b'\xaa', X, 0, X, 0, X, 0, X, 0) - self.assert_pack("1", "1;I", b'\xaa', 0, X, 0, X, 0, X, 0, X) - self.assert_pack("1", "1;R", b'\xaa', 0, X, 0, X, 0, X, 0, X) - self.assert_pack("1", "1;IR", b'\xaa', X, 0, X, 0, X, 0, X, 0) + self.assert_pack("1", "1", b"\xaa", X, 0, X, 0, X, 0, X, 0) + self.assert_pack("1", "1;I", b"\xaa", 0, X, 0, X, 0, X, 0, X) + self.assert_pack("1", "1;R", b"\xaa", 0, X, 0, X, 0, X, 0, X) + self.assert_pack("1", "1;IR", b"\xaa", X, 0, X, 0, X, 0, X, 0) - self.assert_pack( - "1", "L", b'\xff\x00\x00\xff\x00\x00', X, 0, 0, X, 0, 0) + self.assert_pack("1", "L", b"\xff\x00\x00\xff\x00\x00", X, 0, 0, X, 0, 0) def test_L(self): self.assert_pack("L", "L", 1, 1, 2, 3, 4) - self.assert_pack("L", "L;16", b'\x00\xc6\x00\xaf', 198, 175) - self.assert_pack("L", "L;16B", b'\xc6\x00\xaf\x00', 198, 175) + self.assert_pack("L", "L;16", b"\x00\xc6\x00\xaf", 198, 175) + self.assert_pack("L", "L;16B", b"\xc6\x00\xaf\x00", 198, 175) def test_LA(self): self.assert_pack("LA", "LA", 2, (1, 2), (3, 4), (5, 6)) self.assert_pack("LA", "LA;L", 2, (1, 4), (2, 5), (3, 6)) def test_P(self): - self.assert_pack("P", "P;1", b'\xe4', 1, 1, 1, 0, 0, 255, 0, 0) - self.assert_pack("P", "P;2", b'\xe4', 3, 2, 1, 0) - self.assert_pack("P", "P;4", b'\x02\xef', 0, 2, 14, 15) + self.assert_pack("P", "P;1", b"\xe4", 1, 1, 1, 0, 0, 255, 0, 0) + self.assert_pack("P", "P;2", b"\xe4", 3, 2, 1, 0) + self.assert_pack("P", "P;4", b"\x02\xef", 0, 2, 14, 15) self.assert_pack("P", "P", 1, 1, 2, 3, 4) def test_PA(self): @@ -59,116 +58,118 @@ def test_PA(self): def test_RGB(self): self.assert_pack("RGB", "RGB", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) self.assert_pack( - "RGB", "RGBX", - b'\x01\x02\x03\xff\x05\x06\x07\xff', (1, 2, 3), (5, 6, 7)) + "RGB", "RGBX", b"\x01\x02\x03\xff\x05\x06\x07\xff", (1, 2, 3), (5, 6, 7) + ) self.assert_pack( - "RGB", "XRGB", - b'\x00\x02\x03\x04\x00\x06\x07\x08', (2, 3, 4), (6, 7, 8)) + "RGB", "XRGB", b"\x00\x02\x03\x04\x00\x06\x07\x08", (2, 3, 4), (6, 7, 8) + ) self.assert_pack("RGB", "BGR", 3, (3, 2, 1), (6, 5, 4), (9, 8, 7)) self.assert_pack( - "RGB", "BGRX", - b'\x01\x02\x03\x00\x05\x06\x07\x00', (3, 2, 1), (7, 6, 5)) + "RGB", "BGRX", b"\x01\x02\x03\x00\x05\x06\x07\x00", (3, 2, 1), (7, 6, 5) + ) self.assert_pack( - "RGB", "XBGR", - b'\x00\x02\x03\x04\x00\x06\x07\x08', (4, 3, 2), (8, 7, 6)) + "RGB", "XBGR", b"\x00\x02\x03\x04\x00\x06\x07\x08", (4, 3, 2), (8, 7, 6) + ) self.assert_pack("RGB", "RGB;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9)) self.assert_pack("RGB", "R", 1, (1, 9, 9), (2, 9, 9), (3, 9, 9)) self.assert_pack("RGB", "G", 1, (9, 1, 9), (9, 2, 9), (9, 3, 9)) self.assert_pack("RGB", "B", 1, (9, 9, 1), (9, 9, 2), (9, 9, 3)) def test_RGBA(self): - self.assert_pack( - "RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_pack( - "RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) - self.assert_pack( - "RGBA", "RGB", 3, (1, 2, 3, 14), (4, 5, 6, 15), (7, 8, 9, 16)) - self.assert_pack( - "RGBA", "BGR", 3, (3, 2, 1, 14), (6, 5, 4, 15), (9, 8, 7, 16)) - self.assert_pack( - "RGBA", "BGRA", 4, - (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) - self.assert_pack( - "RGBA", "ABGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) - self.assert_pack( - "RGBA", "BGRa", 4, - (191, 127, 63, 4), (223, 191, 159, 8), (233, 212, 191, 12)) - self.assert_pack( - "RGBA", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) - self.assert_pack( - "RGBA", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) - self.assert_pack( - "RGBA", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) - self.assert_pack( - "RGBA", "A", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) + self.assert_pack("RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_pack( + "RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12) + ) + self.assert_pack("RGBA", "RGB", 3, (1, 2, 3, 14), (4, 5, 6, 15), (7, 8, 9, 16)) + self.assert_pack("RGBA", "BGR", 3, (3, 2, 1, 14), (6, 5, 4, 15), (9, 8, 7, 16)) + self.assert_pack("RGBA", "BGRA", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) + self.assert_pack("RGBA", "ABGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) + self.assert_pack( + "RGBA", + "BGRa", + 4, + (191, 127, 63, 4), + (223, 191, 159, 8), + (233, 212, 191, 12), + ) + self.assert_pack("RGBA", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) + self.assert_pack("RGBA", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) + self.assert_pack("RGBA", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) + self.assert_pack("RGBA", "A", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) def test_RGBa(self): - self.assert_pack( - "RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_pack( - "RGBa", "BGRa", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) - self.assert_pack( - "RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) + self.assert_pack("RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_pack("RGBa", "BGRa", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) + self.assert_pack("RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) def test_RGBX(self): - self.assert_pack( - "RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_pack( - "RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) - self.assert_pack( - "RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) - self.assert_pack( - "RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) - self.assert_pack( - "RGBX", "BGRX", - b'\x01\x02\x03\x00\x05\x06\x07\x00\t\n\x0b\x00', - (3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X)) - self.assert_pack( - "RGBX", "XBGR", - b'\x00\x02\x03\x04\x00\x06\x07\x08\x00\n\x0b\x0c', - (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X)) - self.assert_pack("RGBX", "R", 1, - (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) - self.assert_pack("RGBX", "G", 1, - (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) - self.assert_pack("RGBX", "B", 1, - (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) - self.assert_pack("RGBX", "X", 1, - (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) + self.assert_pack("RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_pack( + "RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12) + ) + self.assert_pack("RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) + self.assert_pack("RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) + self.assert_pack( + "RGBX", + "BGRX", + b"\x01\x02\x03\x00\x05\x06\x07\x00\t\n\x0b\x00", + (3, 2, 1, X), + (7, 6, 5, X), + (11, 10, 9, X), + ) + self.assert_pack( + "RGBX", + "XBGR", + b"\x00\x02\x03\x04\x00\x06\x07\x08\x00\n\x0b\x0c", + (4, 3, 2, X), + (8, 7, 6, X), + (12, 11, 10, X), + ) + self.assert_pack("RGBX", "R", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) + self.assert_pack("RGBX", "G", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) + self.assert_pack("RGBX", "B", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) + self.assert_pack("RGBX", "X", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) def test_CMYK(self): - self.assert_pack("CMYK", "CMYK", 4, - (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + self.assert_pack("CMYK", "CMYK", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) self.assert_pack( - "CMYK", "CMYK;I", 4, - (254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243)) + "CMYK", + "CMYK;I", + 4, + (254, 253, 252, 251), + (250, 249, 248, 247), + (246, 245, 244, 243), + ) self.assert_pack( - "CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) - self.assert_pack("CMYK", "K", 1, - (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) + "CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12) + ) + self.assert_pack("CMYK", "K", 1, (6, 7, 0, 1), (6, 7, 0, 2), (0, 7, 0, 3)) def test_YCbCr(self): self.assert_pack("YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) - self.assert_pack("YCbCr", "YCbCr;L", 3, - (1, 4, 7), (2, 5, 8), (3, 6, 9)) - self.assert_pack( - "YCbCr", "YCbCrX", - b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff', - (1, 2, 3), (5, 6, 7), (9, 10, 11)) - self.assert_pack( - "YCbCr", "YCbCrK", - b'\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff', - (1, 2, 3), (5, 6, 7), (9, 10, 11)) - self.assert_pack("YCbCr", "Y", 1, - (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) - self.assert_pack("YCbCr", "Cb", 1, - (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) - self.assert_pack("YCbCr", "Cr", 1, - (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) + self.assert_pack("YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9)) + self.assert_pack( + "YCbCr", + "YCbCrX", + b"\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff", + (1, 2, 3), + (5, 6, 7), + (9, 10, 11), + ) + self.assert_pack( + "YCbCr", + "YCbCrK", + b"\x01\x02\x03\xff\x05\x06\x07\xff\t\n\x0b\xff", + (1, 2, 3), + (5, 6, 7), + (9, 10, 11), + ) + self.assert_pack("YCbCr", "Y", 1, (1, 0, 8, 9), (2, 0, 8, 9), (3, 0, 8, 0)) + self.assert_pack("YCbCr", "Cb", 1, (6, 1, 8, 9), (6, 2, 8, 9), (6, 3, 8, 9)) + self.assert_pack("YCbCr", "Cr", 1, (6, 7, 1, 9), (6, 7, 2, 0), (6, 7, 3, 9)) def test_LAB(self): - self.assert_pack( - "LAB", "LAB", 3, (1, 130, 131), (4, 133, 134), (7, 136, 137)) + self.assert_pack("LAB", "LAB", 3, (1, 130, 131), (4, 133, 134), (7, 136, 137)) self.assert_pack("LAB", "L", 1, (1, 9, 9), (2, 9, 9), (3, 9, 9)) self.assert_pack("LAB", "A", 1, (9, 1, 9), (9, 2, 9), (9, 3, 9)) self.assert_pack("LAB", "B", 1, (9, 9, 1), (9, 9, 2), (9, 9, 3)) @@ -182,34 +183,41 @@ def test_HSV(self): def test_I(self): self.assert_pack("I", "I;16B", 2, 0x0102, 0x0304) self.assert_pack( - "I", "I;32S", - b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) + "I", "I;32S", b"\x83\x00\x00\x01\x01\x00\x00\x83", 0x01000083, -2097151999 + ) - if sys.byteorder == 'little': + if sys.byteorder == "little": self.assert_pack("I", "I", 4, 0x04030201, 0x08070605) self.assert_pack( - "I", "I;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) + "I", + "I;32NS", + b"\x83\x00\x00\x01\x01\x00\x00\x83", + 0x01000083, + -2097151999, + ) else: self.assert_pack("I", "I", 4, 0x01020304, 0x05060708) self.assert_pack( - "I", "I;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097151999, 0x01000083) + "I", + "I;32NS", + b"\x83\x00\x00\x01\x01\x00\x00\x83", + -2097151999, + 0x01000083, + ) def test_F_float(self): - self.assert_pack( - "F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34) + self.assert_pack("F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34) - if sys.byteorder == 'little': - self.assert_pack( - "F", "F", 4, 1.539989614439558e-36, 4.063216068939723e-34) + if sys.byteorder == "little": + self.assert_pack("F", "F", 4, 1.539989614439558e-36, 4.063216068939723e-34) self.assert_pack( - "F", "F;32NF", 4, 1.539989614439558e-36, 4.063216068939723e-34) + "F", "F;32NF", 4, 1.539989614439558e-36, 4.063216068939723e-34 + ) else: + self.assert_pack("F", "F", 4, 2.387939260590663e-38, 6.301941157072183e-36) self.assert_pack( - "F", "F", 4, 2.387939260590663e-38, 6.301941157072183e-36) - self.assert_pack( - "F", "F;32NF", 4, 2.387939260590663e-38, 6.301941157072183e-36) + "F", "F;32NF", 4, 2.387939260590663e-38, 6.301941157072183e-36 + ) class TestLibUnpack(PillowTestCase): @@ -221,54 +229,53 @@ def assert_unpack(self, mode, rawmode, data, *pixels): data_len = data * len(pixels) data = bytes(bytearray(range(1, data_len + 1))) - im = Image.frombytes(mode, (len(pixels), 1), data, - "raw", rawmode, 0, 1) + im = Image.frombytes(mode, (len(pixels), 1), data, "raw", rawmode, 0, 1) for x, pixel in enumerate(pixels): self.assertEqual(pixel, im.getpixel((x, 0))) def test_1(self): - self.assert_unpack("1", "1", b'\x01', 0, 0, 0, 0, 0, 0, 0, X) - self.assert_unpack("1", "1;I", b'\x01', X, X, X, X, X, X, X, 0) - self.assert_unpack("1", "1;R", b'\x01', X, 0, 0, 0, 0, 0, 0, 0) - self.assert_unpack("1", "1;IR", b'\x01', 0, X, X, X, X, X, X, X) + self.assert_unpack("1", "1", b"\x01", 0, 0, 0, 0, 0, 0, 0, X) + self.assert_unpack("1", "1;I", b"\x01", X, X, X, X, X, X, X, 0) + self.assert_unpack("1", "1;R", b"\x01", X, 0, 0, 0, 0, 0, 0, 0) + self.assert_unpack("1", "1;IR", b"\x01", 0, X, X, X, X, X, X, X) - self.assert_unpack("1", "1", b'\xaa', X, 0, X, 0, X, 0, X, 0) - self.assert_unpack("1", "1;I", b'\xaa', 0, X, 0, X, 0, X, 0, X) - self.assert_unpack("1", "1;R", b'\xaa', 0, X, 0, X, 0, X, 0, X) - self.assert_unpack("1", "1;IR", b'\xaa', X, 0, X, 0, X, 0, X, 0) + self.assert_unpack("1", "1", b"\xaa", X, 0, X, 0, X, 0, X, 0) + self.assert_unpack("1", "1;I", b"\xaa", 0, X, 0, X, 0, X, 0, X) + self.assert_unpack("1", "1;R", b"\xaa", 0, X, 0, X, 0, X, 0, X) + self.assert_unpack("1", "1;IR", b"\xaa", X, 0, X, 0, X, 0, X, 0) - self.assert_unpack("1", "1;8", b'\x00\x01\x02\xff', 0, X, X, X) + self.assert_unpack("1", "1;8", b"\x00\x01\x02\xff", 0, X, X, X) def test_L(self): - self.assert_unpack("L", "L;2", b'\xe4', 255, 170, 85, 0) - self.assert_unpack("L", "L;2I", b'\xe4', 0, 85, 170, 255) - self.assert_unpack("L", "L;2R", b'\xe4', 0, 170, 85, 255) - self.assert_unpack("L", "L;2IR", b'\xe4', 255, 85, 170, 0) + self.assert_unpack("L", "L;2", b"\xe4", 255, 170, 85, 0) + self.assert_unpack("L", "L;2I", b"\xe4", 0, 85, 170, 255) + self.assert_unpack("L", "L;2R", b"\xe4", 0, 170, 85, 255) + self.assert_unpack("L", "L;2IR", b"\xe4", 255, 85, 170, 0) - self.assert_unpack("L", "L;4", b'\x02\xef', 0, 34, 238, 255) - self.assert_unpack("L", "L;4I", b'\x02\xef', 255, 221, 17, 0) - self.assert_unpack("L", "L;4R", b'\x02\xef', 68, 0, 255, 119) - self.assert_unpack("L", "L;4IR", b'\x02\xef', 187, 255, 0, 136) + self.assert_unpack("L", "L;4", b"\x02\xef", 0, 34, 238, 255) + self.assert_unpack("L", "L;4I", b"\x02\xef", 255, 221, 17, 0) + self.assert_unpack("L", "L;4R", b"\x02\xef", 68, 0, 255, 119) + self.assert_unpack("L", "L;4IR", b"\x02\xef", 187, 255, 0, 136) self.assert_unpack("L", "L", 1, 1, 2, 3, 4) self.assert_unpack("L", "L;I", 1, 254, 253, 252, 251) self.assert_unpack("L", "L;R", 1, 128, 64, 192, 32) self.assert_unpack("L", "L;16", 2, 2, 4, 6, 8) self.assert_unpack("L", "L;16B", 2, 1, 3, 5, 7) - self.assert_unpack("L", "L;16", b'\x00\xc6\x00\xaf', 198, 175) - self.assert_unpack("L", "L;16B", b'\xc6\x00\xaf\x00', 198, 175) + self.assert_unpack("L", "L;16", b"\x00\xc6\x00\xaf", 198, 175) + self.assert_unpack("L", "L;16B", b"\xc6\x00\xaf\x00", 198, 175) def test_LA(self): self.assert_unpack("LA", "LA", 2, (1, 2), (3, 4), (5, 6)) self.assert_unpack("LA", "LA;L", 2, (1, 4), (2, 5), (3, 6)) def test_P(self): - self.assert_unpack("P", "P;1", b'\xe4', 1, 1, 1, 0, 0, 1, 0, 0) - self.assert_unpack("P", "P;2", b'\xe4', 3, 2, 1, 0) + self.assert_unpack("P", "P;1", b"\xe4", 1, 1, 1, 0, 0, 1, 0, 0) + self.assert_unpack("P", "P;2", b"\xe4", 3, 2, 1, 0) # erroneous? # self.assert_unpack("P", "P;2L", b'\xe4', 1, 1, 1, 0) - self.assert_unpack("P", "P;4", b'\x02\xef', 0, 2, 14, 15) + self.assert_unpack("P", "P;4", b"\x02\xef", 0, 2, 14, 15) # erroneous? # self.assert_unpack("P", "P;4L", b'\x02\xef', 2, 10, 10, 0) self.assert_unpack("P", "P", 1, 1, 2, 3, 4) @@ -293,195 +300,256 @@ def test_RGB(self): self.assert_unpack("RGB", "RGBX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) self.assert_unpack("RGB", "RGBX;L", 4, (1, 4, 7), (2, 5, 8), (3, 6, 9)) self.assert_unpack("RGB", "BGRX", 4, (3, 2, 1), (7, 6, 5), (11, 10, 9)) - self.assert_unpack( - "RGB", "XRGB", 4, (2, 3, 4), (6, 7, 8), (10, 11, 12)) - self.assert_unpack( - "RGB", "XBGR", 4, (4, 3, 2), (8, 7, 6), (12, 11, 10)) - self.assert_unpack( - "RGB", "YCC;P", - b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data - (127, 102, 0), (192, 227, 0), (213, 255, 170), (98, 255, 133)) + self.assert_unpack("RGB", "XRGB", 4, (2, 3, 4), (6, 7, 8), (10, 11, 12)) + self.assert_unpack("RGB", "XBGR", 4, (4, 3, 2), (8, 7, 6), (12, 11, 10)) + self.assert_unpack( + "RGB", + "YCC;P", + b"D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12", # random data + (127, 102, 0), + (192, 227, 0), + (213, 255, 170), + (98, 255, 133), + ) self.assert_unpack("RGB", "R", 1, (1, 0, 0), (2, 0, 0), (3, 0, 0)) self.assert_unpack("RGB", "G", 1, (0, 1, 0), (0, 2, 0), (0, 3, 0)) self.assert_unpack("RGB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3)) def test_RGBA(self): - self.assert_unpack( - "RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6)) - self.assert_unpack( - "RGBA", "LA;16B", 4, (1, 1, 1, 3), (5, 5, 5, 7), (9, 9, 9, 11)) - self.assert_unpack( - "RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_unpack( - "RGBA", "RGBAX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14)) - self.assert_unpack( - "RGBA", "RGBAXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16)) - self.assert_unpack( - "RGBA", "RGBa", 4, - (63, 127, 191, 4), (159, 191, 223, 8), (191, 212, 233, 12)) - self.assert_unpack( - "RGBA", "RGBa", - b'\x01\x02\x03\x00\x10\x20\x30\x7f\x10\x20\x30\xff', - (0, 0, 0, 0), (32, 64, 96, 127), (16, 32, 48, 255)) - self.assert_unpack( - "RGBA", "RGBaX", - b'\x01\x02\x03\x00-\x10\x20\x30\x7f-\x10\x20\x30\xff-', - (0, 0, 0, 0), (32, 64, 96, 127), (16, 32, 48, 255)) - self.assert_unpack( - "RGBA", "RGBaXX", - b'\x01\x02\x03\x00==\x10\x20\x30\x7f!!\x10\x20\x30\xff??', - (0, 0, 0, 0), (32, 64, 96, 127), (16, 32, 48, 255)) - self.assert_unpack( - "RGBA", "RGBa;16L", 8, - (63, 127, 191, 8), (159, 191, 223, 16), (191, 212, 233, 24)) - self.assert_unpack( - "RGBA", "RGBa;16L", - b'\x88\x01\x88\x02\x88\x03\x88\x00' - b'\x88\x10\x88\x20\x88\x30\x88\xff', - (0, 0, 0, 0), (16, 32, 48, 255)) - self.assert_unpack( - "RGBA", "RGBa;16B", 8, - (36, 109, 182, 7), (153, 187, 221, 15), (188, 210, 232, 23)) - self.assert_unpack( - "RGBA", "RGBa;16B", - b'\x01\x88\x02\x88\x03\x88\x00\x88' - b'\x10\x88\x20\x88\x30\x88\xff\x88', - (0, 0, 0, 0), (16, 32, 48, 255)) - self.assert_unpack( - "RGBA", "BGRa", 4, - (191, 127, 63, 4), (223, 191, 159, 8), (233, 212, 191, 12)) - self.assert_unpack( - "RGBA", "BGRa", - b'\x01\x02\x03\x00\x10\x20\x30\xff', - (0, 0, 0, 0), (48, 32, 16, 255)) - self.assert_unpack( - "RGBA", "RGBA;I", 4, - (254, 253, 252, 4), (250, 249, 248, 8), (246, 245, 244, 12)) - self.assert_unpack( - "RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) + self.assert_unpack("RGBA", "LA", 2, (1, 1, 1, 2), (3, 3, 3, 4), (5, 5, 5, 6)) + self.assert_unpack( + "RGBA", "LA;16B", 4, (1, 1, 1, 3), (5, 5, 5, 7), (9, 9, 9, 11) + ) + self.assert_unpack( + "RGBA", "RGBA", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12) + ) + self.assert_unpack( + "RGBA", "RGBAX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14) + ) + self.assert_unpack( + "RGBA", "RGBAXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16) + ) + self.assert_unpack( + "RGBA", + "RGBa", + 4, + (63, 127, 191, 4), + (159, 191, 223, 8), + (191, 212, 233, 12), + ) + self.assert_unpack( + "RGBA", + "RGBa", + b"\x01\x02\x03\x00\x10\x20\x30\x7f\x10\x20\x30\xff", + (0, 0, 0, 0), + (32, 64, 96, 127), + (16, 32, 48, 255), + ) + self.assert_unpack( + "RGBA", + "RGBaX", + b"\x01\x02\x03\x00-\x10\x20\x30\x7f-\x10\x20\x30\xff-", + (0, 0, 0, 0), + (32, 64, 96, 127), + (16, 32, 48, 255), + ) + self.assert_unpack( + "RGBA", + "RGBaXX", + b"\x01\x02\x03\x00==\x10\x20\x30\x7f!!\x10\x20\x30\xff??", + (0, 0, 0, 0), + (32, 64, 96, 127), + (16, 32, 48, 255), + ) + self.assert_unpack( + "RGBA", + "RGBa;16L", + 8, + (63, 127, 191, 8), + (159, 191, 223, 16), + (191, 212, 233, 24), + ) + self.assert_unpack( + "RGBA", + "RGBa;16L", + b"\x88\x01\x88\x02\x88\x03\x88\x00" b"\x88\x10\x88\x20\x88\x30\x88\xff", + (0, 0, 0, 0), + (16, 32, 48, 255), + ) + self.assert_unpack( + "RGBA", + "RGBa;16B", + 8, + (36, 109, 182, 7), + (153, 187, 221, 15), + (188, 210, 232, 23), + ) + self.assert_unpack( + "RGBA", + "RGBa;16B", + b"\x01\x88\x02\x88\x03\x88\x00\x88" b"\x10\x88\x20\x88\x30\x88\xff\x88", + (0, 0, 0, 0), + (16, 32, 48, 255), + ) + self.assert_unpack( + "RGBA", + "BGRa", + 4, + (191, 127, 63, 4), + (223, 191, 159, 8), + (233, 212, 191, 12), + ) + self.assert_unpack( + "RGBA", + "BGRa", + b"\x01\x02\x03\x00\x10\x20\x30\xff", + (0, 0, 0, 0), + (48, 32, 16, 255), + ) + self.assert_unpack( + "RGBA", + "RGBA;I", + 4, + (254, 253, 252, 4), + (250, 249, 248, 8), + (246, 245, 244, 12), + ) + self.assert_unpack( + "RGBA", "RGBA;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12) + ) self.assert_unpack("RGBA", "RGBA;15", 2, (8, 131, 0, 0), (24, 0, 8, 0)) self.assert_unpack("RGBA", "BGRA;15", 2, (0, 131, 8, 0), (8, 0, 24, 0)) - self.assert_unpack( - "RGBA", "RGBA;4B", 2, (17, 0, 34, 0), (51, 0, 68, 0)) - self.assert_unpack( - "RGBA", "RGBA;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) - self.assert_unpack( - "RGBA", "RGBA;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) - self.assert_unpack( - "RGBA", "BGRA", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) - self.assert_unpack( - "RGBA", "ARGB", 4, (2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9)) - self.assert_unpack( - "RGBA", "ABGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) - self.assert_unpack( - "RGBA", "YCCA;P", - b']bE\x04\xdd\xbej\xed57T\xce\xac\xce:\x11', # random data - (0, 161, 0, 4), (255, 255, 255, 237), - (27, 158, 0, 206), (0, 118, 0, 17)) - self.assert_unpack( - "RGBA", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) - self.assert_unpack( - "RGBA", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) - self.assert_unpack( - "RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) - self.assert_unpack( - "RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) + self.assert_unpack("RGBA", "RGBA;4B", 2, (17, 0, 34, 0), (51, 0, 68, 0)) + self.assert_unpack("RGBA", "RGBA;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) + self.assert_unpack("RGBA", "RGBA;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) + self.assert_unpack( + "RGBA", "BGRA", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12) + ) + self.assert_unpack( + "RGBA", "ARGB", 4, (2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9) + ) + self.assert_unpack( + "RGBA", "ABGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9) + ) + self.assert_unpack( + "RGBA", + "YCCA;P", + b"]bE\x04\xdd\xbej\xed57T\xce\xac\xce:\x11", # random data + (0, 161, 0, 4), + (255, 255, 255, 237), + (27, 158, 0, 206), + (0, 118, 0, 17), + ) + self.assert_unpack("RGBA", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) + self.assert_unpack("RGBA", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) + self.assert_unpack("RGBA", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) + self.assert_unpack("RGBA", "A", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) def test_RGBa(self): self.assert_unpack( - "RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + "RGBa", "RGBa", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12) + ) self.assert_unpack( - "RGBa", "BGRa", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12)) + "RGBa", "BGRa", 4, (3, 2, 1, 4), (7, 6, 5, 8), (11, 10, 9, 12) + ) self.assert_unpack( - "RGBa", "aRGB", 4, (2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9)) + "RGBa", "aRGB", 4, (2, 3, 4, 1), (6, 7, 8, 5), (10, 11, 12, 9) + ) self.assert_unpack( - "RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9)) + "RGBa", "aBGR", 4, (4, 3, 2, 1), (8, 7, 6, 5), (12, 11, 10, 9) + ) def test_RGBX(self): - self.assert_unpack("RGBX", "RGB", 3, - (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) - self.assert_unpack("RGBX", "RGB;L", 3, - (1, 4, 7, X), (2, 5, 8, X), (3, 6, 9, X)) + self.assert_unpack("RGBX", "RGB", 3, (1, 2, 3, X), (4, 5, 6, X), (7, 8, 9, X)) + self.assert_unpack("RGBX", "RGB;L", 3, (1, 4, 7, X), (2, 5, 8, X), (3, 6, 9, X)) self.assert_unpack("RGBX", "RGB;16B", 6, (1, 3, 5, X), (7, 9, 11, X)) - self.assert_unpack("RGBX", "BGR", 3, - (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) + self.assert_unpack("RGBX", "BGR", 3, (3, 2, 1, X), (6, 5, 4, X), (9, 8, 7, X)) self.assert_unpack("RGBX", "RGB;15", 2, (8, 131, 0, X), (24, 0, 8, X)) self.assert_unpack("RGBX", "BGR;15", 2, (0, 131, 8, X), (8, 0, 24, X)) self.assert_unpack("RGBX", "RGB;4B", 2, (17, 0, 34, X), (51, 0, 68, X)) self.assert_unpack( - "RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) - self.assert_unpack( - "RGBX", "RGBXX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14)) - self.assert_unpack( - "RGBX", "RGBXXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16)) - self.assert_unpack( - "RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) - self.assert_unpack("RGBX", "RGBX;16L", 8, - (2, 4, 6, 8), (10, 12, 14, 16)) - self.assert_unpack("RGBX", "RGBX;16B", 8, - (1, 3, 5, 7), (9, 11, 13, 15)) - self.assert_unpack("RGBX", "BGRX", 4, - (3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X)) - self.assert_unpack("RGBX", "XRGB", 4, - (2, 3, 4, X), (6, 7, 8, X), (10, 11, 12, X)) - self.assert_unpack("RGBX", "XBGR", 4, - (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X)) - self.assert_unpack( - "RGBX", "YCC;P", - b'D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12', # random data - (127, 102, 0, X), (192, 227, 0, X), - (213, 255, 170, X), (98, 255, 133, X)) - self.assert_unpack("RGBX", "R", 1, - (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) - self.assert_unpack("RGBX", "G", 1, - (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) - self.assert_unpack("RGBX", "B", 1, - (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) - self.assert_unpack("RGBX", "X", 1, - (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) - - def test_CMYK(self): + "RGBX", "RGBX", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12) + ) self.assert_unpack( - "CMYK", "CMYK", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12)) + "RGBX", "RGBXX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14) + ) self.assert_unpack( - "CMYK", "CMYKX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14)) + "RGBX", "RGBXXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16) + ) self.assert_unpack( - "CMYK", "CMYKXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16)) + "RGBX", "RGBX;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12) + ) + self.assert_unpack("RGBX", "RGBX;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) + self.assert_unpack("RGBX", "RGBX;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) self.assert_unpack( - "CMYK", "CMYK;I", 4, - (254, 253, 252, 251), (250, 249, 248, 247), (246, 245, 244, 243)) + "RGBX", "BGRX", 4, (3, 2, 1, X), (7, 6, 5, X), (11, 10, 9, X) + ) self.assert_unpack( - "CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)) - self.assert_unpack("CMYK", "C", 1, - (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) - self.assert_unpack("CMYK", "M", 1, - (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) - self.assert_unpack("CMYK", "Y", 1, - (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) - self.assert_unpack("CMYK", "K", 1, - (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) + "RGBX", "XRGB", 4, (2, 3, 4, X), (6, 7, 8, X), (10, 11, 12, X) + ) self.assert_unpack( - "CMYK", "C;I", 1, (254, 0, 0, 0), (253, 0, 0, 0), (252, 0, 0, 0)) + "RGBX", "XBGR", 4, (4, 3, 2, X), (8, 7, 6, X), (12, 11, 10, X) + ) self.assert_unpack( - "CMYK", "M;I", 1, (0, 254, 0, 0), (0, 253, 0, 0), (0, 252, 0, 0)) + "RGBX", + "YCC;P", + b"D]\x9c\x82\x1a\x91\xfaOC\xe7J\x12", # random data + (127, 102, 0, X), + (192, 227, 0, X), + (213, 255, 170, X), + (98, 255, 133, X), + ) + self.assert_unpack("RGBX", "R", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) + self.assert_unpack("RGBX", "G", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) + self.assert_unpack("RGBX", "B", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) + self.assert_unpack("RGBX", "X", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) + + def test_CMYK(self): self.assert_unpack( - "CMYK", "Y;I", 1, (0, 0, 254, 0), (0, 0, 253, 0), (0, 0, 252, 0)) + "CMYK", "CMYK", 4, (1, 2, 3, 4), (5, 6, 7, 8), (9, 10, 11, 12) + ) self.assert_unpack( - "CMYK", "K;I", 1, (0, 0, 0, 254), (0, 0, 0, 253), (0, 0, 0, 252)) - - def test_YCbCr(self): + "CMYK", "CMYKX", 5, (1, 2, 3, 4), (6, 7, 8, 9), (11, 12, 13, 14) + ) + self.assert_unpack( + "CMYK", "CMYKXX", 6, (1, 2, 3, 4), (7, 8, 9, 10), (13, 14, 15, 16) + ) self.assert_unpack( - "YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) + "CMYK", + "CMYK;I", + 4, + (254, 253, 252, 251), + (250, 249, 248, 247), + (246, 245, 244, 243), + ) self.assert_unpack( - "YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9)) + "CMYK", "CMYK;L", 4, (1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12) + ) + self.assert_unpack("CMYK", "C", 1, (1, 0, 0, 0), (2, 0, 0, 0), (3, 0, 0, 0)) + self.assert_unpack("CMYK", "M", 1, (0, 1, 0, 0), (0, 2, 0, 0), (0, 3, 0, 0)) + self.assert_unpack("CMYK", "Y", 1, (0, 0, 1, 0), (0, 0, 2, 0), (0, 0, 3, 0)) + self.assert_unpack("CMYK", "K", 1, (0, 0, 0, 1), (0, 0, 0, 2), (0, 0, 0, 3)) self.assert_unpack( - "YCbCr", "YCbCrK", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) + "CMYK", "C;I", 1, (254, 0, 0, 0), (253, 0, 0, 0), (252, 0, 0, 0) + ) self.assert_unpack( - "YCbCr", "YCbCrX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) + "CMYK", "M;I", 1, (0, 254, 0, 0), (0, 253, 0, 0), (0, 252, 0, 0) + ) + self.assert_unpack( + "CMYK", "Y;I", 1, (0, 0, 254, 0), (0, 0, 253, 0), (0, 0, 252, 0) + ) + self.assert_unpack( + "CMYK", "K;I", 1, (0, 0, 0, 254), (0, 0, 0, 253), (0, 0, 0, 252) + ) + + def test_YCbCr(self): + self.assert_unpack("YCbCr", "YCbCr", 3, (1, 2, 3), (4, 5, 6), (7, 8, 9)) + self.assert_unpack("YCbCr", "YCbCr;L", 3, (1, 4, 7), (2, 5, 8), (3, 6, 9)) + self.assert_unpack("YCbCr", "YCbCrK", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) + self.assert_unpack("YCbCr", "YCbCrX", 4, (1, 2, 3), (5, 6, 7), (9, 10, 11)) def test_LAB(self): - self.assert_unpack( - "LAB", "LAB", 3, (1, 130, 131), (4, 133, 134), (7, 136, 137)) + self.assert_unpack("LAB", "LAB", 3, (1, 130, 131), (4, 133, 134), (7, 136, 137)) self.assert_unpack("LAB", "L", 1, (1, 0, 0), (2, 0, 0), (3, 0, 0)) self.assert_unpack("LAB", "A", 1, (0, 1, 0), (0, 2, 0), (0, 3, 0)) self.assert_unpack("LAB", "B", 1, (0, 0, 1), (0, 0, 2), (0, 0, 3)) @@ -494,120 +562,141 @@ def test_HSV(self): def test_I(self): self.assert_unpack("I", "I;8", 1, 0x01, 0x02, 0x03, 0x04) - self.assert_unpack("I", "I;8S", b'\x01\x83', 1, -125) + self.assert_unpack("I", "I;8S", b"\x01\x83", 1, -125) self.assert_unpack("I", "I;16", 2, 0x0201, 0x0403) - self.assert_unpack("I", "I;16S", b'\x83\x01\x01\x83', 0x0183, -31999) + self.assert_unpack("I", "I;16S", b"\x83\x01\x01\x83", 0x0183, -31999) self.assert_unpack("I", "I;16B", 2, 0x0102, 0x0304) - self.assert_unpack("I", "I;16BS", b'\x83\x01\x01\x83', -31999, 0x0183) + self.assert_unpack("I", "I;16BS", b"\x83\x01\x01\x83", -31999, 0x0183) self.assert_unpack("I", "I;32", 4, 0x04030201, 0x08070605) self.assert_unpack( - "I", "I;32S", - b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) + "I", "I;32S", b"\x83\x00\x00\x01\x01\x00\x00\x83", 0x01000083, -2097151999 + ) self.assert_unpack("I", "I;32B", 4, 0x01020304, 0x05060708) self.assert_unpack( - "I", "I;32BS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097151999, 0x01000083) + "I", "I;32BS", b"\x83\x00\x00\x01\x01\x00\x00\x83", -2097151999, 0x01000083 + ) - if sys.byteorder == 'little': + if sys.byteorder == "little": self.assert_unpack("I", "I", 4, 0x04030201, 0x08070605) self.assert_unpack("I", "I;16N", 2, 0x0201, 0x0403) - self.assert_unpack("I", "I;16NS", - b'\x83\x01\x01\x83', 0x0183, -31999) + self.assert_unpack("I", "I;16NS", b"\x83\x01\x01\x83", 0x0183, -31999) self.assert_unpack("I", "I;32N", 4, 0x04030201, 0x08070605) self.assert_unpack( - "I", "I;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', 0x01000083, -2097151999) + "I", + "I;32NS", + b"\x83\x00\x00\x01\x01\x00\x00\x83", + 0x01000083, + -2097151999, + ) else: self.assert_unpack("I", "I", 4, 0x01020304, 0x05060708) self.assert_unpack("I", "I;16N", 2, 0x0102, 0x0304) - self.assert_unpack("I", "I;16NS", - b'\x83\x01\x01\x83', -31999, 0x0183) + self.assert_unpack("I", "I;16NS", b"\x83\x01\x01\x83", -31999, 0x0183) self.assert_unpack("I", "I;32N", 4, 0x01020304, 0x05060708) self.assert_unpack( - "I", "I;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097151999, 0x01000083) + "I", + "I;32NS", + b"\x83\x00\x00\x01\x01\x00\x00\x83", + -2097151999, + 0x01000083, + ) def test_F_int(self): self.assert_unpack("F", "F;8", 1, 0x01, 0x02, 0x03, 0x04) - self.assert_unpack("F", "F;8S", b'\x01\x83', 1, -125) + self.assert_unpack("F", "F;8S", b"\x01\x83", 1, -125) self.assert_unpack("F", "F;16", 2, 0x0201, 0x0403) - self.assert_unpack("F", "F;16S", b'\x83\x01\x01\x83', 0x0183, -31999) + self.assert_unpack("F", "F;16S", b"\x83\x01\x01\x83", 0x0183, -31999) self.assert_unpack("F", "F;16B", 2, 0x0102, 0x0304) - self.assert_unpack("F", "F;16BS", b'\x83\x01\x01\x83', -31999, 0x0183) + self.assert_unpack("F", "F;16BS", b"\x83\x01\x01\x83", -31999, 0x0183) self.assert_unpack("F", "F;32", 4, 67305984, 134678016) self.assert_unpack( - "F", "F;32S", - b'\x83\x00\x00\x01\x01\x00\x00\x83', 16777348, -2097152000) + "F", "F;32S", b"\x83\x00\x00\x01\x01\x00\x00\x83", 16777348, -2097152000 + ) self.assert_unpack("F", "F;32B", 4, 0x01020304, 0x05060708) self.assert_unpack( - "F", "F;32BS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', -2097152000, 16777348) + "F", "F;32BS", b"\x83\x00\x00\x01\x01\x00\x00\x83", -2097152000, 16777348 + ) - if sys.byteorder == 'little': + if sys.byteorder == "little": self.assert_unpack("F", "F;16N", 2, 0x0201, 0x0403) - self.assert_unpack( - "F", "F;16NS", - b'\x83\x01\x01\x83', 0x0183, -31999) + self.assert_unpack("F", "F;16NS", b"\x83\x01\x01\x83", 0x0183, -31999) self.assert_unpack("F", "F;32N", 4, 67305984, 134678016) self.assert_unpack( - "F", "F;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', 16777348, -2097152000) + "F", + "F;32NS", + b"\x83\x00\x00\x01\x01\x00\x00\x83", + 16777348, + -2097152000, + ) else: self.assert_unpack("F", "F;16N", 2, 0x0102, 0x0304) - self.assert_unpack( - "F", "F;16NS", - b'\x83\x01\x01\x83', -31999, 0x0183) + self.assert_unpack("F", "F;16NS", b"\x83\x01\x01\x83", -31999, 0x0183) self.assert_unpack("F", "F;32N", 4, 0x01020304, 0x05060708) self.assert_unpack( - "F", "F;32NS", - b'\x83\x00\x00\x01\x01\x00\x00\x83', - -2097152000, 16777348) + "F", + "F;32NS", + b"\x83\x00\x00\x01\x01\x00\x00\x83", + -2097152000, + 16777348, + ) def test_F_float(self): self.assert_unpack( - "F", "F;32F", 4, - 1.539989614439558e-36, 4.063216068939723e-34) + "F", "F;32F", 4, 1.539989614439558e-36, 4.063216068939723e-34 + ) self.assert_unpack( - "F", "F;32BF", 4, - 2.387939260590663e-38, 6.301941157072183e-36) + "F", "F;32BF", 4, 2.387939260590663e-38, 6.301941157072183e-36 + ) self.assert_unpack( - "F", "F;64F", - b'333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0', # by struct.pack - 0.15000000596046448, -1234.5) + "F", + "F;64F", + b"333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0", # by struct.pack + 0.15000000596046448, + -1234.5, + ) self.assert_unpack( - "F", "F;64BF", - b'?\xc3333333\xc0\x93J\x00\x00\x00\x00\x00', # by struct.pack - 0.15000000596046448, -1234.5) + "F", + "F;64BF", + b"?\xc3333333\xc0\x93J\x00\x00\x00\x00\x00", # by struct.pack + 0.15000000596046448, + -1234.5, + ) - if sys.byteorder == 'little': + if sys.byteorder == "little": self.assert_unpack( - "F", "F", 4, - 1.539989614439558e-36, 4.063216068939723e-34) + "F", "F", 4, 1.539989614439558e-36, 4.063216068939723e-34 + ) self.assert_unpack( - "F", "F;32NF", 4, - 1.539989614439558e-36, 4.063216068939723e-34) + "F", "F;32NF", 4, 1.539989614439558e-36, 4.063216068939723e-34 + ) self.assert_unpack( - "F", "F;64NF", - b'333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0', - 0.15000000596046448, -1234.5) + "F", + "F;64NF", + b"333333\xc3?\x00\x00\x00\x00\x00J\x93\xc0", + 0.15000000596046448, + -1234.5, + ) else: self.assert_unpack( - "F", "F", 4, - 2.387939260590663e-38, 6.301941157072183e-36) + "F", "F", 4, 2.387939260590663e-38, 6.301941157072183e-36 + ) self.assert_unpack( - "F", "F;32NF", 4, - 2.387939260590663e-38, 6.301941157072183e-36) + "F", "F;32NF", 4, 2.387939260590663e-38, 6.301941157072183e-36 + ) self.assert_unpack( - "F", "F;64NF", - b'?\xc3333333\xc0\x93J\x00\x00\x00\x00\x00', - 0.15000000596046448, -1234.5) + "F", + "F;64NF", + b"?\xc3333333\xc0\x93J\x00\x00\x00\x00\x00", + 0.15000000596046448, + -1234.5, + ) def test_I16(self): self.assert_unpack("I;16", "I;16", 2, 0x0201, 0x0403, 0x0605) self.assert_unpack("I;16B", "I;16B", 2, 0x0102, 0x0304, 0x0506) self.assert_unpack("I;16L", "I;16L", 2, 0x0201, 0x0403, 0x0605) self.assert_unpack("I;16", "I;12", 2, 0x0010, 0x0203, 0x0040) - if sys.byteorder == 'little': + if sys.byteorder == "little": self.assert_unpack("I;16", "I;16N", 2, 0x0201, 0x0403, 0x0605) self.assert_unpack("I;16B", "I;16N", 2, 0x0201, 0x0403, 0x0605) self.assert_unpack("I;16L", "I;16N", 2, 0x0201, 0x0403, 0x0605) @@ -616,6 +705,14 @@ def test_I16(self): self.assert_unpack("I;16B", "I;16N", 2, 0x0102, 0x0304, 0x0506) self.assert_unpack("I;16L", "I;16N", 2, 0x0102, 0x0304, 0x0506) + def test_CMYK16(self): + self.assert_unpack("CMYK", "CMYK;16L", 8, (2, 4, 6, 8), (10, 12, 14, 16)) + self.assert_unpack("CMYK", "CMYK;16B", 8, (1, 3, 5, 7), (9, 11, 13, 15)) + if sys.byteorder == "little": + self.assert_unpack("CMYK", "CMYK;16N", 8, (2, 4, 6, 8), (10, 12, 14, 16)) + else: + self.assert_unpack("CMYK", "CMYK;16N", 8, (1, 3, 5, 7), (9, 11, 13, 15)) + def test_value_error(self): self.assertRaises(ValueError, self.assert_unpack, "L", "L", 0, 0) self.assertRaises(ValueError, self.assert_unpack, "RGB", "RGB", 2, 0) diff --git a/Tests/test_locale.py b/Tests/test_locale.py index d40019e5979..8836e4f5f87 100644 --- a/Tests/test_locale.py +++ b/Tests/test_locale.py @@ -24,11 +24,10 @@ class TestLocale(PillowTestCase): - def test_sanity(self): Image.open(path) try: locale.setlocale(locale.LC_ALL, "polish") except locale.Error: - unittest.skip('Polish locale not available') + unittest.skip("Polish locale not available") Image.open(path) diff --git a/Tests/test_main.py b/Tests/test_main.py new file mode 100644 index 00000000000..847def83423 --- /dev/null +++ b/Tests/test_main.py @@ -0,0 +1,33 @@ +from __future__ import unicode_literals + +import os +import subprocess +import sys +from unittest import TestCase + + +class TestMain(TestCase): + def test_main(self): + out = subprocess.check_output([sys.executable, "-m", "PIL"]).decode("utf-8") + lines = out.splitlines() + self.assertEqual(lines[0], "-" * 68) + self.assertTrue(lines[1].startswith("Pillow ")) + self.assertEqual(lines[2], "-" * 68) + self.assertTrue(lines[3].startswith("Python modules loaded from ")) + self.assertTrue(lines[4].startswith("Binary modules loaded from ")) + self.assertEqual(lines[5], "-" * 68) + self.assertTrue(lines[6].startswith("Python ")) + jpeg = ( + os.linesep + + "-" * 68 + + os.linesep + + "JPEG image/jpeg" + + os.linesep + + "Extensions: .jfif, .jpe, .jpeg, .jpg" + + os.linesep + + "Features: open, save" + + os.linesep + + "-" * 68 + + os.linesep + ) + self.assertIn(jpeg, out) diff --git a/Tests/test_map.py b/Tests/test_map.py index 2eeb1fc5ff1..33920f118cf 100644 --- a/Tests/test_map.py +++ b/Tests/test_map.py @@ -4,8 +4,7 @@ from PIL import Image -@unittest.skipIf(sys.platform.startswith('win32'), - "Win32 does not call map_buffer") +@unittest.skipIf(sys.platform.startswith("win32"), "Win32 does not call map_buffer") class TestMap(PillowTestCase): def test_overflow(self): # There is the potential to overflow comparisons in map.c @@ -18,7 +17,7 @@ def test_overflow(self): Image.MAX_IMAGE_PIXELS = None # This image hits the offset test. - im = Image.open('Tests/images/l2rgb_read.bmp') + im = Image.open("Tests/images/l2rgb_read.bmp") with self.assertRaises((ValueError, MemoryError, IOError)): im.load() diff --git a/Tests/test_mode_i16.py b/Tests/test_mode_i16.py index 80730a31254..27ca03ce5d4 100644 --- a/Tests/test_mode_i16.py +++ b/Tests/test_mode_i16.py @@ -5,7 +5,7 @@ class TestModeI16(PillowTestCase): - original = hopper().resize((32, 32)).convert('I') + original = hopper().resize((32, 32)).convert("I") def verify(self, im1): im2 = self.original.copy() @@ -18,9 +18,10 @@ def verify(self, im1): p1 = pix1[xy] p2 = pix2[xy] self.assertEqual( - p1, p2, - ("got %r from mode %s at %s, expected %r" % - (p1, im1.mode, xy, p2))) + p1, + p2, + ("got %r from mode %s at %s, expected %r" % (p1, im1.mode, xy, p2)), + ) def test_basic(self): # PIL 1.1 has limited support for 16-bit image data. Check that @@ -51,8 +52,8 @@ def basic(mode): self.verify(imOut) imOut = Image.new(mode, (w, h), None) - imOut.paste(imIn.crop((0, 0, w//2, h)), (0, 0)) - imOut.paste(imIn.crop((w//2, 0, w, h)), (w//2, 0)) + imOut.paste(imIn.crop((0, 0, w // 2, h)), (0, 0)) + imOut.paste(imIn.crop((w // 2, 0, w, h)), (w // 2, 0)) self.verify(imIn) self.verify(imOut) @@ -83,11 +84,10 @@ def basic(mode): basic("I") def test_tobytes(self): - def tobytes(mode): return Image.new(mode, (1, 1), 1).tobytes() - order = 1 if Image._ENDIAN == '<' else -1 + order = 1 if Image._ENDIAN == "<" else -1 self.assertEqual(tobytes("L"), b"\x01") self.assertEqual(tobytes("I;16"), b"\x01\x00") diff --git a/Tests/test_numpy.py b/Tests/test_numpy.py index 53406c67d34..d171e8c9be8 100644 --- a/Tests/test_numpy.py +++ b/Tests/test_numpy.py @@ -15,7 +15,6 @@ @unittest.skipIf(numpy is None, "Numpy is not installed") class TestNumpy(PillowTestCase): def test_numpy_to_image(self): - def to_image(dtype, bands=1, boolean=0): if bands == 1: if boolean: @@ -29,7 +28,7 @@ def to_image(dtype, bands=1, boolean=0): print("data mismatch for", dtype) else: data = list(range(100)) - a = numpy.array([[x]*bands for x in data], dtype=dtype) + a = numpy.array([[x] * bands for x in data], dtype=dtype) a.shape = TEST_IMAGE_SIZE[0], TEST_IMAGE_SIZE[1], bands i = Image.fromarray(a) if list(i.getchannel(0).getdata()) != list(range(100)): @@ -37,8 +36,8 @@ def to_image(dtype, bands=1, boolean=0): return i # Check supported 1-bit integer formats - self.assert_image(to_image(numpy.bool, 1, 1), '1', TEST_IMAGE_SIZE) - self.assert_image(to_image(numpy.bool8, 1, 1), '1', TEST_IMAGE_SIZE) + self.assert_image(to_image(numpy.bool, 1, 1), "1", TEST_IMAGE_SIZE) + self.assert_image(to_image(numpy.bool8, 1, 1), "1", TEST_IMAGE_SIZE) # Check supported 8-bit integer formats self.assert_image(to_image(numpy.uint8), "L", TEST_IMAGE_SIZE) @@ -53,7 +52,7 @@ def to_image(dtype, bands=1, boolean=0): # self.assert_image(to_image(numpy.int), "I", TEST_IMAGE_SIZE) # Check 16-bit integer formats - if Image._ENDIAN == '<': + if Image._ENDIAN == "<": self.assert_image(to_image(numpy.uint16), "I;16", TEST_IMAGE_SIZE) else: self.assert_image(to_image(numpy.uint16), "I;16B", TEST_IMAGE_SIZE) @@ -96,22 +95,22 @@ def _test_img_equals_nparray(self, img, np): np_size = np.shape[1], np.shape[0] self.assertEqual(img.size, np_size) px = img.load() - for x in range(0, img.size[0], int(img.size[0]/10)): - for y in range(0, img.size[1], int(img.size[1]/10)): + for x in range(0, img.size[0], int(img.size[0] / 10)): + for y in range(0, img.size[1], int(img.size[1] / 10)): self.assert_deep_equal(px[x, y], np[y, x]) def test_16bit(self): - img = Image.open('Tests/images/16bit.cropped.tif') + img = Image.open("Tests/images/16bit.cropped.tif") np_img = numpy.array(img) self._test_img_equals_nparray(img, np_img) - self.assertEqual(np_img.dtype, numpy.dtype('u2'), - ("I;16L", 'u2"), + ("I;16L", "", 0), - (b"\x90\x1F\xA3", 8)) - self.assertEqual(PdfParser.get_value(b"asd < 9 0 1 f A > qwe", 3), - (b"\x90\x1F\xA0", 17)) + self.assertEqual(PdfParser.get_value(b"%cmt\n %cmt\n 123\n", 0), (123, 15)) + self.assertEqual(PdfParser.get_value(b"<901FA3>", 0), (b"\x90\x1F\xA3", 8)) + self.assertEqual( + PdfParser.get_value(b"asd < 9 0 1 f A > qwe", 3), (b"\x90\x1F\xA0", 17) + ) self.assertEqual(PdfParser.get_value(b"(asd)", 0), (b"asd", 5)) - self.assertEqual(PdfParser.get_value(b"(asd(qwe)zxc)zzz(aaa)", 0), - (b"asd(qwe)zxc", 13)) - self.assertEqual(PdfParser.get_value(b"(Two \\\nwords.)", 0), - (b"Two words.", 14)) - self.assertEqual(PdfParser.get_value(b"(Two\nlines.)", 0), - (b"Two\nlines.", 12)) - self.assertEqual(PdfParser.get_value(b"(Two\r\nlines.)", 0), - (b"Two\nlines.", 13)) - self.assertEqual(PdfParser.get_value(b"(Two\\nlines.)", 0), - (b"Two\nlines.", 13)) - self.assertEqual(PdfParser.get_value(b"(One\\(paren).", 0), - (b"One(paren", 12)) - self.assertEqual(PdfParser.get_value(b"(One\\)paren).", 0), - (b"One)paren", 12)) + self.assertEqual( + PdfParser.get_value(b"(asd(qwe)zxc)zzz(aaa)", 0), (b"asd(qwe)zxc", 13) + ) + self.assertEqual( + PdfParser.get_value(b"(Two \\\nwords.)", 0), (b"Two words.", 14) + ) + self.assertEqual(PdfParser.get_value(b"(Two\nlines.)", 0), (b"Two\nlines.", 12)) + self.assertEqual( + PdfParser.get_value(b"(Two\r\nlines.)", 0), (b"Two\nlines.", 13) + ) + self.assertEqual( + PdfParser.get_value(b"(Two\\nlines.)", 0), (b"Two\nlines.", 13) + ) + self.assertEqual(PdfParser.get_value(b"(One\\(paren).", 0), (b"One(paren", 12)) + self.assertEqual(PdfParser.get_value(b"(One\\)paren).", 0), (b"One)paren", 12)) self.assertEqual(PdfParser.get_value(b"(\\0053)", 0), (b"\x053", 7)) self.assertEqual(PdfParser.get_value(b"(\\053)", 0), (b"\x2B", 6)) self.assertEqual(PdfParser.get_value(b"(\\53)", 0), (b"\x2B", 5)) @@ -88,37 +97,43 @@ def test_parsing(self): b"D:2018072921": "20180729210000", b"D:20180729214124Z": "20180729214124", b"D:20180729214124+08'00'": "20180729134124", - b"D:20180729214124-05'00'": "20180730024124" + b"D:20180729214124-05'00'": "20180730024124", }.items(): d = PdfParser.get_value( - b"<>", 0)[0] - self.assertEqual( - time.strftime("%Y%m%d%H%M%S", getattr(d, name)), value) + b"<>", 0 + )[0] + self.assertEqual(time.strftime("%Y%m%d%H%M%S", getattr(d, name)), value) def test_pdf_repr(self): self.assertEqual(bytes(IndirectReference(1, 2)), b"1 2 R") - self.assertEqual(bytes(IndirectObjectDef(*IndirectReference(1, 2))), - b"1 2 obj") + self.assertEqual(bytes(IndirectObjectDef(*IndirectReference(1, 2))), b"1 2 obj") self.assertEqual(bytes(PdfName(b"Name#Hash")), b"/Name#23Hash") self.assertEqual(bytes(PdfName("Name#Hash")), b"/Name#23Hash") - self.assertEqual(bytes(PdfDict({b"Name": IndirectReference(1, 2)})), - b"<<\n/Name 1 2 R\n>>") - self.assertEqual(bytes(PdfDict({"Name": IndirectReference(1, 2)})), - b"<<\n/Name 1 2 R\n>>") + self.assertEqual( + bytes(PdfDict({b"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>" + ) + self.assertEqual( + bytes(PdfDict({"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>" + ) self.assertEqual(pdf_repr(IndirectReference(1, 2)), b"1 2 R") - self.assertEqual(pdf_repr(IndirectObjectDef(*IndirectReference(1, 2))), - b"1 2 obj") + self.assertEqual( + pdf_repr(IndirectObjectDef(*IndirectReference(1, 2))), b"1 2 obj" + ) self.assertEqual(pdf_repr(PdfName(b"Name#Hash")), b"/Name#23Hash") self.assertEqual(pdf_repr(PdfName("Name#Hash")), b"/Name#23Hash") - self.assertEqual(pdf_repr(PdfDict({b"Name": IndirectReference(1, 2)})), - b"<<\n/Name 1 2 R\n>>") - self.assertEqual(pdf_repr(PdfDict({"Name": IndirectReference(1, 2)})), - b"<<\n/Name 1 2 R\n>>") + self.assertEqual( + pdf_repr(PdfDict({b"Name": IndirectReference(1, 2)})), + b"<<\n/Name 1 2 R\n>>", + ) + self.assertEqual( + pdf_repr(PdfDict({"Name": IndirectReference(1, 2)})), b"<<\n/Name 1 2 R\n>>" + ) self.assertEqual(pdf_repr(123), b"123") self.assertEqual(pdf_repr(True), b"true") self.assertEqual(pdf_repr(False), b"false") self.assertEqual(pdf_repr(None), b"null") self.assertEqual(pdf_repr(b"a)/b\\(c"), br"(a\)/b\\\(c)") - self.assertEqual(pdf_repr([123, True, {"a": PdfName(b"b")}]), - b"[ 123 true <<\n/a /b\n>> ]") + self.assertEqual( + pdf_repr([123, True, {"a": PdfName(b"b")}]), b"[ 123 true <<\n/a /b\n>> ]" + ) self.assertEqual(pdf_repr(PdfBinary(b"\x90\x1F\xA0")), b"<901FA0>") diff --git a/Tests/test_pickle.py b/Tests/test_pickle.py index ccd08b1e183..710f2a1bd2f 100644 --- a/Tests/test_pickle.py +++ b/Tests/test_pickle.py @@ -4,25 +4,25 @@ class TestPickle(PillowTestCase): - def helper_pickle_file(self, pickle, protocol=0, mode=None): # Arrange - im = Image.open('Tests/images/hopper.jpg') - filename = self.tempfile('temp.pkl') + im = Image.open("Tests/images/hopper.jpg") + filename = self.tempfile("temp.pkl") if mode: im = im.convert(mode) # Act - with open(filename, 'wb') as f: + with open(filename, "wb") as f: pickle.dump(im, f, protocol) - with open(filename, 'rb') as f: + with open(filename, "rb") as f: loaded_im = pickle.load(f) # Assert self.assertEqual(im, loaded_im) - def helper_pickle_string(self, pickle, protocol=0, - test_file='Tests/images/hopper.jpg', mode=None): + def helper_pickle_string( + self, pickle, protocol=0, test_file="Tests/images/hopper.jpg", mode=None + ): im = Image.open(test_file) if mode: im = im.convert(mode) @@ -61,19 +61,19 @@ def test_pickle_p_mode(self): # Act / Assert for test_file in [ - "Tests/images/test-card.png", - "Tests/images/zero_bb.png", - "Tests/images/zero_bb_scale2.png", - "Tests/images/non_zero_bb.png", - "Tests/images/non_zero_bb_scale2.png", - "Tests/images/p_trns_single.png", - "Tests/images/pil123p.png", - "Tests/images/itxt_chunks.png" + "Tests/images/test-card.png", + "Tests/images/zero_bb.png", + "Tests/images/zero_bb_scale2.png", + "Tests/images/non_zero_bb.png", + "Tests/images/non_zero_bb_scale2.png", + "Tests/images/p_trns_single.png", + "Tests/images/pil123p.png", + "Tests/images/itxt_chunks.png", ]: for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): - self.helper_pickle_string(pickle, - protocol=protocol, - test_file=test_file) + self.helper_pickle_string( + pickle, protocol=protocol, test_file=test_file + ) def test_pickle_pa_mode(self): # Arrange @@ -96,16 +96,17 @@ def test_pickle_l_mode(self): def test_pickle_la_mode_with_palette(self): # Arrange import pickle - im = Image.open('Tests/images/hopper.jpg') - filename = self.tempfile('temp.pkl') + + im = Image.open("Tests/images/hopper.jpg") + filename = self.tempfile("temp.pkl") im = im.convert("PA") # Act / Assert for protocol in range(0, pickle.HIGHEST_PROTOCOL + 1): im.mode = "LA" - with open(filename, 'wb') as f: + with open(filename, "wb") as f: pickle.dump(im, f, protocol) - with open(filename, 'rb') as f: + with open(filename, "rb") as f: loaded_im = pickle.load(f) im.mode = "PA" diff --git a/Tests/test_psdraw.py b/Tests/test_psdraw.py index 73ef5e2b27a..319f9b78966 100644 --- a/Tests/test_psdraw.py +++ b/Tests/test_psdraw.py @@ -6,17 +6,16 @@ class TestPsDraw(PillowTestCase): - def _create_document(self, ps): im = Image.open("Tests/images/hopper.ppm") title = "hopper" - box = (1*72, 2*72, 7*72, 10*72) # in points + box = (1 * 72, 2 * 72, 7 * 72, 10 * 72) # in points ps.begin_document(title) # draw diagonal lines in a cross - ps.line((1*72, 2*72), (7*72, 10*72)) - ps.line((7*72, 2*72), (1*72, 10*72)) + ps.line((1 * 72, 2 * 72), (7 * 72, 10 * 72)) + ps.line((7 * 72, 2 * 72), (1 * 72, 10 * 72)) # draw the image (75 dpi) ps.image(box, im, 75) @@ -24,7 +23,7 @@ def _create_document(self, ps): # draw title ps.setfont("Courier", 36) - ps.text((3*72, 4*72), title) + ps.text((3 * 72, 4 * 72), title) ps.end_document() @@ -34,7 +33,7 @@ def test_draw_postscript(self): # https://pillow.readthedocs.io/en/latest/handbook/tutorial.html#drawing-postscript # Arrange - tempfile = self.tempfile('temp.ps') + tempfile = self.tempfile("temp.ps") with open(tempfile, "wb") as fp: # Act ps = PSDraw.PSDraw(fp) diff --git a/Tests/test_qt_image_fromqpixmap.py b/Tests/test_qt_image_fromqpixmap.py index 894d7c8a5db..2bd448c61c1 100644 --- a/Tests/test_qt_image_fromqpixmap.py +++ b/Tests/test_qt_image_fromqpixmap.py @@ -5,12 +5,11 @@ class TestFromQPixmap(PillowQPixmapTestCase, PillowTestCase): - def roundtrip(self, expected): result = ImageQt.fromqpixmap(ImageQt.toqpixmap(expected)) # Qt saves all pixmaps as rgb - self.assert_image_equal(result, expected.convert('RGB')) + self.assert_image_equal(result, expected.convert("RGB")) def test_sanity(self): - for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): + for mode in ("1", "RGB", "RGBA", "L", "P"): self.roundtrip(hopper(mode)) diff --git a/Tests/test_qt_image_toqimage.py b/Tests/test_qt_image_toqimage.py index c1aa64b577c..ec2f032a39c 100644 --- a/Tests/test_qt_image_toqimage.py +++ b/Tests/test_qt_image_toqimage.py @@ -10,30 +10,30 @@ try: from PyQt5 import QtGui from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QApplication + QT_VERSION = 5 except (ImportError, RuntimeError): try: from PySide2 import QtGui - from PySide2.QtWidgets import QWidget, QHBoxLayout, QLabel, \ - QApplication + from PySide2.QtWidgets import QWidget, QHBoxLayout, QLabel, QApplication + QT_VERSION = 5 except (ImportError, RuntimeError): try: from PyQt4 import QtGui - from PyQt4.QtGui import QWidget, QHBoxLayout, QLabel, \ - QApplication + from PyQt4.QtGui import QWidget, QHBoxLayout, QLabel, QApplication + QT_VERSION = 4 except (ImportError, RuntimeError): from PySide import QtGui - from PySide.QtGui import QWidget, QHBoxLayout, QLabel, \ - QApplication + from PySide.QtGui import QWidget, QHBoxLayout, QLabel, QApplication + QT_VERSION = 4 class TestToQImage(PillowQtTestCase, PillowTestCase): - def test_sanity(self): - for mode in ('RGB', 'RGBA', 'L', 'P', '1'): + for mode in ("RGB", "RGBA", "L", "P", "1"): src = hopper(mode) data = ImageQt.toqimage(src) @@ -42,12 +42,12 @@ def test_sanity(self): # reload directly from the qimage rt = ImageQt.fromqimage(data) - if mode in ('L', 'P', '1'): - self.assert_image_equal(rt, src.convert('RGB')) + if mode in ("L", "P", "1"): + self.assert_image_equal(rt, src.convert("RGB")) else: self.assert_image_equal(rt, src) - if mode == '1': + if mode == "1": # BW appears to not save correctly on QT4 and QT5 # kicks out errors on console: # libpng warning: Invalid color type/bit depth combination @@ -56,15 +56,15 @@ def test_sanity(self): continue # Test saving the file - tempfile = self.tempfile('temp_{}.png'.format(mode)) + tempfile = self.tempfile("temp_{}.png".format(mode)) data.save(tempfile) # Check that it actually worked. reloaded = Image.open(tempfile) # Gray images appear to come back in palette mode. # They're roughly equivalent - if QT_VERSION == 4 and mode == 'L': - src = src.convert('P') + if QT_VERSION == 4 and mode == "L": + src = src.convert("P") self.assert_image_equal(reloaded, src) def test_segfault(self): @@ -75,8 +75,8 @@ def test_segfault(self): if ImageQt.qt_is_installed: - class Example(QWidget): + class Example(QWidget): def __init__(self): super(Example, self).__init__() diff --git a/Tests/test_qt_image_toqpixmap.py b/Tests/test_qt_image_toqpixmap.py index 9bb7183b721..11dfab86147 100644 --- a/Tests/test_qt_image_toqpixmap.py +++ b/Tests/test_qt_image_toqpixmap.py @@ -8,14 +8,13 @@ class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase): - def test_sanity(self): - for mode in ('1', 'RGB', 'RGBA', 'L', 'P'): + for mode in ("1", "RGB", "RGBA", "L", "P"): data = ImageQt.toqpixmap(hopper(mode)) self.assertIsInstance(data, QPixmap) self.assertFalse(data.isNull()) # Test saving the file - tempfile = self.tempfile('temp_{}.png'.format(mode)) + tempfile = self.tempfile("temp_{}.png".format(mode)) data.save(tempfile) diff --git a/Tests/test_shell_injection.py b/Tests/test_shell_injection.py index 77fd67f01a8..83c003fc929 100644 --- a/Tests/test_shell_injection.py +++ b/Tests/test_shell_injection.py @@ -9,18 +9,11 @@ TEST_JPG = "Tests/images/hopper.jpg" TEST_GIF = "Tests/images/hopper.gif" -test_filenames = ( - "temp_';", - "temp_\";", - "temp_'\"|", - "temp_'\"||", - "temp_'\"&&", -) +test_filenames = ("temp_';", 'temp_";', "temp_'\"|", "temp_'\"||", "temp_'\"&&") -@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") +@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") class TestShellInjection(PillowTestCase): - def assert_save_filename_check(self, src_img, save_func): for filename in test_filenames: dest_file = self.tempfile(filename) diff --git a/Tests/test_tiff_ifdrational.py b/Tests/test_tiff_ifdrational.py index fae4d7ed6dc..66da8b3f674 100644 --- a/Tests/test_tiff_ifdrational.py +++ b/Tests/test_tiff_ifdrational.py @@ -7,7 +7,6 @@ class Test_IFDRational(PillowTestCase): - def _test_equal(self, num, denom, target): t = IFDRational(num, denom) @@ -44,17 +43,16 @@ def test_nonetype(self): def test_ifd_rational_save(self): methods = (True, False) - if 'libtiff_encoder' not in dir(Image.core): + if "libtiff_encoder" not in dir(Image.core): methods = (False,) for libtiff in methods: TiffImagePlugin.WRITE_LIBTIFF = libtiff im = hopper() - out = self.tempfile('temp.tiff') + out = self.tempfile("temp.tiff") res = IFDRational(301, 1) - im.save(out, dpi=(res, res), compression='raw') + im.save(out, dpi=(res, res), compression="raw") reloaded = Image.open(out) - self.assertEqual(float(IFDRational(301, 1)), - float(reloaded.tag_v2[282])) + self.assertEqual(float(IFDRational(301, 1)), float(reloaded.tag_v2[282])) diff --git a/Tests/test_uploader.py b/Tests/test_uploader.py index e40e7fb86a4..46dbd824aa9 100644 --- a/Tests/test_uploader.py +++ b/Tests/test_uploader.py @@ -3,11 +3,11 @@ class TestUploader(PillowTestCase): def check_upload_equal(self): - result = hopper('P').convert('RGB') - target = hopper('RGB') + result = hopper("P").convert("RGB") + target = hopper("RGB") self.assert_image_equal(result, target) def check_upload_similar(self): - result = hopper('P').convert('RGB') - target = hopper('RGB') + result = hopper("P").convert("RGB") + target = hopper("RGB") self.assert_image_similar(result, target, 0) diff --git a/Tests/test_util.py b/Tests/test_util.py index 08e9c166529..593922f8c63 100644 --- a/Tests/test_util.py +++ b/Tests/test_util.py @@ -4,7 +4,6 @@ class TestUtil(PillowTestCase): - def test_is_string_type(self): # Arrange color = "red" @@ -35,11 +34,12 @@ def test_is_path(self): # Assert self.assertTrue(it_is) - @unittest.skipIf(not _util.py36, 'os.path support for Paths added in 3.6') + @unittest.skipIf(not _util.py36, "os.path support for Paths added in 3.6") def test_path_obj_is_path(self): # Arrange from pathlib import Path - test_path = Path('filename.ext') + + test_path = Path("filename.ext") # Act it_is = _util.isPath(test_path) @@ -50,7 +50,7 @@ def test_path_obj_is_path(self): def test_is_not_path(self): # Arrange filename = self.tempfile("temp.ext") - fp = open(filename, 'w').close() + fp = open(filename, "w").close() # Act it_is_not = _util.isPath(fp) diff --git a/Tests/test_webp_leaks.py b/Tests/test_webp_leaks.py index 03befd5076a..67586ba3afc 100644 --- a/Tests/test_webp_leaks.py +++ b/Tests/test_webp_leaks.py @@ -5,14 +5,14 @@ test_file = "Tests/images/hopper.webp" -@unittest.skipUnless(features.check('webp'), "WebP is not installed") +@unittest.skipUnless(features.check("webp"), "WebP is not installed") class TestWebPLeaks(PillowLeakTestCase): mem_limit = 3 * 1024 # kb iterations = 100 def test_leak_load(self): - with open(test_file, 'rb') as f: + with open(test_file, "rb") as f: im_data = f.read() def core(): diff --git a/docs/conf.py b/docs/conf.py index 2c25588a0c7..0eb137daa98 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,27 +29,26 @@ # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.viewcode', - 'sphinx.ext.intersphinx'] +extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode", "sphinx.ext.intersphinx"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'Pillow (PIL Fork)' -copyright = u'1995-2011 Fredrik Lundh, 2010-2019 Alex Clark and Contributors' -author = u'Fredrik Lundh, Alex Clark and Contributors' +project = u"Pillow (PIL Fork)" +copyright = u"1995-2011 Fredrik Lundh, 2010-2019 Alex Clark and Contributors" +author = u"Fredrik Lundh, Alex Clark and Contributors" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -75,7 +74,7 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all # documents. @@ -93,7 +92,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] @@ -140,7 +139,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static', 'resources'] +html_static_path = ["_static", "resources"] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied @@ -203,20 +202,17 @@ # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. -htmlhelp_basename = 'PillowPILForkdoc' +htmlhelp_basename = "PillowPILForkdoc" # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', - # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', - # Additional stuff for the LaTeX preamble. # 'preamble': '', - # Latex figure (float) alignment # 'figure_align': 'htbp', } @@ -225,8 +221,13 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - (master_doc, 'PillowPILFork.tex', u'Pillow (PIL Fork) Documentation', - u'Alex Clark', 'manual'), + ( + master_doc, + "PillowPILFork.tex", + u"Pillow (PIL Fork) Documentation", + u"Alex Clark", + "manual", + ) ] # The name of an image file (relative to this directory) to place at the top of @@ -255,8 +256,7 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - (master_doc, 'pillowpilfork', u'Pillow (PIL Fork) Documentation', - [author], 1) + (master_doc, "pillowpilfork", u"Pillow (PIL Fork) Documentation", [author], 1) ] # If true, show URL addresses after external links. @@ -269,10 +269,15 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - (master_doc, 'PillowPILFork', u'Pillow (PIL Fork) Documentation', - author, 'PillowPILFork', - 'Pillow is the friendly PIL fork by Alex Clark and Contributors.', - 'Miscellaneous'), + ( + master_doc, + "PillowPILFork", + u"Pillow (PIL Fork) Documentation", + author, + "PillowPILFork", + "Pillow is the friendly PIL fork by Alex Clark and Contributors.", + "Miscellaneous", + ) ] # Documents to append as an appendix to all manuals. @@ -289,4 +294,4 @@ def setup(app): - app.add_javascript('js/script.js') + app.add_javascript("js/script.js") diff --git a/docs/example/DdsImagePlugin.py b/docs/example/DdsImagePlugin.py index 631dc2d61e1..c171bb1f179 100644 --- a/docs/example/DdsImagePlugin.py +++ b/docs/example/DdsImagePlugin.py @@ -61,8 +61,7 @@ DDS_ALPHA = DDPF_ALPHA DDS_PAL8 = DDPF_PALETTEINDEXED8 -DDS_HEADER_FLAGS_TEXTURE = (DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | - DDSD_PIXELFORMAT) +DDS_HEADER_FLAGS_TEXTURE = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT DDS_HEADER_FLAGS_MIPMAP = DDSD_MIPMAPCOUNT DDS_HEADER_FLAGS_VOLUME = DDSD_DEPTH DDS_HEADER_FLAGS_PITCH = DDSD_PITCH @@ -94,9 +93,9 @@ def _decode565(bits): - a = ((bits >> 11) & 0x1f) << 3 - b = ((bits >> 5) & 0x3f) << 2 - c = (bits & 0x1f) << 3 + a = ((bits >> 11) & 0x1F) << 3 + b = ((bits >> 5) & 0x3F) << 2 + c = (bits & 0x1F) << 3 return a, b, c @@ -145,7 +144,7 @@ def _dxt1(data, width, height): r, g, b = 0, 0, 0 idx = 4 * ((y + j) * width + x + i) - ret[idx:idx+4] = struct.pack('4B', r, g, b, 255) + ret[idx : idx + 4] = struct.pack("4B", r, g, b, 255) return bytes(ret) @@ -167,7 +166,7 @@ def _dxtc_alpha(a0, a1, ac0, ac1, ai): elif ac == 6: alpha = 0 elif ac == 7: - alpha = 0xff + alpha = 0xFF else: alpha = ((6 - ac) * a0 + (ac - 1) * a1) // 5 @@ -180,8 +179,7 @@ def _dxt5(data, width, height): for y in range(0, height, 4): for x in range(0, width, 4): - a0, a1, ac0, ac1, c0, c1, code = struct.unpack("<2BHI2HI", - data.read(16)) + a0, a1, ac0, ac1, c0, c1, code = struct.unpack("<2BHI2HI", data.read(16)) r0, g0, b0 = _decode565(c0) r1, g1, b1 = _decode565(c1) @@ -202,7 +200,7 @@ def _dxt5(data, width, height): r, g, b = _c3(r0, r1), _c3(g0, g1), _c3(b0, b1) idx = 4 * ((y + j) * width + x + i) - ret[idx:idx+4] = struct.pack('4B', r, g, b, alpha) + ret[idx : idx + 4] = struct.pack("4B", r, g, b, alpha) return bytes(ret) @@ -230,8 +228,7 @@ def _open(self): # pixel format pfsize, pfflags = struct.unpack("<2I", header.read(8)) fourcc = header.read(4) - bitcount, rmask, gmask, bmask, amask = struct.unpack("<5I", - header.read(20)) + bitcount, rmask, gmask, bmask, amask = struct.unpack("<5I", header.read(20)) if fourcc == b"DXT1": self.decoder = "DXT1" @@ -240,9 +237,7 @@ def _open(self): else: raise NotImplementedError("Unimplemented pixel format %r" % fourcc) - self.tile = [ - (self.decoder, (0, 0) + self.size, 0, (self.mode, 0, 1)) - ] + self.tile = [(self.decoder, (0, 0) + self.size, 0, (self.mode, 0, 1))] def load_seek(self, pos): pass @@ -270,8 +265,8 @@ def decode(self, buffer): return 0, 0 -Image.register_decoder('DXT1', DXT1Decoder) -Image.register_decoder('DXT5', DXT5Decoder) +Image.register_decoder("DXT1", DXT1Decoder) +Image.register_decoder("DXT5", DXT5Decoder) def _validate(prefix): diff --git a/docs/handbook/writing-your-own-file-decoder.rst b/docs/handbook/writing-your-own-file-decoder.rst index f3f34e30dea..0763109ab4f 100644 --- a/docs/handbook/writing-your-own-file-decoder.rst +++ b/docs/handbook/writing-your-own-file-decoder.rst @@ -201,7 +201,8 @@ table describes some commonly used **raw modes**: +-----------+-----------------------------------------------------------------+ | ``BGR`` | 24-bit true colour, stored as (blue, green, red). | +-----------+-----------------------------------------------------------------+ -| ``RGBX`` | 24-bit true colour, stored as (red, green, blue, pad). | +| ``RGBX`` | 24-bit true colour, stored as (red, green, blue, pad). The pad | +| | pixels may vary. | +-----------+-----------------------------------------------------------------+ | ``RGB;L`` | 24-bit true colour, line interleaved (first all red pixels, then| | | all green pixels, finally all blue pixels). | diff --git a/docs/installation.rst b/docs/installation.rst index 43bf5c81ecd..2b9ef72a044 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -410,7 +410,7 @@ These platforms are built and tested for every change. | | PyPy, 3.7/MinGW |x86 | +----------------------------------+-------------------------------+-----------------------+ -\* Mac OS X CI is not run for every commit, but is run for every release. +\* macOS CI is not run for every commit, but is run for every release. Other Platforms ^^^^^^^^^^^^^^^ diff --git a/docs/releasenotes/6.1.0.rst b/docs/releasenotes/6.1.0.rst index 2bb5a3d94e6..98568fb52ec 100644 --- a/docs/releasenotes/6.1.0.rst +++ b/docs/releasenotes/6.1.0.rst @@ -11,6 +11,18 @@ An optional ``include_layered_windows`` parameter has been added to ``ImageGrab. defaulting to ``False``. If true, layered windows will be included in the resulting image on Windows. +Variation fonts +^^^^^^^^^^^^^^^ + +Variation fonts are now supported, allowing for different styles from the same font +file. ``ImageFont.FreeTypeFont`` has four new methods, +:py:meth:`PIL.ImageFont.FreeTypeFont.get_variation_names` and +:py:meth:`PIL.ImageFont.FreeTypeFont.set_variation_by_name` for using named styles, and +:py:meth:`PIL.ImageFont.FreeTypeFont.get_variation_axes` and +:py:meth:`PIL.ImageFont.FreeTypeFont.set_variation_by_axes` for using font axes +instead. An ``IOError`` will be raised if the font is not a variation font. FreeType +2.9.1 or greater is required. + Other Changes ============= @@ -19,3 +31,9 @@ ImageTk.getimage This function is now supported. It returns the contents of an ``ImageTk.PhotoImage`` as an RGBA ``Image.Image`` instance. + +Top To Bottom Complex Text Rendering +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Drawing text in the 'ttb' direction with ImageFont has been significantly improved +and requires Raqm 0.7 or greater. diff --git a/mp_compile.py b/mp_compile.py index f50210ba5d6..04bfc3c9c7c 100644 --- a/mp_compile.py +++ b/mp_compile.py @@ -10,7 +10,7 @@ import sys try: - MAX_PROCS = int(os.environ.get('MAX_CONCURRENCY', min(4, cpu_count()))) + MAX_PROCS = int(os.environ.get("MAX_CONCURRENCY", min(4, cpu_count()))) except NotImplementedError: MAX_PROCS = None @@ -26,9 +26,17 @@ def _mp_compile_one(tp): return -def _mp_compile(self, sources, output_dir=None, macros=None, - include_dirs=None, debug=0, extra_preargs=None, - extra_postargs=None, depends=None): +def _mp_compile( + self, + sources, + output_dir=None, + macros=None, + include_dirs=None, + debug=0, + extra_preargs=None, + extra_postargs=None, + depends=None, +): """Compile one or more source files. see distutils.ccompiler.CCompiler.compile for comments. @@ -37,7 +45,8 @@ def _mp_compile(self, sources, output_dir=None, macros=None, # entirely or implement _compile(). macros, objects, extra_postargs, pp_opts, build = self._setup_compile( - output_dir, macros, include_dirs, sources, depends, extra_postargs) + output_dir, macros, include_dirs, sources, depends, extra_postargs + ) cc_args = self._get_cc_args(pp_opts, debug, extra_preargs) pool = Pool(MAX_PROCS) @@ -45,8 +54,7 @@ def _mp_compile(self, sources, output_dir=None, macros=None, print("Building using %d processes" % pool._processes) except Exception: pass - arr = [(self, obj, build, cc_args, extra_postargs, pp_opts) - for obj in objects] + arr = [(self, obj, build, cc_args, extra_postargs, pp_opts) for obj in objects] pool.map_async(_mp_compile_one, arr) pool.close() pool.join() @@ -56,8 +64,8 @@ def _mp_compile(self, sources, output_dir=None, macros=None, def install(): - fl_win = sys.platform.startswith('win') - fl_cygwin = sys.platform.startswith('cygwin') + fl_win = sys.platform.startswith("win") + fl_cygwin = sys.platform.startswith("cygwin") if fl_win or fl_cygwin: # Windows barfs on multiprocessing installs @@ -72,11 +80,11 @@ def install(): Pool(2) CCompiler.compile = _mp_compile except Exception as msg: - print("Exception installing mp_compile, proceeding without:" - "%s" % msg) + print("Exception installing mp_compile, proceeding without: %s" % msg) else: - print("Single threaded build, not installing mp_compile:" - "%s processes" % MAX_PROCS) + print( + "Single threaded build, not installing mp_compile: %s processes" % MAX_PROCS + ) # We monkeypatch Python 2.7 diff --git a/selftest.py b/selftest.py index f4383b1206a..3e4112ffe88 100755 --- a/selftest.py +++ b/selftest.py @@ -161,12 +161,12 @@ def testimage(): exit_status = 0 - print("-"*68) + print("-" * 68) print("Pillow", Image.__version__, "TEST SUMMARY ") - print("-"*68) + print("-" * 68) print("Python modules loaded from", os.path.dirname(Image.__file__)) print("Binary modules loaded from", os.path.dirname(Image.core.__file__)) - print("-"*68) + print("-" * 68) for name, feature in [ ("pil", "PIL CORE"), ("tkinter", "TKINTER"), @@ -180,16 +180,17 @@ def testimage(): ("jpg_2000", "OPENJPEG (JPEG2000)"), ("zlib", "ZLIB (PNG/ZIP)"), ("libtiff", "LIBTIFF"), - ("raqm", "RAQM (Bidirectional Text)") + ("raqm", "RAQM (Bidirectional Text)"), ]: if features.check(name): print("---", feature, "support ok") else: print("***", feature, "support not installed") - print("-"*68) + print("-" * 68) # use doctest to make sure the test program behaves as documented! import doctest + print("Running selftest:") status = doctest.testmod(sys.modules[__name__]) if status[0]: diff --git a/setup.py b/setup.py index dcb1888721e..5ccf7133d98 100755 --- a/setup.py +++ b/setup.py @@ -29,26 +29,81 @@ warnings.warn( "Pillow does not yet support Python {}.{} and does not yet provide " "prebuilt Windows binaries. We do not recommend building from " - "source on Windows.".format(sys.version_info.major, - sys.version_info.minor), - RuntimeWarning) + "source on Windows.".format(sys.version_info.major, sys.version_info.minor), + RuntimeWarning, + ) _IMAGING = ("decode", "encode", "map", "display", "outline", "path") _LIB_IMAGING = ( - "Access", "AlphaComposite", "Resample", "Bands", "BcnDecode", "BitDecode", - "Blend", "Chops", "ColorLUT", "Convert", "ConvertYCbCr", "Copy", "Crop", - "Dib", "Draw", "Effects", "EpsEncode", "File", "Fill", "Filter", - "FliDecode", "Geometry", "GetBBox", "GifDecode", "GifEncode", "HexDecode", - "Histo", "JpegDecode", "JpegEncode", "Matrix", "ModeFilter", - "Negative", "Offset", "Pack", "PackDecode", "Palette", "Paste", "Quant", - "QuantOctree", "QuantHash", "QuantHeap", "PcdDecode", "PcxDecode", - "PcxEncode", "Point", "RankFilter", "RawDecode", "RawEncode", "Storage", - "SgiRleDecode", "SunRleDecode", "TgaRleDecode", "TgaRleEncode", "Unpack", - "UnpackYCC", "UnsharpMask", "XbmDecode", "XbmEncode", "ZipDecode", - "ZipEncode", "TiffDecode", "Jpeg2KDecode", "Jpeg2KEncode", "BoxBlur", - "QuantPngQuant", "codec_fd") + "Access", + "AlphaComposite", + "Resample", + "Bands", + "BcnDecode", + "BitDecode", + "Blend", + "Chops", + "ColorLUT", + "Convert", + "ConvertYCbCr", + "Copy", + "Crop", + "Dib", + "Draw", + "Effects", + "EpsEncode", + "File", + "Fill", + "Filter", + "FliDecode", + "Geometry", + "GetBBox", + "GifDecode", + "GifEncode", + "HexDecode", + "Histo", + "JpegDecode", + "JpegEncode", + "Matrix", + "ModeFilter", + "Negative", + "Offset", + "Pack", + "PackDecode", + "Palette", + "Paste", + "Quant", + "QuantOctree", + "QuantHash", + "QuantHeap", + "PcdDecode", + "PcxDecode", + "PcxEncode", + "Point", + "RankFilter", + "RawDecode", + "RawEncode", + "Storage", + "SgiRleDecode", + "SunRleDecode", + "TgaRleDecode", + "TgaRleEncode", + "Unpack", + "UnpackYCC", + "UnsharpMask", + "XbmDecode", + "XbmEncode", + "ZipDecode", + "ZipEncode", + "TiffDecode", + "Jpeg2KDecode", + "Jpeg2KEncode", + "BoxBlur", + "QuantPngQuant", + "codec_fd", +) DEBUG = False @@ -61,8 +116,8 @@ class RequiredDependencyException(Exception): pass -PLATFORM_MINGW = 'mingw' in ccompiler.get_default_compiler() -PLATFORM_PYPY = hasattr(sys, 'pypy_version_info') +PLATFORM_MINGW = "mingw" in ccompiler.get_default_compiler() +PLATFORM_PYPY = hasattr(sys, "pypy_version_info") def _dbg(s, tp=None): @@ -77,39 +132,36 @@ def _find_library_dirs_ldconfig(): # Based on ctypes.util from Python 2 if sys.platform.startswith("linux") or sys.platform.startswith("gnu"): - if struct.calcsize('l') == 4: - machine = os.uname()[4] + '-32' + if struct.calcsize("l") == 4: + machine = os.uname()[4] + "-32" else: - machine = os.uname()[4] + '-64' + machine = os.uname()[4] + "-64" mach_map = { - 'x86_64-64': 'libc6,x86-64', - 'ppc64-64': 'libc6,64bit', - 'sparc64-64': 'libc6,64bit', - 's390x-64': 'libc6,64bit', - 'ia64-64': 'libc6,IA-64', - } - abi_type = mach_map.get(machine, 'libc6') + "x86_64-64": "libc6,x86-64", + "ppc64-64": "libc6,64bit", + "sparc64-64": "libc6,64bit", + "s390x-64": "libc6,64bit", + "ia64-64": "libc6,IA-64", + } + abi_type = mach_map.get(machine, "libc6") # Assuming GLIBC's ldconfig (with option -p) # Alpine Linux uses musl that can't print cache - args = ['/sbin/ldconfig', '-p'] - expr = r'.*\(%s.*\) => (.*)' % abi_type + args = ["/sbin/ldconfig", "-p"] + expr = r".*\(%s.*\) => (.*)" % abi_type env = dict(os.environ) - env['LC_ALL'] = 'C' - env['LANG'] = 'C' + env["LC_ALL"] = "C" + env["LANG"] = "C" elif sys.platform.startswith("freebsd"): - args = ['/sbin/ldconfig', '-r'] - expr = r'.* => (.*)' + args = ["/sbin/ldconfig", "-r"] + expr = r".* => (.*)" env = {} - null = open(os.devnull, 'wb') + null = open(os.devnull, "wb") try: with null: - p = subprocess.Popen(args, - stderr=null, - stdout=subprocess.PIPE, - env=env) + p = subprocess.Popen(args, stderr=null, stdout=subprocess.PIPE, env=env) except OSError: # E.g. command not found return [] [data, _] = p.communicate() @@ -130,10 +182,10 @@ def _add_directory(path, subdir, where=None): subdir = os.path.realpath(subdir) if os.path.isdir(subdir) and subdir not in path: if where is None: - _dbg('Appending path %s', subdir) + _dbg("Appending path %s", subdir) path.append(subdir) else: - _dbg('Inserting path %s', subdir) + _dbg("Inserting path %s", subdir) path.insert(where, subdir) elif subdir in path and where is not None: path.remove(subdir) @@ -142,9 +194,9 @@ def _add_directory(path, subdir, where=None): def _find_include_file(self, include): for directory in self.compiler.include_dirs: - _dbg('Checking for include file %s in %s', (include, directory)) + _dbg("Checking for include file %s in %s", (include, directory)) if os.path.isfile(os.path.join(directory, include)): - _dbg('Found %s', include) + _dbg("Found %s", include) return 1 return 0 @@ -152,10 +204,9 @@ def _find_include_file(self, include): def _find_library_file(self, library): ret = self.compiler.find_library_file(self.compiler.library_dirs, library) if ret: - _dbg('Found library %s at %s', (library, ret)) + _dbg("Found library %s at %s", (library, ret)) else: - _dbg("Couldn't find library %s in %s", - (library, self.compiler.library_dirs)) + _dbg("Couldn't find library %s in %s", (library, self.compiler.library_dirs)) return ret @@ -167,18 +218,18 @@ def _cmd_exists(cmd): def _read(file): - with open(file, 'rb') as fp: + with open(file, "rb") as fp: return fp.read() def get_version(): - version_file = 'src/PIL/_version.py' - with open(version_file, 'r') as f: - exec(compile(f.read(), version_file, 'exec')) - return locals()['__version__'] + version_file = "src/PIL/_version.py" + with open(version_file, "r") as f: + exec(compile(f.read(), version_file, "exec")) + return locals()["__version__"] -NAME = 'Pillow' +NAME = "Pillow" PILLOW_VERSION = get_version() JPEG_ROOT = None JPEG2K_ROOT = None @@ -191,17 +242,11 @@ def get_version(): def _pkg_config(name): try: - command_libs = [ - 'pkg-config', - '--libs-only-L', name, - ] - command_cflags = [ - 'pkg-config', - '--cflags-only-I', name, - ] + command_libs = ["pkg-config", "--libs-only-L", name] + command_cflags = ["pkg-config", "--cflags-only-I", name] if not DEBUG: - command_libs.append('--silence-errors') - command_cflags.append('--silence-errors') + command_libs.append("--silence-errors") + command_cflags.append("--silence-errors") libs = ( subprocess.check_output(command_libs) .decode("utf8") @@ -221,10 +266,19 @@ def _pkg_config(name): class pil_build_ext(build_ext): class feature: - features = ['zlib', 'jpeg', 'tiff', 'freetype', 'lcms', 'webp', - 'webpmux', 'jpeg2000', 'imagequant'] + features = [ + "zlib", + "jpeg", + "tiff", + "freetype", + "lcms", + "webp", + "webpmux", + "jpeg2000", + "imagequant", + ] - required = {'jpeg', 'zlib'} + required = {"jpeg", "zlib"} def __init__(self): for f in self.features: @@ -242,23 +296,24 @@ def __iter__(self): feature = feature() - user_options = build_ext.user_options + [ - ('disable-%s' % x, None, 'Disable support for %s' % x) for x in feature - ] + [ - ('enable-%s' % x, None, 'Enable support for %s' % x) for x in feature - ] + [ - ('disable-platform-guessing', None, - 'Disable platform guessing on Linux'), - ('debug', None, 'Debug logging') - ] + [('add-imaging-libs=', None, 'Add libs to _imaging build')] + user_options = ( + build_ext.user_options + + [("disable-%s" % x, None, "Disable support for %s" % x) for x in feature] + + [("enable-%s" % x, None, "Enable support for %s" % x) for x in feature] + + [ + ("disable-platform-guessing", None, "Disable platform guessing on Linux"), + ("debug", None, "Debug logging"), + ] + + [("add-imaging-libs=", None, "Add libs to _imaging build")] + ) def initialize_options(self): self.disable_platform_guessing = None self.add_imaging_libs = "" build_ext.initialize_options(self) for x in self.feature: - setattr(self, 'disable_%s' % x, None) - setattr(self, 'enable_%s' % x, None) + setattr(self, "disable_%s" % x, None) + setattr(self, "enable_%s" % x, None) def finalize_options(self): build_ext.finalize_options(self) @@ -272,16 +327,16 @@ def finalize_options(self): # number of jobs. self.parallel = mp_compile.MAX_PROCS for x in self.feature: - if getattr(self, 'disable_%s' % x): + if getattr(self, "disable_%s" % x): setattr(self.feature, x, False) self.feature.required.discard(x) - _dbg('Disabling %s', x) - if getattr(self, 'enable_%s' % x): + _dbg("Disabling %s", x) + if getattr(self, "enable_%s" % x): raise ValueError( - 'Conflicting options: --enable-%s and --disable-%s' % - (x, x)) - if getattr(self, 'enable_%s' % x): - _dbg('Requiring %s', x) + "Conflicting options: --enable-%s and --disable-%s" % (x, x) + ) + if getattr(self, "enable_%s" % x): + _dbg("Requiring %s", x) self.feature.required.add(x) def build_extensions(self): @@ -292,34 +347,35 @@ def build_extensions(self): _add_directory(include_dirs, "src/libImaging") pkg_config = None - if _cmd_exists('pkg-config'): + if _cmd_exists("pkg-config"): pkg_config = _pkg_config # # add configured kits - for root_name, lib_name in dict(JPEG_ROOT="libjpeg", - JPEG2K_ROOT="libopenjp2", - TIFF_ROOT=("libtiff-5", "libtiff-4"), - ZLIB_ROOT="zlib", - FREETYPE_ROOT="freetype2", - LCMS_ROOT="lcms2", - IMAGEQUANT_ROOT="libimagequant" - ).items(): + for root_name, lib_name in dict( + JPEG_ROOT="libjpeg", + JPEG2K_ROOT="libopenjp2", + TIFF_ROOT=("libtiff-5", "libtiff-4"), + ZLIB_ROOT="zlib", + FREETYPE_ROOT="freetype2", + LCMS_ROOT="lcms2", + IMAGEQUANT_ROOT="libimagequant", + ).items(): root = globals()[root_name] if root is None and root_name in os.environ: prefix = os.environ[root_name] - root = (os.path.join(prefix, 'lib'), os.path.join(prefix, 'include')) + root = (os.path.join(prefix, "lib"), os.path.join(prefix, "include")) if root is None and pkg_config: if isinstance(lib_name, tuple): for lib_name2 in lib_name: - _dbg('Looking for `%s` using pkg-config.' % lib_name2) + _dbg("Looking for `%s` using pkg-config." % lib_name2) root = pkg_config(lib_name2) if root: break else: - _dbg('Looking for `%s` using pkg-config.' % lib_name) + _dbg("Looking for `%s` using pkg-config." % lib_name) root = pkg_config(lib_name) if isinstance(root, tuple): @@ -331,20 +387,20 @@ def build_extensions(self): _add_directory(include_dirs, include_root) # respect CFLAGS/LDFLAGS - for k in ('CFLAGS', 'LDFLAGS'): + for k in ("CFLAGS", "LDFLAGS"): if k in os.environ: - for match in re.finditer(r'-I([^\s]+)', os.environ[k]): + for match in re.finditer(r"-I([^\s]+)", os.environ[k]): _add_directory(include_dirs, match.group(1)) - for match in re.finditer(r'-L([^\s]+)', os.environ[k]): + for match in re.finditer(r"-L([^\s]+)", os.environ[k]): _add_directory(library_dirs, match.group(1)) # include, rpath, if set as environment variables: - for k in ('C_INCLUDE_PATH', 'CPATH', 'INCLUDE'): + for k in ("C_INCLUDE_PATH", "CPATH", "INCLUDE"): if k in os.environ: for d in os.environ[k].split(os.path.pathsep): _add_directory(include_dirs, d) - for k in ('LD_RUN_PATH', 'LIBRARY_PATH', 'LIB'): + for k in ("LD_RUN_PATH", "LIBRARY_PATH", "LIB"): if k in os.environ: for d in os.environ[k].split(os.path.pathsep): _add_directory(library_dirs, d) @@ -362,9 +418,10 @@ def build_extensions(self): elif sys.platform == "cygwin": # pythonX.Y.dll.a is in the /usr/lib/pythonX.Y/config directory - _add_directory(library_dirs, - os.path.join("/usr/lib", "python%s" % - sys.version[:3], "config")) + _add_directory( + library_dirs, + os.path.join("/usr/lib", "python%s" % sys.version[:3], "config"), + ) elif sys.platform == "darwin": # attempt to make sure we pick freetype2 over other versions @@ -379,8 +436,11 @@ def build_extensions(self): # if Homebrew is installed, use its lib and include directories try: - prefix = subprocess.check_output(['brew', '--prefix']).strip( - ).decode('latin1') + prefix = ( + subprocess.check_output(["brew", "--prefix"]) + .strip() + .decode("latin1") + ) except Exception: # Homebrew not installed prefix = None @@ -389,28 +449,30 @@ def build_extensions(self): if prefix: # add Homebrew's include and lib directories - _add_directory(library_dirs, os.path.join(prefix, 'lib')) - _add_directory(include_dirs, os.path.join(prefix, 'include')) - ft_prefix = os.path.join(prefix, 'opt', 'freetype') + _add_directory(library_dirs, os.path.join(prefix, "lib")) + _add_directory(include_dirs, os.path.join(prefix, "include")) + ft_prefix = os.path.join(prefix, "opt", "freetype") if ft_prefix and os.path.isdir(ft_prefix): # freetype might not be linked into Homebrew's prefix - _add_directory(library_dirs, os.path.join(ft_prefix, 'lib')) - _add_directory(include_dirs, - os.path.join(ft_prefix, 'include')) + _add_directory(library_dirs, os.path.join(ft_prefix, "lib")) + _add_directory(include_dirs, os.path.join(ft_prefix, "include")) else: # fall back to freetype from XQuartz if # Homebrew's freetype is missing _add_directory(library_dirs, "/usr/X11/lib") _add_directory(include_dirs, "/usr/X11/include") - elif sys.platform.startswith("linux") or \ - sys.platform.startswith("gnu") or \ - sys.platform.startswith("freebsd"): + elif ( + sys.platform.startswith("linux") + or sys.platform.startswith("gnu") + or sys.platform.startswith("freebsd") + ): for dirname in _find_library_dirs_ldconfig(): _add_directory(library_dirs, dirname) - if sys.platform.startswith("linux") and \ - os.environ.get('ANDROID_ROOT', None): + if sys.platform.startswith("linux") and os.environ.get( + "ANDROID_ROOT", None + ): # termux support for android. # system libraries (zlib) are installed in /system/lib # headers are at $PREFIX/include @@ -441,27 +503,26 @@ def build_extensions(self): if sys.platform == "win32": if PLATFORM_MINGW: - _add_directory(include_dirs, - "C:\\msys64\\mingw32\\include\\libimagequant") + _add_directory( + include_dirs, "C:\\msys64\\mingw32\\include\\libimagequant" + ) # on Windows, look for the OpenJPEG libraries in the location that # the official installer puts them - program_files = os.environ.get('ProgramFiles', '') + program_files = os.environ.get("ProgramFiles", "") best_version = (0, 0) best_path = None for name in os.listdir(program_files): - if name.startswith('OpenJPEG '): - version = tuple(int(x) for x in - name[9:].strip().split('.')) + if name.startswith("OpenJPEG "): + version = tuple(int(x) for x in name[9:].strip().split(".")) if version > best_version: best_version = version best_path = os.path.join(program_files, name) if best_path: - _dbg('Adding %s to search list', best_path) - _add_directory(library_dirs, os.path.join(best_path, 'lib')) - _add_directory(include_dirs, - os.path.join(best_path, 'include')) + _dbg("Adding %s to search list", best_path) + _add_directory(library_dirs, os.path.join(best_path, "lib")) + _add_directory(include_dirs, os.path.join(best_path, "include")) # # insert new dirs *before* default libs, to avoid conflicts @@ -475,51 +536,51 @@ def build_extensions(self): feature = self.feature - if feature.want('zlib'): - _dbg('Looking for zlib') + if feature.want("zlib"): + _dbg("Looking for zlib") if _find_include_file(self, "zlib.h"): if _find_library_file(self, "z"): feature.zlib = "z" - elif (sys.platform == "win32" and - _find_library_file(self, "zlib")): + elif sys.platform == "win32" and _find_library_file(self, "zlib"): feature.zlib = "zlib" # alternative name - if feature.want('jpeg'): - _dbg('Looking for jpeg') + if feature.want("jpeg"): + _dbg("Looking for jpeg") if _find_include_file(self, "jpeglib.h"): if _find_library_file(self, "jpeg"): feature.jpeg = "jpeg" - elif (sys.platform == "win32" and - _find_library_file(self, "libjpeg")): + elif sys.platform == "win32" and _find_library_file(self, "libjpeg"): feature.jpeg = "libjpeg" # alternative name feature.openjpeg_version = None - if feature.want('jpeg2000'): - _dbg('Looking for jpeg2000') + if feature.want("jpeg2000"): + _dbg("Looking for jpeg2000") best_version = None best_path = None # Find the best version for directory in self.compiler.include_dirs: - _dbg('Checking for openjpeg-#.# in %s', directory) + _dbg("Checking for openjpeg-#.# in %s", directory) try: listdir = os.listdir(directory) except Exception: # WindowsError, FileNotFoundError continue for name in listdir: - if name.startswith('openjpeg-') and \ - os.path.isfile(os.path.join(directory, name, - 'openjpeg.h')): - _dbg('Found openjpeg.h in %s/%s', (directory, name)) - version = tuple(int(x) for x in name[9:].split('.')) + if name.startswith("openjpeg-") and os.path.isfile( + os.path.join(directory, name, "openjpeg.h") + ): + _dbg("Found openjpeg.h in %s/%s", (directory, name)) + version = tuple(int(x) for x in name[9:].split(".")) if best_version is None or version > best_version: best_version = version best_path = os.path.join(directory, name) - _dbg('Best openjpeg version %s so far in %s', - (best_version, best_path)) + _dbg( + "Best openjpeg version %s so far in %s", + (best_version, best_path), + ) - if best_version and _find_library_file(self, 'openjp2'): + if best_version and _find_library_file(self, "openjp2"): # Add the directory to the include path so we can include # rather than having to cope with the versioned # include path @@ -528,45 +589,43 @@ def build_extensions(self): # self.compiler.include_dirs. Should investigate how that is # possible. _add_directory(self.compiler.include_dirs, best_path, 0) - feature.jpeg2000 = 'openjp2' - feature.openjpeg_version = '.'.join(str(x) for x in - best_version) + feature.jpeg2000 = "openjp2" + feature.openjpeg_version = ".".join(str(x) for x in best_version) - if feature.want('imagequant'): - _dbg('Looking for imagequant') - if _find_include_file(self, 'libimagequant.h'): + if feature.want("imagequant"): + _dbg("Looking for imagequant") + if _find_include_file(self, "libimagequant.h"): if _find_library_file(self, "imagequant"): feature.imagequant = "imagequant" elif _find_library_file(self, "libimagequant"): feature.imagequant = "libimagequant" - if feature.want('tiff'): - _dbg('Looking for tiff') - if _find_include_file(self, 'tiff.h'): + if feature.want("tiff"): + _dbg("Looking for tiff") + if _find_include_file(self, "tiff.h"): if _find_library_file(self, "tiff"): feature.tiff = "tiff" - if (sys.platform in ["win32", "darwin"] and - _find_library_file(self, "libtiff")): + if sys.platform in ["win32", "darwin"] and _find_library_file( + self, "libtiff" + ): feature.tiff = "libtiff" - if feature.want('freetype'): - _dbg('Looking for freetype') + if feature.want("freetype"): + _dbg("Looking for freetype") if _find_library_file(self, "freetype"): # look for freetype2 include files freetype_version = 0 for subdir in self.compiler.include_dirs: - _dbg('Checking for include file %s in %s', - ("ft2build.h", subdir)) + _dbg("Checking for include file %s in %s", ("ft2build.h", subdir)) if os.path.isfile(os.path.join(subdir, "ft2build.h")): - _dbg('Found %s in %s', ("ft2build.h", subdir)) + _dbg("Found %s in %s", ("ft2build.h", subdir)) freetype_version = 21 subdir = os.path.join(subdir, "freetype2") break subdir = os.path.join(subdir, "freetype2") - _dbg('Checking for include file %s in %s', - ("ft2build.h", subdir)) + _dbg("Checking for include file %s in %s", ("ft2build.h", subdir)) if os.path.isfile(os.path.join(subdir, "ft2build.h")): - _dbg('Found %s in %s', ("ft2build.h", subdir)) + _dbg("Found %s in %s", ("ft2build.h", subdir)) freetype_version = 21 break if freetype_version: @@ -574,8 +633,8 @@ def build_extensions(self): if subdir: _add_directory(self.compiler.include_dirs, subdir, 0) - if feature.want('lcms'): - _dbg('Looking for lcms') + if feature.want("lcms"): + _dbg("Looking for lcms") if _find_include_file(self, "lcms2.h"): if _find_library_file(self, "lcms2"): feature.lcms = "lcms2" @@ -583,30 +642,34 @@ def build_extensions(self): # alternate Windows name. feature.lcms = "lcms2_static" - if feature.want('webp'): - _dbg('Looking for webp') - if (_find_include_file(self, "webp/encode.h") and - _find_include_file(self, "webp/decode.h")): + if feature.want("webp"): + _dbg("Looking for webp") + if _find_include_file(self, "webp/encode.h") and _find_include_file( + self, "webp/decode.h" + ): # In Google's precompiled zip it is call "libwebp": if _find_library_file(self, "webp"): feature.webp = "webp" elif _find_library_file(self, "libwebp"): feature.webp = "libwebp" - if feature.want('webpmux'): - _dbg('Looking for webpmux') - if (_find_include_file(self, "webp/mux.h") and - _find_include_file(self, "webp/demux.h")): - if (_find_library_file(self, "webpmux") and - _find_library_file(self, "webpdemux")): + if feature.want("webpmux"): + _dbg("Looking for webpmux") + if _find_include_file(self, "webp/mux.h") and _find_include_file( + self, "webp/demux.h" + ): + if _find_library_file(self, "webpmux") and _find_library_file( + self, "webpdemux" + ): feature.webpmux = "webpmux" - if (_find_library_file(self, "libwebpmux") and - _find_library_file(self, "libwebpdemux")): + if _find_library_file(self, "libwebpmux") and _find_library_file( + self, "libwebpdemux" + ): feature.webpmux = "libwebpmux" for f in feature: if not getattr(feature, f) and feature.require(f): - if f in ('jpeg', 'zlib'): + if f in ("jpeg", "zlib"): raise RequiredDependencyException(f) raise DependencyException(f) @@ -640,7 +703,7 @@ def build_extensions(self): defs.append(("HAVE_LIBTIFF", None)) if sys.platform == "win32": libs.extend(["kernel32", "user32", "gdi32"]) - if struct.unpack("h", "\0\1".encode('ascii'))[0] == 1: + if struct.unpack("h", "\0\1".encode("ascii"))[0] == 1: defs.append(("WORDS_BIGENDIAN", None)) if sys.platform == "win32" and not (PLATFORM_PYPY or PLATFORM_MINGW): @@ -648,10 +711,7 @@ def build_extensions(self): else: defs.append(("PILLOW_VERSION", '"%s"' % PILLOW_VERSION)) - exts = [(Extension("PIL._imaging", - files, - libraries=libs, - define_macros=defs))] + exts = [(Extension("PIL._imaging", files, libraries=libs, define_macros=defs))] # # additional libraries @@ -659,17 +719,26 @@ def build_extensions(self): if feature.freetype: libs = ["freetype"] defs = [] - exts.append(Extension( - "PIL._imagingft", ["src/_imagingft.c"], libraries=libs, - define_macros=defs)) + exts.append( + Extension( + "PIL._imagingft", + ["src/_imagingft.c"], + libraries=libs, + define_macros=defs, + ) + ) if feature.lcms: extra = [] if sys.platform == "win32": extra.extend(["user32", "gdi32"]) - exts.append(Extension("PIL._imagingcms", - ["src/_imagingcms.c"], - libraries=[feature.lcms] + extra)) + exts.append( + Extension( + "PIL._imagingcms", + ["src/_imagingcms.c"], + libraries=[feature.lcms] + extra, + ) + ) if feature.webp: libs = [feature.webp] @@ -678,18 +747,23 @@ def build_extensions(self): if feature.webpmux: defs.append(("HAVE_WEBPMUX", None)) libs.append(feature.webpmux) - libs.append(feature.webpmux.replace('pmux', 'pdemux')) + libs.append(feature.webpmux.replace("pmux", "pdemux")) - exts.append(Extension("PIL._webp", - ["src/_webp.c"], - libraries=libs, - define_macros=defs)) - - tk_libs = ['psapi'] if sys.platform == 'win32' else [] - exts.append(Extension("PIL._imagingtk", - ["src/_imagingtk.c", "src/Tk/tkImaging.c"], - include_dirs=['src/Tk'], - libraries=tk_libs)) + exts.append( + Extension( + "PIL._webp", ["src/_webp.c"], libraries=libs, define_macros=defs + ) + ) + + tk_libs = ["psapi"] if sys.platform == "win32" else [] + exts.append( + Extension( + "PIL._imagingtk", + ["src/_imagingtk.c", "src/Tk/tkImaging.c"], + include_dirs=["src/Tk"], + libraries=tk_libs, + ) + ) exts.append(Extension("PIL._imagingmath", ["src/_imagingmath.c"])) exts.append(Extension("PIL._imagingmorph", ["src/_imagingmorph.c"])) @@ -717,8 +791,7 @@ def summary_report(self, feature): options = [ (feature.jpeg, "JPEG"), - (feature.jpeg2000, "OPENJPEG (JPEG2000)", - feature.openjpeg_version), + (feature.jpeg2000, "OPENJPEG (JPEG2000)", feature.openjpeg_version), (feature.zlib, "ZLIB (PNG/ZIP)"), (feature.imagequant, "LIBIMAGEQUANT"), (feature.tiff, "LIBTIFF"), @@ -731,9 +804,9 @@ def summary_report(self, feature): all = 1 for option in options: if option[0]: - version = '' + version = "" if len(option) >= 3 and option[2]: - version = ' (%s)' % option[2] + version = " (%s)" % option[2] print("--- %s support available%s" % (option[1], version)) else: print("*** %s support not available" % option[1]) @@ -744,8 +817,10 @@ def summary_report(self, feature): if not all: print("To add a missing option, make sure you have the required") print("library and headers.") - print("See https://pillow.readthedocs.io/en/latest/installation." - "html#building-from-source") + print( + "See https://pillow.readthedocs.io/en/latest/installation." + "html#building-from-source" + ) print("") print("To check the build, run the selftest.py script.") @@ -753,47 +828,49 @@ def summary_report(self, feature): def debug_build(): - return hasattr(sys, 'gettotalrefcount') + return hasattr(sys, "gettotalrefcount") -needs_pytest = {'pytest', 'test', 'ptr'}.intersection(sys.argv) -pytest_runner = ['pytest-runner'] if needs_pytest else [] +needs_pytest = {"pytest", "test", "ptr"}.intersection(sys.argv) +pytest_runner = ["pytest-runner"] if needs_pytest else [] try: - setup(name=NAME, - version=PILLOW_VERSION, - description='Python Imaging Library (Fork)', - long_description=_read('README.rst').decode('utf-8'), - author='Alex Clark (Fork Author)', - author_email='aclark@aclark.net', - url='http://python-pillow.org', - classifiers=[ - "Development Status :: 6 - Mature", - "License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)", # noqa: E501 - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Topic :: Multimedia :: Graphics", - "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera", - "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture", - "Topic :: Multimedia :: Graphics :: Graphics Conversion", - "Topic :: Multimedia :: Graphics :: Viewers", - ], - python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", - cmdclass={"build_ext": pil_build_ext}, - ext_modules=[Extension("PIL._imaging", ["_imaging.c"])], - include_package_data=True, - setup_requires=pytest_runner, - tests_require=['pytest'], - packages=["PIL"], - package_dir={'': 'src'}, - keywords=["Imaging", ], - zip_safe=not (debug_build() or PLATFORM_MINGW), ) + setup( + name=NAME, + version=PILLOW_VERSION, + description="Python Imaging Library (Fork)", + long_description=_read("README.rst").decode("utf-8"), + author="Alex Clark (Fork Author)", + author_email="aclark@aclark.net", + url="http://python-pillow.org", + classifiers=[ + "Development Status :: 6 - Mature", + "License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)", # noqa: E501 + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", + "Topic :: Multimedia :: Graphics", + "Topic :: Multimedia :: Graphics :: Capture :: Digital Camera", + "Topic :: Multimedia :: Graphics :: Capture :: Screen Capture", + "Topic :: Multimedia :: Graphics :: Graphics Conversion", + "Topic :: Multimedia :: Graphics :: Viewers", + ], + python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", + cmdclass={"build_ext": pil_build_ext}, + ext_modules=[Extension("PIL._imaging", ["_imaging.c"])], + include_package_data=True, + setup_requires=pytest_runner, + tests_require=["pytest"], + packages=["PIL"], + package_dir={"": "src"}, + keywords=["Imaging"], + zip_safe=not (debug_build() or PLATFORM_MINGW), + ) except RequiredDependencyException as err: msg = """ @@ -803,7 +880,9 @@ def debug_build(): Please see the install instructions at: https://pillow.readthedocs.io/en/latest/installation.html -""" % (str(err)) +""" % ( + str(err) + ) sys.stderr.write(msg) raise RequiredDependencyException(msg) except DependencyException as err: @@ -812,6 +891,9 @@ def debug_build(): The headers or library files could not be found for %s, which was requested by the option flag --enable-%s -""" % (str(err), str(err)) +""" % ( + str(err), + str(err), + ) sys.stderr.write(msg) raise DependencyException(msg) diff --git a/src/PIL/BmpImagePlugin.py b/src/PIL/BmpImagePlugin.py index f4c1436a1aa..8426e2497b6 100644 --- a/src/PIL/BmpImagePlugin.py +++ b/src/PIL/BmpImagePlugin.py @@ -66,7 +66,8 @@ class BmpImageFile(ImageFile.ImageFile): # -------------------------------------------------- BMP Compression values COMPRESSIONS = {"RAW": 0, "RLE8": 1, "RLE4": 2, "BITFIELDS": 3, "JPEG": 4, "PNG": 5} - RAW, RLE8, RLE4, BITFIELDS, JPEG, PNG = 0, 1, 2, 3, 4, 5 + for k, v in COMPRESSIONS.items(): + vars()[k] = v def _bitmap(self, header=0, offset=0): """ Read relevant info about the BMP """ diff --git a/src/PIL/IcnsImagePlugin.py b/src/PIL/IcnsImagePlugin.py index cd620a337df..9ab9d00f22a 100644 --- a/src/PIL/IcnsImagePlugin.py +++ b/src/PIL/IcnsImagePlugin.py @@ -251,8 +251,6 @@ def _open(self): self.best_size[0] * self.best_size[2], self.best_size[1] * self.best_size[2], ) - # Just use this to see if it's loaded or not yet. - self.tile = ("",) @property def size(self): @@ -286,7 +284,8 @@ def load(self): ) Image.Image.load(self) - if not self.tile: + if self.im and self.im.size == self.size: + # Already loaded return self.load_prepare() # This is likely NOT the best way to do it, but whatever. @@ -298,11 +297,6 @@ def load(self): self.im = im.im self.mode = im.mode self.size = im.size - if self._exclusive_fp: - self.fp.close() - self.fp = None - self.icns = None - self.tile = () self.load_end() diff --git a/src/PIL/IcoImagePlugin.py b/src/PIL/IcoImagePlugin.py index 5ffc86ea7a2..d6a6bc9d30e 100644 --- a/src/PIL/IcoImagePlugin.py +++ b/src/PIL/IcoImagePlugin.py @@ -288,6 +288,9 @@ def size(self, value): self._size = value def load(self): + if self.im and self.im.size == self.size: + # Already loaded + return im = self.ico.getimage(self.size) # if tile is PNG, it won't really be loaded yet im.load() diff --git a/src/PIL/ImageCms.py b/src/PIL/ImageCms.py index b9e8f5ab6c2..647a673f57c 100644 --- a/src/PIL/ImageCms.py +++ b/src/PIL/ImageCms.py @@ -606,7 +606,7 @@ def applyTransform(im, transform, inPlace=False): If inPlace is True and transform.inMode != transform.outMode, a PyCMSError is raised. - If im.mode, transfer.inMode, or transfer.outMode is not supported by + If im.mode, transform.inMode, or transform.outMode is not supported by pyCMSdll or the profiles you used for the transform, a PyCMSError is raised. diff --git a/src/PIL/ImageFont.py b/src/PIL/ImageFont.py index 3cdd5d80a33..7074a70c01a 100644 --- a/src/PIL/ImageFont.py +++ b/src/PIL/ImageFont.py @@ -287,8 +287,8 @@ def getmask(self, text, mode="", direction=None, features=None, language=None): """ Create a bitmap for the text. - If the font uses antialiasing, the bitmap should have mode “L” and use a - maximum value of 255. Otherwise, it should have mode “1”. + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. Otherwise, it should have mode ``1``. :param text: Text to render. :param mode: Used by some graphics drivers to indicate what mode the @@ -348,8 +348,8 @@ def getmask2( """ Create a bitmap for the text. - If the font uses antialiasing, the bitmap should have mode “L” and use a - maximum value of 255. Otherwise, it should have mode “1”. + If the font uses antialiasing, the bitmap should have mode ``L`` and use a + maximum value of 255. Otherwise, it should have mode ``1``. :param text: Text to render. :param mode: Used by some graphics drivers to indicate what mode the @@ -417,6 +417,59 @@ def font_variant( layout_engine=layout_engine or self.layout_engine, ) + def get_variation_names(self): + """ + :returns: A list of the named styles in a variation font. + :exception IOError: If the font is not a variation font. + """ + try: + names = self.font.getvarnames() + except AttributeError: + raise NotImplementedError("FreeType 2.9.1 or greater is required") + return [name.replace(b"\x00", b"") for name in names] + + def set_variation_by_name(self, name): + """ + :param name: The name of the style. + :exception IOError: If the font is not a variation font. + """ + names = self.get_variation_names() + if not isinstance(name, bytes): + name = name.encode() + index = names.index(name) + + if index == getattr(self, "_last_variation_index", None): + # When the same name is set twice in a row, + # there is an 'unknown freetype error' + # https://savannah.nongnu.org/bugs/?56186 + return + self._last_variation_index = index + + self.font.setvarname(index) + + def get_variation_axes(self): + """ + :returns: A list of the axes in a variation font. + :exception IOError: If the font is not a variation font. + """ + try: + axes = self.font.getvaraxes() + except AttributeError: + raise NotImplementedError("FreeType 2.9.1 or greater is required") + for axis in axes: + axis["name"] = axis["name"].replace(b"\x00", b"") + return axes + + def set_variation_by_axes(self, axes): + """ + :param axes: A list of values for each axis. + :exception IOError: If the font is not a variation font. + """ + try: + self.font.setvaraxes(axes) + except AttributeError: + raise NotImplementedError("FreeType 2.9.1 or greater is required") + class TransposedFont(object): "Wrapper for writing rotated or mirrored text" diff --git a/src/PIL/ImageMode.py b/src/PIL/ImageMode.py index fb16f28a467..596be7b9d7e 100644 --- a/src/PIL/ImageMode.py +++ b/src/PIL/ImageMode.py @@ -48,9 +48,17 @@ def getmode(mode): modes["La"] = ModeDescriptor("La", ("L", "a"), "L", "L") modes["PA"] = ModeDescriptor("PA", ("P", "A"), "RGB", "L") # mapping modes - modes["I;16"] = ModeDescriptor("I;16", "I", "L", "L") - modes["I;16L"] = ModeDescriptor("I;16L", "I", "L", "L") - modes["I;16B"] = ModeDescriptor("I;16B", "I", "L", "L") + for i16mode in ( + "I;16", + "I;16S", + "I;16L", + "I;16LS", + "I;16B", + "I;16BS", + "I;16N", + "I;16NS", + ): + modes[i16mode] = ModeDescriptor(i16mode, ("I",), "L", "L") # set global mode cache atomically _modes = modes return _modes[mode] diff --git a/src/PIL/ImageSequence.py b/src/PIL/ImageSequence.py index 1fc6e5de165..84199fe2726 100644 --- a/src/PIL/ImageSequence.py +++ b/src/PIL/ImageSequence.py @@ -32,7 +32,7 @@ def __init__(self, im): if not hasattr(im, "seek"): raise AttributeError("im must have seek method") self.im = im - self.position = 0 + self.position = getattr(self.im, "_min_frame", 0) def __getitem__(self, ix): try: diff --git a/src/PIL/ImageShow.py b/src/PIL/ImageShow.py index 8fc17a63816..82c60489bef 100644 --- a/src/PIL/ImageShow.py +++ b/src/PIL/ImageShow.py @@ -63,16 +63,12 @@ class Viewer(object): def show(self, image, **options): # save temporary image to disk - if image.mode[:4] == "I;16": - # @PIL88 @PIL101 - # "I;16" isn't an 'official' mode, but we still want to - # provide a simple way to show 16-bit images. - base = "L" - # FIXME: auto-contrast if max() > 255? - else: + if not ( + image.mode in ("1", "RGBA") or (self.format == "PNG" and image.mode == "LA") + ): base = Image.getmodebase(image.mode) - if base != image.mode and image.mode != "1" and image.mode != "RGBA": - image = image.convert(base) + if image.mode != base: + image = image.convert(base) return self.show_image(image, **options) @@ -128,7 +124,7 @@ class MacViewer(Viewer): def get_command(self, file, **options): # on darwin open returns immediately resulting in the temp # file removal while app is opening - command = "open -a /Applications/Preview.app" + command = "open -a Preview.app" command = "(%s %s; sleep 20; rm -f %s)&" % ( command, quote(file), @@ -143,12 +139,7 @@ def show_file(self, file, **options): f.write(file) with open(path, "r") as f: subprocess.Popen( - [ - "im=$(cat);" - "open -a /Applications/Preview.app $im;" - "sleep 20;" - "rm -f $im" - ], + ["im=$(cat); open -a Preview.app $im; sleep 20; rm -f $im"], shell=True, stdin=f, ) diff --git a/src/PIL/PsdImagePlugin.py b/src/PIL/PsdImagePlugin.py index a8d370eed3c..576ea90243c 100644 --- a/src/PIL/PsdImagePlugin.py +++ b/src/PIL/PsdImagePlugin.py @@ -54,6 +54,7 @@ class PsdImageFile(ImageFile.ImageFile): format = "PSD" format_description = "Adobe Photoshop" + _close_exclusive_fp_after_loading = False def _open(self): @@ -128,7 +129,7 @@ def _open(self): self.tile = _maketile(self.fp, mode, (0, 0) + self.size, channels) # keep the file open - self._fp = self.fp + self.__fp = self.fp self.frame = 1 self._min_frame = 1 @@ -150,7 +151,7 @@ def seek(self, layer): self.mode = mode self.tile = tile self.frame = layer - self.fp = self._fp + self.fp = self.__fp return name, bbox except IndexError: raise EOFError("no such layer") @@ -167,6 +168,15 @@ def load_prepare(self): if self.mode == "P": Image.Image.load(self) + def _close__fp(self): + try: + if self.__fp != self.fp: + self.__fp.close() + except AttributeError: + pass + finally: + self.__fp = None + def _layerinfo(file): # read layerinfo block diff --git a/src/PIL/__main__.py b/src/PIL/__main__.py new file mode 100644 index 00000000000..a05323f93b6 --- /dev/null +++ b/src/PIL/__main__.py @@ -0,0 +1,3 @@ +from .features import pilinfo + +pilinfo() diff --git a/src/PIL/features.py b/src/PIL/features.py index 940bc85f3de..eff259dfc4b 100644 --- a/src/PIL/features.py +++ b/src/PIL/features.py @@ -1,3 +1,10 @@ +from __future__ import print_function, unicode_literals + +import collections +import os +import sys + +import PIL from . import Image modules = { @@ -84,3 +91,78 @@ def get_supported(): ret.extend(get_supported_features()) ret.extend(get_supported_codecs()) return ret + + +def pilinfo(out=None): + if out is None: + out = sys.stdout + + Image.init() + + print("-" * 68, file=out) + print("Pillow {}".format(PIL.__version__), file=out) + print("-" * 68, file=out) + print( + "Python modules loaded from {}".format(os.path.dirname(Image.__file__)), + file=out, + ) + print( + "Binary modules loaded from {}".format(os.path.dirname(Image.core.__file__)), + file=out, + ) + print("-" * 68, file=out) + + v = sys.version.splitlines() + print("Python {}".format(v[0].strip()), file=out) + for v in v[1:]: + print(" {}".format(v.strip()), file=out) + print("-" * 68, file=out) + + for name, feature in [ + ("pil", "PIL CORE"), + ("tkinter", "TKINTER"), + ("freetype2", "FREETYPE2"), + ("littlecms2", "LITTLECMS2"), + ("webp", "WEBP"), + ("transp_webp", "WEBP Transparency"), + ("webp_mux", "WEBPMUX"), + ("webp_anim", "WEBP Animation"), + ("jpg", "JPEG"), + ("jpg_2000", "OPENJPEG (JPEG2000)"), + ("zlib", "ZLIB (PNG/ZIP)"), + ("libtiff", "LIBTIFF"), + ("raqm", "RAQM (Bidirectional Text)"), + ]: + if check(name): + print("---", feature, "support ok", file=out) + else: + print("***", feature, "support not installed", file=out) + print("-" * 68, file=out) + + extensions = collections.defaultdict(list) + for ext, i in Image.EXTENSION.items(): + extensions[i].append(ext) + + for i in sorted(Image.ID): + line = "{}".format(i) + if i in Image.MIME: + line = "{} {}".format(line, Image.MIME[i]) + print(line, file=out) + + if i in extensions: + print("Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out) + + features = [] + if i in Image.OPEN: + features.append("open") + if i in Image.SAVE: + features.append("save") + if i in Image.SAVE_ALL: + features.append("save_all") + if i in Image.DECODERS: + features.append("decode") + if i in Image.ENCODERS: + features.append("encode") + + print("Features: {}".format(", ".join(features)), file=out) + print("-" * 68, file=out) diff --git a/src/_imagingft.c b/src/_imagingft.c index 0b7e447309a..f6bd787ef5c 100644 --- a/src/_imagingft.c +++ b/src/_imagingft.c @@ -25,6 +25,8 @@ #include #include FT_FREETYPE_H #include FT_GLYPH_H +#include FT_MULTIPLE_MASTERS_H +#include FT_SFNT_NAMES_H #define KEEP_PY_UNICODE #include "py3.h" @@ -54,7 +56,7 @@ typedef struct { - int index, x_offset, x_advance, y_offset; + int index, x_offset, x_advance, y_offset, y_advance; unsigned int cluster; } GlyphInfo; @@ -79,6 +81,9 @@ typedef struct { static PyTypeObject Font_Type; +typedef bool (*t_raqm_version_atleast)(unsigned int major, + unsigned int minor, + unsigned int micro); typedef raqm_t* (*t_raqm_create)(void); typedef int (*t_raqm_set_text)(raqm_t *rq, const uint32_t *text, @@ -107,6 +112,7 @@ typedef void (*t_raqm_destroy) (raqm_t *rq); typedef struct { void* raqm; int version; + t_raqm_version_atleast version_atleast; t_raqm_create create; t_raqm_set_text set_text; t_raqm_set_text_utf8 set_text_utf8; @@ -162,6 +168,7 @@ setraqm(void) } #if !defined(_MSC_VER) + p_raqm.version_atleast = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_atleast"); p_raqm.create = (t_raqm_create)dlsym(p_raqm.raqm, "raqm_create"); p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text"); p_raqm.set_text_utf8 = (t_raqm_set_text_utf8)dlsym(p_raqm.raqm, "raqm_set_text_utf8"); @@ -194,6 +201,7 @@ setraqm(void) return 2; } #else + p_raqm.version_atleast = (t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_atleast"); p_raqm.create = (t_raqm_create)GetProcAddress(p_raqm.raqm, "raqm_create"); p_raqm.set_text = (t_raqm_set_text)GetProcAddress(p_raqm.raqm, "raqm_set_text"); p_raqm.set_text_utf8 = (t_raqm_set_text_utf8)GetProcAddress(p_raqm.raqm, "raqm_set_text_utf8"); @@ -407,9 +415,13 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * direction = RAQM_DIRECTION_RTL; else if (strcmp(dir, "ltr") == 0) direction = RAQM_DIRECTION_LTR; - else if (strcmp(dir, "ttb") == 0) + else if (strcmp(dir, "ttb") == 0) { direction = RAQM_DIRECTION_TTB; - else { + if (p_raqm.version_atleast == NULL || !(*p_raqm.version_atleast)(0, 7, 0)) { + PyErr_SetString(PyExc_ValueError, "libraqm 0.7 or greater required for 'ttb' direction"); + goto failed; + } + } else { PyErr_SetString(PyExc_ValueError, "direction must be either 'rtl', 'ltr' or 'ttb'"); goto failed; } @@ -473,7 +485,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * goto failed; } - if (p_raqm.version == 1){ + if (p_raqm.version == 1) { glyphs_01 = (*p_raqm.get_glyphs_01)(rq, &count); if (glyphs_01 == NULL) { PyErr_SetString(PyExc_ValueError, "raqm_get_glyphs() failed."); @@ -496,12 +508,13 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * goto failed; } - if (p_raqm.version == 1){ + if (p_raqm.version == 1) { for (i = 0; i < count; i++) { (*glyph_info)[i].index = glyphs_01[i].index; (*glyph_info)[i].x_offset = glyphs_01[i].x_offset; (*glyph_info)[i].x_advance = glyphs_01[i].x_advance; (*glyph_info)[i].y_offset = glyphs_01[i].y_offset; + (*glyph_info)[i].y_advance = glyphs_01[i].y_advance; (*glyph_info)[i].cluster = glyphs_01[i].cluster; } } else { @@ -510,6 +523,7 @@ text_layout_raqm(PyObject* string, FontObject* self, const char* dir, PyObject * (*glyph_info)[i].x_offset = glyphs[i].x_offset; (*glyph_info)[i].x_advance = glyphs[i].x_advance; (*glyph_info)[i].y_offset = glyphs[i].y_offset; + (*glyph_info)[i].y_advance = glyphs[i].y_advance; (*glyph_info)[i].cluster = glyphs[i].cluster; } } @@ -576,9 +590,11 @@ text_layout_fallback(PyObject* string, FontObject* self, const char* dir, PyObje if (FT_Get_Kerning(self->face, last_index, (*glyph_info)[i].index, ft_kerning_default,&delta) == 0) (*glyph_info)[i-1].x_advance += PIXEL(delta.x); + (*glyph_info)[i-1].y_advance += PIXEL(delta.y); } (*glyph_info)[i].x_advance = glyph->metrics.horiAdvance; + (*glyph_info)[i].y_advance = glyph->metrics.vertAdvance; last_index = (*glyph_info)[i].index; (*glyph_info)[i].cluster = ch; } @@ -602,9 +618,10 @@ text_layout(PyObject* string, FontObject* self, const char* dir, PyObject *featu static PyObject* font_getsize(FontObject* self, PyObject* args) { - int x, y_max, y_min; + int x_position, x_max, x_min, y_max, y_min; FT_Face face; int xoffset, yoffset; + int horizontal_dir; const char *dir = NULL; const char *lang = NULL; size_t i, count; @@ -617,18 +634,18 @@ font_getsize(FontObject* self, PyObject* args) if (!PyArg_ParseTuple(args, "O|zOz:getsize", &string, &dir, &features, &lang)) return NULL; - face = NULL; - xoffset = yoffset = 0; - y_max = y_min = 0; - count = text_layout(string, self, dir, features, lang, &glyph_info, 0); if (PyErr_Occurred()) { return NULL; } + face = NULL; + xoffset = yoffset = 0; + x_position = x_max = x_min = y_max = y_min = 0; - for (x = i = 0; i < count; i++) { - int index, error; + horizontal_dir = dir && strcmp(dir, "ttb") == 0 ? 0 : 1; + for (i = 0; i < count; i++) { + int index, error, offset, x_advanced; FT_BBox bbox; FT_Glyph glyph; face = self->face; @@ -640,35 +657,62 @@ font_getsize(FontObject* self, PyObject* args) if (error) return geterror(error); - if (i == 0 && face->glyph->metrics.horiBearingX < 0) { - xoffset = face->glyph->metrics.horiBearingX; - x -= xoffset; + if (i == 0) { + if (horizontal_dir) { + if (face->glyph->metrics.horiBearingX < 0) { + xoffset = face->glyph->metrics.horiBearingX; + x_position -= xoffset; + } + } else { + if (face->glyph->metrics.vertBearingY < 0) { + yoffset = face->glyph->metrics.vertBearingY; + y_max -= yoffset; + } + } } - x += glyph_info[i].x_advance; + FT_Get_Glyph(face->glyph, &glyph); + FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_SUBPIXELS, &bbox); + if (horizontal_dir) { + x_position += glyph_info[i].x_advance; - if (i == count - 1) - { - int offset; + x_advanced = x_position; offset = glyph_info[i].x_advance - face->glyph->metrics.width - face->glyph->metrics.horiBearingX; if (offset < 0) - x -= offset; - } - - FT_Get_Glyph(face->glyph, &glyph); - FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_SUBPIXELS, &bbox); - bbox.yMax -= glyph_info[i].y_offset; - bbox.yMin -= glyph_info[i].y_offset; - if (bbox.yMax > y_max) - y_max = bbox.yMax; - if (bbox.yMin < y_min) - y_min = bbox.yMin; + x_advanced -= offset; + if (x_advanced > x_max) + x_max = x_advanced; + + bbox.yMax += glyph_info[i].y_offset; + bbox.yMin += glyph_info[i].y_offset; + if (bbox.yMax > y_max) + y_max = bbox.yMax; + if (bbox.yMin < y_min) + y_min = bbox.yMin; + + // find max distance of baseline from top + if (face->glyph->metrics.horiBearingY > yoffset) + yoffset = face->glyph->metrics.horiBearingY; + } else { + y_max -= glyph_info[i].y_advance; + + if (i == count - 1) { + // trim end gap from final glyph + int offset; + offset = -glyph_info[i].y_advance - + face->glyph->metrics.height - + face->glyph->metrics.vertBearingY; + if (offset < 0) + y_max -= offset; + } - /* find max distance of baseline from top */ - if (face->glyph->metrics.horiBearingY > yoffset) - yoffset = face->glyph->metrics.horiBearingY; + if (bbox.xMax > x_max) + x_max = bbox.xMax; + if (i == 0 || bbox.xMin < x_min) + x_min = bbox.xMin; + } FT_Done_Glyph(glyph); } @@ -679,20 +723,28 @@ font_getsize(FontObject* self, PyObject* args) } if (face) { - - /* left bearing */ - if (xoffset < 0) - x -= xoffset; - else - xoffset = 0; - /* difference between the font ascender and the distance of - * the baseline from the top */ - yoffset = PIXEL(self->face->size->metrics.ascender - yoffset); + if (horizontal_dir) { + // left bearing + if (xoffset < 0) + x_max -= xoffset; + else + xoffset = 0; + + /* difference between the font ascender and the distance of + * the baseline from the top */ + yoffset = PIXEL(self->face->size->metrics.ascender - yoffset); + } else { + // top bearing + if (yoffset < 0) + y_max -= yoffset; + else + yoffset = 0; + } } return Py_BuildValue( "(ii)(ii)", - PIXEL(x), PIXEL(y_max - y_min), + PIXEL(x_max - x_min), PIXEL(y_max - y_min), PIXEL(xoffset), yoffset ); } @@ -703,7 +755,7 @@ font_render(FontObject* self, PyObject* args) int x; unsigned int y; Imaging im; - int index, error, ascender; + int index, error, ascender, horizontal_dir; int load_flags; unsigned char *source; FT_GlyphSlot glyph; @@ -714,6 +766,8 @@ font_render(FontObject* self, PyObject* args) int mask = 0; int temp; int xx, x0, x1; + int yy; + unsigned int bitmap_y; const char *dir = NULL; const char *lang = NULL; size_t i, count; @@ -747,27 +801,34 @@ font_render(FontObject* self, PyObject* args) return geterror(error); glyph = self->face->glyph; - temp = (glyph->bitmap.rows - glyph->bitmap_top); + temp = glyph->bitmap.rows - glyph->bitmap_top; temp -= PIXEL(glyph_info[i].y_offset); if (temp > ascender) ascender = temp; } - for (x = i = 0; i < count; i++) { + x = y = 0; + horizontal_dir = dir && strcmp(dir, "ttb") == 0 ? 0 : 1; + for (i = 0; i < count; i++) { index = glyph_info[i].index; error = FT_Load_Glyph(self->face, index, load_flags); if (error) return geterror(error); - if (i == 0 && self->face->glyph->metrics.horiBearingX < 0) { - x = -self->face->glyph->metrics.horiBearingX; - } - glyph = self->face->glyph; + if (horizontal_dir) { + if (i == 0 && self->face->glyph->metrics.horiBearingX < 0) { + x = -self->face->glyph->metrics.horiBearingX; + } + xx = PIXEL(x) + glyph->bitmap_left; + xx += PIXEL(glyph_info[i].x_offset); + } else { + if (self->face->glyph->metrics.vertBearingX < 0) { + x = -self->face->glyph->metrics.vertBearingX; + } + xx = im->xsize / 2 - glyph->bitmap.width / 2; + } - source = (unsigned char*) glyph->bitmap.buffer; - xx = PIXEL(x) + glyph->bitmap_left; - xx += PIXEL(glyph_info[i].x_offset); x0 = 0; x1 = glyph->bitmap.width; if (xx < 0) @@ -775,51 +836,200 @@ font_render(FontObject* self, PyObject* args) if (xx + x1 > im->xsize) x1 = im->xsize - xx; - if (mask) { - /* use monochrome mask (on palette images, etc) */ - for (y = 0; y < glyph->bitmap.rows; y++) { - int yy = y + im->ysize - (PIXEL(glyph->metrics.horiBearingY) + ascender); + source = (unsigned char*) glyph->bitmap.buffer; + for (bitmap_y = 0; bitmap_y < glyph->bitmap.rows; bitmap_y++) { + if (horizontal_dir) { + yy = bitmap_y + im->ysize - (PIXEL(glyph->metrics.horiBearingY) + ascender); yy -= PIXEL(glyph_info[i].y_offset); - if (yy >= 0 && yy < im->ysize) { - /* blend this glyph into the buffer */ - unsigned char *target = im->image8[yy] + xx; - int i, j, m = 128; - for (i = j = 0; j < x1; j++) { - if (j >= x0 && (source[i] & m)) + } else { + yy = bitmap_y + PIXEL(y + glyph->metrics.vertBearingY) + ascender; + yy += PIXEL(glyph_info[i].y_offset); + } + if (yy >= 0 && yy < im->ysize) { + // blend this glyph into the buffer + unsigned char *target = im->image8[yy] + xx; + if (mask) { + // use monochrome mask (on palette images, etc) + int j, k, m = 128; + for (j = k = 0; j < x1; j++) { + if (j >= x0 && (source[k] & m)) target[j] = 255; if (!(m >>= 1)) { m = 128; - i++; + k++; } } - } - source += glyph->bitmap.pitch; - } - } else { - /* use antialiased rendering */ - for (y = 0; y < glyph->bitmap.rows; y++) { - int yy = y + im->ysize - (PIXEL(glyph->metrics.horiBearingY) + ascender); - yy -= PIXEL(glyph_info[i].y_offset); - if (yy >= 0 && yy < im->ysize) { - /* blend this glyph into the buffer */ - - int i; - unsigned char *target = im->image8[yy] + xx; - for (i = x0; i < x1; i++) { - if (target[i] < source[i]) - target[i] = source[i]; + } else { + // use antialiased rendering + int k; + for (k = x0; k < x1; k++) { + if (target[k] < source[k]) + target[k] = source[k]; } } - source += glyph->bitmap.pitch; } + source += glyph->bitmap.pitch; } x += glyph_info[i].x_advance; + y -= glyph_info[i].y_advance; } PyMem_Del(glyph_info); Py_RETURN_NONE; } +#if FREETYPE_MAJOR > 2 ||\ + (FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 9) ||\ + (FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 9 && FREETYPE_PATCH == 1) + static PyObject* + font_getvarnames(FontObject* self, PyObject* args) + { + int error; + FT_UInt i, j, num_namedstyles, name_count; + FT_MM_Var *master; + FT_SfntName name; + PyObject *list_names, *list_name; + + error = FT_Get_MM_Var(self->face, &master); + if (error) + return geterror(error); + + num_namedstyles = master->num_namedstyles; + list_names = PyList_New(num_namedstyles); + + name_count = FT_Get_Sfnt_Name_Count(self->face); + for (i = 0; i < name_count; i++) { + error = FT_Get_Sfnt_Name(self->face, i, &name); + if (error) + return geterror(error); + + for (j = 0; j < num_namedstyles; j++) { + if (PyList_GetItem(list_names, j) != NULL) + continue; + + if (master->namedstyle[j].strid == name.name_id) { + list_name = Py_BuildValue(PY_ARG_BYTES_LENGTH, + name.string, name.string_len); + PyList_SetItem(list_names, j, list_name); + break; + } + } + } + + FT_Done_MM_Var(library, master); + + return list_names; + } + + static PyObject* + font_getvaraxes(FontObject* self, PyObject* args) + { + int error; + FT_UInt i, j, num_axis, name_count; + FT_MM_Var* master; + FT_Var_Axis axis; + FT_SfntName name; + PyObject *list_axes, *list_axis, *axis_name; + error = FT_Get_MM_Var(self->face, &master); + if (error) + return geterror(error); + + num_axis = master->num_axis; + name_count = FT_Get_Sfnt_Name_Count(self->face); + + list_axes = PyList_New(num_axis); + for (i = 0; i < num_axis; i++) { + axis = master->axis[i]; + + list_axis = PyDict_New(); + PyDict_SetItemString(list_axis, "minimum", + PyInt_FromLong(axis.minimum / 65536)); + PyDict_SetItemString(list_axis, "default", + PyInt_FromLong(axis.def / 65536)); + PyDict_SetItemString(list_axis, "maximum", + PyInt_FromLong(axis.maximum / 65536)); + + for (j = 0; j < name_count; j++) { + error = FT_Get_Sfnt_Name(self->face, j, &name); + if (error) + return geterror(error); + + if (name.name_id == axis.strid) { + axis_name = Py_BuildValue(PY_ARG_BYTES_LENGTH, + name.string, name.string_len); + PyDict_SetItemString(list_axis, "name", axis_name); + break; + } + } + + PyList_SetItem(list_axes, i, list_axis); + } + + FT_Done_MM_Var(library, master); + + return list_axes; + } + + static PyObject* + font_setvarname(FontObject* self, PyObject* args) + { + int error; + + int instance_index; + if (!PyArg_ParseTuple(args, "i", &instance_index)) + return NULL; + + error = FT_Set_Named_Instance(self->face, instance_index); + if (error) + return geterror(error); + + Py_INCREF(Py_None); + return Py_None; + } + + static PyObject* + font_setvaraxes(FontObject* self, PyObject* args) + { + int error; + + PyObject *axes, *item; + Py_ssize_t i, num_coords; + FT_Fixed *coords; + FT_Fixed coord; + if (!PyArg_ParseTuple(args, "O", &axes)) + return NULL; + + if (!PyList_Check(axes)) { + PyErr_SetString(PyExc_TypeError, "argument must be a list"); + return NULL; + } + + num_coords = PyObject_Length(axes); + coords = malloc(2 * sizeof(coords)); + for (i = 0; i < num_coords; i++) { + item = PyList_GET_ITEM(axes, i); + if (PyFloat_Check(item)) + coord = PyFloat_AS_DOUBLE(item); + else if (PyInt_Check(item)) + coord = (float) PyInt_AS_LONG(item); + else if (PyNumber_Check(item)) + coord = PyFloat_AsDouble(item); + else { + PyErr_SetString(PyExc_TypeError, "list must contain numbers"); + return NULL; + } + coords[i] = coord * 65536; + } + + error = FT_Set_Var_Design_Coordinates(self->face, num_coords, coords); + if (error) + return geterror(error); + + Py_INCREF(Py_None); + return Py_None; + } +#endif + static void font_dealloc(FontObject* self) { @@ -835,6 +1045,14 @@ font_dealloc(FontObject* self) static PyMethodDef font_methods[] = { {"render", (PyCFunction) font_render, METH_VARARGS}, {"getsize", (PyCFunction) font_getsize, METH_VARARGS}, +#if FREETYPE_MAJOR > 2 ||\ + (FREETYPE_MAJOR == 2 && FREETYPE_MINOR > 9) ||\ + (FREETYPE_MAJOR == 2 && FREETYPE_MINOR == 9 && FREETYPE_PATCH == 1) + {"getvarnames", (PyCFunction) font_getvarnames, METH_VARARGS }, + {"getvaraxes", (PyCFunction) font_getvaraxes, METH_VARARGS }, + {"setvarname", (PyCFunction) font_setvarname, METH_VARARGS}, + {"setvaraxes", (PyCFunction) font_setvaraxes, METH_VARARGS}, +#endif {NULL, NULL} }; diff --git a/src/libImaging/Storage.c b/src/libImaging/Storage.c index 7e0c14339d7..389089e118e 100644 --- a/src/libImaging/Storage.c +++ b/src/libImaging/Storage.c @@ -405,7 +405,7 @@ ImagingAllocateArray(Imaging im, int dirty, int block_size) // printf("NEW size: %dx%d, ls: %d, lpb: %d, blocks: %d\n", // im->xsize, im->ysize, aligned_linesize, lines_per_block, blocks_count); - /* One extra ponter is always NULL */ + /* One extra pointer is always NULL */ im->blocks = calloc(sizeof(*im->blocks), blocks_count + 1); if ( ! im->blocks) { return (Imaging) ImagingError_MemoryError(); diff --git a/src/libImaging/Unpack.c b/src/libImaging/Unpack.c index 02196d25559..9b73dd60d32 100644 --- a/src/libImaging/Unpack.c +++ b/src/libImaging/Unpack.c @@ -1333,7 +1333,7 @@ static struct { {"RGB", "RGBX;L", 32, unpackRGBAL}, {"RGB", "RGBA;L", 32, unpackRGBAL}, {"RGB", "BGRX", 32, ImagingUnpackBGRX}, - {"RGB", "XRGB", 24, ImagingUnpackXRGB}, + {"RGB", "XRGB", 32, ImagingUnpackXRGB}, {"RGB", "XBGR", 32, ImagingUnpackXBGR}, {"RGB", "YCC;P", 24, ImagingUnpackYCC}, {"RGB", "R", 8, band0}, @@ -1403,7 +1403,7 @@ static struct { {"RGBX", "RGBX;16L", 64, unpackRGBA16L}, {"RGBX", "RGBX;16B", 64, unpackRGBA16B}, {"RGBX", "BGRX", 32, ImagingUnpackBGRX}, - {"RGBX", "XRGB", 24, ImagingUnpackXRGB}, + {"RGBX", "XRGB", 32, ImagingUnpackXRGB}, {"RGBX", "XBGR", 32, ImagingUnpackXBGR}, {"RGBX", "YCC;P", 24, ImagingUnpackYCC}, {"RGBX", "R", 8, band0}, @@ -1418,6 +1418,7 @@ static struct { {"CMYK", "CMYK;I", 32, unpackCMYKI}, {"CMYK", "CMYK;L", 32, unpackRGBAL}, {"CMYK", "CMYK;16L", 64, unpackRGBA16L}, + {"CMYK", "CMYK;16B", 64, unpackRGBA16B}, {"CMYK", "C", 8, band0}, {"CMYK", "M", 8, band1}, {"CMYK", "Y", 8, band2}, @@ -1427,6 +1428,12 @@ static struct { {"CMYK", "Y;I", 8, band2I}, {"CMYK", "K;I", 8, band3I}, +#ifdef WORDS_BIGENDIAN + {"CMYK", "CMYK;16N", 64, unpackRGBA16B}, +#else + {"CMYK", "CMYK;16N", 64, unpackRGBA16L}, +#endif + /* video (YCbCr) */ {"YCbCr", "YCbCr", 24, ImagingUnpackRGB}, {"YCbCr", "YCbCr;L", 24, unpackRGBL}, diff --git a/tox.ini b/tox.ini index 06de6f0938e..08fbebf0533 100644 --- a/tox.ini +++ b/tox.ini @@ -24,7 +24,7 @@ deps = [testenv:lint] commands = - black --check --diff src + black --check --diff . flake8 --statistics --count check-manifest deps = diff --git a/winbuild/build.py b/winbuild/build.py index 01c847bae19..d0490e6d829 100755 --- a/winbuild/build.py +++ b/winbuild/build.py @@ -6,18 +6,29 @@ import getopt import os -from config import (compilers, compiler_from_env, pythons, pyversion_from_env, - bit_from_env, VIRT_BASE, X64_EXT) +from config import ( + compilers, + compiler_from_env, + pythons, + pyversion_from_env, + bit_from_env, + VIRT_BASE, + X64_EXT, +) def setup_vms(): ret = [] for py in pythons: - for arch in ('', X64_EXT): - ret.append("virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s" - % (py, arch, VIRT_BASE, py, arch)) - ret.append(r"%s%s%s\Scripts\pip.exe install pytest pytest-cov" % - (VIRT_BASE, py, arch)) + for arch in ("", X64_EXT): + ret.append( + "virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s" + % (py, arch, VIRT_BASE, py, arch) + ) + ret.append( + r"%s%s%s\Scripts\pip.exe install pytest pytest-cov" + % (VIRT_BASE, py, arch) + ) return "\n".join(ret) @@ -25,16 +36,17 @@ def run_script(params): (version, script) = params try: print("Running %s" % version) - filename = 'build_pillow_%s.cmd' % version - with open(filename, 'w') as f: + filename = "build_pillow_%s.cmd" % version + with open(filename, "w") as f: f.write(script) - command = ['powershell', "./%s" % filename] - proc = subprocess.Popen(command, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - ) + command = ["powershell", "./%s" % filename] + proc = subprocess.Popen( + command, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) (trace, stderr) = proc.communicate() status = proc.returncode print("-- stderr --") @@ -55,7 +67,9 @@ def header(op): set INCLIB=%%~dp0\depends set BLDOPT=%s cd /D %%MPLSRC%% -""" % (op) +""" % ( + op + ) def footer(): @@ -66,10 +80,13 @@ def footer(): def vc_setup(compiler, bit): script = "" - if compiler['vc_version'] == '2015': + if compiler["vc_version"] == "2015": arch = "x86" if bit == 32 else "x86_amd64" - script = r""" -call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %s""" % arch + script = ( + r""" +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %s""" + % arch + ) return script @@ -77,27 +94,27 @@ def build_one(py_ver, compiler, bit): # UNDONE virtual envs if we're not running on AppVeyor args = {} args.update(compiler) - if 'PYTHON' in os.environ: - args['python_path'] = "%PYTHON%" + if "PYTHON" in os.environ: + args["python_path"] = "%PYTHON%" else: - args['python_path'] = "%s%s\\Scripts" % (VIRT_BASE, py_ver) + args["python_path"] = "%s%s\\Scripts" % (VIRT_BASE, py_ver) - args['executable'] = "python.exe" - if 'EXECUTABLE' in os.environ: - args['executable'] = "%EXECUTABLE%" + args["executable"] = "python.exe" + if "EXECUTABLE" in os.environ: + args["executable"] = "%EXECUTABLE%" - args['py_ver'] = py_ver - if '27' in py_ver: - args['tcl_ver'] = '85' + args["py_ver"] = py_ver + if "27" in py_ver: + args["tcl_ver"] = "85" else: - args['tcl_ver'] = '86' + args["tcl_ver"] = "86" - if compiler['vc_version'] == '2015': - args['imaging_libs'] = ' build_ext --add-imaging-libs=msvcrt' + if compiler["vc_version"] == "2015": + args["imaging_libs"] = " build_ext --add-imaging-libs=msvcrt" else: - args['imaging_libs'] = '' + args["imaging_libs"] = "" - args['vc_setup'] = vc_setup(compiler, bit) + args["vc_setup"] = vc_setup(compiler, bit) script = r""" setlocal EnableDelayedExpansion @@ -119,34 +136,44 @@ def build_one(py_ver, compiler, bit): def clean(): try: - shutil.rmtree('../build') + shutil.rmtree("../build") except Exception: # could already be removed pass - run_script(('virtualenvs', setup_vms())) + run_script(("virtualenvs", setup_vms())) def main(op): scripts = [] for py_version, py_info in pythons.items(): - py_compilers = compilers[py_info['compiler']][py_info['vc']] - scripts.append((py_version, - "\n".join([header(op), - build_one(py_version, - py_compilers[32], 32), - footer()]))) - - scripts.append(("%s%s" % (py_version, X64_EXT), - "\n".join([header(op), - build_one("%sx64" % py_version, - py_compilers[64], 64), - footer()]))) + py_compilers = compilers[py_info["compiler"]][py_info["vc"]] + scripts.append( + ( + py_version, + "\n".join( + [header(op), build_one(py_version, py_compilers[32], 32), footer()] + ), + ) + ) + + scripts.append( + ( + "%s%s" % (py_version, X64_EXT), + "\n".join( + [ + header(op), + build_one("%sx64" % py_version, py_compilers[64], 64), + footer(), + ] + ), + ) + ) results = map(run_script, scripts) for (version, status, trace, err) in results: - print("Compiled %s: %s" % (version, status and 'ERR' or 'OK')) + print("Compiled %s: %s" % (version, status and "ERR" or "OK")) def run_one(op): @@ -155,27 +182,28 @@ def run_one(op): py_version = pyversion_from_env() bit = bit_from_env() - run_script((py_version, - "\n".join([header(op), - build_one(py_version, compiler, bit), - footer()]) - )) + run_script( + ( + py_version, + "\n".join([header(op), build_one(py_version, compiler, bit), footer()]), + ) + ) -if __name__ == '__main__': - opts, args = getopt.getopt(sys.argv[1:], '', ['clean', 'dist', 'wheel']) +if __name__ == "__main__": + opts, args = getopt.getopt(sys.argv[1:], "", ["clean", "dist", "wheel"]) opts = dict(opts) - if '--clean' in opts: + if "--clean" in opts: clean() - op = 'install' - if '--dist' in opts: + op = "install" + if "--dist" in opts: op = "bdist_wininst --user-access-control=auto" - elif '--wheel' in opts: + elif "--wheel" in opts: op = "bdist_wheel" - if 'PYTHON' in os.environ: + if "PYTHON" in os.environ: run_one(op) else: main(op) diff --git a/winbuild/build_dep.py b/winbuild/build_dep.py index 15a7852677b..13ac7472e6a 100644 --- a/winbuild/build_dep.py +++ b/winbuild/build_dep.py @@ -3,8 +3,7 @@ import os from fetch import fetch -from config import (compilers, all_compilers, compiler_from_env, bit_from_env, - libs) +from config import compilers, all_compilers, compiler_from_env, bit_from_env, libs from build import vc_setup @@ -12,8 +11,8 @@ def _relpath(*args): return os.path.join(os.getcwd(), *args) -build_dir = _relpath('build') -inc_dir = _relpath('depends') +build_dir = _relpath("build") +inc_dir = _relpath("depends") def check_sig(filename, signame): @@ -32,37 +31,40 @@ def mkdirs(): pass for compiler in all_compilers(): try: - os.mkdir(os.path.join(inc_dir, compiler['inc_dir'])) + os.mkdir(os.path.join(inc_dir, compiler["inc_dir"])) except OSError: pass def extract(src, dest): - if '.zip' in src: + if ".zip" in src: return unzip(src, dest) - if '.tar.gz' in src or '.tgz' in src: + if ".tar.gz" in src or ".tgz" in src: return untar(src, dest) def extract_libs(): for name, lib in libs.items(): - filename = lib['filename'] + filename = lib["filename"] if not os.path.exists(filename): - filename = fetch(lib['url']) - if name == 'openjpeg': + filename = fetch(lib["url"]) + if name == "openjpeg": for compiler in all_compilers(): - if not os.path.exists(os.path.join( - build_dir, lib['dir']+compiler['inc_dir'])): + if not os.path.exists( + os.path.join(build_dir, lib["dir"] + compiler["inc_dir"]) + ): extract(filename, build_dir) - os.rename(os.path.join(build_dir, lib['dir']), - os.path.join( - build_dir, lib['dir']+compiler['inc_dir'])) + os.rename( + os.path.join(build_dir, lib["dir"]), + os.path.join(build_dir, lib["dir"] + compiler["inc_dir"]), + ) else: extract(filename, build_dir) def extract_openjpeg(compiler): - return r""" + return ( + r""" rem build openjpeg setlocal @echo on @@ -72,12 +74,15 @@ def extract_openjpeg(compiler): copy /Y /B openjpeg-2.0.0-win32-x86\bin\ %%INCLIB%% copy /Y /B openjpeg-2.0.0-win32-x86\lib\ %%INCLIB%% endlocal -""" % compiler +""" + % compiler + ) def cp_tk(ver_85, ver_86): - versions = {'ver_85': ver_85, 'ver_86': ver_86} - return r""" + versions = {"ver_85": ver_85, "ver_86": ver_86} + return ( + r""" mkdir %%INCLIB%%\tcl85\include\X11 copy /Y /B %%BUILD%%\tcl%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\ copy /Y /B %%BUILD%%\tk%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\ @@ -87,7 +92,9 @@ def cp_tk(ver_85, ver_86): copy /Y /B %%BUILD%%\tcl%(ver_86)s\generic\*.h %%INCLIB%%\tcl86\include\ copy /Y /B %%BUILD%%\tk%(ver_86)s\generic\*.h %%INCLIB%%\tcl86\include\ copy /Y /B %%BUILD%%\tk%(ver_86)s\xlib\X11\* %%INCLIB%%\tcl86\include\X11\ -""" % versions +""" + % versions + ) def header(): @@ -96,15 +103,21 @@ def header(): set CMAKE="cmake.exe" set INCLIB=%~dp0\depends set BUILD=%~dp0\build -""" + "\n".join(r'set %s=%%BUILD%%\%s' % (k.upper(), v['dir']) - for (k, v) in libs.items() if v['dir']) +""" + "\n".join( + r"set %s=%%BUILD%%\%s" % (k.upper(), v["dir"]) + for (k, v) in libs.items() + if v["dir"] + ) def setup_compiler(compiler): - return r"""setlocal EnableDelayedExpansion + return ( + r"""setlocal EnableDelayedExpansion call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s set INCLIB=%%INCLIB%%\%(inc_dir)s -""" % compiler # noqa: E501 +""" # noqa: E501 + % compiler + ) def end_compiler(): @@ -114,15 +127,18 @@ def end_compiler(): def nmake_openjpeg(compiler, bit): - if compiler['env_version'] == 'v7.0': + if compiler["env_version"] == "v7.0": return "" - atts = {'op_ver': '2.3.1'} + atts = {"op_ver": "2.3.1"} atts.update(compiler) - return r""" + return ( + r""" rem build openjpeg setlocal -""" + vc_setup(compiler, bit) + r""" +""" + + vc_setup(compiler, bit) + + r""" @echo on cd /D %%OPENJPEG%%%(inc_dir)s @@ -133,15 +149,20 @@ def nmake_openjpeg(compiler, bit): mkdir %%INCLIB%%\openjpeg-%(op_ver)s copy /Y /B src\lib\openjp2\*.h %%INCLIB%%\openjpeg-%(op_ver)s endlocal -""" % atts # noqa: E501 +""" # noqa: E501 + % atts + ) def nmake_libs(compiler, bit): # undone -- pre, makes, headers, libs - script = r""" + script = ( + r""" rem Build libjpeg setlocal -""" + vc_setup(compiler, bit) + r""" +""" + + vc_setup(compiler, bit) + + r""" cd /D %%JPEG%% nmake -f makefile.vc setup-vc6 nmake -f makefile.vc clean @@ -165,7 +186,9 @@ def nmake_libs(compiler, bit): rem Build webp setlocal -""" + vc_setup(compiler, bit) + r""" +""" + + vc_setup(compiler, bit) + + r""" cd /D %%WEBP%% rd /S /Q %%WEBP%%\output\release-static nmake -f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output all @@ -176,7 +199,9 @@ def nmake_libs(compiler, bit): rem Build libtiff setlocal -""" + vc_setup(compiler, bit) + r""" +""" + + vc_setup(compiler, bit) + + r""" rem do after building jpeg and zlib copy %%~dp0\nmake.opt %%TIFF%% @@ -188,6 +213,7 @@ def nmake_libs(compiler, bit): copy /Y /B libtiff\tiff*.h %%INCLIB%% endlocal """ + ) return script % compiler @@ -200,36 +226,49 @@ def msbuild_freetype(compiler, bit): """ properties = r"""/p:Configuration="Release" /p:Platform=%(platform)s""" if bit == 64: - script += r'copy /Y /B ' +\ - r'"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\x64\*.Lib" ' +\ - r'%%FREETYPE%%\builds\windows\vc2010' + script += ( + r"copy /Y /B " + + r'"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\x64\*.Lib" ' + + r"%%FREETYPE%%\builds\windows\vc2010" + ) properties += r" /p:_IsNativeEnvironment=false" - script += r""" -%%MSBUILD%% %%FREETYPE%%\builds\windows\vc2010\freetype.sln /t:Clean;Build """+properties+r""" /m + script += ( + r""" +%%MSBUILD%% %%FREETYPE%%\builds\windows\vc2010\freetype.sln /t:Clean;Build """ + + properties + + r""" /m xcopy /Y /E /Q %%FREETYPE%%\include %%INCLIB%% """ + ) freetypeReleaseDir = r"%%FREETYPE%%\objs\%(platform)s\Release" - script += r""" -copy /Y /B """+freetypeReleaseDir+r"""\freetype.lib %%INCLIB%%\freetype.lib -copy /Y /B """+freetypeReleaseDir+r"""\freetype.dll %%INCLIB%%\..\freetype.dll + script += ( + r""" +copy /Y /B """ + + freetypeReleaseDir + + r"""\freetype.lib %%INCLIB%%\freetype.lib +copy /Y /B """ + + freetypeReleaseDir + + r"""\freetype.dll %%INCLIB%%\..\freetype.dll endlocal """ - return script % compiler # noqa: E501 + ) + return script % compiler def build_lcms2(compiler): - if compiler['env_version'] == 'v7.1': + if compiler["env_version"] == "v7.1": return build_lcms_71(compiler) return build_lcms_70(compiler) def build_lcms_70(compiler): """Link error here on x64""" - if compiler['platform'] == 'x64': - return '' + if compiler["platform"] == "x64": + return "" """Build LCMS on VC2008. This version is only 32bit/Win32""" - return r""" + return ( + r""" rem Build lcms2 setlocal rd /S /Q %%LCMS%%\Lib @@ -239,11 +278,14 @@ def build_lcms_70(compiler): xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%% copy /Y /B %%LCMS%%\Lib\MS\*.lib %%INCLIB%% endlocal -""" % compiler # noqa: E501 +""" # noqa: E501 + % compiler + ) def build_lcms_71(compiler): - return r""" + return ( + r""" rem Build lcms2 setlocal rd /S /Q %%LCMS%%\Lib @@ -253,21 +295,26 @@ def build_lcms_71(compiler): xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%% copy /Y /B %%LCMS%%\Lib\MS\*.lib %%INCLIB%% endlocal -""" % compiler # noqa: E501 +""" # noqa: E501 + % compiler + ) def build_ghostscript(compiler, bit): - script = r""" + script = ( + r""" rem Build gs setlocal -""" + vc_setup(compiler, bit) + r""" -set MSVC_VERSION=""" + { - "2010": "90", - "2015": "14" - }[compiler['vc_version']] + r""" +""" + + vc_setup(compiler, bit) + + r""" +set MSVC_VERSION=""" + + {"2010": "90", "2015": "14"}[compiler["vc_version"]] + + r""" set RCOMP="C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Bin\RC.Exe" cd /D %%GHOSTSCRIPT%% """ + ) if bit == 64: script += r""" set WIN64="" @@ -277,7 +324,7 @@ def build_ghostscript(compiler, bit): copy /Y /B bin\ C:\Python27\ endlocal """ - return script % compiler # noqa: E501 + return script % compiler def add_compiler(compiler, bit): @@ -295,17 +342,15 @@ def add_compiler(compiler, bit): mkdirs() extract_libs() -script = [header(), - cp_tk(libs['tk-8.5']['version'], - libs['tk-8.6']['version'])] +script = [header(), cp_tk(libs["tk-8.5"]["version"], libs["tk-8.6"]["version"])] -if 'PYTHON' in os.environ: +if "PYTHON" in os.environ: add_compiler(compiler_from_env(), bit_from_env()) else: # for compiler in all_compilers(): # add_compiler(compiler) add_compiler(compilers[7.0][2010][32], 32) -with open('build_deps.cmd', 'w') as f: +with open("build_deps.cmd", "w") as f: f.write("\n".join(script)) diff --git a/winbuild/config.py b/winbuild/config.py index a1299f1d56f..cd7cc269874 100644 --- a/winbuild/config.py +++ b/winbuild/config.py @@ -1,83 +1,85 @@ import os -SF_MIRROR = 'http://iweb.dl.sourceforge.net' -PILLOW_DEPENDS_DIR = 'C:\\pillow-depends\\' - -pythons = {'27': {'compiler': 7, 'vc': 2010}, - 'pypy2': {'compiler': 7, 'vc': 2010}, - '35': {'compiler': 7.1, 'vc': 2015}, - '36': {'compiler': 7.1, 'vc': 2015}, - '37': {'compiler': 7.1, 'vc': 2015}} +SF_MIRROR = "http://iweb.dl.sourceforge.net" +PILLOW_DEPENDS_DIR = "C:\\pillow-depends\\" + +pythons = { + "27": {"compiler": 7, "vc": 2010}, + "pypy2": {"compiler": 7, "vc": 2010}, + "35": {"compiler": 7.1, "vc": 2015}, + "36": {"compiler": 7.1, "vc": 2015}, + "37": {"compiler": 7.1, "vc": 2015}, +} VIRT_BASE = "c:/vp/" -X64_EXT = os.environ.get('X64_EXT', "x64") +X64_EXT = os.environ.get("X64_EXT", "x64") libs = { # 'openjpeg': { # 'filename': 'openjpeg-2.0.0-win32-x86.zip', # 'version': '2.0' # }, - 'zlib': { - 'url': 'http://zlib.net/zlib1211.zip', - 'filename': PILLOW_DEPENDS_DIR + 'zlib1211.zip', - 'dir': 'zlib-1.2.11', + "zlib": { + "url": "http://zlib.net/zlib1211.zip", + "filename": PILLOW_DEPENDS_DIR + "zlib1211.zip", + "dir": "zlib-1.2.11", }, - 'jpeg': { - 'url': 'http://www.ijg.org/files/jpegsr9c.zip', - 'filename': PILLOW_DEPENDS_DIR + 'jpegsr9c.zip', - 'dir': 'jpeg-9c', + "jpeg": { + "url": "http://www.ijg.org/files/jpegsr9c.zip", + "filename": PILLOW_DEPENDS_DIR + "jpegsr9c.zip", + "dir": "jpeg-9c", }, - 'tiff': { - 'url': 'ftp://download.osgeo.org/libtiff/tiff-4.0.10.tar.gz', - 'filename': PILLOW_DEPENDS_DIR + 'tiff-4.0.10.tar.gz', - 'dir': 'tiff-4.0.10', + "tiff": { + "url": "ftp://download.osgeo.org/libtiff/tiff-4.0.10.tar.gz", + "filename": PILLOW_DEPENDS_DIR + "tiff-4.0.10.tar.gz", + "dir": "tiff-4.0.10", }, - 'freetype': { - 'url': 'https://download.savannah.gnu.org/releases/freetype/freetype-2.10.0.tar.gz', # noqa: E501 - 'filename': PILLOW_DEPENDS_DIR + 'freetype-2.10.0.tar.gz', - 'dir': 'freetype-2.10.0', + "freetype": { + "url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.0.tar.gz", # noqa: E501 + "filename": PILLOW_DEPENDS_DIR + "freetype-2.10.0.tar.gz", + "dir": "freetype-2.10.0", }, - 'lcms': { - 'url': SF_MIRROR+'/project/lcms/lcms/2.7/lcms2-2.7.zip', - 'filename': PILLOW_DEPENDS_DIR + 'lcms2-2.7.zip', - 'dir': 'lcms2-2.7', + "lcms": { + "url": SF_MIRROR + "/project/lcms/lcms/2.7/lcms2-2.7.zip", + "filename": PILLOW_DEPENDS_DIR + "lcms2-2.7.zip", + "dir": "lcms2-2.7", }, - 'ghostscript': { - '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', - 'dir': 'ghostscript-9.27', + "ghostscript": { + "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", + "dir": "ghostscript-9.27", }, - 'tcl-8.5': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.19/tcl8519-src.zip', - 'filename': PILLOW_DEPENDS_DIR + 'tcl8519-src.zip', - 'dir': '', + "tcl-8.5": { + "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tcl8519-src.zip", + "filename": PILLOW_DEPENDS_DIR + "tcl8519-src.zip", + "dir": "", }, - 'tk-8.5': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.5.19/tk8519-src.zip', - 'filename': PILLOW_DEPENDS_DIR + 'tk8519-src.zip', - 'dir': '', - 'version': '8.5.19', + "tk-8.5": { + "url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tk8519-src.zip", + "filename": PILLOW_DEPENDS_DIR + "tk8519-src.zip", + "dir": "", + "version": "8.5.19", }, - 'tcl-8.6': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.9/tcl869-src.zip', - 'filename': PILLOW_DEPENDS_DIR + 'tcl869-src.zip', - 'dir': '', + "tcl-8.6": { + "url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tcl869-src.zip", + "filename": PILLOW_DEPENDS_DIR + "tcl869-src.zip", + "dir": "", }, - 'tk-8.6': { - 'url': SF_MIRROR+'/project/tcl/Tcl/8.6.9/tk869-src.zip', - 'filename': PILLOW_DEPENDS_DIR + 'tk869-src.zip', - 'dir': '', - 'version': '8.6.9', + "tk-8.6": { + "url": SF_MIRROR + "/project/tcl/Tcl/8.6.9/tk869-src.zip", + "filename": PILLOW_DEPENDS_DIR + "tk869-src.zip", + "dir": "", + "version": "8.6.9", }, - 'webp': { - 'url': 'http://downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz', - 'filename': PILLOW_DEPENDS_DIR + 'libwebp-1.0.2.tar.gz', - 'dir': 'libwebp-1.0.2', + "webp": { + "url": "http://downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz", + "filename": PILLOW_DEPENDS_DIR + "libwebp-1.0.2.tar.gz", + "dir": "libwebp-1.0.2", }, - 'openjpeg': { - 'url': 'https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz', - 'filename': PILLOW_DEPENDS_DIR + 'openjpeg-2.3.1.tar.gz', - 'dir': 'openjpeg-2.3.1', + "openjpeg": { + "url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz", + "filename": PILLOW_DEPENDS_DIR + "openjpeg-2.3.1.tar.gz", + "dir": "openjpeg-2.3.1", }, } @@ -85,63 +87,63 @@ 7: { 2010: { 64: { - 'env_version': 'v7.0', - 'vc_version': '2010', - 'env_flags': '/x64 /xp', - 'inc_dir': 'msvcr90-x64', - 'platform': 'x64', - 'webp_platform': 'x64', + "env_version": "v7.0", + "vc_version": "2010", + "env_flags": "/x64 /xp", + "inc_dir": "msvcr90-x64", + "platform": "x64", + "webp_platform": "x64", }, 32: { - 'env_version': 'v7.0', - 'vc_version': '2010', - 'env_flags': '/x86 /xp', - 'inc_dir': 'msvcr90-x32', - 'platform': 'Win32', - 'webp_platform': 'x86', - } + "env_version": "v7.0", + "vc_version": "2010", + "env_flags": "/x86 /xp", + "inc_dir": "msvcr90-x32", + "platform": "Win32", + "webp_platform": "x86", + }, } }, 7.1: { 2015: { 64: { - 'env_version': 'v7.1', - 'vc_version': '2015', - 'env_flags': '/x64 /vista', - 'inc_dir': 'msvcr10-x64', - 'platform': 'x64', - 'webp_platform': 'x64', + "env_version": "v7.1", + "vc_version": "2015", + "env_flags": "/x64 /vista", + "inc_dir": "msvcr10-x64", + "platform": "x64", + "webp_platform": "x64", }, 32: { - 'env_version': 'v7.1', - 'vc_version': '2015', - 'env_flags': '/x86 /vista', - 'inc_dir': 'msvcr10-x32', - 'platform': 'Win32', - 'webp_platform': 'x86', - } + "env_version": "v7.1", + "vc_version": "2015", + "env_flags": "/x86 /vista", + "inc_dir": "msvcr10-x32", + "platform": "Win32", + "webp_platform": "x86", + }, } - } + }, } def pyversion_from_env(): - py = os.environ['PYTHON'] + py = os.environ["PYTHON"] - py_version = '27' + py_version = "27" for k in pythons: if k in py: py_version = k break - if '64' in py: - py_version = '%s%s' % (py_version, X64_EXT) + if "64" in py: + py_version = "%s%s" % (py_version, X64_EXT) return py_version def compiler_from_env(): - py = os.environ['PYTHON'] + py = os.environ["PYTHON"] for k, v in pythons.items(): if k in py: @@ -149,13 +151,13 @@ def compiler_from_env(): break bit = bit_from_env() - return compilers[py_info['compiler']][py_info['vc']][bit] + return compilers[py_info["compiler"]][py_info["vc"]][bit] def bit_from_env(): - py = os.environ['PYTHON'] + py = os.environ["PYTHON"] - return 64 if '64' in py else 32 + return 64 if "64" in py else 32 def all_compilers(): diff --git a/winbuild/fetch.py b/winbuild/fetch.py index 830a64ee548..804e4ef0c12 100644 --- a/winbuild/fetch.py +++ b/winbuild/fetch.py @@ -5,7 +5,7 @@ def fetch(url): - name = urllib.parse.urlsplit(url)[2].split('/')[-1] + name = urllib.parse.urlsplit(url)[2].split("/")[-1] if not os.path.exists(name): print("Fetching", url) @@ -14,10 +14,10 @@ def fetch(url): except urllib.error.URLError: r = urllib.request.urlopen(url) content = r.read() - with open(name, 'wb') as fd: + with open(name, "wb") as fd: fd.write(content) return name -if __name__ == '__main__': +if __name__ == "__main__": fetch(sys.argv[1]) diff --git a/winbuild/get_pythons.py b/winbuild/get_pythons.py index 376f056b7be..63326fa82ee 100644 --- a/winbuild/get_pythons.py +++ b/winbuild/get_pythons.py @@ -1,12 +1,14 @@ from fetch import fetch import os -if __name__ == '__main__': - for version in ['2.7.15', '3.4.4']: - for platform in ['', '.amd64']: - for extension in ['', '.asc']: - fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' - % (version, version, platform, extension)) +if __name__ == "__main__": + for version in ["2.7.15", "3.4.4"]: + for platform in ["", ".amd64"]: + for extension in ["", ".asc"]: + fetch( + "https://www.python.org/ftp/python/%s/python-%s%s.msi%s" + % (version, version, platform, extension) + ) # find pip, if it's not in the path! - os.system('pip install virtualenv') + os.system("pip install virtualenv") diff --git a/winbuild/test.py b/winbuild/test.py index 84e0713087a..bb68fca278b 100755 --- a/winbuild/test.py +++ b/winbuild/test.py @@ -12,16 +12,14 @@ def test_one(params): python, architecture = params try: print("Running: %s, %s" % params) - command = [r'%s\%s%s\Scripts\python.exe' % - (VIRT_BASE, python, architecture), - 'test-installed.py', - '--processes=-0', - '--process-timeout=30', - ] - command.extend(glob.glob('Tests/test*.py')) - proc = subprocess.Popen(command, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE) + command = [ + r"%s\%s%s\Scripts\python.exe" % (VIRT_BASE, python, architecture), + "test-installed.py", + "--processes=-0", + "--process-timeout=30", + ] + command.extend(glob.glob("Tests/test*.py")) + proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE) (trace, stderr) = proc.communicate() status = proc.returncode print("Done with %s, %s -- %s" % (python, architecture, status)) @@ -31,16 +29,17 @@ def test_one(params): return (python, architecture, -1, str(msg)) -if __name__ == '__main__': +if __name__ == "__main__": - os.chdir('..') - matrix = [(python, architecture) for python in pythons - for architecture in ('', X64_EXT)] + os.chdir("..") + matrix = [ + (python, architecture) for python in pythons for architecture in ("", X64_EXT) + ] results = map(test_one, matrix) for (python, architecture, status, trace) in results: - print("%s%s: %s" % (python, architecture, status and 'ERR' or 'PASS')) + print("%s%s: %s" % (python, architecture, status and "ERR" or "PASS")) res = all(status for (python, architecture, status, trace) in results) sys.exit(res) diff --git a/winbuild/untar.py b/winbuild/untar.py index d85be384c86..f2713b2f27d 100644 --- a/winbuild/untar.py +++ b/winbuild/untar.py @@ -3,9 +3,9 @@ def untar(src, dest): - with tarfile.open(src, 'r:gz') as tgz: + with tarfile.open(src, "r:gz") as tgz: tgz.extractall(dest) -if __name__ == '__main__': +if __name__ == "__main__": untar(sys.argv[1], sys.argv[2]) diff --git a/winbuild/unzip.py b/winbuild/unzip.py index 5a464757c2b..eb17a2e6330 100644 --- a/winbuild/unzip.py +++ b/winbuild/unzip.py @@ -7,5 +7,5 @@ def unzip(src, dest): zf.extractall(dest) -if __name__ == '__main__': +if __name__ == "__main__": unzip(sys.argv[1], sys.argv[2])