Format with Black

This commit is contained in:
Hugo 2019-06-13 18:53:42 +03:00
parent a986fed5b4
commit d08475442b
40 changed files with 778 additions and 696 deletions

View File

@ -4,5 +4,5 @@ from PIL import Image
import sys import sys
if sys.maxsize < 2**32: if sys.maxsize < 2 ** 32:
im = Image.new('L', (999999, 999999), 0) im = Image.new("L", (999999, 999999), 0)

View File

@ -26,18 +26,21 @@ def timer(func, label, *args):
starttime = time.time() starttime = time.time()
for x in range(iterations): for x in range(iterations):
func(*args) func(*args)
if time.time()-starttime > 10: if time.time() - starttime > 10:
print("%s: breaking at %s iterations, %.6f per iteration" % ( print(
label, x+1, (time.time()-starttime)/(x+1.0))) "%s: breaking at %s iterations, %.6f per iteration"
% (label, x + 1, (time.time() - starttime) / (x + 1.0))
)
break break
if x == iterations-1: if x == iterations - 1:
endtime = time.time() endtime = time.time()
print("%s: %.4f s %.6f per iteration" % ( print(
label, endtime-starttime, (endtime-starttime)/(x+1.0))) "%s: %.4f s %.6f per iteration"
% (label, endtime - starttime, (endtime - starttime) / (x + 1.0))
)
class BenchCffiAccess(PillowTestCase): class BenchCffiAccess(PillowTestCase):
def test_direct(self): def test_direct(self):
im = hopper() im = hopper()
im.load() im.load()
@ -48,11 +51,11 @@ class BenchCffiAccess(PillowTestCase):
self.assertEqual(caccess[(0, 0)], access[(0, 0)]) self.assertEqual(caccess[(0, 0)], access[(0, 0)])
print("Size: %sx%s" % im.size) print("Size: %sx%s" % im.size)
timer(iterate_get, 'PyAccess - get', im.size, access) timer(iterate_get, "PyAccess - get", im.size, access)
timer(iterate_set, 'PyAccess - set', im.size, access) timer(iterate_set, "PyAccess - set", im.size, access)
timer(iterate_get, 'C-api - get', im.size, caccess) timer(iterate_get, "C-api - get", im.size, caccess)
timer(iterate_set, 'C-api - set', im.size, caccess) timer(iterate_set, "C-api - set", im.size, caccess)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -2,6 +2,7 @@ from . import helper
import timeit import timeit
import sys import sys
sys.path.insert(0, ".") sys.path.insert(0, ".")

View File

@ -12,5 +12,5 @@ class TestFliOverflow(PillowTestCase):
im.load() im.load()
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -6,7 +6,6 @@ from PIL._util import py3
from io import BytesIO from io import BytesIO
if py3: if py3:
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", "latin-1")))
'latin-1')))
else: 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")))

View File

@ -9,11 +9,11 @@ min_iterations = 100
max_iterations = 10000 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): class TestImagingLeaks(PillowTestCase):
def _get_mem_usage(self): def _get_mem_usage(self):
from resource import getpagesize, getrusage, RUSAGE_SELF from resource import getpagesize, getrusage, RUSAGE_SELF
mem = getrusage(RUSAGE_SELF).ru_maxrss mem = getrusage(RUSAGE_SELF).ru_maxrss
return mem * getpagesize() / 1024 / 1024 return mem * getpagesize() / 1024 / 1024
@ -25,20 +25,22 @@ class TestImagingLeaks(PillowTestCase):
if i < min_iterations: if i < min_iterations:
mem_limit = mem + 1 mem_limit = mem + 1
continue 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) self.assertLessEqual(mem, mem_limit, msg)
def test_leak_putdata(self): def test_leak_putdata(self):
im = Image.new('RGB', (25, 25)) im = Image.new("RGB", (25, 25))
self._test_leak(min_iterations, max_iterations, self._test_leak(min_iterations, max_iterations, im.putdata, im.getdata())
im.putdata, im.getdata())
def test_leak_getlist(self): def test_leak_getlist(self):
im = Image.new('P', (25, 25)) im = Image.new("P", (25, 25))
self._test_leak(min_iterations, max_iterations, self._test_leak(
# Pass a new list at each iteration. min_iterations,
lambda: im.point(range(256))) max_iterations,
# Pass a new list at each iteration.
lambda: im.point(range(256)),
)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -6,10 +6,16 @@ from PIL._util import py3
from io import BytesIO from io import BytesIO
if py3: if py3:
Image.open(BytesIO(bytes( Image.open(
'\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang', BytesIO(
'latin-1'))) bytes(
"\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang",
"latin-1",
)
)
)
else: else:
Image.open(BytesIO(bytes( Image.open(
'\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang'))) BytesIO(bytes("\x00\x00\x00\x0cjP\x20\x20\x0d\x0a\x87\x0a\x00\x00\x00\x00hang"))
)

View File

@ -4,21 +4,22 @@ from PIL import Image
from io import BytesIO from io import BytesIO
# Limits for testing the leak # Limits for testing the leak
mem_limit = 1024*1048576 mem_limit = 1024 * 1048576
stack_size = 8*1048576 stack_size = 8 * 1048576
iterations = int((mem_limit/stack_size)*2) iterations = int((mem_limit / stack_size) * 2)
codecs = dir(Image.core) codecs = dir(Image.core)
test_file = "Tests/images/rgb_trns_ycbc.jp2" 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): class TestJpegLeaks(PillowTestCase):
def setUp(self): def setUp(self):
if "jpeg2k_encoder" not in codecs or "jpeg2k_decoder" not in codecs: 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): def test_leak_load(self):
from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK
setrlimit(RLIMIT_STACK, (stack_size, stack_size)) setrlimit(RLIMIT_STACK, (stack_size, stack_size))
setrlimit(RLIMIT_AS, (mem_limit, mem_limit)) setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
for _ in range(iterations): for _ in range(iterations):
@ -27,6 +28,7 @@ class TestJpegLeaks(PillowTestCase):
def test_leak_save(self): def test_leak_save(self):
from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK from resource import setrlimit, RLIMIT_AS, RLIMIT_STACK
setrlimit(RLIMIT_STACK, (stack_size, stack_size)) setrlimit(RLIMIT_STACK, (stack_size, stack_size))
setrlimit(RLIMIT_AS, (mem_limit, mem_limit)) setrlimit(RLIMIT_AS, (mem_limit, mem_limit))
for _ in range(iterations): for _ in range(iterations):
@ -38,5 +40,5 @@ class TestJpegLeaks(PillowTestCase):
test_output.read() test_output.read()
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -5,11 +5,11 @@ from .helper import unittest, PillowTestCase
class TestJ2kEncodeOverflow(PillowTestCase): class TestJ2kEncodeOverflow(PillowTestCase):
def test_j2k_overflow(self): def test_j2k_overflow(self):
im = Image.new('RGBA', (1024, 131584)) im = Image.new("RGBA", (1024, 131584))
target = self.tempfile('temp.jpc') target = self.tempfile("temp.jpc")
with self.assertRaises(IOError): with self.assertRaises(IOError):
im.save(target) im.save(target)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -14,7 +14,7 @@ valgrind --tool=massif python test-installed.py -s -v Tests/check_jpeg_leaks.py
""" """
@unittest.skipIf(sys.platform.startswith('win32'), "requires Unix or macOS") @unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
class TestJpegLeaks(PillowTestCase): class TestJpegLeaks(PillowTestCase):
""" """
@ -74,9 +74,11 @@ post-patch:
""" """
def test_qtables_leak(self): 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 16 11 10 16 24 40 51 61
12 12 14 19 26 58 60 55 12 12 14 19 26 58 60 55
14 13 16 24 40 57 69 56 14 13 16 24 40 57 69 56
@ -85,9 +87,14 @@ post-patch:
24 35 55 64 81 104 113 92 24 35 55 64 81 104 113 92
49 64 78 87 103 121 120 101 49 64 78 87 103 121 120 101
72 92 95 98 112 100 103 99 72 92 95 98 112 100 103 99
""".split(None)] """.split(
None
)
]
standard_chrominance_qtable = [int(s) for s in """ standard_chrominance_qtable = [
int(s)
for s in """
17 18 24 47 99 99 99 99 17 18 24 47 99 99 99 99
18 21 26 66 99 99 99 99 18 21 26 66 99 99 99 99
24 26 56 99 99 99 99 99 24 26 56 99 99 99 99 99
@ -96,10 +103,12 @@ post-patch:
99 99 99 99 99 99 99 99 99 99 99 99 99 99 99 99
99 99 99 99 99 99 99 99 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, qtables = [standard_l_qtable, standard_chrominance_qtable]
standard_chrominance_qtable]
for _ in range(iterations): for _ in range(iterations):
test_output = BytesIO() test_output = BytesIO()
@ -161,8 +170,8 @@ post patch:
0 11.33 0 11.33
""" """
im = hopper('RGB') im = hopper("RGB")
exif = b'12345678'*4096 exif = b"12345678" * 4096
for _ in range(iterations): for _ in range(iterations):
test_output = BytesIO() test_output = BytesIO()
@ -195,12 +204,12 @@ base case:
0 +----------------------------------------------------------------------->Gi 0 +----------------------------------------------------------------------->Gi
0 7.882 0 7.882
""" """
im = hopper('RGB') im = hopper("RGB")
for _ in range(iterations): for _ in range(iterations):
test_output = BytesIO() test_output = BytesIO()
im.save(test_output, "JPEG") im.save(test_output, "JPEG")
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -22,12 +22,11 @@ YDIM = 32769
XDIM = 48000 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): class LargeMemoryTest(PillowTestCase):
def _write_png(self, xdim, ydim): def _write_png(self, xdim, ydim):
f = self.tempfile('temp.png') f = self.tempfile("temp.png")
im = Image.new('L', (xdim, ydim), 0) im = Image.new("L", (xdim, ydim), 0)
im.save(f) im.save(f)
def test_large(self): def test_large(self):
@ -44,5 +43,5 @@ class LargeMemoryTest(PillowTestCase):
Image.fromarray(arr) Image.fromarray(arr)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -11,6 +11,7 @@ from .helper import unittest, PillowTestCase
# Raspberry Pis). # Raspberry Pis).
from PIL import Image from PIL import Image
try: try:
import numpy as np import numpy as np
except ImportError: except ImportError:
@ -20,14 +21,13 @@ YDIM = 32769
XDIM = 48000 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): class LargeMemoryNumpyTest(PillowTestCase):
def _write_png(self, xdim, ydim): def _write_png(self, xdim, ydim):
dtype = np.uint8 dtype = np.uint8
a = np.zeros((xdim, ydim), dtype=dtype) a = np.zeros((xdim, ydim), dtype=dtype)
f = self.tempfile('temp.png') f = self.tempfile("temp.png")
im = Image.fromarray(a, 'L') im = Image.fromarray(a, "L")
im.save(f) im.save(f)
def test_large(self): def test_large(self):
@ -39,5 +39,5 @@ class LargeMemoryNumpyTest(PillowTestCase):
self._write_png(XDIM, XDIM) self._write_png(XDIM, XDIM)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -15,5 +15,5 @@ class TestLibtiffSegfault(PillowTestCase):
im.load() im.load()
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -17,10 +17,10 @@ class TestPngDos(PillowTestCase):
ImageFile.LOAD_TRUNCATED_IMAGES = False ImageFile.LOAD_TRUNCATED_IMAGES = False
for s in im.text.values(): 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(): 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): def test_dos_text(self):
@ -32,20 +32,20 @@ class TestPngDos(PillowTestCase):
return return
for s in im.text.values(): 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): def test_dos_total_memory(self):
im = Image.new('L', (1, 1)) im = Image.new("L", (1, 1))
compressed_data = zlib.compress(b'a'*1024*1023) compressed_data = zlib.compress(b"a" * 1024 * 1023)
info = PngImagePlugin.PngInfo() info = PngImagePlugin.PngInfo()
for x in range(64): for x in range(64):
info.add_text('t%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) info.add_itxt("i%s" % x, compressed_data, zip=True)
b = BytesIO() b = BytesIO()
im.save(b, 'PNG', pnginfo=info) im.save(b, "PNG", pnginfo=info)
b.seek(0) b.seek(0)
try: try:
@ -57,9 +57,10 @@ class TestPngDos(PillowTestCase):
total_len = 0 total_len = 0
for txt in im2.text.values(): for txt in im2.text.values():
total_len += len(txt) total_len += len(txt)
self.assertLess(total_len, 64*1024*1024, self.assertLess(
"Total text chunks greater than 64M") total_len, 64 * 1024 * 1024, "Total text chunks greater than 64M"
)
if __name__ == '__main__': if __name__ == "__main__":
unittest.main() unittest.main()

View File

@ -11,12 +11,13 @@ from PIL import Image, ImageMath
from PIL._util import py3 from PIL._util import py3
import logging import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
HAS_UPLOADER = False HAS_UPLOADER = False
if os.environ.get('SHOW_ERRORS', None): if os.environ.get("SHOW_ERRORS", None):
# local img.show for errors. # local img.show for errors.
HAS_UPLOADER = True HAS_UPLOADER = True
@ -25,9 +26,12 @@ if os.environ.get('SHOW_ERRORS', None):
def upload(self, a, b): def upload(self, a, b):
a.show() a.show()
b.show() b.show()
else: else:
try: try:
import test_image_results import test_image_results
HAS_UPLOADER = True HAS_UPLOADER = True
except ImportError: except ImportError:
pass pass
@ -35,19 +39,18 @@ else:
def convert_to_comparable(a, b): def convert_to_comparable(a, b):
new_a, new_b = a, b new_a, new_b = a, b
if a.mode == 'P': if a.mode == "P":
new_a = Image.new('L', a.size) new_a = Image.new("L", a.size)
new_b = Image.new('L', b.size) new_b = Image.new("L", b.size)
new_a.putdata(a.getdata()) new_a.putdata(a.getdata())
new_b.putdata(b.getdata()) new_b.putdata(b.getdata())
elif a.mode == 'I;16': elif a.mode == "I;16":
new_a = a.convert('I') new_a = a.convert("I")
new_b = b.convert('I') new_b = b.convert("I")
return new_a, new_b return new_a, new_b
class PillowTestCase(unittest.TestCase): class PillowTestCase(unittest.TestCase):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
unittest.TestCase.__init__(self, *args, **kwargs) unittest.TestCase.__init__(self, *args, **kwargs)
# holds last result object passed to run method: # holds last result object passed to run method:
@ -75,32 +78,32 @@ class PillowTestCase(unittest.TestCase):
def assert_deep_equal(self, a, b, msg=None): def assert_deep_equal(self, a, b, msg=None):
try: try:
self.assertEqual( self.assertEqual(
len(a), len(b), len(a), len(b), msg or "got length %s, expected %s" % (len(a), len(b))
msg or "got length %s, expected %s" % (len(a), len(b))) )
self.assertTrue( self.assertTrue(
all(x == y for x, y in zip(a, b)), all(x == y for x, y in zip(a, b)), msg or "got %s, expected %s" % (a, b)
msg or "got %s, expected %s" % (a, b)) )
except Exception: except Exception:
self.assertEqual(a, b, msg) self.assertEqual(a, b, msg)
def assert_image(self, im, mode, size, msg=None): def assert_image(self, im, mode, size, msg=None):
if mode is not None: if mode is not None:
self.assertEqual( self.assertEqual(
im.mode, mode, im.mode, mode, msg or "got mode %r, expected %r" % (im.mode, mode)
msg or "got mode %r, expected %r" % (im.mode, mode)) )
if size is not None: if size is not None:
self.assertEqual( self.assertEqual(
im.size, size, im.size, size, msg or "got size %r, expected %r" % (im.size, size)
msg or "got size %r, expected %r" % (im.size, size)) )
def assert_image_equal(self, a, b, msg=None): def assert_image_equal(self, a, b, msg=None):
self.assertEqual( self.assertEqual(
a.mode, b.mode, a.mode, b.mode, msg or "got mode %r, expected %r" % (a.mode, b.mode)
msg or "got mode %r, expected %r" % (a.mode, b.mode)) )
self.assertEqual( self.assertEqual(
a.size, b.size, a.size, b.size, msg or "got size %r, expected %r" % (a.size, b.size)
msg or "got size %r, expected %r" % (a.size, b.size)) )
if a.tobytes() != b.tobytes(): if a.tobytes() != b.tobytes():
if HAS_UPLOADER: if HAS_UPLOADER:
try: try:
@ -120,26 +123,28 @@ class PillowTestCase(unittest.TestCase):
def assert_image_similar(self, a, b, epsilon, msg=None): def assert_image_similar(self, a, b, epsilon, msg=None):
epsilon = float(epsilon) epsilon = float(epsilon)
self.assertEqual( self.assertEqual(
a.mode, b.mode, a.mode, b.mode, msg or "got mode %r, expected %r" % (a.mode, b.mode)
msg or "got mode %r, expected %r" % (a.mode, b.mode)) )
self.assertEqual( self.assertEqual(
a.size, b.size, a.size, b.size, msg or "got size %r, expected %r" % (a.size, b.size)
msg or "got size %r, expected %r" % (a.size, b.size)) )
a, b = convert_to_comparable(a, b) a, b = convert_to_comparable(a, b)
diff = 0 diff = 0
for ach, bch in zip(a.split(), b.split()): 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())) 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: try:
self.assertGreaterEqual( self.assertGreaterEqual(
epsilon, ave_diff, epsilon,
(msg or '') + ave_diff,
" average pixel value difference %.4f > epsilon %.4f" % ( (msg or "")
ave_diff, epsilon)) + " average pixel value difference %.4f > epsilon %.4f"
% (ave_diff, epsilon),
)
except Exception as e: except Exception as e:
if HAS_UPLOADER: if HAS_UPLOADER:
try: try:
@ -149,8 +154,7 @@ class PillowTestCase(unittest.TestCase):
pass pass
raise e raise e
def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, def assert_image_similar_tofile(self, a, filename, epsilon, msg=None, mode=None):
mode=None):
with Image.open(filename) as img: with Image.open(filename) as img:
if mode: if mode:
img = img.convert(mode) img = img.convert(mode)
@ -168,9 +172,9 @@ class PillowTestCase(unittest.TestCase):
# Verify some things. # Verify some things.
if warn_class is None: if warn_class is None:
self.assertEqual(len(w), 0, self.assertEqual(
"Expected no warnings, got %s" % len(w), 0, "Expected no warnings, got %s" % [v.category for v in w]
[v.category for v in w]) )
else: else:
self.assertGreaterEqual(len(w), 1) self.assertGreaterEqual(len(w), 1)
found = False found = False
@ -192,27 +196,26 @@ class PillowTestCase(unittest.TestCase):
value = True value = True
for i, target in enumerate(targets): for i, target in enumerate(targets):
value *= (target - threshold <= actuals[i] <= target + threshold) value *= target - threshold <= actuals[i] <= target + threshold
self.assertTrue(value, self.assertTrue(value, msg + ": " + repr(actuals) + " != " + repr(targets))
msg + ': ' + repr(actuals) + ' != ' + repr(targets))
def skipKnownBadTest(self, msg=None, platform=None, def skipKnownBadTest(self, msg=None, platform=None, travis=None, interpreter=None):
travis=None, interpreter=None):
# Skip if platform/travis matches, and # Skip if platform/travis matches, and
# PILLOW_RUN_KNOWN_BAD is not true in the environment. # PILLOW_RUN_KNOWN_BAD is not true in the environment.
if 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)) print(os.environ.get("PILLOW_RUN_KNOWN_BAD", False))
return return
skip = True skip = True
if platform is not None: if platform is not None:
skip = sys.platform.startswith(platform) skip = sys.platform.startswith(platform)
if travis is not None: 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: if interpreter is not None:
skip = skip and (interpreter == 'pypy' and skip = skip and (
hasattr(sys, 'pypy_version_info')) interpreter == "pypy" and hasattr(sys, "pypy_version_info")
)
if skip: if skip:
self.skipTest(msg or "Known Bad Test") self.skipTest(msg or "Known Bad Test")
@ -234,7 +237,7 @@ class PillowTestCase(unittest.TestCase):
raise IOError() 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): class PillowLeakTestCase(PillowTestCase):
# requires unix/macOS # requires unix/macOS
iterations = 100 # count iterations = 100 # count
@ -249,8 +252,9 @@ class PillowLeakTestCase(PillowTestCase):
""" """
from resource import getrusage, RUSAGE_SELF from resource import getrusage, RUSAGE_SELF
mem = getrusage(RUSAGE_SELF).ru_maxrss mem = getrusage(RUSAGE_SELF).ru_maxrss
if sys.platform == 'darwin': if sys.platform == "darwin":
# man 2 getrusage: # man 2 getrusage:
# ru_maxrss # ru_maxrss
# This is the maximum resident set size utilized (in bytes). # This is the maximum resident set size utilized (in bytes).
@ -266,8 +270,8 @@ class PillowLeakTestCase(PillowTestCase):
start_mem = self._get_mem_usage() start_mem = self._get_mem_usage()
for cycle in range(self.iterations): for cycle in range(self.iterations):
core() core()
mem = (self._get_mem_usage() - start_mem) mem = self._get_mem_usage() - start_mem
msg = 'memory usage limit exceeded in iteration %d' % cycle msg = "memory usage limit exceeded in iteration %d" % cycle
self.assertLess(mem, self.mem_limit, msg) self.assertLess(mem, self.mem_limit, msg)
@ -281,11 +285,13 @@ if not py3:
def fromstring(data): def fromstring(data):
from io import BytesIO from io import BytesIO
return Image.open(BytesIO(data)) return Image.open(BytesIO(data))
def tostring(im, string_format, **options): def tostring(im, string_format, **options):
from io import BytesIO from io import BytesIO
out = BytesIO() out = BytesIO()
im.save(out, string_format, **options) im.save(out, string_format, **options)
return out.getvalue() return out.getvalue()
@ -318,7 +324,8 @@ def command_succeeds(cmd):
command succeeds, or False if an OSError was raised by subprocess.Popen. command succeeds, or False if an OSError was raised by subprocess.Popen.
""" """
import subprocess import subprocess
with open(os.devnull, 'wb') as f:
with open(os.devnull, "wb") as f:
try: try:
subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT) subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT)
except OSError: except OSError:
@ -327,40 +334,41 @@ def command_succeeds(cmd):
def djpeg_available(): def djpeg_available():
return command_succeeds(['djpeg', '-version']) return command_succeeds(["djpeg", "-version"])
def cjpeg_available(): def cjpeg_available():
return command_succeeds(['cjpeg', '-version']) return command_succeeds(["cjpeg", "-version"])
def netpbm_available(): def netpbm_available():
return (command_succeeds(["ppmquant", "--version"]) and return command_succeeds(["ppmquant", "--version"]) and command_succeeds(
command_succeeds(["ppmtogif", "--version"])) ["ppmtogif", "--version"]
)
def imagemagick_available(): def imagemagick_available():
return IMCONVERT and command_succeeds([IMCONVERT, '-version']) return IMCONVERT and command_succeeds([IMCONVERT, "-version"])
def on_appveyor(): def on_appveyor():
return 'APPVEYOR' in os.environ return "APPVEYOR" in os.environ
if sys.platform == 'win32': if sys.platform == "win32":
IMCONVERT = os.environ.get('MAGICK_HOME', '') IMCONVERT = os.environ.get("MAGICK_HOME", "")
if IMCONVERT: if IMCONVERT:
IMCONVERT = os.path.join(IMCONVERT, 'convert.exe') IMCONVERT = os.path.join(IMCONVERT, "convert.exe")
else: else:
IMCONVERT = 'convert' IMCONVERT = "convert"
def distro(): def distro():
if os.path.exists('/etc/os-release'): if os.path.exists("/etc/os-release"):
with open('/etc/os-release', 'r') as f: with open("/etc/os-release", "r") as f:
for line in f: for line in f:
if 'ID=' in line: if "ID=" in line:
return line.strip().split('=')[1] return line.strip().split("=")[1]
class cached_property(object): class cached_property(object):

View File

@ -5,6 +5,7 @@ import os
import traceback import traceback
import sys import sys
sys.path.insert(0, ".") sys.path.insert(0, ".")
for file in glob.glob("src/PIL/*.py"): for file in glob.glob("src/PIL/*.py"):

View File

@ -4,21 +4,33 @@ from __future__ import print_function
modes = [ modes = [
"1", "1",
"L", "LA", "La", "L",
"I", "I;16", "I;16L", "I;16B", "I;32L", "I;32B", "LA",
"La",
"I",
"I;16",
"I;16L",
"I;16B",
"I;32L",
"I;32B",
"F", "F",
"P", "PA", "P",
"RGB", "RGBA", "RGBa", "RGBX", "PA",
"RGB",
"RGBA",
"RGBa",
"RGBX",
"CMYK", "CMYK",
"YCbCr", "YCbCr",
"LAB", "HSV", "LAB",
] "HSV",
]
def hash(s, i): def hash(s, i):
# djb2 hash: multiply by 33 and xor character # djb2 hash: multiply by 33 and xor character
for c in s: for c in s:
i = (((i << 5) + i) ^ ord(c)) & 0xffffffff i = (((i << 5) + i) ^ ord(c)) & 0xFFFFFFFF
return i return i

View File

@ -5,7 +5,6 @@ import PIL.Image
class TestSanity(PillowTestCase): class TestSanity(PillowTestCase):
def test_sanity(self): def test_sanity(self):
# Make sure we have the binary extension # Make sure we have the binary extension
@ -13,7 +12,7 @@ class TestSanity(PillowTestCase):
# Create an image and do stuff with it. # Create an image and do stuff with it.
im = PIL.Image.new("1", (100, 100)) 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) self.assertEqual(len(im.tobytes()), 1300)
# Create images in all remaining major modes. # Create images in all remaining major modes.

View File

@ -4,21 +4,20 @@ from PIL import _binary
class TestBinary(PillowTestCase): class TestBinary(PillowTestCase):
def test_standard(self): def test_standard(self):
self.assertEqual(_binary.i8(b'*'), 42) self.assertEqual(_binary.i8(b"*"), 42)
self.assertEqual(_binary.o8(42), b'*') self.assertEqual(_binary.o8(42), b"*")
def test_little_endian(self): def test_little_endian(self):
self.assertEqual(_binary.i16le(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.i32le(b"\xff\xff\x00\x00"), 65535)
self.assertEqual(_binary.o16le(65535), b'\xff\xff') self.assertEqual(_binary.o16le(65535), b"\xff\xff")
self.assertEqual(_binary.o32le(65535), b'\xff\xff\x00\x00') self.assertEqual(_binary.o32le(65535), b"\xff\xff\x00\x00")
def test_big_endian(self): def test_big_endian(self):
self.assertEqual(_binary.i16be(b'\x00\x00\xff\xff'), 0) self.assertEqual(_binary.i16be(b"\x00\x00\xff\xff"), 0)
self.assertEqual(_binary.i32be(b'\x00\x00\xff\xff'), 65535) self.assertEqual(_binary.i32be(b"\x00\x00\xff\xff"), 65535)
self.assertEqual(_binary.o16be(65535), b'\xff\xff') self.assertEqual(_binary.o16be(65535), b"\xff\xff")
self.assertEqual(_binary.o32be(65535), b'\x00\x00\xff\xff') self.assertEqual(_binary.o32be(65535), b"\x00\x00\xff\xff")

View File

@ -4,19 +4,22 @@ from .helper import PillowTestCase
from PIL import Image from PIL import Image
import os import os
base = os.path.join('Tests', 'images', 'bmp') base = os.path.join("Tests", "images", "bmp")
class TestBmpReference(PillowTestCase): class TestBmpReference(PillowTestCase):
def get_files(self, d, ext=".bmp"):
def get_files(self, d, ext='.bmp'): return [
return [os.path.join(base, d, f) for f os.path.join(base, d, f)
in os.listdir(os.path.join(base, d)) if ext in f] for f in os.listdir(os.path.join(base, d))
if ext in f
]
def test_bad(self): def test_bad(self):
""" These shouldn't crash/dos, but they shouldn't return anything """ These shouldn't crash/dos, but they shouldn't return anything
either """ either """
for f in self.get_files('b'): for f in self.get_files("b"):
def open(f): def open(f):
try: try:
im = Image.open(f) im = Image.open(f)
@ -41,13 +44,12 @@ class TestBmpReference(PillowTestCase):
"pal8os2sp.bmp", "pal8os2sp.bmp",
"rgb32bf-xbgr.bmp", "rgb32bf-xbgr.bmp",
] ]
for f in self.get_files('q'): for f in self.get_files("q"):
try: try:
im = Image.open(f) im = Image.open(f)
im.load() im.load()
if os.path.basename(f) not in supported: if os.path.basename(f) not in supported:
print("Please add %s to the partially supported" print("Please add %s to the partially supported bmp specs." % f)
" bmp specs." % f)
except Exception: # as msg: except Exception: # as msg:
if os.path.basename(f) in supported: if os.path.basename(f) in supported:
raise raise
@ -57,49 +59,52 @@ class TestBmpReference(PillowTestCase):
html directory that we can compare against. """ html directory that we can compare against. """
# Target files, if they're not just replacing the extension # Target files, if they're not just replacing the extension
file_map = {'pal1wb.bmp': 'pal1.png', file_map = {
'pal4rle.bmp': 'pal4.png', "pal1wb.bmp": "pal1.png",
'pal8-0.bmp': 'pal8.png', "pal4rle.bmp": "pal4.png",
'pal8rle.bmp': 'pal8.png', "pal8-0.bmp": "pal8.png",
'pal8topdown.bmp': 'pal8.png', "pal8rle.bmp": "pal8.png",
'pal8nonsquare.bmp': 'pal8nonsquare-v.png', "pal8topdown.bmp": "pal8.png",
'pal8os2.bmp': 'pal8.png', "pal8nonsquare.bmp": "pal8nonsquare-v.png",
'pal8os2sp.bmp': 'pal8.png', "pal8os2.bmp": "pal8.png",
'pal8os2v2.bmp': 'pal8.png', "pal8os2sp.bmp": "pal8.png",
'pal8os2v2-16.bmp': 'pal8.png', "pal8os2v2.bmp": "pal8.png",
'pal8v4.bmp': 'pal8.png', "pal8os2v2-16.bmp": "pal8.png",
'pal8v5.bmp': 'pal8.png', "pal8v4.bmp": "pal8.png",
'rgb16-565pal.bmp': 'rgb16-565.png', "pal8v5.bmp": "pal8.png",
'rgb24pal.bmp': 'rgb24.png', "rgb16-565pal.bmp": "rgb16-565.png",
'rgb32.bmp': 'rgb24.png', "rgb24pal.bmp": "rgb24.png",
'rgb32bf.bmp': 'rgb24.png' "rgb32.bmp": "rgb24.png",
} "rgb32bf.bmp": "rgb24.png",
}
def get_compare(f): def get_compare(f):
name = os.path.split(f)[1] name = os.path.split(f)[1]
if name in file_map: 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] 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: try:
im = Image.open(f) im = Image.open(f)
im.load() im.load()
compare = Image.open(get_compare(f)) compare = Image.open(get_compare(f))
compare.load() compare.load()
if im.mode == 'P': if im.mode == "P":
# assert image similar doesn't really work # assert image similar doesn't really work
# with paletized image, since the palette might # with paletized image, since the palette might
# be differently ordered for an equivalent image. # be differently ordered for an equivalent image.
im = im.convert('RGBA') im = im.convert("RGBA")
compare = im.convert('RGBA') compare = im.convert("RGBA")
self.assert_image_similar(im, compare, 5) self.assert_image_similar(im, compare, 5)
except Exception as msg: except Exception as msg:
# there are three here that are unsupported: # there are three here that are unsupported:
unsupported = (os.path.join(base, 'g', 'rgb32bf.bmp'), unsupported = (
os.path.join(base, 'g', 'pal8rle.bmp'), os.path.join(base, "g", "rgb32bf.bmp"),
os.path.join(base, 'g', 'pal4rle.bmp')) os.path.join(base, "g", "pal8rle.bmp"),
os.path.join(base, "g", "pal4rle.bmp"),
)
if f not in unsupported: if f not in unsupported:
self.fail("Unsupported Image %s: %s" % (f, msg)) self.fail("Unsupported Image %s: %s" % (f, msg))

View File

@ -4,6 +4,7 @@ from PIL import Image, ImageFilter
sample = Image.new("L", (7, 5)) sample = Image.new("L", (7, 5))
# fmt: off
sample.putdata(sum([ sample.putdata(sum([
[210, 50, 20, 10, 220, 230, 80], [210, 50, 20, 10, 220, 230, 80],
[190, 210, 20, 180, 170, 40, 110], [190, 210, 20, 180, 170, 40, 110],
@ -11,10 +12,10 @@ sample.putdata(sum([
[220, 40, 230, 80, 130, 250, 40], [220, 40, 230, 80, 130, 250, 40],
[250, 0, 80, 30, 60, 20, 110], [250, 0, 80, 30, 60, 20, 110],
], [])) ], []))
# fmt: on
class TestBoxBlurApi(PillowTestCase): class TestBoxBlurApi(PillowTestCase):
def test_imageops_box_blur(self): def test_imageops_box_blur(self):
i = sample.filter(ImageFilter.BoxBlur(1)) i = sample.filter(ImageFilter.BoxBlur(1))
self.assertEqual(i.mode, sample.mode) self.assertEqual(i.mode, sample.mode)
@ -23,7 +24,6 @@ class TestBoxBlurApi(PillowTestCase):
class TestBoxBlur(PillowTestCase): class TestBoxBlur(PillowTestCase):
def box_blur(self, image, radius=1, n=1): def box_blur(self, image, radius=1, n=1):
return image._new(image.im.box_blur(radius, n)) return image._new(image.im.box_blur(radius, n))
@ -32,8 +32,7 @@ class TestBoxBlur(PillowTestCase):
for data_row in data: for data_row in data:
im_row = [next(it) for _ in range(im.size[0])] im_row = [next(it) for _ in range(im.size[0])]
if any( if any(
abs(data_v - im_v) > delta abs(data_v - im_v) > delta for data_v, im_v in zip(data_row, im_row)
for data_v, im_v in zip(data_row, im_row)
): ):
self.assertEqual(im_row, data_row) self.assertEqual(im_row, data_row)
self.assertRaises(StopIteration, next, it) self.assertRaises(StopIteration, next, it)
@ -41,7 +40,7 @@ class TestBoxBlur(PillowTestCase):
def assertBlur(self, im, radius, data, passes=1, delta=0): def assertBlur(self, im, radius, data, passes=1, delta=0):
# check grayscale image # check grayscale image
self.assertImage(self.box_blur(im, radius, passes), data, delta) 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(): for band in self.box_blur(rgba, radius, passes).split():
self.assertImage(band, data, delta) self.assertImage(band, data, delta)
@ -61,110 +60,135 @@ class TestBoxBlur(PillowTestCase):
def test_radius_0(self): def test_radius_0(self):
self.assertBlur( self.assertBlur(
sample, 0, sample,
0,
[ [
# fmt: off
[210, 50, 20, 10, 220, 230, 80], [210, 50, 20, 10, 220, 230, 80],
[190, 210, 20, 180, 170, 40, 110], [190, 210, 20, 180, 170, 40, 110],
[120, 210, 250, 60, 220, 0, 220], [120, 210, 250, 60, 220, 0, 220],
[220, 40, 230, 80, 130, 250, 40], [220, 40, 230, 80, 130, 250, 40],
[250, 0, 80, 30, 60, 20, 110], [250, 0, 80, 30, 60, 20, 110],
] # fmt: on
],
) )
def test_radius_0_02(self): def test_radius_0_02(self):
self.assertBlur( self.assertBlur(
sample, 0.02, sample,
0.02,
[ [
# fmt: off
[206, 55, 20, 17, 215, 223, 83], [206, 55, 20, 17, 215, 223, 83],
[189, 203, 31, 171, 169, 46, 110], [189, 203, 31, 171, 169, 46, 110],
[125, 206, 241, 69, 210, 13, 210], [125, 206, 241, 69, 210, 13, 210],
[215, 49, 221, 82, 131, 235, 48], [215, 49, 221, 82, 131, 235, 48],
[244, 7, 80, 32, 60, 27, 107], [244, 7, 80, 32, 60, 27, 107],
# fmt: on
], ],
delta=2, delta=2,
) )
def test_radius_0_05(self): def test_radius_0_05(self):
self.assertBlur( self.assertBlur(
sample, 0.05, sample,
0.05,
[ [
# fmt: off
[202, 62, 22, 27, 209, 215, 88], [202, 62, 22, 27, 209, 215, 88],
[188, 194, 44, 161, 168, 56, 111], [188, 194, 44, 161, 168, 56, 111],
[131, 201, 229, 81, 198, 31, 198], [131, 201, 229, 81, 198, 31, 198],
[209, 62, 209, 86, 133, 216, 59], [209, 62, 209, 86, 133, 216, 59],
[237, 17, 80, 36, 60, 35, 103], [237, 17, 80, 36, 60, 35, 103],
# fmt: on
], ],
delta=2, delta=2,
) )
def test_radius_0_1(self): def test_radius_0_1(self):
self.assertBlur( self.assertBlur(
sample, 0.1, sample,
0.1,
[ [
# fmt: off
[196, 72, 24, 40, 200, 203, 93], [196, 72, 24, 40, 200, 203, 93],
[187, 183, 62, 148, 166, 68, 111], [187, 183, 62, 148, 166, 68, 111],
[139, 193, 213, 96, 182, 54, 182], [139, 193, 213, 96, 182, 54, 182],
[201, 78, 193, 91, 133, 191, 73], [201, 78, 193, 91, 133, 191, 73],
[227, 31, 80, 42, 61, 47, 99], [227, 31, 80, 42, 61, 47, 99],
# fmt: on
], ],
delta=1, delta=1,
) )
def test_radius_0_5(self): def test_radius_0_5(self):
self.assertBlur( self.assertBlur(
sample, 0.5, sample,
0.5,
[ [
# fmt: off
[176, 101, 46, 83, 163, 165, 111], [176, 101, 46, 83, 163, 165, 111],
[176, 149, 108, 122, 144, 120, 117], [176, 149, 108, 122, 144, 120, 117],
[164, 171, 159, 141, 134, 119, 129], [164, 171, 159, 141, 134, 119, 129],
[170, 136, 133, 114, 116, 124, 109], [170, 136, 133, 114, 116, 124, 109],
[184, 95, 72, 70, 69, 81, 89], [184, 95, 72, 70, 69, 81, 89],
# fmt: on
], ],
delta=1, delta=1,
) )
def test_radius_1(self): def test_radius_1(self):
self.assertBlur( self.assertBlur(
sample, 1, sample,
1,
[ [
# fmt: off
[170, 109, 63, 97, 146, 153, 116], [170, 109, 63, 97, 146, 153, 116],
[168, 142, 112, 128, 126, 143, 121], [168, 142, 112, 128, 126, 143, 121],
[169, 166, 142, 149, 126, 131, 114], [169, 166, 142, 149, 126, 131, 114],
[159, 156, 109, 127, 94, 117, 112], [159, 156, 109, 127, 94, 117, 112],
[164, 128, 63, 87, 76, 89, 90], [164, 128, 63, 87, 76, 89, 90],
# fmt: on
], ],
delta=1, delta=1,
) )
def test_radius_1_5(self): def test_radius_1_5(self):
self.assertBlur( self.assertBlur(
sample, 1.5, sample,
1.5,
[ [
# fmt: off
[155, 120, 105, 112, 124, 137, 130], [155, 120, 105, 112, 124, 137, 130],
[160, 136, 124, 125, 127, 134, 130], [160, 136, 124, 125, 127, 134, 130],
[166, 147, 130, 125, 120, 121, 119], [166, 147, 130, 125, 120, 121, 119],
[168, 145, 119, 109, 103, 105, 110], [168, 145, 119, 109, 103, 105, 110],
[168, 134, 96, 85, 85, 89, 97], [168, 134, 96, 85, 85, 89, 97],
# fmt: on
], ],
delta=1, delta=1,
) )
def test_radius_bigger_then_half(self): def test_radius_bigger_then_half(self):
self.assertBlur( self.assertBlur(
sample, 3, sample,
3,
[ [
# fmt: off
[144, 145, 142, 128, 114, 115, 117], [144, 145, 142, 128, 114, 115, 117],
[148, 145, 137, 122, 109, 111, 112], [148, 145, 137, 122, 109, 111, 112],
[152, 145, 131, 117, 103, 107, 108], [152, 145, 131, 117, 103, 107, 108],
[156, 144, 126, 111, 97, 102, 103], [156, 144, 126, 111, 97, 102, 103],
[160, 144, 121, 106, 92, 98, 99], [160, 144, 121, 106, 92, 98, 99],
# fmt: on
], ],
delta=1, delta=1,
) )
def test_radius_bigger_then_width(self): def test_radius_bigger_then_width(self):
self.assertBlur( self.assertBlur(
sample, 10, sample,
10,
[ [
[158, 153, 147, 141, 135, 129, 123], [158, 153, 147, 141, 135, 129, 123],
[159, 153, 147, 141, 136, 130, 124], [159, 153, 147, 141, 136, 130, 124],
@ -177,7 +201,8 @@ class TestBoxBlur(PillowTestCase):
def test_extreme_large_radius(self): def test_extreme_large_radius(self):
self.assertBlur( self.assertBlur(
sample, 600, sample,
600,
[ [
[162, 162, 162, 162, 162, 162, 162], [162, 162, 162, 162, 162, 162, 162],
[162, 162, 162, 162, 162, 162, 162], [162, 162, 162, 162, 162, 162, 162],
@ -190,13 +215,16 @@ class TestBoxBlur(PillowTestCase):
def test_two_passes(self): def test_two_passes(self):
self.assertBlur( self.assertBlur(
sample, 1, sample,
1,
[ [
# fmt: off
[153, 123, 102, 109, 132, 135, 129], [153, 123, 102, 109, 132, 135, 129],
[159, 138, 123, 121, 133, 131, 126], [159, 138, 123, 121, 133, 131, 126],
[162, 147, 136, 124, 127, 121, 121], [162, 147, 136, 124, 127, 121, 121],
[159, 140, 125, 108, 111, 106, 108], [159, 140, 125, 108, 111, 106, 108],
[154, 126, 105, 87, 94, 93, 97], [154, 126, 105, 87, 94, 93, 97],
# fmt: on
], ],
passes=2, passes=2,
delta=1, delta=1,
@ -204,13 +232,16 @@ class TestBoxBlur(PillowTestCase):
def test_three_passes(self): def test_three_passes(self):
self.assertBlur( self.assertBlur(
sample, 1, sample,
1,
[ [
# fmt: off
[146, 131, 116, 118, 126, 131, 130], [146, 131, 116, 118, 126, 131, 130],
[151, 138, 125, 123, 126, 128, 127], [151, 138, 125, 123, 126, 128, 127],
[154, 143, 129, 123, 120, 120, 119], [154, 143, 129, 123, 120, 120, 119],
[152, 139, 122, 113, 108, 108, 108], [152, 139, 122, 113, 108, 108, 108],
[148, 132, 112, 102, 97, 99, 100], [148, 132, 112, 102, 97, 99, 100],
# fmt: on
], ],
passes=3, passes=3,
delta=1, delta=1,

View File

@ -20,185 +20,197 @@ class TestColorLut3DCoreAPI(PillowTestCase):
table = [ table = [
[ [
r / float(size1D-1) if size1D != 1 else 0, r / float(size1D - 1) if size1D != 1 else 0,
g / float(size2D-1) if size2D != 1 else 0, g / float(size2D - 1) if size2D != 1 else 0,
b / float(size3D-1) if size3D != 1 else 0, b / float(size3D - 1) if size3D != 1 else 0,
r / float(size1D-1) if size1D != 1 else 0, r / float(size1D - 1) if size1D != 1 else 0,
g / float(size2D-1) if size2D != 1 else 0, g / float(size2D - 1) if size2D != 1 else 0,
][:channels] ][:channels]
for b in range(size3D) for b in range(size3D)
for g in range(size2D) for g in range(size2D)
for r in range(size1D) for r in range(size1D)
] ]
return ( return (
channels, size1D, size2D, size3D, channels,
[item for sublist in table for item in sublist]) size1D,
size2D,
size3D,
[item for sublist in table for item in sublist],
)
def test_wrong_args(self): def test_wrong_args(self):
im = Image.new('RGB', (10, 10), 0) im = Image.new("RGB", (10, 10), 0)
with self.assertRaisesRegex(ValueError, "filter"): with self.assertRaisesRegex(ValueError, "filter"):
im.im.color_lut_3d('RGB', im.im.color_lut_3d("RGB", Image.CUBIC, *self.generate_identity_table(3, 3))
Image.CUBIC,
*self.generate_identity_table(3, 3))
with self.assertRaisesRegex(ValueError, "image mode"): with self.assertRaisesRegex(ValueError, "image mode"):
im.im.color_lut_3d('wrong', im.im.color_lut_3d(
Image.LINEAR, "wrong", Image.LINEAR, *self.generate_identity_table(3, 3)
*self.generate_identity_table(3, 3)) )
with self.assertRaisesRegex(ValueError, "table_channels"): with self.assertRaisesRegex(ValueError, "table_channels"):
im.im.color_lut_3d('RGB', im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(5, 3))
Image.LINEAR,
*self.generate_identity_table(5, 3))
with self.assertRaisesRegex(ValueError, "table_channels"): with self.assertRaisesRegex(ValueError, "table_channels"):
im.im.color_lut_3d('RGB', im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(1, 3))
Image.LINEAR,
*self.generate_identity_table(1, 3))
with self.assertRaisesRegex(ValueError, "table_channels"): with self.assertRaisesRegex(ValueError, "table_channels"):
im.im.color_lut_3d('RGB', im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(2, 3))
Image.LINEAR,
*self.generate_identity_table(2, 3))
with self.assertRaisesRegex(ValueError, "Table size"): with self.assertRaisesRegex(ValueError, "Table size"):
im.im.color_lut_3d('RGB', im.im.color_lut_3d(
Image.LINEAR, "RGB", Image.LINEAR, *self.generate_identity_table(3, (1, 3, 3))
*self.generate_identity_table(3, (1, 3, 3))) )
with self.assertRaisesRegex(ValueError, "Table size"): with self.assertRaisesRegex(ValueError, "Table size"):
im.im.color_lut_3d('RGB', im.im.color_lut_3d(
Image.LINEAR, "RGB", Image.LINEAR, *self.generate_identity_table(3, (66, 3, 3))
*self.generate_identity_table(3, (66, 3, 3))) )
with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"): with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"):
im.im.color_lut_3d('RGB', im.im.color_lut_3d("RGB", Image.LINEAR, 3, 2, 2, 2, [0, 0, 0] * 7)
Image.LINEAR,
3, 2, 2, 2, [0, 0, 0] * 7)
with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"): with self.assertRaisesRegex(ValueError, r"size1D \* size2D \* size3D"):
im.im.color_lut_3d('RGB', im.im.color_lut_3d("RGB", Image.LINEAR, 3, 2, 2, 2, [0, 0, 0] * 9)
Image.LINEAR,
3, 2, 2, 2, [0, 0, 0] * 9)
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
im.im.color_lut_3d('RGB', im.im.color_lut_3d("RGB", Image.LINEAR, 3, 2, 2, 2, [0, 0, "0"] * 8)
Image.LINEAR,
3, 2, 2, 2, [0, 0, "0"] * 8)
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
im.im.color_lut_3d('RGB', im.im.color_lut_3d("RGB", Image.LINEAR, 3, 2, 2, 2, 16)
Image.LINEAR,
3, 2, 2, 2, 16)
def test_correct_args(self): 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, im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(3, 3))
*self.generate_identity_table(3, 3))
im.im.color_lut_3d('CMYK', Image.LINEAR, im.im.color_lut_3d("CMYK", Image.LINEAR, *self.generate_identity_table(4, 3))
*self.generate_identity_table(4, 3))
im.im.color_lut_3d('RGB', Image.LINEAR, im.im.color_lut_3d(
*self.generate_identity_table(3, (2, 3, 3))) "RGB", Image.LINEAR, *self.generate_identity_table(3, (2, 3, 3))
)
im.im.color_lut_3d('RGB', Image.LINEAR, im.im.color_lut_3d(
*self.generate_identity_table(3, (65, 3, 3))) "RGB", Image.LINEAR, *self.generate_identity_table(3, (65, 3, 3))
)
im.im.color_lut_3d('RGB', Image.LINEAR, im.im.color_lut_3d(
*self.generate_identity_table(3, (3, 65, 3))) "RGB", Image.LINEAR, *self.generate_identity_table(3, (3, 65, 3))
)
im.im.color_lut_3d('RGB', Image.LINEAR, im.im.color_lut_3d(
*self.generate_identity_table(3, (3, 3, 65))) "RGB", Image.LINEAR, *self.generate_identity_table(3, (3, 3, 65))
)
def test_wrong_mode(self): def test_wrong_mode(self):
with self.assertRaisesRegex(ValueError, "wrong mode"): with self.assertRaisesRegex(ValueError, "wrong mode"):
im = Image.new('L', (10, 10), 0) im = Image.new("L", (10, 10), 0)
im.im.color_lut_3d('RGB', Image.LINEAR, im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(3, 3))
*self.generate_identity_table(3, 3))
with self.assertRaisesRegex(ValueError, "wrong mode"): with self.assertRaisesRegex(ValueError, "wrong mode"):
im = Image.new('RGB', (10, 10), 0) im = Image.new("RGB", (10, 10), 0)
im.im.color_lut_3d('L', Image.LINEAR, im.im.color_lut_3d("L", Image.LINEAR, *self.generate_identity_table(3, 3))
*self.generate_identity_table(3, 3))
with self.assertRaisesRegex(ValueError, "wrong mode"): with self.assertRaisesRegex(ValueError, "wrong mode"):
im = Image.new('L', (10, 10), 0) im = Image.new("L", (10, 10), 0)
im.im.color_lut_3d('L', Image.LINEAR, im.im.color_lut_3d("L", Image.LINEAR, *self.generate_identity_table(3, 3))
*self.generate_identity_table(3, 3))
with self.assertRaisesRegex(ValueError, "wrong mode"): with self.assertRaisesRegex(ValueError, "wrong mode"):
im = Image.new('RGB', (10, 10), 0) im = Image.new("RGB", (10, 10), 0)
im.im.color_lut_3d('RGBA', Image.LINEAR, im.im.color_lut_3d(
*self.generate_identity_table(3, 3)) "RGBA", Image.LINEAR, *self.generate_identity_table(3, 3)
)
with self.assertRaisesRegex(ValueError, "wrong mode"): with self.assertRaisesRegex(ValueError, "wrong mode"):
im = Image.new('RGB', (10, 10), 0) im = Image.new("RGB", (10, 10), 0)
im.im.color_lut_3d('RGB', Image.LINEAR, im.im.color_lut_3d("RGB", Image.LINEAR, *self.generate_identity_table(4, 3))
*self.generate_identity_table(4, 3))
def test_correct_mode(self): def test_correct_mode(self):
im = Image.new('RGBA', (10, 10), 0) im = Image.new("RGBA", (10, 10), 0)
im.im.color_lut_3d('RGBA', Image.LINEAR, im.im.color_lut_3d("RGBA", Image.LINEAR, *self.generate_identity_table(3, 3))
*self.generate_identity_table(3, 3))
im = Image.new('RGBA', (10, 10), 0) im = Image.new("RGBA", (10, 10), 0)
im.im.color_lut_3d('RGBA', Image.LINEAR, im.im.color_lut_3d("RGBA", Image.LINEAR, *self.generate_identity_table(4, 3))
*self.generate_identity_table(4, 3))
im = Image.new('RGB', (10, 10), 0) im = Image.new("RGB", (10, 10), 0)
im.im.color_lut_3d('HSV', Image.LINEAR, im.im.color_lut_3d("HSV", Image.LINEAR, *self.generate_identity_table(3, 3))
*self.generate_identity_table(3, 3))
im = Image.new('RGB', (10, 10), 0) im = Image.new("RGB", (10, 10), 0)
im.im.color_lut_3d('RGBA', Image.LINEAR, im.im.color_lut_3d("RGBA", Image.LINEAR, *self.generate_identity_table(4, 3))
*self.generate_identity_table(4, 3))
def test_identities(self): def test_identities(self):
g = Image.linear_gradient('L') g = Image.linear_gradient("L")
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), im = Image.merge(
g.transpose(Image.ROTATE_180)]) "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)]
)
# Fast test with small cubes # Fast test with small cubes
for size in [2, 3, 5, 7, 11, 16, 17]: for size in [2, 3, 5, 7, 11, 16, 17]:
self.assert_image_equal(im, im._new( self.assert_image_equal(
im.im.color_lut_3d('RGB', Image.LINEAR, im,
*self.generate_identity_table(3, size)))) im._new(
im.im.color_lut_3d(
"RGB", Image.LINEAR, *self.generate_identity_table(3, size)
)
),
)
# Not so fast # Not so fast
self.assert_image_equal(im, im._new( self.assert_image_equal(
im.im.color_lut_3d('RGB', Image.LINEAR, im,
*self.generate_identity_table(3, (2, 2, 65))))) im._new(
im.im.color_lut_3d(
"RGB", Image.LINEAR, *self.generate_identity_table(3, (2, 2, 65))
)
),
)
def test_identities_4_channels(self): def test_identities_4_channels(self):
g = Image.linear_gradient('L') g = Image.linear_gradient("L")
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), im = Image.merge(
g.transpose(Image.ROTATE_180)]) "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)]
)
# Red channel copied to alpha # Red channel copied to alpha
self.assert_image_equal( self.assert_image_equal(
Image.merge('RGBA', (im.split()*2)[:4]), Image.merge("RGBA", (im.split() * 2)[:4]),
im._new(im.im.color_lut_3d('RGBA', Image.LINEAR, im._new(
*self.generate_identity_table(4, 17)))) im.im.color_lut_3d(
"RGBA", Image.LINEAR, *self.generate_identity_table(4, 17)
)
),
)
def test_copy_alpha_channel(self): def test_copy_alpha_channel(self):
g = Image.linear_gradient('L') g = Image.linear_gradient("L")
im = Image.merge('RGBA', [g, g.transpose(Image.ROTATE_90), im = Image.merge(
g.transpose(Image.ROTATE_180), "RGBA",
g.transpose(Image.ROTATE_270)]) [
g,
g.transpose(Image.ROTATE_90),
g.transpose(Image.ROTATE_180),
g.transpose(Image.ROTATE_270),
],
)
self.assert_image_equal(im, im._new( self.assert_image_equal(
im.im.color_lut_3d('RGBA', Image.LINEAR, im,
*self.generate_identity_table(3, 17)))) im._new(
im.im.color_lut_3d(
"RGBA", Image.LINEAR, *self.generate_identity_table(3, 17)
)
),
)
def test_channels_order(self): def test_channels_order(self):
g = Image.linear_gradient('L') g = Image.linear_gradient("L")
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), im = Image.merge(
g.transpose(Image.ROTATE_180)]) "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)]
)
# Reverse channels by splitting and using table # Reverse channels by splitting and using table
# fmt: off
self.assert_image_equal( self.assert_image_equal(
Image.merge('RGB', im.split()[::-1]), Image.merge('RGB', im.split()[::-1]),
im._new(im.im.color_lut_3d('RGB', Image.LINEAR, im._new(im.im.color_lut_3d('RGB', Image.LINEAR,
@ -209,12 +221,15 @@ class TestColorLut3DCoreAPI(PillowTestCase):
1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1,
1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1,
]))) ])))
# fmt: on
def test_overflow(self): def test_overflow(self):
g = Image.linear_gradient('L') g = Image.linear_gradient("L")
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), im = Image.merge(
g.transpose(Image.ROTATE_180)]) "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, transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR,
3, 2, 2, 2, 3, 2, 2, 2,
[ [
@ -224,6 +239,7 @@ class TestColorLut3DCoreAPI(PillowTestCase):
-1, -1, 2, 2, -1, 2, -1, -1, 2, 2, -1, 2,
-1, 2, 2, 2, 2, 2, -1, 2, 2, 2, 2, 2,
])).load() ])).load()
# fmt: on
self.assertEqual(transformed[0, 0], (0, 0, 255)) self.assertEqual(transformed[0, 0], (0, 0, 255))
self.assertEqual(transformed[50, 50], (0, 0, 255)) self.assertEqual(transformed[50, 50], (0, 0, 255))
self.assertEqual(transformed[255, 0], (0, 255, 255)) self.assertEqual(transformed[255, 0], (0, 255, 255))
@ -233,6 +249,7 @@ class TestColorLut3DCoreAPI(PillowTestCase):
self.assertEqual(transformed[255, 255], (255, 255, 0)) self.assertEqual(transformed[255, 255], (255, 255, 0))
self.assertEqual(transformed[205, 205], (255, 255, 0)) self.assertEqual(transformed[205, 205], (255, 255, 0))
# fmt: off
transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR, transformed = im._new(im.im.color_lut_3d('RGB', Image.LINEAR,
3, 2, 2, 2, 3, 2, 2, 2,
[ [
@ -242,6 +259,7 @@ class TestColorLut3DCoreAPI(PillowTestCase):
-3, -3, 5, 5, -3, 5, -3, -3, 5, 5, -3, 5,
-3, 5, 5, 5, 5, 5, -3, 5, 5, 5, 5, 5,
])).load() ])).load()
# fmt: on
self.assertEqual(transformed[0, 0], (0, 0, 255)) self.assertEqual(transformed[0, 0], (0, 0, 255))
self.assertEqual(transformed[50, 50], (0, 0, 255)) self.assertEqual(transformed[50, 50], (0, 0, 255))
self.assertEqual(transformed[255, 0], (0, 255, 255)) self.assertEqual(transformed[255, 0], (0, 255, 255))
@ -286,14 +304,15 @@ class TestColorLut3DFilter(PillowTestCase):
self.assertEqual(tuple(lut.size), (2, 2, 2)) self.assertEqual(tuple(lut.size), (2, 2, 2))
self.assertEqual(lut.name, "Color 3D LUT") self.assertEqual(lut.name, "Color 3D LUT")
# fmt: off
lut = ImageFilter.Color3DLUT((2, 2, 2), [ lut = ImageFilter.Color3DLUT((2, 2, 2), [
(0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11), (0, 1, 2), (3, 4, 5), (6, 7, 8), (9, 10, 11),
(12, 13, 14), (15, 16, 17), (18, 19, 20), (21, 22, 23)]) (12, 13, 14), (15, 16, 17), (18, 19, 20), (21, 22, 23)])
# fmt: on
self.assertEqual(tuple(lut.size), (2, 2, 2)) self.assertEqual(tuple(lut.size), (2, 2, 2))
self.assertEqual(lut.table, list(range(24))) self.assertEqual(lut.table, list(range(24)))
lut = ImageFilter.Color3DLUT((2, 2, 2), [(0, 1, 2, 3)] * 8, lut = ImageFilter.Color3DLUT((2, 2, 2), [(0, 1, 2, 3)] * 8, channels=4)
channels=4)
self.assertEqual(tuple(lut.size), (2, 2, 2)) self.assertEqual(tuple(lut.size), (2, 2, 2))
self.assertEqual(lut.table, list(range(4)) * 8) self.assertEqual(lut.table, list(range(4)) * 8)
@ -318,7 +337,7 @@ class TestColorLut3DFilter(PillowTestCase):
self.assertEqual(lut.table.shape, (table.size,)) self.assertEqual(lut.table.shape, (table.size,))
# Check application # Check application
Image.new('RGB', (10, 10), 0).filter(lut) Image.new("RGB", (10, 10), 0).filter(lut)
# Check copy # Check copy
table[0] = 33 table[0] = 33
@ -332,40 +351,34 @@ class TestColorLut3DFilter(PillowTestCase):
@unittest.skipIf(numpy is None, "Numpy is not installed") @unittest.skipIf(numpy is None, "Numpy is not installed")
def test_numpy_formats(self): def test_numpy_formats(self):
g = Image.linear_gradient('L') g = Image.linear_gradient("L")
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), im = Image.merge(
g.transpose(Image.ROTATE_180)]) "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)]
)
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.float32)[:-1] lut.table = numpy.array(lut.table, dtype=numpy.float32)[:-1]
with self.assertRaisesRegex(ValueError, "should have table_channels"): with self.assertRaisesRegex(ValueError, "should have table_channels"):
im.filter(lut) im.filter(lut)
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b)) lut.table = numpy.array(lut.table, dtype=numpy.float32).reshape((7 * 9 * 11), 3)
lut.table = (numpy.array(lut.table, dtype=numpy.float32)
.reshape((7 * 9 * 11), 3))
with self.assertRaisesRegex(ValueError, "should have table_channels"): with self.assertRaisesRegex(ValueError, "should have table_channels"):
im.filter(lut) im.filter(lut)
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.float16) lut.table = numpy.array(lut.table, dtype=numpy.float16)
self.assert_image_equal(im, im.filter(lut)) self.assert_image_equal(im, im.filter(lut))
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.float32) lut.table = numpy.array(lut.table, dtype=numpy.float32)
self.assert_image_equal(im, im.filter(lut)) self.assert_image_equal(im, im.filter(lut))
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.float64) lut.table = numpy.array(lut.table, dtype=numpy.float64)
self.assert_image_equal(im, im.filter(lut)) self.assert_image_equal(im, im.filter(lut))
lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lut = ImageFilter.Color3DLUT.generate((7, 9, 11), lambda r, g, b: (r, g, b))
lambda r, g, b: (r, g, b))
lut.table = numpy.array(lut.table, dtype=numpy.int32) lut.table = numpy.array(lut.table, dtype=numpy.int32)
im.filter(lut) im.filter(lut)
lut.table = numpy.array(lut.table, dtype=numpy.int8) lut.table = numpy.array(lut.table, dtype=numpy.int8)
@ -373,54 +386,65 @@ class TestColorLut3DFilter(PillowTestCase):
def test_repr(self): def test_repr(self):
lut = ImageFilter.Color3DLUT(2, [0, 1, 2] * 8) lut = ImageFilter.Color3DLUT(2, [0, 1, 2] * 8)
self.assertEqual(repr(lut), self.assertEqual(repr(lut), "<Color3DLUT from list size=2x2x2 channels=3>")
"<Color3DLUT from list size=2x2x2 channels=3>")
lut = ImageFilter.Color3DLUT( lut = ImageFilter.Color3DLUT(
(3, 4, 5), array('f', [0, 0, 0, 0] * (3 * 4 * 5)), (3, 4, 5),
channels=4, target_mode='YCbCr', _copy_table=False) array("f", [0, 0, 0, 0] * (3 * 4 * 5)),
channels=4,
target_mode="YCbCr",
_copy_table=False,
)
self.assertEqual( self.assertEqual(
repr(lut), repr(lut), "<Color3DLUT from array size=3x4x5 channels=4 target_mode=YCbCr>"
"<Color3DLUT from array size=3x4x5 channels=4 target_mode=YCbCr>") )
class TestGenerateColorLut3D(PillowTestCase): class TestGenerateColorLut3D(PillowTestCase):
def test_wrong_channels_count(self): def test_wrong_channels_count(self):
with self.assertRaisesRegex(ValueError, "3 or 4 output channels"): with self.assertRaisesRegex(ValueError, "3 or 4 output channels"):
ImageFilter.Color3DLUT.generate( 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"): with self.assertRaisesRegex(ValueError, "should have either channels"):
ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b, r)) ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b, r))
with self.assertRaisesRegex(ValueError, "should have either channels"): with self.assertRaisesRegex(ValueError, "should have either channels"):
ImageFilter.Color3DLUT.generate( 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): def test_3_channels(self):
lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b)) lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
self.assertEqual(tuple(lut.size), (5, 5, 5)) self.assertEqual(tuple(lut.size), (5, 5, 5))
self.assertEqual(lut.name, "Color 3D LUT") self.assertEqual(lut.name, "Color 3D LUT")
# fmt: off
self.assertEqual(lut.table[:24], [ 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, 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]) 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): def test_4_channels(self):
lut = ImageFilter.Color3DLUT.generate( 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(tuple(lut.size), (5, 5, 5))
self.assertEqual(lut.name, "Color 3D LUT") self.assertEqual(lut.name, "Color 3D LUT")
# fmt: off
self.assertEqual(lut.table[:24], [ 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.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 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): def test_apply(self):
lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b)) lut = ImageFilter.Color3DLUT.generate(5, lambda r, g, b: (r, g, b))
g = Image.linear_gradient('L') g = Image.linear_gradient("L")
im = Image.merge('RGB', [g, g.transpose(Image.ROTATE_90), im = Image.merge(
g.transpose(Image.ROTATE_180)]) "RGB", [g, g.transpose(Image.ROTATE_90), g.transpose(Image.ROTATE_180)]
)
self.assertEqual(im, im.filter(lut)) self.assertEqual(im, im.filter(lut))
@ -442,80 +466,96 @@ class TestTransformColorLut3D(PillowTestCase):
def test_target_mode(self): def test_target_mode(self):
source = ImageFilter.Color3DLUT.generate( 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)) 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') lut = source.transform(lambda r, g, b: (r, g, b), target_mode="RGB")
self.assertEqual(lut.mode, 'RGB') self.assertEqual(lut.mode, "RGB")
def test_3_to_3_channels(self): def test_3_to_3_channels(self):
source = ImageFilter.Color3DLUT.generate( source = ImageFilter.Color3DLUT.generate((3, 4, 5), lambda r, g, b: (r, g, b))
(3, 4, 5), lambda r, g, b: (r, g, b)) lut = source.transform(lambda r, g, b: (r * r, g * g, b * b))
lut = source.transform(lambda r, g, b: (r*r, g*g, b*b))
self.assertEqual(tuple(lut.size), tuple(source.size)) self.assertEqual(tuple(lut.size), tuple(source.size))
self.assertEqual(len(lut.table), len(source.table)) self.assertEqual(len(lut.table), len(source.table))
self.assertNotEqual(lut.table, source.table) self.assertNotEqual(lut.table, source.table)
self.assertEqual(lut.table[0:10], [ self.assertEqual(
0.0, 0.0, 0.0, 0.25, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0]) 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): def test_3_to_4_channels(self):
source = ImageFilter.Color3DLUT.generate( source = ImageFilter.Color3DLUT.generate((6, 5, 4), lambda r, g, b: (r, g, b))
(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)
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.assertEqual(tuple(lut.size), tuple(source.size))
self.assertNotEqual(len(lut.table), len(source.table)) self.assertNotEqual(len(lut.table), len(source.table))
self.assertNotEqual(lut.table, source.table) self.assertNotEqual(lut.table, source.table)
# fmt: off
self.assertEqual(lut.table[0:16], [ self.assertEqual(lut.table[0:16], [
0.0, 0.0, 0.0, 1, 0.2**2, 0.0, 0.0, 1, 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]) 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): def test_4_to_3_channels(self):
source = ImageFilter.Color3DLUT.generate( source = ImageFilter.Color3DLUT.generate(
(3, 6, 5), lambda r, g, b: (r, g, b, 1), channels=4) (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) 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.assertEqual(tuple(lut.size), tuple(source.size))
self.assertNotEqual(len(lut.table), len(source.table)) self.assertNotEqual(len(lut.table), len(source.table))
self.assertNotEqual(lut.table, source.table) self.assertNotEqual(lut.table, source.table)
# fmt: off
self.assertEqual(lut.table[0:18], [ 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, 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]) 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): def test_4_to_4_channels(self):
source = ImageFilter.Color3DLUT.generate( source = ImageFilter.Color3DLUT.generate(
(6, 5, 4), lambda r, g, b: (r, g, b, 1), channels=4) (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)) )
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(tuple(lut.size), tuple(source.size))
self.assertEqual(len(lut.table), len(source.table)) self.assertEqual(len(lut.table), len(source.table))
self.assertNotEqual(lut.table, source.table) self.assertNotEqual(lut.table, source.table)
# fmt: off
self.assertEqual(lut.table[0:16], [ self.assertEqual(lut.table[0:16], [
0.0, 0.0, 0.0, 0.5, 0.2**2, 0.0, 0.0, 0.5, 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]) 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): def test_with_normals_3_channels(self):
source = ImageFilter.Color3DLUT.generate( 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( lut = source.transform(
lambda nr, ng, nb, r, g, b: (nr - r, ng - g, nb - b), lambda nr, ng, nb, r, g, b: (nr - r, ng - g, nb - b), with_normals=True
with_normals=True) )
self.assertEqual(tuple(lut.size), tuple(source.size)) self.assertEqual(tuple(lut.size), tuple(source.size))
self.assertEqual(len(lut.table), len(source.table)) self.assertEqual(len(lut.table), len(source.table))
self.assertNotEqual(lut.table, source.table) self.assertNotEqual(lut.table, source.table)
# fmt: off
self.assertEqual(lut.table[0:18], [ 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.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]) 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): def test_with_normals_4_channels(self):
source = ImageFilter.Color3DLUT.generate( 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( lut = source.transform(
lambda nr, ng, nb, r, g, b, a: (nr - r, ng - g, nb - b, a-0.5), lambda nr, ng, nb, r, g, b, a: (nr - r, ng - g, nb - b, a - 0.5),
with_normals=True) with_normals=True,
)
self.assertEqual(tuple(lut.size), tuple(source.size)) self.assertEqual(tuple(lut.size), tuple(source.size))
self.assertEqual(len(lut.table), len(source.table)) self.assertEqual(len(lut.table), len(source.table))
self.assertNotEqual(lut.table, source.table) self.assertNotEqual(lut.table, source.table)
# fmt: off
self.assertEqual(lut.table[0:16], [ 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.25, 0.0, 0.0, 0.5,
0.0, 0.0, 0.0, 0.5, 0.0, 0.16, 0.0, 0.5]) 0.0, 0.0, 0.0, 0.5, 0.0, 0.16, 0.0, 0.5])
# fmt: on

View File

@ -6,39 +6,39 @@ from .helper import unittest, PillowTestCase
from PIL import Image from PIL import Image
is_pypy = hasattr(sys, 'pypy_version_info') is_pypy = hasattr(sys, "pypy_version_info")
class TestCoreStats(PillowTestCase): class TestCoreStats(PillowTestCase):
def test_get_stats(self): def test_get_stats(self):
# Create at least one image # Create at least one image
Image.new('RGB', (10, 10)) Image.new("RGB", (10, 10))
stats = Image.core.get_stats() stats = Image.core.get_stats()
self.assertIn('new_count', stats) self.assertIn("new_count", stats)
self.assertIn('reused_blocks', stats) self.assertIn("reused_blocks", stats)
self.assertIn('freed_blocks', stats) self.assertIn("freed_blocks", stats)
self.assertIn('allocated_blocks', stats) self.assertIn("allocated_blocks", stats)
self.assertIn('reallocated_blocks', stats) self.assertIn("reallocated_blocks", stats)
self.assertIn('blocks_cached', stats) self.assertIn("blocks_cached", stats)
def test_reset_stats(self): def test_reset_stats(self):
Image.core.reset_stats() Image.core.reset_stats()
stats = Image.core.get_stats() stats = Image.core.get_stats()
self.assertEqual(stats['new_count'], 0) self.assertEqual(stats["new_count"], 0)
self.assertEqual(stats['reused_blocks'], 0) self.assertEqual(stats["reused_blocks"], 0)
self.assertEqual(stats['freed_blocks'], 0) self.assertEqual(stats["freed_blocks"], 0)
self.assertEqual(stats['allocated_blocks'], 0) self.assertEqual(stats["allocated_blocks"], 0)
self.assertEqual(stats['reallocated_blocks'], 0) self.assertEqual(stats["reallocated_blocks"], 0)
self.assertEqual(stats['blocks_cached'], 0) self.assertEqual(stats["blocks_cached"], 0)
class TestCoreMemory(PillowTestCase): class TestCoreMemory(PillowTestCase):
def tearDown(self): def tearDown(self):
# Restore default values # Restore default values
Image.core.set_alignment(1) 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.set_blocks_max(0)
Image.core.clear_cache() Image.core.clear_cache()
@ -54,7 +54,7 @@ class TestCoreMemory(PillowTestCase):
self.assertEqual(alignment, i) self.assertEqual(alignment, i)
# Try to construct new image # 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, 0)
self.assertRaises(ValueError, Image.core.set_alignment, -1) self.assertRaises(ValueError, Image.core.set_alignment, -1)
@ -66,13 +66,13 @@ class TestCoreMemory(PillowTestCase):
self.assertGreaterEqual(block_size, 4096) self.assertGreaterEqual(block_size, 4096)
def test_set_block_size(self): 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) Image.core.set_block_size(i)
block_size = Image.core.get_block_size() block_size = Image.core.get_block_size()
self.assertEqual(block_size, i) self.assertEqual(block_size, i)
# Try to construct new image # 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, 0)
self.assertRaises(ValueError, Image.core.set_block_size, -1) self.assertRaises(ValueError, Image.core.set_block_size, -1)
@ -82,13 +82,13 @@ class TestCoreMemory(PillowTestCase):
Image.core.reset_stats() Image.core.reset_stats()
Image.core.set_blocks_max(0) Image.core.set_blocks_max(0)
Image.core.set_block_size(4096) Image.core.set_block_size(4096)
Image.new('RGB', (256, 256)) Image.new("RGB", (256, 256))
stats = Image.core.get_stats() stats = Image.core.get_stats()
self.assertGreaterEqual(stats['new_count'], 1) self.assertGreaterEqual(stats["new_count"], 1)
self.assertGreaterEqual(stats['allocated_blocks'], 64) self.assertGreaterEqual(stats["allocated_blocks"], 64)
if not is_pypy: if not is_pypy:
self.assertGreaterEqual(stats['freed_blocks'], 64) self.assertGreaterEqual(stats["freed_blocks"], 64)
def test_get_blocks_max(self): def test_get_blocks_max(self):
blocks_max = Image.core.get_blocks_max() blocks_max = Image.core.get_blocks_max()
@ -102,7 +102,7 @@ class TestCoreMemory(PillowTestCase):
self.assertEqual(blocks_max, i) self.assertEqual(blocks_max, i)
# Try to construct new image # Try to construct new image
Image.new('RGB', (10, 10)) Image.new("RGB", (10, 10))
self.assertRaises(ValueError, Image.core.set_blocks_max, -1) self.assertRaises(ValueError, Image.core.set_blocks_max, -1)
@ -111,15 +111,15 @@ class TestCoreMemory(PillowTestCase):
Image.core.reset_stats() Image.core.reset_stats()
Image.core.set_blocks_max(128) Image.core.set_blocks_max(128)
Image.core.set_block_size(4096) 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() stats = Image.core.get_stats()
self.assertGreaterEqual(stats['new_count'], 2) self.assertGreaterEqual(stats["new_count"], 2)
self.assertGreaterEqual(stats['allocated_blocks'], 64) self.assertGreaterEqual(stats["allocated_blocks"], 64)
self.assertGreaterEqual(stats['reused_blocks'], 64) self.assertGreaterEqual(stats["reused_blocks"], 64)
self.assertEqual(stats['freed_blocks'], 0) self.assertEqual(stats["freed_blocks"], 0)
self.assertEqual(stats['blocks_cached'], 64) self.assertEqual(stats["blocks_cached"], 64)
@unittest.skipIf(is_pypy, "images are not collected") @unittest.skipIf(is_pypy, "images are not collected")
def test_clear_cache_stats(self): def test_clear_cache_stats(self):
@ -127,55 +127,55 @@ class TestCoreMemory(PillowTestCase):
Image.core.clear_cache() Image.core.clear_cache()
Image.core.set_blocks_max(128) Image.core.set_blocks_max(128)
Image.core.set_block_size(4096) 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 # Keep 16 blocks in cache
Image.core.clear_cache(16) Image.core.clear_cache(16)
stats = Image.core.get_stats() stats = Image.core.get_stats()
self.assertGreaterEqual(stats['new_count'], 2) self.assertGreaterEqual(stats["new_count"], 2)
self.assertGreaterEqual(stats['allocated_blocks'], 64) self.assertGreaterEqual(stats["allocated_blocks"], 64)
self.assertGreaterEqual(stats['reused_blocks'], 64) self.assertGreaterEqual(stats["reused_blocks"], 64)
self.assertGreaterEqual(stats['freed_blocks'], 48) self.assertGreaterEqual(stats["freed_blocks"], 48)
self.assertEqual(stats['blocks_cached'], 16) self.assertEqual(stats["blocks_cached"], 16)
def test_large_images(self): def test_large_images(self):
Image.core.reset_stats() Image.core.reset_stats()
Image.core.set_blocks_max(0) Image.core.set_blocks_max(0)
Image.core.set_block_size(4096) Image.core.set_block_size(4096)
Image.new('RGB', (2048, 16)) Image.new("RGB", (2048, 16))
Image.core.clear_cache() Image.core.clear_cache()
stats = Image.core.get_stats() stats = Image.core.get_stats()
self.assertGreaterEqual(stats['new_count'], 1) self.assertGreaterEqual(stats["new_count"], 1)
self.assertGreaterEqual(stats['allocated_blocks'], 16) self.assertGreaterEqual(stats["allocated_blocks"], 16)
self.assertGreaterEqual(stats['reused_blocks'], 0) self.assertGreaterEqual(stats["reused_blocks"], 0)
self.assertEqual(stats['blocks_cached'], 0) self.assertEqual(stats["blocks_cached"], 0)
if not is_pypy: if not is_pypy:
self.assertGreaterEqual(stats['freed_blocks'], 16) self.assertGreaterEqual(stats["freed_blocks"], 16)
class TestEnvVars(PillowTestCase): class TestEnvVars(PillowTestCase):
def tearDown(self): def tearDown(self):
# Restore default values # Restore default values
Image.core.set_alignment(1) 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.set_blocks_max(0)
Image.core.clear_cache() Image.core.clear_cache()
def test_units(self): def test_units(self):
Image._apply_env_variables({'PILLOW_BLOCKS_MAX': '2K'}) Image._apply_env_variables({"PILLOW_BLOCKS_MAX": "2K"})
self.assertEqual(Image.core.get_blocks_max(), 2*1024) self.assertEqual(Image.core.get_blocks_max(), 2 * 1024)
Image._apply_env_variables({'PILLOW_BLOCK_SIZE': '2m'}) Image._apply_env_variables({"PILLOW_BLOCK_SIZE": "2m"})
self.assertEqual(Image.core.get_block_size(), 2*1024*1024) self.assertEqual(Image.core.get_block_size(), 2 * 1024 * 1024)
def test_warnings(self): def test_warnings(self):
self.assert_warning( self.assert_warning(
UserWarning, Image._apply_env_variables, UserWarning, Image._apply_env_variables, {"PILLOW_ALIGNMENT": "15"}
{'PILLOW_ALIGNMENT': '15'}) )
self.assert_warning( self.assert_warning(
UserWarning, Image._apply_env_variables, UserWarning, Image._apply_env_variables, {"PILLOW_BLOCK_SIZE": "1024"}
{'PILLOW_BLOCK_SIZE': '1024'}) )
self.assert_warning( self.assert_warning(
UserWarning, Image._apply_env_variables, UserWarning, Image._apply_env_variables, {"PILLOW_BLOCKS_MAX": "wat"}
{'PILLOW_BLOCKS_MAX': 'wat'}) )

View File

@ -8,7 +8,6 @@ ORIGINAL_LIMIT = Image.MAX_IMAGE_PIXELS
class TestDecompressionBomb(PillowTestCase): class TestDecompressionBomb(PillowTestCase):
def tearDown(self): def tearDown(self):
Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT Image.MAX_IMAGE_PIXELS = ORIGINAL_LIMIT
@ -33,20 +32,17 @@ class TestDecompressionBomb(PillowTestCase):
Image.MAX_IMAGE_PIXELS = 128 * 128 - 1 Image.MAX_IMAGE_PIXELS = 128 * 128 - 1
self.assertEqual(Image.MAX_IMAGE_PIXELS, 128 * 128 - 1) self.assertEqual(Image.MAX_IMAGE_PIXELS, 128 * 128 - 1)
self.assert_warning(Image.DecompressionBombWarning, self.assert_warning(Image.DecompressionBombWarning, Image.open, TEST_FILE)
Image.open, TEST_FILE)
def test_exception(self): def test_exception(self):
# Set limit to trigger exception on the test file # Set limit to trigger exception on the test file
Image.MAX_IMAGE_PIXELS = 64 * 128 - 1 Image.MAX_IMAGE_PIXELS = 64 * 128 - 1
self.assertEqual(Image.MAX_IMAGE_PIXELS, 64 * 128 - 1) self.assertEqual(Image.MAX_IMAGE_PIXELS, 64 * 128 - 1)
self.assertRaises(Image.DecompressionBombError, self.assertRaises(Image.DecompressionBombError, lambda: Image.open(TEST_FILE))
lambda: Image.open(TEST_FILE))
class TestDecompressionCrop(PillowTestCase): class TestDecompressionCrop(PillowTestCase):
def setUp(self): def setUp(self):
self.src = hopper() self.src = hopper()
Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width * 4 - 1 Image.MAX_IMAGE_PIXELS = self.src.height * self.src.width * 4 - 1
@ -58,21 +54,17 @@ class TestDecompressionCrop(PillowTestCase):
# Crops can extend the extents, therefore we should have the # Crops can extend the extents, therefore we should have the
# same decompression bomb warnings on them. # same decompression bomb warnings on them.
box = (0, 0, self.src.width * 2, self.src.height * 2) box = (0, 0, self.src.width * 2, self.src.height * 2)
self.assert_warning(Image.DecompressionBombWarning, self.assert_warning(Image.DecompressionBombWarning, self.src.crop, box)
self.src.crop, box)
def test_crop_decompression_checks(self): def test_crop_decompression_checks(self):
im = Image.new("RGB", (100, 100)) im = Image.new("RGB", (100, 100))
good_values = ((-9999, -9999, -9990, -9990), good_values = ((-9999, -9999, -9990, -9990), (-999, -999, -990, -990))
(-999, -999, -990, -990))
warning_values = ((-160, -160, 99, 99), warning_values = ((-160, -160, 99, 99), (160, 160, -99, -99))
(160, 160, -99, -99))
error_values = ((-99909, -99990, 99999, 99999), error_values = ((-99909, -99990, 99999, 99999), (99909, 99990, -99999, -99999))
(99909, 99990, -99999, -99999))
for value in good_values: for value in good_values:
self.assertEqual(im.crop(value).size, (9, 9)) self.assertEqual(im.crop(value).size, (9, 9))

View File

@ -4,41 +4,36 @@ from PIL import features
try: try:
from PIL import _webp from PIL import _webp
HAVE_WEBP = True HAVE_WEBP = True
except ImportError: except ImportError:
HAVE_WEBP = False HAVE_WEBP = False
class TestFeatures(PillowTestCase): class TestFeatures(PillowTestCase):
def test_check(self): def test_check(self):
# Check the correctness of the convenience function # Check the correctness of the convenience function
for module in features.modules: for module in features.modules:
self.assertEqual(features.check_module(module), self.assertEqual(features.check_module(module), features.check(module))
features.check(module))
for codec in features.codecs: for codec in features.codecs:
self.assertEqual(features.check_codec(codec), self.assertEqual(features.check_codec(codec), features.check(codec))
features.check(codec))
for feature in features.features: for feature in features.features:
self.assertEqual(features.check_feature(feature), self.assertEqual(features.check_feature(feature), features.check(feature))
features.check(feature))
@unittest.skipUnless(HAVE_WEBP, "WebP not available") @unittest.skipUnless(HAVE_WEBP, "WebP not available")
def test_webp_transparency(self): def test_webp_transparency(self):
self.assertEqual(features.check('transp_webp'), self.assertEqual(
not _webp.WebPDecoderBuggyAlpha()) features.check("transp_webp"), not _webp.WebPDecoderBuggyAlpha()
self.assertEqual(features.check('transp_webp'), )
_webp.HAVE_TRANSPARENCY) self.assertEqual(features.check("transp_webp"), _webp.HAVE_TRANSPARENCY)
@unittest.skipUnless(HAVE_WEBP, "WebP not available") @unittest.skipUnless(HAVE_WEBP, "WebP not available")
def test_webp_mux(self): def test_webp_mux(self):
self.assertEqual(features.check('webp_mux'), self.assertEqual(features.check("webp_mux"), _webp.HAVE_WEBPMUX)
_webp.HAVE_WEBPMUX)
@unittest.skipUnless(HAVE_WEBP, "WebP not available") @unittest.skipUnless(HAVE_WEBP, "WebP not available")
def test_webp_anim(self): def test_webp_anim(self):
self.assertEqual(features.check('webp_anim'), self.assertEqual(features.check("webp_anim"), _webp.HAVE_WEBPANIM)
_webp.HAVE_WEBPANIM)
def test_check_modules(self): def test_check_modules(self):
for feature in features.modules: for feature in features.modules:

View File

@ -5,11 +5,10 @@ import io
class TestFileBmp(PillowTestCase): class TestFileBmp(PillowTestCase):
def roundtrip(self, im): def roundtrip(self, im):
outfile = self.tempfile("temp.bmp") outfile = self.tempfile("temp.bmp")
im.save(outfile, 'BMP') im.save(outfile, "BMP")
reloaded = Image.open(outfile) reloaded = Image.open(outfile)
reloaded.load() reloaded.load()
@ -28,8 +27,7 @@ class TestFileBmp(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp: with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError, BmpImagePlugin.BmpImageFile, fp)
BmpImagePlugin.BmpImageFile, fp)
def test_save_to_bytes(self): def test_save_to_bytes(self):
output = io.BytesIO() output = io.BytesIO()
@ -62,27 +60,27 @@ class TestFileBmp(PillowTestCase):
im = Image.open("Tests/images/hopper.bmp") im = Image.open("Tests/images/hopper.bmp")
# Act # Act
im.save(outfile, 'JPEG', dpi=im.info['dpi']) im.save(outfile, "JPEG", dpi=im.info["dpi"])
# Assert # Assert
reloaded = Image.open(outfile) reloaded = Image.open(outfile)
reloaded.load() 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(im.size, reloaded.size)
self.assertEqual(reloaded.format, "JPEG") self.assertEqual(reloaded.format, "JPEG")
def test_load_dpi_rounding(self): def test_load_dpi_rounding(self):
# Round up # Round up
im = Image.open('Tests/images/hopper.bmp') im = Image.open("Tests/images/hopper.bmp")
self.assertEqual(im.info["dpi"], (96, 96)) self.assertEqual(im.info["dpi"], (96, 96))
# Round down # Round down
im = Image.open('Tests/images/hopper_roundDown.bmp') im = Image.open("Tests/images/hopper_roundDown.bmp")
self.assertEqual(im.info["dpi"], (72, 72)) self.assertEqual(im.info["dpi"], (72, 72))
def test_save_dpi_rounding(self): def test_save_dpi_rounding(self):
outfile = self.tempfile("temp.bmp") outfile = self.tempfile("temp.bmp")
im = Image.open('Tests/images/hopper.bmp') im = Image.open("Tests/images/hopper.bmp")
im.save(outfile, dpi=(72.2, 72.2)) im.save(outfile, dpi=(72.2, 72.2))
reloaded = Image.open(outfile) reloaded = Image.open(outfile)
@ -94,17 +92,17 @@ class TestFileBmp(PillowTestCase):
def test_load_dib(self): def test_load_dib(self):
# test for #1293, Imagegrab returning Unsupported Bitfields Format # test for #1293, Imagegrab returning Unsupported Bitfields Format
im = Image.open('Tests/images/clipboard.dib') im = Image.open("Tests/images/clipboard.dib")
self.assertEqual(im.format, "DIB") self.assertEqual(im.format, "DIB")
self.assertEqual(im.get_format_mimetype(), "image/bmp") 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) self.assert_image_equal(im, target)
def test_save_dib(self): def test_save_dib(self):
outfile = self.tempfile("temp.dib") outfile = self.tempfile("temp.dib")
im = Image.open('Tests/images/clipboard.dib') im = Image.open("Tests/images/clipboard.dib")
im.save(outfile) im.save(outfile)
reloaded = Image.open(outfile) reloaded = Image.open(outfile)

View File

@ -6,7 +6,6 @@ TEST_FILE = "Tests/images/gfs.t06z.rassda.tm00.bufr_d"
class TestFileBufrStub(PillowTestCase): class TestFileBufrStub(PillowTestCase):
def test_open(self): def test_open(self):
# Act # Act
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
@ -23,8 +22,9 @@ class TestFileBufrStub(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
# Act / Assert # Act / Assert
self.assertRaises(SyntaxError, self.assertRaises(
BufrStubImagePlugin.BufrStubImageFile, invalid_file) SyntaxError, BufrStubImagePlugin.BufrStubImageFile, invalid_file
)
def test_load(self): def test_load(self):
# Arrange # Arrange

View File

@ -7,7 +7,6 @@ TEST_FILE = "Tests/images/dummy.container"
class TestFileContainer(PillowTestCase): class TestFileContainer(PillowTestCase):
def test_sanity(self): def test_sanity(self):
dir(Image) dir(Image)
dir(ContainerIO) dir(ContainerIO)
@ -106,14 +105,16 @@ class TestFileContainer(PillowTestCase):
def test_readlines(self): def test_readlines(self):
# Arrange # Arrange
expected = ["This is line 1\n", expected = [
"This is line 2\n", "This is line 1\n",
"This is line 3\n", "This is line 2\n",
"This is line 4\n", "This is line 3\n",
"This is line 5\n", "This is line 4\n",
"This is line 6\n", "This is line 5\n",
"This is line 7\n", "This is line 6\n",
"This is line 8\n"] "This is line 7\n",
"This is line 8\n",
]
with open(TEST_FILE) as fh: with open(TEST_FILE) as fh:
container = ContainerIO.ContainerIO(fh, 0, 120) container = ContainerIO.ContainerIO(fh, 0, 120)

View File

@ -6,7 +6,6 @@ TEST_FILE = "Tests/images/deerstalker.cur"
class TestFileCur(PillowTestCase): class TestFileCur(PillowTestCase):
def test_sanity(self): def test_sanity(self):
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
@ -20,8 +19,7 @@ class TestFileCur(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError, CurImagePlugin.CurImageFile, invalid_file)
CurImagePlugin.CurImageFile, invalid_file)
no_cursors_file = "Tests/images/no_cursors.cur" no_cursors_file = "Tests/images/no_cursors.cur"

View File

@ -7,7 +7,6 @@ TEST_FILE = "Tests/images/hopper.dcx"
class TestFileDcx(PillowTestCase): class TestFileDcx(PillowTestCase):
def test_sanity(self): def test_sanity(self):
# Arrange # Arrange
@ -24,12 +23,12 @@ class TestFileDcx(PillowTestCase):
def open(): def open():
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
im.load() im.load()
self.assert_warning(None, open) self.assert_warning(None, open)
def test_invalid_file(self): def test_invalid_file(self):
with open("Tests/images/flower.jpg", "rb") as fp: with open("Tests/images/flower.jpg", "rb") as fp:
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError, DcxImagePlugin.DcxImageFile, fp)
DcxImagePlugin.DcxImageFile, fp)
def test_tell(self): def test_tell(self):
# Arrange # Arrange
@ -55,7 +54,7 @@ class TestFileDcx(PillowTestCase):
self.assertLess(im.tell(), n_frames) self.assertLess(im.tell(), n_frames)
# Test that seeking to the last frame does not raise an error # Test that seeking to the last frame does not raise an error
im.seek(n_frames-1) im.seek(n_frames - 1)
def test_seek_too_far(self): def test_seek_too_far(self):
# Arrange # Arrange

View File

@ -15,7 +15,7 @@ class TestFileDds(PillowTestCase):
def test_sanity_dxt1(self): def test_sanity_dxt1(self):
"""Check DXT1 images can be opened""" """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 = Image.open(TEST_FILE_DXT1)
im.load() im.load()
@ -24,12 +24,12 @@ class TestFileDds(PillowTestCase):
self.assertEqual(im.mode, "RGBA") self.assertEqual(im.mode, "RGBA")
self.assertEqual(im.size, (256, 256)) 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): def test_sanity_dxt5(self):
"""Check DXT5 images can be opened""" """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 = Image.open(TEST_FILE_DXT5)
im.load() im.load()
@ -43,7 +43,7 @@ class TestFileDds(PillowTestCase):
def test_sanity_dxt3(self): def test_sanity_dxt3(self):
"""Check DXT3 images can be opened""" """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 = Image.open(TEST_FILE_DXT3)
im.load() im.load()
@ -57,7 +57,7 @@ class TestFileDds(PillowTestCase):
def test_dx10_bc7(self): def test_dx10_bc7(self):
"""Check DX10 images can be opened""" """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 = Image.open(TEST_FILE_DX10_BC7)
im.load() im.load()
@ -69,13 +69,16 @@ class TestFileDds(PillowTestCase):
self.assert_image_equal(target, im) self.assert_image_equal(target, im)
def test_unimplemented_dxgi_format(self): def test_unimplemented_dxgi_format(self):
self.assertRaises(NotImplementedError, Image.open, self.assertRaises(
"Tests/images/unimplemented_dxgi_format.dds") NotImplementedError,
Image.open,
"Tests/images/unimplemented_dxgi_format.dds",
)
def test_uncompressed_rgb(self): def test_uncompressed_rgb(self):
"""Check uncompressed RGB images can be opened""" """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 = Image.open(TEST_FILE_UNCOMPRESSED_RGB)
im.load() im.load()
@ -110,7 +113,7 @@ class TestFileDds(PillowTestCase):
def test_short_header(self): def test_short_header(self):
""" Check a short header""" """ Check a short header"""
with open(TEST_FILE_DXT5, 'rb') as f: with open(TEST_FILE_DXT5, "rb") as f:
img_file = f.read() img_file = f.read()
def short_header(): def short_header():
@ -121,7 +124,7 @@ class TestFileDds(PillowTestCase):
def test_short_file(self): def test_short_file(self):
""" Check that the appropriate error is thrown for a short file""" """ 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() img_file = f.read()
def short_file(): def short_file():
@ -131,5 +134,8 @@ class TestFileDds(PillowTestCase):
self.assertRaises(IOError, short_file) self.assertRaises(IOError, short_file)
def test_unimplemented_pixel_format(self): def test_unimplemented_pixel_format(self):
self.assertRaises(NotImplementedError, Image.open, self.assertRaises(
"Tests/images/unimplemented_pixel_format.dds") NotImplementedError,
Image.open,
"Tests/images/unimplemented_pixel_format.dds",
)

View File

@ -21,7 +21,6 @@ file3 = "Tests/images/binary_preview_map.eps"
class TestFileEps(PillowTestCase): class TestFileEps(PillowTestCase):
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_sanity(self): def test_sanity(self):
# Regular scale # Regular scale
@ -53,8 +52,7 @@ class TestFileEps(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError, EpsImagePlugin.EpsImageFile, invalid_file)
EpsImagePlugin.EpsImageFile, invalid_file)
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_cmyk(self): def test_cmyk(self):
@ -67,8 +65,8 @@ class TestFileEps(PillowTestCase):
cmyk_image.load() cmyk_image.load()
self.assertEqual(cmyk_image.mode, "RGB") self.assertEqual(cmyk_image.mode, "RGB")
if 'jpeg_decoder' in dir(Image.core): if "jpeg_decoder" in dir(Image.core):
target = Image.open('Tests/images/pil_sample_rgb.jpg') target = Image.open("Tests/images/pil_sample_rgb.jpg")
self.assert_image_similar(cmyk_image, target, 10) self.assert_image_similar(cmyk_image, target, 10)
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
@ -86,19 +84,19 @@ class TestFileEps(PillowTestCase):
def test_file_object(self): def test_file_object(self):
# issue 479 # issue 479
image1 = Image.open(file1) image1 = Image.open(file1)
with open(self.tempfile('temp_file.eps'), 'wb') as fh: with open(self.tempfile("temp_file.eps"), "wb") as fh:
image1.save(fh, 'EPS') image1.save(fh, "EPS")
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_iobase_object(self): def test_iobase_object(self):
# issue 479 # issue 479
image1 = Image.open(file1) image1 = Image.open(file1)
with io.open(self.tempfile('temp_iobase.eps'), 'wb') as fh: with io.open(self.tempfile("temp_iobase.eps"), "wb") as fh:
image1.save(fh, 'EPS') image1.save(fh, "EPS")
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
def test_bytesio_object(self): def test_bytesio_object(self):
with open(file1, 'rb') as f: with open(file1, "rb") as f:
img_bytes = io.BytesIO(f.read()) img_bytes = io.BytesIO(f.read())
img = Image.open(img_bytes) img = Image.open(img_bytes)
@ -110,7 +108,7 @@ class TestFileEps(PillowTestCase):
def test_image_mode_not_supported(self): def test_image_mode_not_supported(self):
im = hopper("RGBA") im = hopper("RGBA")
tmpfile = self.tempfile('temp.eps') tmpfile = self.tempfile("temp.eps")
self.assertRaises(ValueError, im.save, tmpfile) self.assertRaises(ValueError, im.save, tmpfile)
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")
@ -195,33 +193,33 @@ class TestFileEps(PillowTestCase):
Image.open(file3) Image.open(file3)
def _test_readline(self, t, ending): def _test_readline(self, t, ending):
ending = "Failure with line ending: %s" % ("".join( ending = "Failure with line ending: %s" % (
"%s" % ord(s) "".join("%s" % ord(s) for s in ending)
for s in ending)) )
self.assertEqual(t.readline().strip('\r\n'), 'something', 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"), "else", ending)
self.assertEqual(t.readline().strip('\r\n'), 'baz', ending) self.assertEqual(t.readline().strip("\r\n"), "baz", ending)
self.assertEqual(t.readline().strip('\r\n'), 'bif', ending) self.assertEqual(t.readline().strip("\r\n"), "bif", ending)
def _test_readline_io_psfile(self, test_string, 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) t = EpsImagePlugin.PSFile(f)
self._test_readline(t, ending) self._test_readline(t, ending)
def _test_readline_file_psfile(self, test_string, ending): def _test_readline_file_psfile(self, test_string, ending):
f = self.tempfile('temp.txt') f = self.tempfile("temp.txt")
with open(f, 'wb') as w: with open(f, "wb") as w:
w.write(test_string.encode('latin-1')) w.write(test_string.encode("latin-1"))
with open(f, 'rb') as r: with open(f, "rb") as r:
t = EpsImagePlugin.PSFile(r) t = EpsImagePlugin.PSFile(r)
self._test_readline(t, ending) self._test_readline(t, ending)
def test_readline(self): def test_readline(self):
# check all the freaking line endings possible from the spec # check all the freaking line endings possible from the spec
# test_string = u'something\r\nelse\n\rbaz\rbif\n' # test_string = u'something\r\nelse\n\rbaz\rbif\n'
line_endings = ['\r\n', '\n', '\n\r', '\r'] line_endings = ["\r\n", "\n", "\n\r", "\r"]
strings = ['something', 'else', 'baz', 'bif'] strings = ["something", "else", "baz", "bif"]
for ending in line_endings: for ending in line_endings:
s = ending.join(strings) s = ending.join(strings)
@ -231,10 +229,12 @@ class TestFileEps(PillowTestCase):
def test_open_eps(self): def test_open_eps(self):
# https://github.com/python-pillow/Pillow/issues/1104 # https://github.com/python-pillow/Pillow/issues/1104
# Arrange # Arrange
FILES = ["Tests/images/illu10_no_preview.eps", FILES = [
"Tests/images/illu10_preview.eps", "Tests/images/illu10_no_preview.eps",
"Tests/images/illuCS6_no_preview.eps", "Tests/images/illu10_preview.eps",
"Tests/images/illuCS6_preview.eps"] "Tests/images/illuCS6_no_preview.eps",
"Tests/images/illuCS6_preview.eps",
]
# Act / Assert # Act / Assert
for filename in FILES: for filename in FILES:

View File

@ -6,7 +6,6 @@ TEST_FILE = "Tests/images/hopper.fits"
class TestFileFitsStub(PillowTestCase): class TestFileFitsStub(PillowTestCase):
def test_open(self): def test_open(self):
# Act # Act
im = Image.open(TEST_FILE) im = Image.open(TEST_FILE)
@ -23,8 +22,9 @@ class TestFileFitsStub(PillowTestCase):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
# Act / Assert # Act / Assert
self.assertRaises(SyntaxError, self.assertRaises(
FitsStubImagePlugin.FITSStubImageFile, invalid_file) SyntaxError, FitsStubImagePlugin.FITSStubImageFile, invalid_file
)
def test_load(self): def test_load(self):
# Arrange # Arrange
@ -42,5 +42,5 @@ class TestFileFitsStub(PillowTestCase):
# Act / Assert: stub cannot save without an implemented handler # Act / Assert: stub cannot save without an implemented handler
self.assertRaises(IOError, im.save, dummy_filename) self.assertRaises(IOError, im.save, dummy_filename)
self.assertRaises( self.assertRaises(
IOError, IOError, FitsStubImagePlugin._save, im, dummy_fp, dummy_filename
FitsStubImagePlugin._save, im, dummy_fp, dummy_filename) )

View File

@ -11,7 +11,6 @@ animated_test_file = "Tests/images/a.fli"
class TestFileFli(PillowTestCase): class TestFileFli(PillowTestCase):
def test_sanity(self): def test_sanity(self):
im = Image.open(static_test_file) im = Image.open(static_test_file)
im.load() im.load()
@ -31,6 +30,7 @@ class TestFileFli(PillowTestCase):
def open(): def open():
im = Image.open(static_test_file) im = Image.open(static_test_file)
im.load() im.load()
self.assert_warning(None, open) self.assert_warning(None, open)
def test_tell(self): def test_tell(self):
@ -46,8 +46,7 @@ class TestFileFli(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError, FliImagePlugin.FliImageFile, invalid_file)
FliImagePlugin.FliImageFile, invalid_file)
def test_n_frames(self): def test_n_frames(self):
im = Image.open(static_test_file) im = Image.open(static_test_file)
@ -67,7 +66,7 @@ class TestFileFli(PillowTestCase):
self.assertLess(im.tell(), n_frames) self.assertLess(im.tell(), n_frames)
# Test that seeking to the last frame does not raise an error # Test that seeking to the last frame does not raise an error
im.seek(n_frames-1) im.seek(n_frames - 1)
def test_seek_tell(self): def test_seek_tell(self):
im = Image.open(animated_test_file) im = Image.open(animated_test_file)

View File

@ -10,14 +10,11 @@ else:
@unittest.skipUnless(olefile_installed, "olefile package not installed") @unittest.skipUnless(olefile_installed, "olefile package not installed")
class TestFileFpx(PillowTestCase): class TestFileFpx(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
# Test an invalid OLE file # Test an invalid OLE file
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError, FpxImagePlugin.FpxImageFile, invalid_file)
FpxImagePlugin.FpxImageFile, invalid_file)
# Test a valid OLE file, but not an FPX file # Test a valid OLE file, but not an FPX file
ole_file = "Tests/images/test-ole-file.doc" ole_file = "Tests/images/test-ole-file.doc"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError, FpxImagePlugin.FpxImageFile, ole_file)
FpxImagePlugin.FpxImageFile, ole_file)

View File

@ -3,14 +3,13 @@ from PIL import Image
class TestFileFtex(PillowTestCase): class TestFileFtex(PillowTestCase):
def test_load_raw(self): def test_load_raw(self):
im = Image.open('Tests/images/ftex_uncompressed.ftu') im = Image.open("Tests/images/ftex_uncompressed.ftu")
target = Image.open('Tests/images/ftex_uncompressed.png') target = Image.open("Tests/images/ftex_uncompressed.png")
self.assert_image_equal(im, target) self.assert_image_equal(im, target)
def test_load_dxt1(self): def test_load_dxt1(self):
im = Image.open('Tests/images/ftex_dxt1.ftc') im = Image.open("Tests/images/ftex_dxt1.ftc")
target = Image.open('Tests/images/ftex_dxt1.png') target = Image.open("Tests/images/ftex_dxt1.png")
self.assert_image_similar(im, target.convert('RGBA'), 15) self.assert_image_similar(im, target.convert("RGBA"), 15)

View File

@ -4,16 +4,14 @@ from PIL import Image, GbrImagePlugin
class TestFileGbr(PillowTestCase): class TestFileGbr(PillowTestCase):
def test_invalid_file(self): def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError, GbrImagePlugin.GbrImageFile, invalid_file)
GbrImagePlugin.GbrImageFile, invalid_file)
def test_gbr_file(self): 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) self.assert_image_equal(target, im)

View File

@ -6,15 +6,13 @@ TEST_GD_FILE = "Tests/images/hopper.gd"
class TestFileGd(PillowTestCase): class TestFileGd(PillowTestCase):
def test_sanity(self): def test_sanity(self):
im = GdImageFile.open(TEST_GD_FILE) im = GdImageFile.open(TEST_GD_FILE)
self.assertEqual(im.size, (128, 128)) self.assertEqual(im.size, (128, 128))
self.assertEqual(im.format, "GD") self.assertEqual(im.format, "GD")
def test_bad_mode(self): def test_bad_mode(self):
self.assertRaises(ValueError, self.assertRaises(ValueError, GdImageFile.open, TEST_GD_FILE, "bad mode")
GdImageFile.open, TEST_GD_FILE, 'bad mode')
def test_invalid_file(self): def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"

View File

@ -6,6 +6,7 @@ from io import BytesIO
try: try:
from PIL import _webp from PIL import _webp
HAVE_WEBP = True HAVE_WEBP = True
except ImportError: except ImportError:
HAVE_WEBP = False HAVE_WEBP = False
@ -20,7 +21,6 @@ with open(TEST_GIF, "rb") as f:
class TestFileGif(PillowTestCase): class TestFileGif(PillowTestCase):
def setUp(self): def setUp(self):
if "gif_encoder" not in codecs or "gif_decoder" not in codecs: if "gif_encoder" not in codecs or "gif_decoder" not in codecs:
self.skipTest("gif support not available") # can this happen? self.skipTest("gif support not available") # can this happen?
@ -37,13 +37,13 @@ class TestFileGif(PillowTestCase):
def open(): def open():
im = Image.open(TEST_GIF) im = Image.open(TEST_GIF)
im.load() im.load()
self.assert_warning(None, open) self.assert_warning(None, open)
def test_invalid_file(self): def test_invalid_file(self):
invalid_file = "Tests/images/flower.jpg" invalid_file = "Tests/images/flower.jpg"
self.assertRaises(SyntaxError, self.assertRaises(SyntaxError, GifImagePlugin.GifImageFile, invalid_file)
GifImagePlugin.GifImageFile, invalid_file)
def test_optimize(self): def test_optimize(self):
def test_grayscale(optimize): def test_grayscale(optimize):
@ -70,19 +70,22 @@ class TestFileGif(PillowTestCase):
# Check for correctness after conversion back to RGB # Check for correctness after conversion back to RGB
def check(colors, size, expected_palette_length): def check(colors, size, expected_palette_length):
# make an image with empty colors in the start of the palette range # make an image with empty colors in the start of the palette range
im = Image.frombytes('P', (colors, colors), im = Image.frombytes(
bytes(bytearray(range(256-colors, 256))*colors)) "P",
(colors, colors),
bytes(bytearray(range(256 - colors, 256)) * colors),
)
im = im.resize((size, size)) im = im.resize((size, size))
outfile = BytesIO() outfile = BytesIO()
im.save(outfile, 'GIF') im.save(outfile, "GIF")
outfile.seek(0) outfile.seek(0)
reloaded = Image.open(outfile) reloaded = Image.open(outfile)
# check palette length # 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.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 # These do optimize the palette
check(128, 511, 128) check(128, 511, 128)
@ -106,79 +109,76 @@ class TestFileGif(PillowTestCase):
self.assertEqual(im.mode, "L") self.assertEqual(im.mode, "L")
def test_roundtrip(self): def test_roundtrip(self):
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im = hopper() im = hopper()
im.save(out) im.save(out)
reread = Image.open(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): def test_roundtrip2(self):
# see https://github.com/python-pillow/Pillow/issues/403 # see https://github.com/python-pillow/Pillow/issues/403
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im = Image.open(TEST_GIF) im = Image.open(TEST_GIF)
im2 = im.copy() im2 = im.copy()
im2.save(out) im2.save(out)
reread = Image.open(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): def test_roundtrip_save_all(self):
# Single frame image # Single frame image
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im = hopper() im = hopper()
im.save(out, save_all=True) im.save(out, save_all=True)
reread = Image.open(out) reread = Image.open(out)
self.assert_image_similar(reread.convert('RGB'), im, 50) self.assert_image_similar(reread.convert("RGB"), im, 50)
# Multiframe image # Multiframe image
im = Image.open("Tests/images/dispose_bgnd.gif") im = Image.open("Tests/images/dispose_bgnd.gif")
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im.save(out, save_all=True) im.save(out, save_all=True)
reread = Image.open(out) reread = Image.open(out)
self.assertEqual(reread.n_frames, 5) self.assertEqual(reread.n_frames, 5)
def test_headers_saving_for_animated_gifs(self): def test_headers_saving_for_animated_gifs(self):
important_headers = ['background', 'version', 'duration', 'loop'] important_headers = ["background", "version", "duration", "loop"]
# Multiframe image # Multiframe image
im = Image.open("Tests/images/dispose_bgnd.gif") im = Image.open("Tests/images/dispose_bgnd.gif")
info = im.info.copy() info = im.info.copy()
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im.save(out, save_all=True) im.save(out, save_all=True)
reread = Image.open(out) reread = Image.open(out)
for header in important_headers: for header in important_headers:
self.assertEqual( self.assertEqual(info[header], reread.info[header])
info[header],
reread.info[header]
)
def test_palette_handling(self): def test_palette_handling(self):
# see https://github.com/python-pillow/Pillow/issues/513 # see https://github.com/python-pillow/Pillow/issues/513
im = Image.open(TEST_GIF) im = Image.open(TEST_GIF)
im = im.convert('RGB') im = im.convert("RGB")
im = im.resize((100, 100), Image.LANCZOS) im = im.resize((100, 100), Image.LANCZOS)
im2 = im.convert('P', palette=Image.ADAPTIVE, colors=256) im2 = im.convert("P", palette=Image.ADAPTIVE, colors=256)
f = self.tempfile('temp.gif') f = self.tempfile("temp.gif")
im2.save(f, optimize=True) im2.save(f, optimize=True)
reloaded = Image.open(f) 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): def test_palette_434(self):
# see https://github.com/python-pillow/Pillow/issues/434 # see https://github.com/python-pillow/Pillow/issues/434
def roundtrip(im, *args, **kwargs): def roundtrip(im, *args, **kwargs):
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im.copy().save(out, *args, **kwargs) im.copy().save(out, *args, **kwargs)
reloaded = Image.open(out) reloaded = Image.open(out)
@ -192,7 +192,7 @@ class TestFileGif(PillowTestCase):
im = im.convert("RGB") im = im.convert("RGB")
# check automatic P conversion # check automatic P conversion
reloaded = roundtrip(im).convert('RGB') reloaded = roundtrip(im).convert("RGB")
self.assert_image_equal(im, reloaded) self.assert_image_equal(im, reloaded)
@unittest.skipUnless(netpbm_available(), "netpbm not available") @unittest.skipUnless(netpbm_available(), "netpbm not available")
@ -240,10 +240,7 @@ class TestFileGif(PillowTestCase):
self.assert_image_equal(im, expected) self.assert_image_equal(im, expected)
def test_n_frames(self): def test_n_frames(self):
for path, n_frames in [ for path, n_frames in [[TEST_GIF, 1], ["Tests/images/iss634.gif", 42]]:
[TEST_GIF, 1],
['Tests/images/iss634.gif', 42]
]:
# Test is_animated before n_frames # Test is_animated before n_frames
im = Image.open(path) im = Image.open(path)
self.assertEqual(im.is_animated, n_frames != 1) self.assertEqual(im.is_animated, n_frames != 1)
@ -262,7 +259,7 @@ class TestFileGif(PillowTestCase):
self.assertLess(im.tell(), n_frames) self.assertLess(im.tell(), n_frames)
# Test that seeking to the last frame does not raise an error # Test that seeking to the last frame does not raise an error
im.seek(n_frames-1) im.seek(n_frames - 1)
def test_dispose_none(self): def test_dispose_none(self):
img = Image.open("Tests/images/dispose_none.gif") img = Image.open("Tests/images/dispose_none.gif")
@ -292,18 +289,15 @@ class TestFileGif(PillowTestCase):
pass pass
def test_save_dispose(self): def test_save_dispose(self):
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im_list = [ im_list = [
Image.new('L', (100, 100), '#000'), Image.new("L", (100, 100), "#000"),
Image.new('L', (100, 100), '#111'), Image.new("L", (100, 100), "#111"),
Image.new('L', (100, 100), '#222'), Image.new("L", (100, 100), "#222"),
] ]
for method in range(0, 4): for method in range(0, 4):
im_list[0].save( im_list[0].save(
out, out, save_all=True, append_images=im_list[1:], disposal=method
save_all=True,
append_images=im_list[1:],
disposal=method
) )
img = Image.open(out) img = Image.open(out)
for _ in range(2): for _ in range(2):
@ -315,14 +309,14 @@ class TestFileGif(PillowTestCase):
out, out,
save_all=True, save_all=True,
append_images=im_list[1:], append_images=im_list[1:],
disposal=tuple(range(len(im_list))) disposal=tuple(range(len(im_list))),
) )
img = Image.open(out) img = Image.open(out)
for i in range(2): for i in range(2):
img.seek(img.tell() + 1) img.seek(img.tell() + 1)
self.assertEqual(img.disposal_method, i+1) self.assertEqual(img.disposal_method, i + 1)
def test_iss634(self): def test_iss634(self):
img = Image.open("Tests/images/iss634.gif") img = Image.open("Tests/images/iss634.gif")
@ -330,42 +324,39 @@ class TestFileGif(PillowTestCase):
img.seek(img.tell() + 1) img.seek(img.tell() + 1)
# all transparent pixels should be replaced with the color from the # all transparent pixels should be replaced with the color from the
# first frame # first frame
self.assertEqual(img.histogram()[img.info['transparency']], 0) self.assertEqual(img.histogram()[img.info["transparency"]], 0)
def test_duration(self): def test_duration(self):
duration = 1000 duration = 1000
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im = Image.new('L', (100, 100), '#000') im = Image.new("L", (100, 100), "#000")
# Check that the argument has priority over the info settings # Check that the argument has priority over the info settings
im.info['duration'] = 100 im.info["duration"] = 100
im.save(out, duration=duration) im.save(out, duration=duration)
reread = Image.open(out) reread = Image.open(out)
self.assertEqual(reread.info['duration'], duration) self.assertEqual(reread.info["duration"], duration)
def test_multiple_duration(self): def test_multiple_duration(self):
duration_list = [1000, 2000, 3000] duration_list = [1000, 2000, 3000]
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im_list = [ im_list = [
Image.new('L', (100, 100), '#000'), Image.new("L", (100, 100), "#000"),
Image.new('L', (100, 100), '#111'), Image.new("L", (100, 100), "#111"),
Image.new('L', (100, 100), '#222') Image.new("L", (100, 100), "#222"),
] ]
# duration as list # duration as list
im_list[0].save( im_list[0].save(
out, out, save_all=True, append_images=im_list[1:], duration=duration_list
save_all=True,
append_images=im_list[1:],
duration=duration_list
) )
reread = Image.open(out) reread = Image.open(out)
for duration in duration_list: for duration in duration_list:
self.assertEqual(reread.info['duration'], duration) self.assertEqual(reread.info["duration"], duration)
try: try:
reread.seek(reread.tell() + 1) reread.seek(reread.tell() + 1)
except EOFError: except EOFError:
@ -373,15 +364,12 @@ class TestFileGif(PillowTestCase):
# duration as tuple # duration as tuple
im_list[0].save( im_list[0].save(
out, out, save_all=True, append_images=im_list[1:], duration=tuple(duration_list)
save_all=True,
append_images=im_list[1:],
duration=tuple(duration_list)
) )
reread = Image.open(out) reread = Image.open(out)
for duration in duration_list: for duration in duration_list:
self.assertEqual(reread.info['duration'], duration) self.assertEqual(reread.info["duration"], duration)
try: try:
reread.seek(reread.tell() + 1) reread.seek(reread.tell() + 1)
except EOFError: except EOFError:
@ -390,20 +378,17 @@ class TestFileGif(PillowTestCase):
def test_identical_frames(self): def test_identical_frames(self):
duration_list = [1000, 1500, 2000, 4000] duration_list = [1000, 1500, 2000, 4000]
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im_list = [ 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), "#000"),
Image.new('L', (100, 100), '#000'), Image.new("L", (100, 100), "#000"),
Image.new('L', (100, 100), '#111') Image.new("L", (100, 100), "#111"),
] ]
# duration as list # duration as list
im_list[0].save( im_list[0].save(
out, out, save_all=True, append_images=im_list[1:], duration=duration_list
save_all=True,
append_images=im_list[1:],
duration=duration_list
) )
reread = Image.open(out) reread = Image.open(out)
@ -411,64 +396,63 @@ class TestFileGif(PillowTestCase):
self.assertEqual(reread.n_frames, 2) self.assertEqual(reread.n_frames, 2)
# Assert that the new duration is the total of the identical frames # Assert that the new duration is the total of the identical frames
self.assertEqual(reread.info['duration'], 4500) self.assertEqual(reread.info["duration"], 4500)
def test_number_of_loops(self): def test_number_of_loops(self):
number_of_loops = 2 number_of_loops = 2
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im = Image.new('L', (100, 100), '#000') im = Image.new("L", (100, 100), "#000")
im.save(out, loop=number_of_loops) im.save(out, loop=number_of_loops)
reread = Image.open(out) reread = Image.open(out)
self.assertEqual(reread.info['loop'], number_of_loops) self.assertEqual(reread.info["loop"], number_of_loops)
def test_background(self): def test_background(self):
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im = Image.new('L', (100, 100), '#000') im = Image.new("L", (100, 100), "#000")
im.info['background'] = 1 im.info["background"] = 1
im.save(out) im.save(out)
reread = Image.open(out) 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: if HAVE_WEBP and _webp.HAVE_WEBPANIM:
im = Image.open("Tests/images/hopper.webp") im = Image.open("Tests/images/hopper.webp")
self.assertIsInstance(im.info['background'], tuple) self.assertIsInstance(im.info["background"], tuple)
im.save(out) im.save(out)
def test_comment(self): def test_comment(self):
im = Image.open(TEST_GIF) im = Image.open(TEST_GIF)
self.assertEqual(im.info['comment'], self.assertEqual(im.info["comment"], b"File written by Adobe Photoshop\xa8 4.0")
b"File written by Adobe Photoshop\xa8 4.0")
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im = Image.new('L', (100, 100), '#000') im = Image.new("L", (100, 100), "#000")
im.info['comment'] = b"Test comment text" im.info["comment"] = b"Test comment text"
im.save(out) im.save(out)
reread = Image.open(out) 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): def test_comment_over_255(self):
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im = Image.new('L', (100, 100), '#000') im = Image.new("L", (100, 100), "#000")
comment = b"Test comment text" comment = b"Test comment text"
while len(comment) < 256: while len(comment) < 256:
comment += comment comment += comment
im.info['comment'] = comment im.info["comment"] = comment
im.save(out) im.save(out)
reread = Image.open(out) reread = Image.open(out)
self.assertEqual(reread.info['comment'], comment) self.assertEqual(reread.info["comment"], comment)
def test_zero_comment_subblocks(self): def test_zero_comment_subblocks(self):
im = Image.open('Tests/images/hopper_zero_comment_subblocks.gif') im = Image.open("Tests/images/hopper_zero_comment_subblocks.gif")
expected = Image.open(TEST_GIF) expected = Image.open(TEST_GIF)
self.assert_image_equal(im, expected) self.assert_image_equal(im, expected)
def test_version(self): def test_version(self):
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
def assertVersionAfterSave(im, version): def assertVersionAfterSave(im, version):
im.save(out) im.save(out)
@ -476,11 +460,11 @@ class TestFileGif(PillowTestCase):
self.assertEqual(reread.info["version"], version) self.assertEqual(reread.info["version"], version)
# Test that GIF87a is used by default # Test that GIF87a is used by default
im = Image.new('L', (100, 100), '#000') im = Image.new("L", (100, 100), "#000")
assertVersionAfterSave(im, b"GIF87a") assertVersionAfterSave(im, b"GIF87a")
# Test setting the version to 89a # 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" im.info["version"] = b"89a"
assertVersionAfterSave(im, b"GIF89a") assertVersionAfterSave(im, b"GIF89a")
@ -497,12 +481,11 @@ class TestFileGif(PillowTestCase):
assertVersionAfterSave(im, b"GIF87a") assertVersionAfterSave(im, b"GIF87a")
def test_append_images(self): def test_append_images(self):
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
# Test appending single frame images # Test appending single frame images
im = Image.new('RGB', (100, 100), '#f00') im = Image.new("RGB", (100, 100), "#f00")
ims = [Image.new('RGB', (100, 100), color) for color ims = [Image.new("RGB", (100, 100), color) for color in ["#0f0", "#00f"]]
in ['#0f0', '#00f']]
im.copy().save(out, save_all=True, append_images=ims) im.copy().save(out, save_all=True, append_images=ims)
reread = Image.open(out) reread = Image.open(out)
@ -512,6 +495,7 @@ class TestFileGif(PillowTestCase):
def imGenerator(ims): def imGenerator(ims):
for im in ims: for im in ims:
yield im yield im
im.save(out, save_all=True, append_images=imGenerator(ims)) im.save(out, save_all=True, append_images=imGenerator(ims))
reread = Image.open(out) reread = Image.open(out)
@ -533,44 +517,43 @@ class TestFileGif(PillowTestCase):
# the top palette entry to trigger the bug. # the top palette entry to trigger the bug.
data = bytes(bytearray(range(1, 254))) 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.frombytes(data)
im.putpalette(palette) im.putpalette(palette)
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im.save(out, transparency=253) im.save(out, transparency=253)
reloaded = Image.open(out) reloaded = Image.open(out)
self.assertEqual(reloaded.info['transparency'], 253) self.assertEqual(reloaded.info["transparency"], 253)
def test_rgb_transparency(self): def test_rgb_transparency(self):
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
# Single frame # Single frame
im = Image.new('RGB', (1, 1)) im = Image.new("RGB", (1, 1))
im.info['transparency'] = (255, 0, 0) im.info["transparency"] = (255, 0, 0)
self.assert_warning(UserWarning, im.save, out) self.assert_warning(UserWarning, im.save, out)
reloaded = Image.open(out) reloaded = Image.open(out)
self.assertNotIn('transparency', reloaded.info) self.assertNotIn("transparency", reloaded.info)
# Multiple frames # Multiple frames
im = Image.new('RGB', (1, 1)) im = Image.new("RGB", (1, 1))
im.info['transparency'] = b"" im.info["transparency"] = b""
ims = [Image.new('RGB', (1, 1))] ims = [Image.new("RGB", (1, 1))]
self.assert_warning(UserWarning, self.assert_warning(UserWarning, im.save, out, save_all=True, append_images=ims)
im.save, out, save_all=True, append_images=ims)
reloaded = Image.open(out) reloaded = Image.open(out)
self.assertNotIn('transparency', reloaded.info) self.assertNotIn("transparency", reloaded.info)
def test_bbox(self): def test_bbox(self):
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im = Image.new('RGB', (100, 100), '#fff') im = Image.new("RGB", (100, 100), "#fff")
ims = [Image.new("RGB", (100, 100), '#000')] ims = [Image.new("RGB", (100, 100), "#000")]
im.save(out, save_all=True, append_images=ims) im.save(out, save_all=True, append_images=ims)
reread = Image.open(out) reread = Image.open(out)
@ -579,26 +562,26 @@ class TestFileGif(PillowTestCase):
def test_palette_save_L(self): def test_palette_save_L(self):
# generate an L mode image with a separate palette # generate an L mode image with a separate palette
im = hopper('P') im = hopper("P")
im_l = Image.frombytes('L', im.size, im.tobytes()) im_l = Image.frombytes("L", im.size, im.tobytes())
palette = bytes(bytearray(im.getpalette())) palette = bytes(bytearray(im.getpalette()))
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im_l.save(out, palette=palette) im_l.save(out, palette=palette)
reloaded = Image.open(out) 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): def test_palette_save_P(self):
# pass in a different palette, then construct what the image # pass in a different palette, then construct what the image
# would look like. # would look like.
# Forcing a non-straight grayscale palette. # Forcing a non-straight grayscale palette.
im = hopper('P') im = hopper("P")
palette = bytes(bytearray([255-i//3 for i in range(768)])) 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) im.save(out, palette=palette)
reloaded = Image.open(out) reloaded = Image.open(out)
@ -609,10 +592,10 @@ class TestFileGif(PillowTestCase):
# pass in a different palette, as an ImagePalette.ImagePalette # pass in a different palette, as an ImagePalette.ImagePalette
# effectively the same as test_palette_save_P # effectively the same as test_palette_save_P
im = hopper('P') im = hopper("P")
palette = ImagePalette.ImagePalette('RGB', list(range(256))[::-1]*3) palette = ImagePalette.ImagePalette("RGB", list(range(256))[::-1] * 3)
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im.save(out, palette=palette) im.save(out, palette=palette)
reloaded = Image.open(out) reloaded = Image.open(out)
@ -622,22 +605,22 @@ class TestFileGif(PillowTestCase):
def test_save_I(self): def test_save_I(self):
# Test saving something that would trigger the auto-convert to 'L' # Test saving something that would trigger the auto-convert to 'L'
im = hopper('I') im = hopper("I")
out = self.tempfile('temp.gif') out = self.tempfile("temp.gif")
im.save(out) im.save(out)
reloaded = Image.open(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): def test_getdata(self):
# test getheader/getdata against legacy values # test getheader/getdata against legacy values
# Create a 'P' image with holes in the palette # Create a 'P' image with holes in the palette
im = Image._wedge().resize((16, 16)) im = Image._wedge().resize((16, 16))
im.putpalette(ImagePalette.ImagePalette('RGB')) im.putpalette(ImagePalette.ImagePalette("RGB"))
im.info = {'background': 0} 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 GifImagePlugin._FORCE_OPTIMIZE = True
try: try:
@ -645,10 +628,11 @@ class TestFileGif(PillowTestCase):
d = GifImagePlugin.getdata(im) d = GifImagePlugin.getdata(im)
import pickle import pickle
# Enable to get target values on pre-refactor version # Enable to get target values on pre-refactor version
# with open('Tests/images/gif_header_data.pkl', 'wb') as f: # with open('Tests/images/gif_header_data.pkl', 'wb') as f:
# pickle.dump((h, d), f, 1) # 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) (h_target, d_target) = pickle.load(f)
self.assertEqual(h, h_target) self.assertEqual(h, h_target)
@ -658,14 +642,14 @@ class TestFileGif(PillowTestCase):
def test_lzw_bits(self): def test_lzw_bits(self):
# see https://github.com/python-pillow/Pillow/issues/2811 # see https://github.com/python-pillow/Pillow/issues/2811
im = Image.open('Tests/images/issue_2811.gif') im = Image.open("Tests/images/issue_2811.gif")
self.assertEqual(im.tile[0][3][0], 11) # LZW bits self.assertEqual(im.tile[0][3][0], 11) # LZW bits
# codec error prepatch # codec error prepatch
im.load() im.load()
def test_extents(self): def test_extents(self):
im = Image.open('Tests/images/test_extents.gif') im = Image.open("Tests/images/test_extents.gif")
self.assertEqual(im.size, (100, 100)) self.assertEqual(im.size, (100, 100))
im.seek(1) im.seek(1)
self.assertEqual(im.size, (150, 150)) self.assertEqual(im.size, (150, 150))

View File

@ -4,7 +4,6 @@ from PIL import GimpGradientFile
class TestImage(PillowTestCase): class TestImage(PillowTestCase):
def test_linear_pos_le_middle(self): def test_linear_pos_le_middle(self):
# Arrange # Arrange
middle = 0.5 middle = 0.5
@ -96,6 +95,7 @@ class TestImage(PillowTestCase):
def test_load_via_imagepalette(self): def test_load_via_imagepalette(self):
# Arrange # Arrange
from PIL import ImagePalette from PIL import ImagePalette
test_file = "Tests/images/gimp_gradient.ggr" test_file = "Tests/images/gimp_gradient.ggr"
# Act # Act
@ -109,6 +109,7 @@ class TestImage(PillowTestCase):
def test_load_1_3_via_imagepalette(self): def test_load_1_3_via_imagepalette(self):
# Arrange # Arrange
from PIL import ImagePalette from PIL import ImagePalette
# GIMP 1.3 gradient files contain a name field # GIMP 1.3 gradient files contain a name field
test_file = "Tests/images/gimp_gradient_with_name.ggr" test_file = "Tests/images/gimp_gradient_with_name.ggr"