Drop support for EOL Python 2.7 (#4109)

Drop support for EOL Python 2.7
This commit is contained in:
Hugo van Kemenade 2019-11-20 17:23:10 +02:00 committed by GitHub
commit f90a21965a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
157 changed files with 588 additions and 1515 deletions

View File

@ -40,11 +40,6 @@ install:
- xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\ - xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\
- xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images - xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images
- cd c:\pillow\winbuild\ - cd c:\pillow\winbuild\
- ps: |
if ($env:PYTHON -eq "c:/vp/pypy2")
{
c:\pillow\winbuild\appveyor_install_pypy2.cmd
}
- ps: | - ps: |
if ($env:PYTHON -eq "c:/vp/pypy3") if ($env:PYTHON -eq "c:/vp/pypy3")
{ {

View File

@ -9,14 +9,14 @@ Please send a pull request to the master branch. Please include [documentation](
- Fork the Pillow repository. - Fork the Pillow repository.
- Create a branch from master. - Create a branch from master.
- Develop bug fixes, features, tests, etc. - Develop bug fixes, features, tests, etc.
- Run the test suite on Python 2.7 and 3.x. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests. - Run the test suite. You can enable [Travis CI](https://travis-ci.org/profile/) and [AppVeyor](https://ci.appveyor.com/projects/new) on your repo to catch test failures prior to the pull request, and [Codecov](https://codecov.io/gh) to see if the changed code is covered by tests.
- Create a pull request to pull the changes from your branch to the Pillow master. - Create a pull request to pull the changes from your branch to the Pillow master.
### Guidelines ### Guidelines
- Separate code commits from reformatting commits. - Separate code commits from reformatting commits.
- Provide tests for any newly added code. - Provide tests for any newly added code.
- Follow PEP8. - Follow PEP 8.
- When committing only documentation changes please include [ci skip] in the commit message to avoid running tests on Travis-CI and AppVeyor. - When committing only documentation changes please include [ci skip] in the commit message to avoid running tests on Travis-CI and AppVeyor.
## Reporting Issues ## Reporting Issues

View File

@ -6,8 +6,8 @@ notifications:
irc: "chat.freenode.net#pil" irc: "chat.freenode.net#pil"
# Run fast lint first to get fast feedback. # Run fast lint first to get fast feedback.
# Run slow PyPy* next, to give them a headstart and reduce waiting time. # Run slow PyPy next, to give it a headstart and reduce waiting time.
# Run latest 3.x and 2.x next, to get quick compatibility results. # Run latest 3.x next, to get quick compatibility results.
# Then run the remainder, with fastest Docker jobs last. # Then run the remainder, with fastest Docker jobs last.
matrix: matrix:

View File

@ -11,16 +11,12 @@ coveralls-lcov -v -n coverage.filtered.info > coverage.c.json
coverage report coverage report
pip install codecov pip install codecov
if [[ $TRAVIS_PYTHON_VERSION != "2.7_with_system_site_packages" ]]; then pip install coveralls-merge
# Not working here. Just skip it, it's being removed soon. coveralls-merge coverage.c.json
pip install coveralls-merge
coveralls-merge coverage.c.json
fi
codecov codecov
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ] && [ "$DOCKER" == "" ]; then if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ] && [ "$DOCKER" == "" ]; then
# Coverage and quality reports on just the latest diff. # Coverage and quality reports on just the latest diff.
# (Installation is very slow on Py3, so just do it for Py2.)
depends/diffcover-install.sh depends/diffcover-install.sh
depends/diffcover-run.sh depends/diffcover-run.sh
fi fi

View File

@ -20,8 +20,8 @@ if [[ $TRAVIS_PYTHON_VERSION == 3.* ]]; then
pip install pyqt5 pip install pyqt5
fi fi
# docs only on Python 2.7 # docs only on Python 3.7
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then pip install -r requirements.txt ; fi if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ]; then pip install -r requirements.txt ; fi
# webp # webp
pushd depends && ./install_webp.sh && popd pushd depends && ./install_webp.sh && popd

View File

@ -5,4 +5,4 @@ set -e
python -m pytest -v -x --cov PIL --cov-report term Tests python -m pytest -v -x --cov PIL --cov-report term Tests
# Docs # Docs
if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then make doccheck; fi if [ "$TRAVIS_PYTHON_VERSION" == "3.7" ]; then make doccheck; fi

View File

@ -3,7 +3,7 @@
.DEFAULT_GOAL := release-test .DEFAULT_GOAL := release-test
clean: clean:
python setup.py clean python3 setup.py clean
rm src/PIL/*.so || true rm src/PIL/*.so || true
rm -r build || true rm -r build || true
find . -name __pycache__ | xargs rm -r || true find . -name __pycache__ | xargs rm -r || true
@ -15,8 +15,8 @@ co:
done done
coverage: coverage:
python selftest.py python3 selftest.py
python setup.py test python3 setup.py test
rm -r htmlcov || true rm -r htmlcov || true
coverage report coverage report
@ -30,7 +30,7 @@ doccheck:
$(MAKE) -C docs linkcheck || true $(MAKE) -C docs linkcheck || true
docserve: docserve:
cd docs/_build/html && python -mSimpleHTTPServer 2> /dev/null& cd docs/_build/html && python3 -mSimpleHTTPServer 2> /dev/null&
help: help:
@echo "Welcome to Pillow development. Please use \`make <target>\` where <target> is one of" @echo "Welcome to Pillow development. Please use \`make <target>\` where <target> is one of"
@ -50,22 +50,22 @@ help:
@echo " upload-test build and upload sdists to test.pythonpackages.com" @echo " upload-test build and upload sdists to test.pythonpackages.com"
inplace: clean inplace: clean
python setup.py develop build_ext --inplace python3 setup.py develop build_ext --inplace
install: install:
python setup.py install python3 setup.py install
python selftest.py python3 selftest.py
install-coverage: install-coverage:
CFLAGS="-coverage" python setup.py build_ext install CFLAGS="-coverage" python3 setup.py build_ext install
python selftest.py python3 selftest.py
debug: debug:
# make a debug version if we don't have a -dbg python. Leaves in symbols # make a debug version if we don't have a -dbg python. Leaves in symbols
# for our stuff, kills optimization, and redirects to dev null so we # for our stuff, kills optimization, and redirects to dev null so we
# see any build failures. # see any build failures.
make clean > /dev/null make clean > /dev/null
CFLAGS='-g -O0' python setup.py build_ext install > /dev/null CFLAGS='-g -O0' python3 setup.py build_ext install > /dev/null
install-req: install-req:
pip install -r requirements.txt pip install -r requirements.txt
@ -76,17 +76,17 @@ install-venv:
release-test: release-test:
$(MAKE) install-req $(MAKE) install-req
python setup.py develop python3 setup.py develop
python selftest.py python3 selftest.py
python -m pytest Tests python3 -m pytest Tests
python setup.py install python3 setup.py install
python -m pytest -qq python3 -m pytest -qq
check-manifest check-manifest
pyroma . pyroma .
viewdoc viewdoc
sdist: sdist:
python setup.py sdist --format=gztar python3 setup.py sdist --format=gztar
test: test:
pytest -qq pytest -qq
@ -97,10 +97,10 @@ upload-test:
# username: # username:
# password: # password:
# repository = http://test.pythonpackages.com # repository = http://test.pythonpackages.com
python setup.py sdist --format=gztar upload -r test python3 setup.py sdist --format=gztar upload -r test
upload: upload:
python setup.py sdist --format=gztar upload python3 setup.py sdist --format=gztar upload
readme: readme:
viewdoc viewdoc

View File

@ -1,7 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python
from __future__ import division
from PIL import Image from PIL import Image
from .helper import PillowTestCase, is_win32, unittest from .helper import PillowTestCase, is_win32, unittest

View File

@ -1,6 +1,4 @@
#!/usr/bin/env python #!/usr/bin/env python
from __future__ import print_function
import base64 import base64
import os import os

View File

@ -1,7 +1,6 @@
""" """
Helper functions. Helper functions.
""" """
from __future__ import print_function
import logging import logging
import os import os
@ -12,7 +11,6 @@ import unittest
from io import BytesIO from io import BytesIO
from PIL import Image, ImageMath from PIL import Image, ImageMath
from PIL._util import py3
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -76,10 +74,13 @@ 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), msg or "got length %s, expected %s" % (len(a), len(b)) len(a),
len(b),
msg or "got length {}, expected {}".format(len(a), len(b)),
) )
self.assertTrue( self.assertTrue(
all(x == y for x, y in zip(a, b)), msg or "got %s, expected %s" % (a, b) all(x == y for x, y in zip(a, b)),
msg or "got {}, expected {}".format(a, b),
) )
except Exception: except Exception:
self.assertEqual(a, b, msg) self.assertEqual(a, b, msg)
@ -87,20 +88,24 @@ class PillowTestCase(unittest.TestCase):
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, msg or "got mode %r, expected %r" % (im.mode, mode) im.mode,
mode,
msg or "got mode {!r}, expected {!r}".format(im.mode, mode),
) )
if size is not None: if size is not None:
self.assertEqual( self.assertEqual(
im.size, size, msg or "got size %r, expected %r" % (im.size, size) im.size,
size,
msg or "got size {!r}, expected {!r}".format(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, msg or "got mode %r, expected %r" % (a.mode, b.mode) a.mode, b.mode, msg or "got mode {!r}, expected {!r}".format(a.mode, b.mode)
) )
self.assertEqual( self.assertEqual(
a.size, b.size, msg or "got size %r, expected %r" % (a.size, b.size) a.size, b.size, msg or "got size {!r}, expected {!r}".format(a.size, b.size)
) )
if a.tobytes() != b.tobytes(): if a.tobytes() != b.tobytes():
if HAS_UPLOADER: if HAS_UPLOADER:
@ -121,10 +126,10 @@ 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, msg or "got mode %r, expected %r" % (a.mode, b.mode) a.mode, b.mode, msg or "got mode {!r}, expected {!r}".format(a.mode, b.mode)
) )
self.assertEqual( self.assertEqual(
a.size, b.size, msg or "got size %r, expected %r" % (a.size, b.size) a.size, b.size, msg or "got size {!r}, expected {!r}".format(a.size, b.size)
) )
a, b = convert_to_comparable(a, b) a, b = convert_to_comparable(a, b)
@ -216,12 +221,12 @@ class PillowTestCase(unittest.TestCase):
def open_withImagemagick(self, f): def open_withImagemagick(self, f):
if not imagemagick_available(): if not imagemagick_available():
raise IOError() raise OSError()
outfile = self.tempfile("temp.png") outfile = self.tempfile("temp.png")
if command_succeeds([IMCONVERT, f, outfile]): if command_succeeds([IMCONVERT, f, outfile]):
return Image.open(outfile) return Image.open(outfile)
raise IOError() raise OSError()
@unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS") @unittest.skipIf(sys.platform.startswith("win32"), "requires Unix or macOS")
@ -264,11 +269,6 @@ class PillowLeakTestCase(PillowTestCase):
# helpers # helpers
if not py3:
# Remove DeprecationWarning in Python 3
PillowTestCase.assertRaisesRegex = PillowTestCase.assertRaisesRegexp
PillowTestCase.assertRegex = PillowTestCase.assertRegexpMatches
def fromstring(data): def fromstring(data):
return Image.open(BytesIO(data)) return Image.open(BytesIO(data))
@ -306,11 +306,10 @@ def command_succeeds(cmd):
Runs the command, which must be a list of strings. Returns True if the Runs the command, which must be a list of strings. Returns True if the
command succeeds, or False if an OSError was raised by subprocess.Popen. command succeeds, or False if an OSError was raised by subprocess.Popen.
""" """
with open(os.devnull, "wb") as f: try:
try: subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
subprocess.call(cmd, stdout=f, stderr=subprocess.STDOUT) except OSError:
except OSError: return False
return False
return True return True
@ -373,7 +372,7 @@ def distro():
return line.strip().split("=")[1] return line.strip().split("=")[1]
class cached_property(object): class cached_property:
def __init__(self, func): def __init__(self, func):
self.func = func self.func = func

View File

@ -1,5 +1,3 @@
from __future__ import print_function
import glob import glob
import os import os
import sys import sys

View File

@ -1,7 +1,5 @@
# brute-force search for access descriptor hash table # brute-force search for access descriptor hash table
from __future__ import print_function
modes = [ modes = [
"1", "1",
"L", "L",

View File

@ -1,5 +1,3 @@
from __future__ import print_function
import os import os
from PIL import Image from PIL import Image
@ -109,4 +107,4 @@ class TestBmpReference(PillowTestCase):
os.path.join(base, "g", "pal4rle.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 {}: {}".format(f, msg))

View File

@ -1,5 +1,3 @@
from __future__ import division
from array import array from array import array
from PIL import Image, ImageFilter from PIL import Image, ImageFilter

View File

@ -1,5 +1,3 @@
from __future__ import division, print_function
import sys import sys
from PIL import Image from PIL import Image

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import io import io
from PIL import features from PIL import features

View File

@ -91,7 +91,7 @@ class TestFileEps(PillowTestCase):
def test_iobase_object(self): def test_iobase_object(self):
# issue 479 # issue 479
with Image.open(file1) as image1: with Image.open(file1) as image1:
with io.open(self.tempfile("temp_iobase.eps"), "wb") as fh: with open(self.tempfile("temp_iobase.eps"), "wb") as fh:
image1.save(fh, "EPS") image1.save(fh, "EPS")
@unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available") @unittest.skipUnless(HAS_GHOSTSCRIPT, "Ghostscript not available")

View File

@ -87,9 +87,7 @@ class TestFileGif(PillowTestCase):
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( im = Image.frombytes(
"P", "P", (colors, colors), bytes(range(256 - colors, 256)) * colors
(colors, colors),
bytes(bytearray(range(256 - colors, 256)) * colors),
) )
im = im.resize((size, size)) im = im.resize((size, size))
outfile = BytesIO() outfile = BytesIO()
@ -119,7 +117,7 @@ class TestFileGif(PillowTestCase):
check(256, 511, 256) check(256, 511, 256)
def test_optimize_full_l(self): def test_optimize_full_l(self):
im = Image.frombytes("L", (16, 16), bytes(bytearray(range(256)))) im = Image.frombytes("L", (16, 16), bytes(range(256)))
test_file = BytesIO() test_file = BytesIO()
im.save(test_file, "GIF", optimize=True) im.save(test_file, "GIF", optimize=True)
self.assertEqual(im.mode, "L") self.assertEqual(im.mode, "L")
@ -632,8 +630,7 @@ class TestFileGif(PillowTestCase):
# Tests appending using a generator # Tests appending using a generator
def imGenerator(ims): def imGenerator(ims):
for im in ims: yield from ims
yield im
im.save(out, save_all=True, append_images=imGenerator(ims)) im.save(out, save_all=True, append_images=imGenerator(ims))
@ -655,7 +652,7 @@ class TestFileGif(PillowTestCase):
# that's > 128 items where the transparent color is actually # that's > 128 items where the transparent color is actually
# the top palette entry to trigger the bug. # the top palette entry to trigger the bug.
data = bytes(bytearray(range(1, 254))) data = bytes(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))
@ -703,7 +700,7 @@ class TestFileGif(PillowTestCase):
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(im.getpalette())
out = self.tempfile("temp.gif") out = self.tempfile("temp.gif")
im_l.save(out, palette=palette) im_l.save(out, palette=palette)
@ -718,7 +715,7 @@ class TestFileGif(PillowTestCase):
# 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([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)
@ -759,7 +756,7 @@ class TestFileGif(PillowTestCase):
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([255 - i // 3 for i in range(768)])
GifImagePlugin._FORCE_OPTIMIZE = True GifImagePlugin._FORCE_OPTIMIZE = True
try: try:

View File

@ -1,3 +1,6 @@
import sys
from io import StringIO
from PIL import Image, IptcImagePlugin from PIL import Image, IptcImagePlugin
from .helper import PillowTestCase, hopper from .helper import PillowTestCase, hopper
@ -52,12 +55,6 @@ class TestFileIptc(PillowTestCase):
# Arrange # Arrange
c = b"abc" c = b"abc"
# Temporarily redirect stdout # Temporarily redirect stdout
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
import sys
old_stdout = sys.stdout old_stdout = sys.stdout
sys.stdout = mystdout = StringIO() sys.stdout = mystdout = StringIO()

View File

@ -1,5 +1,3 @@
from __future__ import print_function
import base64 import base64
import distutils.version import distutils.version
import io import io
@ -10,7 +8,6 @@ from collections import namedtuple
from ctypes import c_float from ctypes import c_float
from PIL import Image, TiffImagePlugin, TiffTags, features from PIL import Image, TiffImagePlugin, TiffTags, features
from PIL._util import py3
from .helper import PillowTestCase, hopper from .helper import PillowTestCase, hopper
@ -263,7 +260,6 @@ class TestFileLibTiff(LibTiffTestCase):
tc(4.25, TiffTags.FLOAT, True), tc(4.25, TiffTags.FLOAT, True),
tc(4.25, TiffTags.DOUBLE, True), tc(4.25, TiffTags.DOUBLE, True),
tc("custom tag value", TiffTags.ASCII, True), tc("custom tag value", TiffTags.ASCII, True),
tc(u"custom tag value", TiffTags.ASCII, True),
tc(b"custom tag value", TiffTags.BYTE, True), tc(b"custom tag value", TiffTags.BYTE, True),
tc((4, 5, 6), TiffTags.SHORT, True), tc((4, 5, 6), TiffTags.SHORT, True),
tc((123456789, 9, 34, 234, 219387, 92432323), TiffTags.LONG, True), tc((123456789, 9, 34, 234, 219387, 92432323), TiffTags.LONG, True),
@ -361,12 +357,8 @@ class TestFileLibTiff(LibTiffTestCase):
b = im.tobytes() b = im.tobytes()
# Bytes are in image native order (little endian) # Bytes are in image native order (little endian)
if py3: self.assertEqual(b[0], ord(b"\xe0"))
self.assertEqual(b[0], ord(b"\xe0")) self.assertEqual(b[1], ord(b"\x01"))
self.assertEqual(b[1], ord(b"\x01"))
else:
self.assertEqual(b[0], b"\xe0")
self.assertEqual(b[1], b"\x01")
out = self.tempfile("temp.tif") out = self.tempfile("temp.tif")
# out = "temp.le.tif" # out = "temp.le.tif"
@ -387,12 +379,8 @@ class TestFileLibTiff(LibTiffTestCase):
b = im.tobytes() b = im.tobytes()
# Bytes are in image native order (big endian) # Bytes are in image native order (big endian)
if py3: self.assertEqual(b[0], ord(b"\x01"))
self.assertEqual(b[0], ord(b"\x01")) self.assertEqual(b[1], ord(b"\xe0"))
self.assertEqual(b[1], ord(b"\xe0"))
else:
self.assertEqual(b[0], b"\x01")
self.assertEqual(b[1], b"\xe0")
out = self.tempfile("temp.tif") out = self.tempfile("temp.tif")
im.save(out) im.save(out)

View File

@ -99,8 +99,7 @@ class TestFilePdf(PillowTestCase):
# Test appending using a generator # Test appending using a generator
def imGenerator(ims): def imGenerator(ims):
for im in ims: yield from ims
yield im
im.save(outfile, save_all=True, append_images=imGenerator(ims)) im.save(outfile, save_all=True, append_images=imGenerator(ims))
@ -163,13 +162,10 @@ class TestFilePdf(PillowTestCase):
def test_pdf_append_fails_on_nonexistent_file(self): def test_pdf_append_fails_on_nonexistent_file(self):
im = hopper("RGB") im = hopper("RGB")
temp_dir = tempfile.mkdtemp() with tempfile.TemporaryDirectory() as temp_dir:
try:
self.assertRaises( self.assertRaises(
IOError, im.save, os.path.join(temp_dir, "nonexistent.pdf"), append=True IOError, im.save, os.path.join(temp_dir, "nonexistent.pdf"), append=True
) )
finally:
os.rmdir(temp_dir)
def check_pdf_pages_consistency(self, pdf): def check_pdf_pages_consistency(self, pdf):
pages_info = pdf.read_indirect(pdf.pages_ref) pages_info = pdf.read_indirect(pdf.pages_ref)
@ -207,7 +203,7 @@ class TestFilePdf(PillowTestCase):
# append some info # append some info
pdf.info.Title = "abc" pdf.info.Title = "abc"
pdf.info.Author = "def" pdf.info.Author = "def"
pdf.info.Subject = u"ghi\uABCD" pdf.info.Subject = "ghi\uABCD"
pdf.info.Keywords = "qw)e\\r(ty" pdf.info.Keywords = "qw)e\\r(ty"
pdf.info.Creator = "hopper()" pdf.info.Creator = "hopper()"
pdf.start_writing() pdf.start_writing()
@ -235,7 +231,7 @@ class TestFilePdf(PillowTestCase):
self.assertEqual(pdf.info.Title, "abc") self.assertEqual(pdf.info.Title, "abc")
self.assertEqual(pdf.info.Producer, "PdfParser") self.assertEqual(pdf.info.Producer, "PdfParser")
self.assertEqual(pdf.info.Keywords, "qw)e\\r(ty") self.assertEqual(pdf.info.Keywords, "qw)e\\r(ty")
self.assertEqual(pdf.info.Subject, u"ghi\uABCD") self.assertEqual(pdf.info.Subject, "ghi\uABCD")
self.assertIn(b"CreationDate", pdf.info) self.assertIn(b"CreationDate", pdf.info)
self.assertIn(b"ModDate", pdf.info) self.assertIn(b"ModDate", pdf.info)
self.check_pdf_pages_consistency(pdf) self.check_pdf_pages_consistency(pdf)

View File

@ -2,7 +2,6 @@ import zlib
from io import BytesIO from io import BytesIO
from PIL import Image, ImageFile, PngImagePlugin from PIL import Image, ImageFile, PngImagePlugin
from PIL._util import py3
from .helper import PillowLeakTestCase, PillowTestCase, hopper, is_win32, unittest from .helper import PillowLeakTestCase, PillowTestCase, hopper, is_win32, unittest
@ -448,9 +447,7 @@ class TestFilePng(PillowTestCase):
self.assertIsInstance(im.info["Text"], str) self.assertIsInstance(im.info["Text"], str)
def test_unicode_text(self): def test_unicode_text(self):
# Check preservation of non-ASCII characters on Python 3 # Check preservation of non-ASCII characters
# This cannot really be meaningfully tested on Python 2,
# since it didn't preserve charsets to begin with.
def rt_text(value): def rt_text(value):
im = Image.new("RGB", (32, 32)) im = Image.new("RGB", (32, 32))
@ -459,17 +456,11 @@ class TestFilePng(PillowTestCase):
im = roundtrip(im, pnginfo=info) im = roundtrip(im, pnginfo=info)
self.assertEqual(im.info, {"Text": value}) self.assertEqual(im.info, {"Text": value})
if py3: rt_text(" Aa" + chr(0xA0) + chr(0xC4) + chr(0xFF)) # Latin1
rt_text(" Aa" + chr(0xA0) + chr(0xC4) + chr(0xFF)) # Latin1 rt_text(chr(0x400) + chr(0x472) + chr(0x4FF)) # Cyrillic
rt_text(chr(0x400) + chr(0x472) + chr(0x4FF)) # Cyrillic # CJK:
rt_text( rt_text(chr(0x4E00) + chr(0x66F0) + chr(0x9FBA) + chr(0x3042) + chr(0xAC00))
chr(0x4E00) rt_text("A" + chr(0xC4) + chr(0x472) + chr(0x3042)) # Combined
+ chr(0x66F0)
+ chr(0x9FBA) # CJK
+ chr(0x3042)
+ chr(0xAC00)
)
rt_text("A" + chr(0xC4) + chr(0x472) + chr(0x3042)) # Combined
def test_scary(self): def test_scary(self):
# Check reading of evil PNG file. For information, see: # Check reading of evil PNG file. For information, see:

View File

@ -4,7 +4,6 @@ from io import BytesIO
import pytest import pytest
from PIL import Image, TiffImagePlugin from PIL import Image, TiffImagePlugin
from PIL._util import py3
from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION from PIL.TiffImagePlugin import RESOLUTION_UNIT, X_RESOLUTION, Y_RESOLUTION
from .helper import PillowTestCase, hopper, is_pypy, is_win32, unittest from .helper import PillowTestCase, hopper, is_pypy, is_win32, unittest
@ -198,12 +197,8 @@ class TestFileTiff(PillowTestCase):
b = im.tobytes() b = im.tobytes()
# Bytes are in image native order (little endian) # Bytes are in image native order (little endian)
if py3: self.assertEqual(b[0], ord(b"\xe0"))
self.assertEqual(b[0], ord(b"\xe0")) self.assertEqual(b[1], ord(b"\x01"))
self.assertEqual(b[1], ord(b"\x01"))
else:
self.assertEqual(b[0], b"\xe0")
self.assertEqual(b[1], b"\x01")
def test_big_endian(self): def test_big_endian(self):
im = Image.open("Tests/images/16bit.MM.cropped.tif") im = Image.open("Tests/images/16bit.MM.cropped.tif")
@ -213,12 +208,8 @@ class TestFileTiff(PillowTestCase):
b = im.tobytes() b = im.tobytes()
# Bytes are in image native order (big endian) # Bytes are in image native order (big endian)
if py3: self.assertEqual(b[0], ord(b"\x01"))
self.assertEqual(b[0], ord(b"\x01")) self.assertEqual(b[1], ord(b"\xe0"))
self.assertEqual(b[1], ord(b"\xe0"))
else:
self.assertEqual(b[0], b"\x01")
self.assertEqual(b[1], b"\xe0")
def test_16bit_s(self): def test_16bit_s(self):
im = Image.open("Tests/images/16bit.s.tif") im = Image.open("Tests/images/16bit.s.tif")
@ -545,8 +536,7 @@ class TestFileTiff(PillowTestCase):
# Test appending using a generator # Test appending using a generator
def imGenerator(ims): def imGenerator(ims):
for im in ims: yield from ims
yield im
mp = BytesIO() mp = BytesIO()
im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims)) im.save(mp, format="TIFF", save_all=True, append_images=imGenerator(ims))

View File

@ -161,13 +161,13 @@ class TestFileTiffMetadata(PillowTestCase):
self.assert_deep_equal( self.assert_deep_equal(
original[tag], original[tag],
value, value,
"%s didn't roundtrip, %s, %s" % (tag, original[tag], value), "{} didn't roundtrip, {}, {}".format(tag, original[tag], value),
) )
else: else:
self.assertEqual( self.assertEqual(
original[tag], original[tag],
value, value,
"%s didn't roundtrip, %s, %s" % (tag, original[tag], value), "{} didn't roundtrip, {}, {}".format(tag, original[tag], value),
) )
for tag, value in original.items(): for tag, value in original.items():

View File

@ -91,8 +91,7 @@ class TestFileWebpAnimation(PillowTestCase):
# Tests appending using a generator # Tests appending using a generator
def imGenerator(ims): def imGenerator(ims):
for im in ims: yield from ims
yield im
temp_file2 = self.tempfile("temp_generator.webp") temp_file2 = self.tempfile("temp_generator.webp")
frame1.copy().save( frame1.copy().save(

View File

@ -110,9 +110,9 @@ class TestFileWebpMetadata(PillowTestCase):
if not _webp.HAVE_WEBPANIM: if not _webp.HAVE_WEBPANIM:
self.skipTest("WebP animation support not available") self.skipTest("WebP animation support not available")
iccp_data = "<iccp_data>".encode("utf-8") iccp_data = b"<iccp_data>"
exif_data = "<exif_data>".encode("utf-8") exif_data = b"<exif_data>"
xmp_data = "<xmp_data>".encode("utf-8") xmp_data = b"<xmp_data>"
temp_file = self.tempfile("temp.webp") temp_file = self.tempfile("temp.webp")
frame1 = Image.open("Tests/images/anim_frame1.webp") frame1 = Image.open("Tests/images/anim_frame1.webp")

View File

@ -1,5 +1,3 @@
from __future__ import division
from PIL import Image, ImageDraw, ImageFont, features from PIL import Image, ImageDraw, ImageFont, features
from .helper import PillowLeakTestCase, is_win32, unittest from .helper import PillowLeakTestCase, is_win32, unittest

View File

@ -1,5 +1,4 @@
from PIL import FontFile, Image, ImageDraw, ImageFont, PcfFontFile from PIL import FontFile, Image, ImageDraw, ImageFont, PcfFontFile
from PIL._util import py3
from .helper import PillowTestCase from .helper import PillowTestCase
@ -74,6 +73,5 @@ class TestFontPcf(PillowTestCase):
def test_high_characters(self): def test_high_characters(self):
message = "".join(chr(i + 1) for i in range(140, 232)) message = "".join(chr(i + 1) for i in range(140, 232))
self._test_high_characters(message) self._test_high_characters(message)
# accept bytes instances in Py3. # accept bytes instances.
if py3: self._test_high_characters(message.encode("latin1"))
self._test_high_characters(message.encode("latin1"))

View File

@ -2,7 +2,6 @@ import colorsys
import itertools import itertools
from PIL import Image from PIL import Image
from PIL._util import py3
from .helper import PillowTestCase, hopper from .helper import PillowTestCase, hopper
@ -49,27 +48,18 @@ class TestFormatHSV(PillowTestCase):
(r, g, b) = im.split() (r, g, b) = im.split()
if py3: conv_func = self.int_to_float
conv_func = self.int_to_float
else:
conv_func = self.str_to_float
if hasattr(itertools, "izip"):
iter_helper = itertools.izip
else:
iter_helper = itertools.zip_longest
converted = [ converted = [
self.tuple_to_ints(func(conv_func(_r), conv_func(_g), conv_func(_b))) self.tuple_to_ints(func(conv_func(_r), conv_func(_g), conv_func(_b)))
for (_r, _g, _b) in iter_helper(r.tobytes(), g.tobytes(), b.tobytes()) for (_r, _g, _b) in itertools.zip_longest(
r.tobytes(), g.tobytes(), b.tobytes()
)
] ]
if py3: new_bytes = b"".join(
new_bytes = b"".join( bytes(chr(h) + chr(s) + chr(v), "latin-1") for (h, s, v) in converted
bytes(chr(h) + chr(s) + chr(v), "latin-1") for (h, s, v) in converted )
)
else:
new_bytes = b"".join(chr(h) + chr(s) + chr(v) for (h, s, v) in converted)
hsv = Image.frombytes(mode, r.size, new_bytes) hsv = Image.frombytes(mode, r.size, new_bytes)

View File

@ -3,7 +3,6 @@ import shutil
import tempfile import tempfile
from PIL import Image, UnidentifiedImageError from PIL import Image, UnidentifiedImageError
from PIL._util import py3
from .helper import PillowTestCase, hopper, is_win32, unittest from .helper import PillowTestCase, hopper, is_win32, unittest
@ -83,20 +82,14 @@ class TestImage(PillowTestCase):
im.size = (3, 4) im.size = (3, 4)
def test_invalid_image(self): def test_invalid_image(self):
if py3: import io
import io
im = io.BytesIO(b"") im = io.BytesIO(b"")
else:
import StringIO
im = StringIO.StringIO("")
self.assertRaises(UnidentifiedImageError, Image.open, im) self.assertRaises(UnidentifiedImageError, Image.open, im)
def test_bad_mode(self): def test_bad_mode(self):
self.assertRaises(ValueError, Image.open, "filename", "bad mode") self.assertRaises(ValueError, Image.open, "filename", "bad mode")
@unittest.skipUnless(Image.HAS_PATHLIB, "requires pathlib/pathlib2")
def test_pathlib(self): def test_pathlib(self):
from PIL.Image import Path from PIL.Image import Path
@ -116,7 +109,7 @@ class TestImage(PillowTestCase):
def test_fp_name(self): def test_fp_name(self):
temp_file = self.tempfile("temp.jpg") temp_file = self.tempfile("temp.jpg")
class FP(object): class FP:
def write(a, b): def write(a, b):
pass pass
@ -595,11 +588,11 @@ class TestImage(PillowTestCase):
try: try:
im.load() im.load()
self.assertFail() self.assertFail()
except IOError as e: except OSError as e:
self.assertEqual(str(e), "buffer overrun when reading image file") self.assertEqual(str(e), "buffer overrun when reading image file")
class MockEncoder(object): class MockEncoder:
pass pass

View File

@ -125,7 +125,7 @@ class TestImageGetPixel(AccessTest):
self.assertEqual( self.assertEqual(
im.getpixel((0, 0)), im.getpixel((0, 0)),
c, c,
"put/getpixel roundtrip failed for mode %s, color %s" % (mode, c), "put/getpixel roundtrip failed for mode {}, color {}".format(mode, c),
) )
# check putpixel negative index # check putpixel negative index
@ -154,7 +154,7 @@ class TestImageGetPixel(AccessTest):
self.assertEqual( self.assertEqual(
im.getpixel((0, 0)), im.getpixel((0, 0)),
c, c,
"initial color failed for mode %s, color %s " % (mode, c), "initial color failed for mode {}, color {} ".format(mode, c),
) )
# check initial color negative index # check initial color negative index
self.assertEqual( self.assertEqual(
@ -348,12 +348,8 @@ class TestEmbeddable(unittest.TestCase):
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
char *home = "%s"; char *home = "%s";
#if PY_MAJOR_VERSION >= 3
wchar_t *whome = Py_DecodeLocale(home, NULL); wchar_t *whome = Py_DecodeLocale(home, NULL);
Py_SetPythonHome(whome); Py_SetPythonHome(whome);
#else
Py_SetPythonHome(home);
#endif
Py_InitializeEx(0); Py_InitializeEx(0);
Py_DECREF(PyImport_ImportModule("PIL.Image")); Py_DECREF(PyImport_ImportModule("PIL.Image"));
@ -363,9 +359,7 @@ int main(int argc, char* argv[])
Py_DECREF(PyImport_ImportModule("PIL.Image")); Py_DECREF(PyImport_ImportModule("PIL.Image"));
Py_Finalize(); Py_Finalize();
#if PY_MAJOR_VERSION >= 3
PyMem_RawFree(whome); PyMem_RawFree(whome);
#endif
return 0; return 0;
} }

View File

@ -25,7 +25,7 @@ class TestImageArray(PillowTestCase):
self.assertEqual(test("RGBX"), (3, (100, 128, 4), "|u1", 51200)) self.assertEqual(test("RGBX"), (3, (100, 128, 4), "|u1", 51200))
def test_fromarray(self): def test_fromarray(self):
class Wrapper(object): class Wrapper:
""" Class with API matching Image.fromarray """ """ Class with API matching Image.fromarray """
def __init__(self, img, arr_params): def __init__(self, img, arr_params):

View File

@ -6,7 +6,7 @@ from .test_imageqt import PillowQtTestCase
class TestFromQImage(PillowQtTestCase, PillowTestCase): class TestFromQImage(PillowQtTestCase, PillowTestCase):
def setUp(self): def setUp(self):
super(TestFromQImage, self).setUp() super().setUp()
self.files_to_test = [ self.files_to_test = [
hopper(), hopper(),
Image.open("Tests/images/transparent.png"), Image.open("Tests/images/transparent.png"),

View File

@ -1,5 +1,3 @@
from PIL._util import py3
from .helper import PillowTestCase, hopper from .helper import PillowTestCase, hopper
@ -8,7 +6,5 @@ class TestImageGetIm(PillowTestCase):
im = hopper() im = hopper()
type_repr = repr(type(im.getim())) type_repr = repr(type(im.getim()))
if py3: self.assertIn("PyCapsule", type_repr)
self.assertIn("PyCapsule", type_repr)
self.assertIsInstance(im.im.id, int) self.assertIsInstance(im.im.id, int)

View File

@ -1,5 +1,3 @@
from __future__ import division, print_function
from contextlib import contextmanager from contextlib import contextmanager
from PIL import Image, ImageDraw from PIL import Image, ImageDraw

View File

@ -35,7 +35,7 @@ class TestImageEnhance(PillowTestCase):
self.assert_image_equal( self.assert_image_equal(
im.getchannel("A"), im.getchannel("A"),
original.getchannel("A"), original.getchannel("A"),
"Diff on %s: %s" % (op, amount), "Diff on {}: {}".format(op, amount),
) )
def test_alpha(self): def test_alpha(self):

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
import copy import copy
import distutils.version import distutils.version
import os import os
@ -20,7 +19,7 @@ HAS_FREETYPE = features.check("freetype2")
HAS_RAQM = features.check("raqm") HAS_RAQM = features.check("raqm")
class SimplePatcher(object): class SimplePatcher:
def __init__(self, parent_obj, attr_name, value): def __init__(self, parent_obj, attr_name, value):
self._parent_obj = parent_obj self._parent_obj = parent_obj
self._attr_name = attr_name self._attr_name = attr_name
@ -462,12 +461,12 @@ class TestImageFont(PillowTestCase):
# issue #2826 # issue #2826
font = ImageFont.load_default() font = ImageFont.load_default()
with self.assertRaises(UnicodeEncodeError): with self.assertRaises(UnicodeEncodeError):
font.getsize(u"") font.getsize("")
@unittest.skipIf(sys.version.startswith("2") or is_pypy(), "requires CPython 3.3+") @unittest.skipIf(is_pypy(), "requires CPython")
def test_unicode_extended(self): def test_unicode_extended(self):
# issue #3777 # issue #3777
text = u"A\u278A\U0001F12B" text = "A\u278A\U0001F12B"
target = "Tests/images/unicode_extended.png" target = "Tests/images/unicode_extended.png"
ttf = ImageFont.truetype( ttf = ImageFont.truetype(

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
from PIL import Image, ImageDraw, ImageFont, features from PIL import Image, ImageDraw, ImageFont, features
from .helper import PillowTestCase, unittest from .helper import PillowTestCase, unittest

View File

@ -1,5 +1,3 @@
from __future__ import print_function
from PIL import Image, ImageMath from PIL import Image, ImageMath
from .helper import PillowTestCase from .helper import PillowTestCase
@ -7,7 +5,7 @@ from .helper import PillowTestCase
def pixel(im): def pixel(im):
if hasattr(im, "im"): if hasattr(im, "im"):
return "%s %r" % (im.mode, im.getpixel((0, 0))) return "{} {!r}".format(im.mode, im.getpixel((0, 0)))
else: else:
if isinstance(im, int): if isinstance(im, int):
return int(im) # hack to deal with booleans return int(im) # hack to deal with booleans

View File

@ -11,7 +11,7 @@ except ImportError:
class TestImageOps(PillowTestCase): class TestImageOps(PillowTestCase):
class Deformer(object): class Deformer:
def getmesh(self, im): def getmesh(self, im):
x, y = im.size x, y = im.size
return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))] return [((0, 0, x, y), (0, 0, x, 0, x, y, y, 0))]

View File

@ -5,7 +5,7 @@ from .helper import PillowTestCase
class TestImageOpsUsm(PillowTestCase): class TestImageOpsUsm(PillowTestCase):
def setUp(self): def setUp(self):
super(TestImageOpsUsm, self).setUp() super().setUp()
self.im = Image.open("Tests/images/hopper.ppm") self.im = Image.open("Tests/images/hopper.ppm")
self.addCleanup(self.im.close) self.addCleanup(self.im.close)
self.snakes = Image.open("Tests/images/color_snakes.png") self.snakes = Image.open("Tests/images/color_snakes.png")

View File

@ -2,7 +2,6 @@ import array
import struct import struct
from PIL import Image, ImagePath from PIL import Image, ImagePath
from PIL._util import py3
from .helper import PillowTestCase from .helper import PillowTestCase
@ -75,10 +74,7 @@ class TestImagePath(PillowTestCase):
# This fails due to the invalid malloc above, # This fails due to the invalid malloc above,
# and segfaults # and segfaults
for i in range(200000): for i in range(200000):
if py3: x[i] = b"0" * 16
x[i] = b"0" * 16
else:
x[i] = "0" * 16
class evil: class evil:

View File

@ -15,7 +15,7 @@ else:
test_case.skipTest("Qt bindings are not installed") test_case.skipTest("Qt bindings are not installed")
class PillowQtTestCase(object): class PillowQtTestCase:
def setUp(self): def setUp(self):
skip_if_qt_is_not_installed(self) skip_if_qt_is_not_installed(self)

View File

@ -1,15 +1,12 @@
from PIL import Image from PIL import Image
from PIL._util import py3
from .helper import PillowTestCase, hopper, unittest from .helper import PillowTestCase, hopper, unittest
try: try:
from PIL import ImageTk from PIL import ImageTk
if py3: import tkinter as tk
import tkinter as tk
else:
import Tkinter as tk
dir(ImageTk) dir(ImageTk)
HAS_TK = True HAS_TK = True
except (OSError, ImportError): except (OSError, ImportError):

View File

@ -18,7 +18,7 @@ class TestLibPack(PillowTestCase):
if isinstance(data, int): if isinstance(data, int):
data_len = data * len(pixels) data_len = data * len(pixels)
data = bytes(bytearray(range(1, data_len + 1))) data = bytes(range(1, data_len + 1))
self.assertEqual(data, im.tobytes("raw", rawmode)) self.assertEqual(data, im.tobytes("raw", rawmode))
@ -226,7 +226,7 @@ class TestLibUnpack(PillowTestCase):
""" """
if isinstance(data, int): if isinstance(data, int):
data_len = data * len(pixels) data_len = data * len(pixels)
data = bytes(bytearray(range(1, data_len + 1))) data = bytes(range(1, data_len + 1))
im = Image.frombytes(mode, (len(pixels), 1), data, "raw", rawmode, 0, 1) im = Image.frombytes(mode, (len(pixels), 1), data, "raw", rawmode, 0, 1)

View File

@ -1,5 +1,3 @@
from __future__ import print_function
import locale import locale
from PIL import Image from PIL import Image

View File

@ -1,5 +1,3 @@
from __future__ import unicode_literals
import os import os
import subprocess import subprocess
import sys import sys

View File

@ -20,7 +20,11 @@ class TestModeI16(PillowTestCase):
self.assertEqual( self.assertEqual(
p1, p1,
p2, p2,
("got %r from mode %s at %s, expected %r" % (p1, im1.mode, xy, p2)), (
"got {!r} from mode {} at {}, expected {!r}".format(
p1, im1.mode, xy, p2
)
),
) )
def test_basic(self): def test_basic(self):

View File

@ -1,5 +1,3 @@
from __future__ import print_function
from PIL import Image from PIL import Image
from .helper import PillowTestCase, hopper, unittest from .helper import PillowTestCase, hopper, unittest

View File

@ -22,7 +22,7 @@ class TestPdfParser(PillowTestCase):
self.assertEqual(encode_text("abc"), b"\xFE\xFF\x00a\x00b\x00c") self.assertEqual(encode_text("abc"), b"\xFE\xFF\x00a\x00b\x00c")
self.assertEqual(decode_text(b"\xFE\xFF\x00a\x00b\x00c"), "abc") self.assertEqual(decode_text(b"\xFE\xFF\x00a\x00b\x00c"), "abc")
self.assertEqual(decode_text(b"abc"), "abc") self.assertEqual(decode_text(b"abc"), "abc")
self.assertEqual(decode_text(b"\x1B a \x1C"), u"\u02D9 a \u02DD") self.assertEqual(decode_text(b"\x1B a \x1C"), "\u02D9 a \u02DD")
def test_indirect_refs(self): def test_indirect_refs(self):
self.assertEqual(IndirectReference(1, 2), IndirectReference(1, 2)) self.assertEqual(IndirectReference(1, 2), IndirectReference(1, 2))

View File

@ -1,5 +1,6 @@
import os import os
import sys import sys
from io import StringIO
from PIL import Image, PSDraw from PIL import Image, PSDraw
@ -47,10 +48,6 @@ class TestPsDraw(PillowTestCase):
def test_stdout(self): def test_stdout(self):
# Temporarily redirect stdout # Temporarily redirect stdout
try:
from cStringIO import StringIO
except ImportError:
from io import StringIO
old_stdout = sys.stdout old_stdout = sys.stdout
sys.stdout = mystdout = StringIO() sys.stdout = mystdout = StringIO()

View File

@ -57,7 +57,7 @@ if ImageQt.qt_is_installed:
class Example(QWidget): class Example(QWidget):
def __init__(self): def __init__(self):
super(Example, self).__init__() super().__init__()
img = hopper().resize((1000, 1000)) img = hopper().resize((1000, 1000))

View File

@ -4,26 +4,6 @@ from .helper import PillowTestCase, unittest
class TestUtil(PillowTestCase): class TestUtil(PillowTestCase):
def test_is_string_type(self):
# Arrange
color = "red"
# Act
it_is = _util.isStringType(color)
# Assert
self.assertTrue(it_is)
def test_is_not_string_type(self):
# Arrange
color = (255, 0, 0)
# Act
it_is_not = _util.isStringType(color)
# Assert
self.assertFalse(it_is_not)
def test_is_path(self): def test_is_path(self):
# Arrange # Arrange
fp = "filename.ext" fp = "filename.ext"

View File

@ -1,5 +1,3 @@
from __future__ import print_function
import io import io
import queue import queue
import sys import sys

View File

@ -1,5 +1,3 @@
from __future__ import print_function
from PIL import Image from PIL import Image

View File

@ -1,43 +0,0 @@
# This is a sample Dockerfile to build Pillow on Alpine Linux
# with all/most of the dependencies working.
#
# Tcl/Tk isn't detecting
# Freetype has different metrics so tests are failing.
# sudo and bash are required for the webp build script.
FROM alpine
USER root
RUN apk --no-cache add python \
build-base \
python-dev \
py-pip \
# Pillow dependencies
jpeg-dev \
zlib-dev \
freetype-dev \
lcms2-dev \
openjpeg-dev \
tiff-dev \
tk-dev \
tcl-dev
# install from pip, without webp
#RUN LIBRARY_PATH=/lib:/usr/lib /bin/sh -c "pip install Pillow"
# install from git, run tests, including webp
RUN apk --no-cache add git \
bash \
sudo
RUN git clone https://github.com/python-pillow/Pillow.git /Pillow
RUN pip install virtualenv && virtualenv /vpy && source /vpy/bin/activate && pip install nose
RUN echo "#!/bin/bash" >> /test && \
echo "source /vpy/bin/activate && cd /Pillow " >> test && \
echo "pushd depends && ./install_webp.sh && ./install_imagequant.sh && popd" >> test && \
echo "LIBRARY_PATH=/lib:/usr/lib make install && make test" >> test
RUN chmod +x /test
CMD ["/test"]

View File

@ -1,18 +0,0 @@
#!/bin/sh
#
# Installs all of the dependencies for Pillow for Debian 8.2
# for both system Pythons 2.7 and 3.4
#
# Also works for Raspbian Jessie
#
sudo apt-get -y install python-dev python-setuptools \
python3-dev python-virtualenv cmake
sudo apt-get -y install libtiff5-dev libjpeg62-turbo-dev zlib1g-dev \
libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev \
python-tk python3-tk libharfbuzz-dev libfribidi-dev
./install_openjpeg.sh
./install_imagequant.sh
./install_raqm.sh

View File

@ -1,18 +0,0 @@
#!/bin/sh
#
# Installs all of the dependencies for Pillow for Fedora 23
# for both system Pythons 2.7 and 3.4
#
# note that Fedora does ship packages for Pillow as python-pillow
# this is a workaround for
# "gcc: error: /usr/lib/rpm/redhat/redhat-hardened-cc1: No such file or directory"
# errors when compiling.
sudo dnf install redhat-rpm-config
sudo dnf install python-devel python3-devel python-virtualenv make gcc
sudo dnf install libtiff-devel libjpeg-devel zlib-devel freetype-devel \
lcms2-devel libwebp-devel openjpeg2-devel tkinter python3-tkinter \
tcl-devel tk-devel harfbuzz-devel fribidi-devel libraqm-devel

View File

@ -1,13 +0,0 @@
#!/bin/sh
#
# Installs all of the dependencies for Pillow for Freebsd 10.x
# for both system Pythons 2.7 and 3.4
#
sudo pkg install python2 python3 py27-pip py27-virtualenv wget cmake
# Openjpeg fails badly using the openjpeg package.
# I can't find a python3.4 version of tkinter
sudo pkg install jpeg-turbo tiff webp lcms2 freetype2 harfbuzz fribidi py27-tkinter
./install_raqm_cmake.sh

View File

@ -1,17 +0,0 @@
#!/bin/sh
#
# Installs all of the dependencies for Pillow for Ubuntu 12.04
# for both system Pythons 2.7 and 3.2
#
sudo apt-get -y install python-dev python-setuptools \
python3-dev python-virtualenv cmake
sudo apt-get install libtiff4-dev libjpeg8-dev zlib1g-dev \
libfreetype6-dev liblcms2-dev tcl8.5-dev \
tk8.5-dev python-tk python3-tk
./install_openjpeg.sh
./install_webp.sh
./install_imagequant.sh

View File

@ -1,16 +0,0 @@
#!/bin/sh
#
# Installs all of the dependencies for Pillow for Ubuntu 14.04
# for both system Pythons 2.7 and 3.4
#
sudo apt-get update
sudo apt-get -y install python-dev python-setuptools \
python3-dev python-virtualenv cmake
sudo apt-get -y install libtiff5-dev libjpeg8-dev zlib1g-dev \
libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev \
python-tk python3-tk libharfbuzz-dev libfribidi-dev
./install_openjpeg.sh
./install_imagequant.sh
./install_raqm.sh

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
# #
# Pillow (PIL Fork) documentation build configuration file, created by # Pillow (PIL Fork) documentation build configuration file, created by
# sphinx-quickstart on Sat Apr 4 07:54:11 2015. # sphinx-quickstart on Sat Apr 4 07:54:11 2015.
@ -42,9 +41,9 @@ source_suffix = ".rst"
master_doc = "index" master_doc = "index"
# General information about the project. # General information about the project.
project = u"Pillow (PIL Fork)" project = "Pillow (PIL Fork)"
copyright = u"1995-2011 Fredrik Lundh, 2010-2019 Alex Clark and Contributors" copyright = "1995-2011 Fredrik Lundh, 2010-2019 Alex Clark and Contributors"
author = u"Fredrik Lundh, Alex Clark and Contributors" author = "Fredrik Lundh, Alex Clark and Contributors"
# The version info for the project you're documenting, acts as replacement for # The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the # |version| and |release|, also used in various other places throughout the
@ -220,8 +219,8 @@ latex_documents = [
( (
master_doc, master_doc,
"PillowPILFork.tex", "PillowPILFork.tex",
u"Pillow (PIL Fork) Documentation", "Pillow (PIL Fork) Documentation",
u"Alex Clark", "Alex Clark",
"manual", "manual",
) )
] ]
@ -252,7 +251,7 @@ latex_documents = [
# One entry per manual page. List of tuples # One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section). # (source start file, name, description, authors, manual section).
man_pages = [ man_pages = [
(master_doc, "pillowpilfork", u"Pillow (PIL Fork) Documentation", [author], 1) (master_doc, "pillowpilfork", "Pillow (PIL Fork) Documentation", [author], 1)
] ]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
@ -268,7 +267,7 @@ texinfo_documents = [
( (
master_doc, master_doc,
"PillowPILFork", "PillowPILFork",
u"Pillow (PIL Fork) Documentation", "Pillow (PIL Fork) Documentation",
author, author,
"PillowPILFork", "PillowPILFork",
"Pillow is the friendly PIL fork by Alex Clark and Contributors.", "Pillow is the friendly PIL fork by Alex Clark and Contributors.",

View File

@ -212,10 +212,10 @@ class DdsImageFile(ImageFile.ImageFile):
def _open(self): def _open(self):
magic, header_size = struct.unpack("<II", self.fp.read(8)) magic, header_size = struct.unpack("<II", self.fp.read(8))
if header_size != 124: if header_size != 124:
raise IOError("Unsupported header size %r" % (header_size)) raise OSError("Unsupported header size %r" % (header_size))
header_bytes = self.fp.read(header_size - 4) header_bytes = self.fp.read(header_size - 4)
if len(header_bytes) != 120: if len(header_bytes) != 120:
raise IOError("Incomplete header: %s bytes" % len(header_bytes)) raise OSError("Incomplete header: %s bytes" % len(header_bytes))
header = BytesIO(header_bytes) header = BytesIO(header_bytes)
flags, height, width = struct.unpack("<3I", header.read(12)) flags, height, width = struct.unpack("<3I", header.read(12))
@ -250,7 +250,7 @@ class DXT1Decoder(ImageFile.PyDecoder):
try: try:
self.set_as_raw(_dxt1(self.fd, self.state.xsize, self.state.ysize)) self.set_as_raw(_dxt1(self.fd, self.state.xsize, self.state.ysize))
except struct.error: except struct.error:
raise IOError("Truncated DDS file") raise OSError("Truncated DDS file")
return 0, 0 return 0, 0
@ -261,7 +261,7 @@ class DXT5Decoder(ImageFile.PyDecoder):
try: try:
self.set_as_raw(_dxt5(self.fd, self.state.xsize, self.state.ysize)) self.set_as_raw(_dxt5(self.fd, self.state.xsize, self.state.ysize))
except struct.error: except struct.error:
raise IOError("Truncated DDS file") raise OSError("Truncated DDS file")
return 0, 0 return 0, 0

View File

@ -18,7 +18,6 @@ in the :py:mod:`~PIL.Image` module::
If successful, this function returns an :py:class:`~PIL.Image.Image` object. If successful, this function returns an :py:class:`~PIL.Image.Image` object.
You can now use instance attributes to examine the file contents:: You can now use instance attributes to examine the file contents::
>>> from __future__ import print_function
>>> print(im.format, im.size, im.mode) >>> print(im.format, im.size, im.mode)
PPM (512, 512) RGB PPM (512, 512) RGB
@ -67,7 +66,6 @@ Convert files to JPEG
:: ::
from __future__ import print_function
import os, sys import os, sys
from PIL import Image from PIL import Image
@ -89,7 +87,6 @@ Create JPEG thumbnails
:: ::
from __future__ import print_function
import os, sys import os, sys
from PIL import Image from PIL import Image
@ -120,7 +117,6 @@ Identify Image Files
:: ::
from __future__ import print_function
import sys import sys
from PIL import Image from PIL import Image

View File

@ -125,10 +125,9 @@ External Libraries
.. note:: .. note::
There are scripts to install the dependencies for some operating There are Dockerfiles in our `Docker images repo
systems included in the ``depends`` directory. Also see the <https://github.com/python-pillow/docker-images>`_ to install the
Dockerfiles in our `docker images repo dependencies for some operating systems.
<https://github.com/python-pillow/docker-images>`_.
Many of Pillow's features require external libraries: Many of Pillow's features require external libraries:

View File

@ -1,93 +0,0 @@
# A monkey patch of the base distutils.ccompiler to use parallel builds
# Tested on 2.7, looks to be identical to 3.3.
# Only applied on Python 2.7 because otherwise, it conflicts with Python's
# own newly-added support for parallel builds.
from __future__ import print_function
import os
import sys
from distutils.ccompiler import CCompiler
from multiprocessing import Pool, cpu_count
try:
MAX_PROCS = int(os.environ.get("MAX_CONCURRENCY", min(4, cpu_count())))
except NotImplementedError:
MAX_PROCS = None
# hideous monkeypatching. but. but. but.
def _mp_compile_one(tp):
(self, obj, build, cc_args, extra_postargs, pp_opts) = tp
try:
src, ext = build[obj]
except KeyError:
return
self._compile(obj, src, ext, cc_args, extra_postargs, pp_opts)
return
def _mp_compile(
self,
sources,
output_dir=None,
macros=None,
include_dirs=None,
debug=0,
extra_preargs=None,
extra_postargs=None,
depends=None,
):
"""Compile one or more source files.
see distutils.ccompiler.CCompiler.compile for comments.
"""
# A concrete compiler class can either override this method
# entirely or implement _compile().
macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
output_dir, macros, include_dirs, sources, depends, extra_postargs
)
cc_args = self._get_cc_args(pp_opts, debug, extra_preargs)
pool = Pool(MAX_PROCS)
try:
print("Building using %d processes" % pool._processes)
except Exception:
pass
arr = [(self, obj, build, cc_args, extra_postargs, pp_opts) for obj in objects]
pool.map_async(_mp_compile_one, arr)
pool.close()
pool.join()
# Return *all* object filenames, not just the ones we just built.
return objects
def install():
fl_win = sys.platform.startswith("win")
fl_cygwin = sys.platform.startswith("cygwin")
if fl_win or fl_cygwin:
# Windows barfs on multiprocessing installs
print("Single threaded build for Windows")
return
if MAX_PROCS != 1:
# explicitly don't enable if environment says 1 processor
try:
# bug, only enable if we can make a Pool. see issue #790 and
# https://stackoverflow.com/questions/6033599/oserror-38-errno-38-with-multiprocessing
Pool(2)
CCompiler.compile = _mp_compile
except Exception as msg:
print("Exception installing mp_compile, proceeding without: %s" % msg)
else:
print(
"Single threaded build, not installing mp_compile: %s processes" % MAX_PROCS
)
# We monkeypatch Python 2.7
if sys.version_info.major < 3:
install()

View File

@ -1,6 +1,5 @@
#!/usr/bin/env python #!/usr/bin/env python
# minimal sanity check # minimal sanity check
from __future__ import print_function
import sys import sys

View File

@ -7,7 +7,6 @@
# Final rating: 10/10 # Final rating: 10/10
# Your cheese is so fresh most people think it's a cream: Mascarpone # Your cheese is so fresh most people think it's a cream: Mascarpone
# ------------------------------ # ------------------------------
from __future__ import print_function
import os import os
import re import re
@ -20,10 +19,6 @@ from distutils.command.build_ext import build_ext
from setuptools import Extension, setup from setuptools import Extension, setup
# monkey patch import hook. Even though flake8 says it's not used, it is.
# comment this out to disable multi threaded builds.
import mp_compile
def get_version(): def get_version():
version_file = "src/PIL/_version.py" version_file = "src/PIL/_version.py"
@ -189,10 +184,10 @@ def _find_library_dirs_ldconfig():
expr = r".* => (.*)" expr = r".* => (.*)"
env = {} env = {}
null = open(os.devnull, "wb")
try: try:
with null: p = subprocess.Popen(
p = subprocess.Popen(args, stderr=null, stdout=subprocess.PIPE, env=env) args, stderr=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env
)
except OSError: # E.g. command not found except OSError: # E.g. command not found
return [] return []
[data, _] = p.communicate() [data, _] = p.communicate()
@ -305,8 +300,7 @@ class pil_build_ext(build_ext):
return getattr(self, feat) is None return getattr(self, feat) is None
def __iter__(self): def __iter__(self):
for x in self.features: yield from self.features
yield x
feature = feature() feature = feature()
@ -334,12 +328,15 @@ class pil_build_ext(build_ext):
if self.debug: if self.debug:
global DEBUG global DEBUG
DEBUG = True DEBUG = True
if sys.version_info.major >= 3 and not self.parallel: if not self.parallel:
# For Python 2.7, we monkeypatch distutils to have parallel # If --parallel (or -j) wasn't specified, we want to reproduce the same
# builds. If --parallel (or -j) wasn't specified, we want to # behavior as before, that is, auto-detect the number of jobs.
# reproduce the same behavior as before, that is, auto-detect the try:
# number of jobs. self.parallel = int(
self.parallel = mp_compile.MAX_PROCS os.environ.get("MAX_CONCURRENCY", min(4, os.cpu_count()))
)
except TypeError:
self.parallel = None
for x in self.feature: for x in self.feature:
if getattr(self, "disable_%s" % x): if getattr(self, "disable_%s" % x):
setattr(self.feature, x, False) setattr(self.feature, x, False)
@ -347,7 +344,7 @@ class pil_build_ext(build_ext):
_dbg("Disabling %s", x) _dbg("Disabling %s", x)
if getattr(self, "enable_%s" % x): if getattr(self, "enable_%s" % x):
raise ValueError( raise ValueError(
"Conflicting options: --enable-%s and --disable-%s" % (x, x) "Conflicting options: --enable-{} and --disable-{}".format(x, x)
) )
if getattr(self, "enable_%s" % x): if getattr(self, "enable_%s" % x):
_dbg("Requiring %s", x) _dbg("Requiring %s", x)
@ -719,7 +716,7 @@ class pil_build_ext(build_ext):
defs.append(("HAVE_LIBTIFF", None)) defs.append(("HAVE_LIBTIFF", None))
if sys.platform == "win32": if sys.platform == "win32":
libs.extend(["kernel32", "user32", "gdi32"]) libs.extend(["kernel32", "user32", "gdi32"])
if struct.unpack("h", "\0\1".encode("ascii"))[0] == 1: if struct.unpack("h", b"\0\1")[0] == 1:
defs.append(("WORDS_BIGENDIAN", None)) defs.append(("WORDS_BIGENDIAN", None))
if sys.platform == "win32" and not (PLATFORM_PYPY or PLATFORM_MINGW): if sys.platform == "win32" and not (PLATFORM_PYPY or PLATFORM_MINGW):
@ -800,7 +797,7 @@ class pil_build_ext(build_ext):
print("-" * 68) print("-" * 68)
print("version Pillow %s" % PILLOW_VERSION) print("version Pillow %s" % PILLOW_VERSION)
v = sys.version.split("[") v = sys.version.split("[")
print("platform %s %s" % (sys.platform, v[0].strip())) print("platform {} {}".format(sys.platform, v[0].strip()))
for v in v[1:]: for v in v[1:]:
print(" [%s" % v.strip()) print(" [%s" % v.strip())
print("-" * 68) print("-" * 68)
@ -823,7 +820,7 @@ class pil_build_ext(build_ext):
version = "" version = ""
if len(option) >= 3 and option[2]: if len(option) >= 3 and option[2]:
version = " (%s)" % option[2] version = " (%s)" % option[2]
print("--- %s support available%s" % (option[1], version)) print("--- {} support available{}".format(option[1], version))
else: else:
print("*** %s support not available" % option[1]) print("*** %s support not available" % option[1])
all = 0 all = 0
@ -868,13 +865,12 @@ try:
classifiers=[ classifiers=[
"Development Status :: 6 - Mature", "Development Status :: 6 - Mature",
"License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)", # noqa: E501 "License :: OSI Approved :: Historical Permission Notice and Disclaimer (HPND)", # noqa: E501
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy", "Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Multimedia :: Graphics", "Topic :: Multimedia :: Graphics",
@ -883,7 +879,7 @@ try:
"Topic :: Multimedia :: Graphics :: Graphics Conversion", "Topic :: Multimedia :: Graphics :: Graphics Conversion",
"Topic :: Multimedia :: Graphics :: Viewers", "Topic :: Multimedia :: Graphics :: Viewers",
], ],
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*", python_requires=">=3.5",
cmdclass={"build_ext": pil_build_ext}, cmdclass={"build_ext": pil_build_ext},
ext_modules=[Extension("PIL._imaging", ["_imaging.c"])], ext_modules=[Extension("PIL._imaging", ["_imaging.c"])],
include_package_data=True, include_package_data=True,

View File

@ -17,7 +17,6 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from __future__ import print_function
from . import FontFile, Image from . import FontFile, Image

View File

@ -283,7 +283,7 @@ class _BLPBaseDecoder(ImageFile.PyDecoder):
self._read_blp_header() self._read_blp_header()
self._load() self._load()
except struct.error: except struct.error:
raise IOError("Truncated Blp file") raise OSError("Truncated Blp file")
return 0, 0 return 0, 0
def _read_palette(self): def _read_palette(self):

View File

@ -144,7 +144,7 @@ class BmpImageFile(ImageFile.ImageFile):
file_info["a_mask"], file_info["a_mask"],
) )
else: else:
raise IOError("Unsupported BMP header type (%d)" % file_info["header_size"]) raise OSError("Unsupported BMP header type (%d)" % file_info["header_size"])
# ------------------ Special case : header is reported 40, which # ------------------ Special case : header is reported 40, which
# ---------------------- is shorter than real size for bpp >= 16 # ---------------------- is shorter than real size for bpp >= 16
@ -159,12 +159,12 @@ class BmpImageFile(ImageFile.ImageFile):
# ------------------------------- Check abnormal values for DOS attacks # ------------------------------- Check abnormal values for DOS attacks
if file_info["width"] * file_info["height"] > 2 ** 31: if file_info["width"] * file_info["height"] > 2 ** 31:
raise IOError("Unsupported BMP Size: (%dx%d)" % self.size) raise OSError("Unsupported BMP Size: (%dx%d)" % self.size)
# ---------------------- Check bit depth for unusual unsupported values # ---------------------- Check bit depth for unusual unsupported values
self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None)) self.mode, raw_mode = BIT2MODE.get(file_info["bits"], (None, None))
if self.mode is None: if self.mode is None:
raise IOError("Unsupported BMP pixel depth (%d)" % file_info["bits"]) raise OSError("Unsupported BMP pixel depth (%d)" % file_info["bits"])
# ---------------- Process BMP with Bitfields compression (not palette) # ---------------- Process BMP with Bitfields compression (not palette)
if file_info["compression"] == self.BITFIELDS: if file_info["compression"] == self.BITFIELDS:
@ -202,21 +202,21 @@ class BmpImageFile(ImageFile.ImageFile):
): ):
raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])] raw_mode = MASK_MODES[(file_info["bits"], file_info["rgb_mask"])]
else: else:
raise IOError("Unsupported BMP bitfields layout") raise OSError("Unsupported BMP bitfields layout")
else: else:
raise IOError("Unsupported BMP bitfields layout") raise OSError("Unsupported BMP bitfields layout")
elif file_info["compression"] == self.RAW: elif file_info["compression"] == self.RAW:
if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset if file_info["bits"] == 32 and header == 22: # 32-bit .cur offset
raw_mode, self.mode = "BGRA", "RGBA" raw_mode, self.mode = "BGRA", "RGBA"
else: else:
raise IOError("Unsupported BMP compression (%d)" % file_info["compression"]) raise OSError("Unsupported BMP compression (%d)" % file_info["compression"])
# --------------- Once the header is processed, process the palette/LUT # --------------- Once the header is processed, process the palette/LUT
if self.mode == "P": # Paletted for 1, 4 and 8 bit images if self.mode == "P": # Paletted for 1, 4 and 8 bit images
# ---------------------------------------------------- 1-bit images # ---------------------------------------------------- 1-bit images
if not (0 < file_info["colors"] <= 65536): if not (0 < file_info["colors"] <= 65536):
raise IOError("Unsupported BMP Palette size (%d)" % file_info["colors"]) raise OSError("Unsupported BMP Palette size (%d)" % file_info["colors"])
else: else:
padding = file_info["palette_padding"] padding = file_info["palette_padding"]
palette = read(padding * file_info["colors"]) palette = read(padding * file_info["colors"])
@ -305,7 +305,7 @@ def _save(im, fp, filename, bitmap_header=True):
try: try:
rawmode, bits, colors = SAVE[im.mode] rawmode, bits, colors = SAVE[im.mode]
except KeyError: except KeyError:
raise IOError("cannot write mode %s as BMP" % im.mode) raise OSError("cannot write mode %s as BMP" % im.mode)
info = im.encoderinfo info = im.encoderinfo

View File

@ -60,7 +60,7 @@ class BufrStubImageFile(ImageFile.StubImageFile):
def _save(im, fp, filename): def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"): if _handler is None or not hasattr("_handler", "save"):
raise IOError("BUFR save handler not installed") raise OSError("BUFR save handler not installed")
_handler.save(im, fp, filename) _handler.save(im, fp, filename)

View File

@ -21,7 +21,7 @@
import io import io
class ContainerIO(object): class ContainerIO:
def __init__(self, file, offset, length): def __init__(self, file, offset, length):
""" """
Create file object. Create file object.

View File

@ -15,9 +15,6 @@
# #
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from __future__ import print_function
from . import BmpImagePlugin, Image from . import BmpImagePlugin, Image
from ._binary import i8, i16le as i16, i32le as i32 from ._binary import i8, i16le as i16, i32le as i32

View File

@ -106,10 +106,10 @@ class DdsImageFile(ImageFile.ImageFile):
def _open(self): def _open(self):
magic, header_size = struct.unpack("<II", self.fp.read(8)) magic, header_size = struct.unpack("<II", self.fp.read(8))
if header_size != 124: if header_size != 124:
raise IOError("Unsupported header size %r" % (header_size)) raise OSError("Unsupported header size %r" % (header_size))
header_bytes = self.fp.read(header_size - 4) header_bytes = self.fp.read(header_size - 4)
if len(header_bytes) != 120: if len(header_bytes) != 120:
raise IOError("Incomplete header: %s bytes" % len(header_bytes)) raise OSError("Incomplete header: %s bytes" % len(header_bytes))
header = BytesIO(header_bytes) header = BytesIO(header_bytes)
flags, height, width = struct.unpack("<3I", header.read(12)) flags, height, width = struct.unpack("<3I", header.read(12))

View File

@ -40,15 +40,8 @@ gs_windows_binary = None
if sys.platform.startswith("win"): if sys.platform.startswith("win"):
import shutil import shutil
if hasattr(shutil, "which"):
which = shutil.which
else:
# Python 2
import distutils.spawn
which = distutils.spawn.find_executable
for binary in ("gswin32c", "gswin64c", "gs"): for binary in ("gswin32c", "gswin64c", "gs"):
if which(binary) is not None: if shutil.which(binary) is not None:
gs_windows_binary = binary gs_windows_binary = binary
break break
else: else:
@ -60,8 +53,7 @@ def has_ghostscript():
return True return True
if not sys.platform.startswith("win"): if not sys.platform.startswith("win"):
try: try:
with open(os.devnull, "wb") as devnull: subprocess.check_call(["gs", "--version"], stdout=subprocess.DEVNULL)
subprocess.check_call(["gs", "--version"], stdout=devnull)
return True return True
except OSError: except OSError:
# No Ghostscript # No Ghostscript
@ -139,7 +131,7 @@ def Ghostscript(tile, size, fp, scale=1):
if gs_windows_binary is not None: if gs_windows_binary is not None:
if not gs_windows_binary: if not gs_windows_binary:
raise WindowsError("Unable to locate Ghostscript on paths") raise OSError("Unable to locate Ghostscript on paths")
command[0] = gs_windows_binary command[0] = gs_windows_binary
# push data through Ghostscript # push data through Ghostscript
@ -162,7 +154,7 @@ def Ghostscript(tile, size, fp, scale=1):
return im.im.copy() return im.im.copy()
class PSFile(object): class PSFile:
""" """
Wrapper for bytesio object that treats either CR or LF as end of line. Wrapper for bytesio object that treats either CR or LF as end of line.
""" """
@ -272,7 +264,7 @@ class EpsImageFile(ImageFile.ImageFile):
# tools mistakenly put in the Comments section # tools mistakenly put in the Comments section
pass pass
else: else:
raise IOError("bad EPS header") raise OSError("bad EPS header")
s_raw = fp.readline() s_raw = fp.readline()
s = s_raw.strip("\r\n") s = s_raw.strip("\r\n")
@ -307,7 +299,7 @@ class EpsImageFile(ImageFile.ImageFile):
break break
if not box: if not box:
raise IOError("cannot determine EPS bounding box") raise OSError("cannot determine EPS bounding box")
def _find_offset(self, fp): def _find_offset(self, fp):
@ -371,9 +363,8 @@ def _save(im, fp, filename, eps=1):
base_fp = fp base_fp = fp
wrapped_fp = False wrapped_fp = False
if fp != sys.stdout: if fp != sys.stdout:
if sys.version_info.major > 2: fp = io.TextIOWrapper(fp, encoding="latin-1")
fp = io.TextIOWrapper(fp, encoding="latin-1") wrapped_fp = True
wrapped_fp = True
try: try:
if eps: if eps:

View File

@ -63,7 +63,7 @@ class FITSStubImageFile(ImageFile.StubImageFile):
def _save(im, fp, filename): def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"): if _handler is None or not hasattr("_handler", "save"):
raise IOError("FITS save handler not installed") raise OSError("FITS save handler not installed")
_handler.save(im, fp, filename) _handler.save(im, fp, filename)

View File

@ -14,7 +14,6 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from __future__ import print_function
import os import os
@ -35,7 +34,7 @@ def puti16(fp, values):
# Base class for raster font file handlers. # Base class for raster font file handlers.
class FontFile(object): class FontFile:
bitmap = None bitmap = None

View File

@ -14,9 +14,6 @@
# #
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from __future__ import print_function
import olefile import olefile
from . import Image, ImageFile from . import Image, ImageFile
@ -62,7 +59,7 @@ class FpxImageFile(ImageFile.ImageFile):
try: try:
self.ole = olefile.OleFileIO(self.fp) self.ole = olefile.OleFileIO(self.fp)
except IOError: except OSError:
raise SyntaxError("not an FPX file; invalid OLE file") raise SyntaxError("not an FPX file; invalid OLE file")
if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B": if self.ole.root.clsid != "56616700-C154-11CE-8553-00AA00A1F95B":
@ -141,7 +138,7 @@ class FpxImageFile(ImageFile.ImageFile):
length = i32(s, 32) length = i32(s, 32)
if size != self.size: if size != self.size:
raise IOError("subimage mismatch") raise OSError("subimage mismatch")
# get tile descriptors # get tile descriptors
fp.seek(28 + offset) fp.seek(28 + offset)
@ -214,7 +211,7 @@ class FpxImageFile(ImageFile.ImageFile):
self.tile_prefix = self.jpeg[jpeg_tables] self.tile_prefix = self.jpeg[jpeg_tables]
else: else:
raise IOError("unknown/invalid compression") raise OSError("unknown/invalid compression")
x = x + xtile x = x + xtile
if x >= xsize: if x >= xsize:

View File

@ -618,20 +618,20 @@ def _save_netpbm(im, fp, filename):
with open(filename, "wb") as f: with open(filename, "wb") as f:
if im.mode != "RGB": if im.mode != "RGB":
with open(os.devnull, "wb") as devnull: subprocess.check_call(
subprocess.check_call(["ppmtogif", tempfile], stdout=f, stderr=devnull) ["ppmtogif", tempfile], stdout=f, stderr=subprocess.DEVNULL
)
else: else:
# Pipe ppmquant output into ppmtogif # Pipe ppmquant output into ppmtogif
# "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename) # "ppmquant 256 %s | ppmtogif > %s" % (tempfile, filename)
quant_cmd = ["ppmquant", "256", tempfile] quant_cmd = ["ppmquant", "256", tempfile]
togif_cmd = ["ppmtogif"] togif_cmd = ["ppmtogif"]
with open(os.devnull, "wb") as devnull: quant_proc = subprocess.Popen(
quant_proc = subprocess.Popen( quant_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
quant_cmd, stdout=subprocess.PIPE, stderr=devnull )
) togif_proc = subprocess.Popen(
togif_proc = subprocess.Popen( togif_cmd, stdin=quant_proc.stdout, stdout=f, stderr=subprocess.DEVNULL
togif_cmd, stdin=quant_proc.stdout, stdout=f, stderr=devnull )
)
# Allow ppmquant to receive SIGPIPE if ppmtogif exits # Allow ppmquant to receive SIGPIPE if ppmtogif exits
quant_proc.stdout.close() quant_proc.stdout.close()
@ -848,7 +848,7 @@ def getdata(im, offset=(0, 0), **params):
""" """
class Collector(object): class Collector:
data = [] data = []
def write(self, data): def write(self, data):

View File

@ -60,7 +60,7 @@ def sphere_decreasing(middle, pos):
SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing] SEGMENTS = [linear, curved, sine, sphere_increasing, sphere_decreasing]
class GradientFile(object): class GradientFile:
gradient = None gradient = None
@ -132,7 +132,7 @@ class GimpGradientFile(GradientFile):
cspace = int(s[12]) cspace = int(s[12])
if cspace != 0: if cspace != 0:
raise IOError("cannot handle HSV colour space") raise OSError("cannot handle HSV colour space")
gradient.append((x0, x1, xm, rgb0, rgb1, segment)) gradient.append((x0, x1, xm, rgb0, rgb1, segment))

View File

@ -22,7 +22,7 @@ from ._binary import o8
# File handler for GIMP's palette format. # File handler for GIMP's palette format.
class GimpPaletteFile(object): class GimpPaletteFile:
rawmode = "RGB" rawmode = "RGB"

View File

@ -61,7 +61,7 @@ class GribStubImageFile(ImageFile.StubImageFile):
def _save(im, fp, filename): def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"): if _handler is None or not hasattr("_handler", "save"):
raise IOError("GRIB save handler not installed") raise OSError("GRIB save handler not installed")
_handler.save(im, fp, filename) _handler.save(im, fp, filename)

View File

@ -60,7 +60,7 @@ class HDF5StubImageFile(ImageFile.StubImageFile):
def _save(im, fp, filename): def _save(im, fp, filename):
if _handler is None or not hasattr("_handler", "save"): if _handler is None or not hasattr("_handler", "save"):
raise IOError("HDF5 save handler not installed") raise OSError("HDF5 save handler not installed")
_handler.save(im, fp, filename) _handler.save(im, fp, filename)

View File

@ -129,7 +129,7 @@ def read_png_or_jpeg2000(fobj, start_length, size):
raise ValueError("Unsupported icon subimage format") raise ValueError("Unsupported icon subimage format")
class IcnsFile(object): class IcnsFile:
SIZES = { SIZES = {
(512, 512, 2): [(b"ic10", read_png_or_jpeg2000)], (512, 512, 2): [(b"ic10", read_png_or_jpeg2000)],
@ -314,42 +314,40 @@ def _save(im, fp, filename):
fp.flush() fp.flush()
# create the temporary set of pngs # create the temporary set of pngs
iconset = tempfile.mkdtemp(".iconset") with tempfile.TemporaryDirectory(".iconset") as iconset:
provided_images = {im.width: im for im in im.encoderinfo.get("append_images", [])} provided_images = {
last_w = None im.width: im for im in im.encoderinfo.get("append_images", [])
second_path = None }
for w in [16, 32, 128, 256, 512]: last_w = None
prefix = "icon_{}x{}".format(w, w) second_path = None
for w in [16, 32, 128, 256, 512]:
prefix = "icon_{}x{}".format(w, w)
first_path = os.path.join(iconset, prefix + ".png") first_path = os.path.join(iconset, prefix + ".png")
if last_w == w: if last_w == w:
shutil.copyfile(second_path, first_path) shutil.copyfile(second_path, first_path)
else: else:
im_w = provided_images.get(w, im.resize((w, w), Image.LANCZOS)) im_w = provided_images.get(w, im.resize((w, w), Image.LANCZOS))
im_w.save(first_path) im_w.save(first_path)
second_path = os.path.join(iconset, prefix + "@2x.png") second_path = os.path.join(iconset, prefix + "@2x.png")
im_w2 = provided_images.get(w * 2, im.resize((w * 2, w * 2), Image.LANCZOS)) im_w2 = provided_images.get(w * 2, im.resize((w * 2, w * 2), Image.LANCZOS))
im_w2.save(second_path) im_w2.save(second_path)
last_w = w * 2 last_w = w * 2
# iconutil -c icns -o {} {} # iconutil -c icns -o {} {}
convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset] convert_cmd = ["iconutil", "-c", "icns", "-o", filename, iconset]
with open(os.devnull, "wb") as devnull:
convert_proc = subprocess.Popen( convert_proc = subprocess.Popen(
convert_cmd, stdout=subprocess.PIPE, stderr=devnull convert_cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
) )
convert_proc.stdout.close() convert_proc.stdout.close()
retcode = convert_proc.wait() retcode = convert_proc.wait()
# remove the temporary files if retcode:
shutil.rmtree(iconset) raise subprocess.CalledProcessError(retcode, convert_cmd)
if retcode:
raise subprocess.CalledProcessError(retcode, convert_cmd)
Image.register_open(IcnsImageFile.format, IcnsImageFile, lambda x: x[:4] == b"icns") Image.register_open(IcnsImageFile.format, IcnsImageFile, lambda x: x[:4] == b"icns")

View File

@ -82,7 +82,7 @@ def _accept(prefix):
return prefix[:4] == _MAGIC return prefix[:4] == _MAGIC
class IcoFile(object): class IcoFile:
def __init__(self, buf): def __init__(self, buf):
""" """
Parse image from file-like object containing ico file data Parse image from file-like object containing ico file data

View File

@ -25,6 +25,7 @@
# #
import atexit import atexit
import builtins
import io import io
import logging import logging
import math import math
@ -34,29 +35,15 @@ import struct
import sys import sys
import tempfile import tempfile
import warnings import warnings
from collections.abc import Callable, MutableMapping
from pathlib import Path
# VERSION was removed in Pillow 6.0.0. # VERSION was removed in Pillow 6.0.0.
# PILLOW_VERSION was removed in Pillow 7.0.0. # PILLOW_VERSION was removed in Pillow 7.0.0.
# Use __version__ instead. # Use __version__ instead.
from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins from . import ImageMode, TiffTags, UnidentifiedImageError, __version__, _plugins
from ._binary import i8, i32le from ._binary import i8, i32le
from ._util import deferred_error, isPath, isStringType, py3 from ._util import deferred_error, isPath
try:
import builtins
except ImportError:
import __builtin__
builtins = __builtin__
try:
# Python 3
from collections.abc import Callable, MutableMapping
except ImportError:
# Python 2.7
from collections import Callable, MutableMapping
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -101,22 +88,6 @@ except ImportError as v:
) )
elif str(v).startswith("The _imaging extension"): elif str(v).startswith("The _imaging extension"):
warnings.warn(str(v), RuntimeWarning) warnings.warn(str(v), RuntimeWarning)
elif "Symbol not found: _PyUnicodeUCS2_" in str(v):
# should match _PyUnicodeUCS2_FromString and
# _PyUnicodeUCS2_AsLatin1String
warnings.warn(
"The _imaging extension was built for Python with UCS2 support; "
"recompile Pillow or build Python --without-wide-unicode. ",
RuntimeWarning,
)
elif "Symbol not found: _PyUnicodeUCS4_" in str(v):
# should match _PyUnicodeUCS4_FromString and
# _PyUnicodeUCS4_AsLatin1String
warnings.warn(
"The _imaging extension was built for Python with UCS4 support; "
"recompile Pillow or build Python --with-wide-unicode. ",
RuntimeWarning,
)
# Fail here anyway. Don't let people run with a mostly broken Pillow. # Fail here anyway. Don't let people run with a mostly broken Pillow.
# see docs/porting.rst # see docs/porting.rst
raise raise
@ -129,18 +100,6 @@ try:
except ImportError: except ImportError:
cffi = None cffi = None
try:
from pathlib import Path
HAS_PATHLIB = True
except ImportError:
try:
from pathlib2 import Path
HAS_PATHLIB = True
except ImportError:
HAS_PATHLIB = False
def isImageType(t): def isImageType(t):
""" """
@ -447,7 +406,7 @@ def _getdecoder(mode, decoder_name, args, extra=()):
decoder = getattr(core, decoder_name + "_decoder") decoder = getattr(core, decoder_name + "_decoder")
return decoder(mode, *args + extra) return decoder(mode, *args + extra)
except AttributeError: except AttributeError:
raise IOError("decoder %s not available" % decoder_name) raise OSError("decoder %s not available" % decoder_name)
def _getencoder(mode, encoder_name, args, extra=()): def _getencoder(mode, encoder_name, args, extra=()):
@ -468,7 +427,7 @@ def _getencoder(mode, encoder_name, args, extra=()):
encoder = getattr(core, encoder_name + "_encoder") encoder = getattr(core, encoder_name + "_encoder")
return encoder(mode, *args + extra) return encoder(mode, *args + extra)
except AttributeError: except AttributeError:
raise IOError("encoder %s not available" % encoder_name) raise OSError("encoder %s not available" % encoder_name)
# -------------------------------------------------------------------- # --------------------------------------------------------------------
@ -479,7 +438,7 @@ def coerce_e(value):
return value if isinstance(value, _E) else _E(value) return value if isinstance(value, _E) else _E(value)
class _E(object): class _E:
def __init__(self, data): def __init__(self, data):
self.data = data self.data = data
@ -520,7 +479,7 @@ def _getscaleoffset(expr):
# Implementation wrapper # Implementation wrapper
class Image(object): class Image:
""" """
This class represents an image object. To create This class represents an image object. To create
:py:class:`~PIL.Image.Image` objects, use the appropriate factory :py:class:`~PIL.Image.Image` objects, use the appropriate factory
@ -662,10 +621,6 @@ class Image(object):
and self.tobytes() == other.tobytes() and self.tobytes() == other.tobytes()
) )
def __ne__(self, other):
eq = self == other
return not eq
def __repr__(self): def __repr__(self):
return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % ( return "<%s.%s image mode=%s size=%dx%d at 0x%X>" % (
self.__class__.__module__, self.__class__.__module__,
@ -1335,10 +1290,7 @@ class Image(object):
self.load() self.load()
try: try:
if py3: return list(self.im.getpalette())
return list(self.im.getpalette())
else:
return [i8(c) for c in self.im.getpalette()]
except ValueError: except ValueError:
return None # no palette return None # no palette
@ -1489,7 +1441,7 @@ class Image(object):
raise ValueError("cannot determine region size; use 4-item box") raise ValueError("cannot determine region size; use 4-item box")
box += (box[0] + size[0], box[1] + size[1]) box += (box[0] + size[0], box[1] + size[1])
if isStringType(im): if isinstance(im, str):
from . import ImageColor from . import ImageColor
im = ImageColor.getcolor(im, self.mode) im = ImageColor.getcolor(im, self.mode)
@ -1689,10 +1641,7 @@ class Image(object):
palette = ImagePalette.raw(data.rawmode, data.palette) palette = ImagePalette.raw(data.rawmode, data.palette)
else: else:
if not isinstance(data, bytes): if not isinstance(data, bytes):
if py3: data = bytes(data)
data = bytes(data)
else:
data = "".join(chr(x) for x in data)
palette = ImagePalette.raw(rawmode, data) palette = ImagePalette.raw(rawmode, data)
self.mode = "PA" if "A" in self.mode else "P" self.mode = "PA" if "A" in self.mode else "P"
self.palette = palette self.palette = palette
@ -2024,7 +1973,7 @@ class Image(object):
if isPath(fp): if isPath(fp):
filename = fp filename = fp
open_fp = True open_fp = True
elif HAS_PATHLIB and isinstance(fp, Path): elif isinstance(fp, Path):
filename = str(fp) filename = str(fp)
open_fp = True open_fp = True
if not filename and hasattr(fp, "name") and isPath(fp.name): if not filename and hasattr(fp, "name") and isPath(fp.name):
@ -2146,7 +2095,7 @@ class Image(object):
""" """
self.load() self.load()
if isStringType(channel): if isinstance(channel, str):
try: try:
channel = self.getbands().index(channel) channel = self.getbands().index(channel)
except ValueError: except ValueError:
@ -2411,12 +2360,12 @@ class Image(object):
# Abstract handlers. # Abstract handlers.
class ImagePointHandler(object): class ImagePointHandler:
# used as a mixin by point transforms (for use with im.point) # used as a mixin by point transforms (for use with im.point)
pass pass
class ImageTransformHandler(object): class ImageTransformHandler:
# used as a mixin by geometry transforms (for use with im.transform) # used as a mixin by geometry transforms (for use with im.transform)
pass pass
@ -2474,7 +2423,7 @@ def new(mode, size, color=0):
# don't initialize # don't initialize
return Image()._new(core.new(mode, size)) return Image()._new(core.new(mode, size))
if isStringType(color): if isinstance(color, str):
# css3-style specifier # css3-style specifier
from . import ImageColor from . import ImageColor
@ -2736,7 +2685,7 @@ def open(fp, mode="r"):
exclusive_fp = False exclusive_fp = False
filename = "" filename = ""
if HAS_PATHLIB and isinstance(fp, Path): if isinstance(fp, Path):
filename = str(fp.resolve()) filename = str(fp.resolve())
elif isPath(fp): elif isPath(fp):
filename = fp filename = fp
@ -3302,11 +3251,6 @@ class Exif(MutableMapping):
def __contains__(self, tag): def __contains__(self, tag):
return tag in self._data or (self._info is not None and tag in self._info) return tag in self._data or (self._info is not None and tag in self._info)
if not py3:
def has_key(self, tag):
return tag in self
def __setitem__(self, tag, value): def __setitem__(self, tag, value):
if self._info is not None and tag in self._info: if self._info is not None and tag in self._info:
del self._info[tag] del self._info[tag]

View File

@ -15,12 +15,9 @@
# See the README file for information on usage and redistribution. See # See the README file for information on usage and redistribution. See
# below for the original description. # below for the original description.
from __future__ import print_function
import sys import sys
from PIL import Image from PIL import Image
from PIL._util import isStringType
try: try:
from PIL import _imagingcms from PIL import _imagingcms
@ -152,7 +149,7 @@ for flag in FLAGS.values():
# Profile. # Profile.
class ImageCmsProfile(object): class ImageCmsProfile:
def __init__(self, profile): def __init__(self, profile):
""" """
:param profile: Either a string representing a filename, :param profile: Either a string representing a filename,
@ -161,7 +158,7 @@ class ImageCmsProfile(object):
""" """
if isStringType(profile): if isinstance(profile, str):
self._set(core.profile_open(profile), profile) self._set(core.profile_open(profile), profile)
elif hasattr(profile, "read"): elif hasattr(profile, "read"):
self._set(core.profile_frombytes(profile.read())) self._set(core.profile_frombytes(profile.read()))
@ -374,7 +371,7 @@ def profileToProfile(
imOut = None imOut = None
else: else:
imOut = transform.apply(im) imOut = transform.apply(im)
except (IOError, TypeError, ValueError) as v: except (OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
return imOut return imOut
@ -398,7 +395,7 @@ def getOpenProfile(profileFilename):
try: try:
return ImageCmsProfile(profileFilename) return ImageCmsProfile(profileFilename)
except (IOError, TypeError, ValueError) as v: except (OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
@ -479,7 +476,7 @@ def buildTransform(
return ImageCmsTransform( return ImageCmsTransform(
inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags inputProfile, outputProfile, inMode, outMode, renderingIntent, flags=flags
) )
except (IOError, TypeError, ValueError) as v: except (OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
@ -590,7 +587,7 @@ def buildProofTransform(
proofRenderingIntent, proofRenderingIntent,
flags, flags,
) )
except (IOError, TypeError, ValueError) as v: except (OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
@ -733,9 +730,9 @@ def getProfileName(profile):
return (profile.profile.profile_description or "") + "\n" return (profile.profile.profile_description or "") + "\n"
if not manufacturer or len(model) > 30: if not manufacturer or len(model) > 30:
return model + "\n" return model + "\n"
return "%s - %s\n" % (model, manufacturer) return "{} - {}\n".format(model, manufacturer)
except (AttributeError, IOError, TypeError, ValueError) as v: except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
@ -775,7 +772,7 @@ def getProfileInfo(profile):
arr.append(elt) arr.append(elt)
return "\r\n\r\n".join(arr) + "\r\n\r\n" return "\r\n\r\n".join(arr) + "\r\n\r\n"
except (AttributeError, IOError, TypeError, ValueError) as v: except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
@ -803,7 +800,7 @@ def getProfileCopyright(profile):
if not isinstance(profile, ImageCmsProfile): if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile) profile = ImageCmsProfile(profile)
return (profile.profile.copyright or "") + "\n" return (profile.profile.copyright or "") + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v: except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
@ -831,7 +828,7 @@ def getProfileManufacturer(profile):
if not isinstance(profile, ImageCmsProfile): if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile) profile = ImageCmsProfile(profile)
return (profile.profile.manufacturer or "") + "\n" return (profile.profile.manufacturer or "") + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v: except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
@ -860,7 +857,7 @@ def getProfileModel(profile):
if not isinstance(profile, ImageCmsProfile): if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile) profile = ImageCmsProfile(profile)
return (profile.profile.model or "") + "\n" return (profile.profile.model or "") + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v: except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
@ -889,7 +886,7 @@ def getProfileDescription(profile):
if not isinstance(profile, ImageCmsProfile): if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile) profile = ImageCmsProfile(profile)
return (profile.profile.profile_description or "") + "\n" return (profile.profile.profile_description or "") + "\n"
except (AttributeError, IOError, TypeError, ValueError) as v: except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
@ -928,7 +925,7 @@ def getDefaultIntent(profile):
if not isinstance(profile, ImageCmsProfile): if not isinstance(profile, ImageCmsProfile):
profile = ImageCmsProfile(profile) profile = ImageCmsProfile(profile)
return profile.profile.rendering_intent return profile.profile.rendering_intent
except (AttributeError, IOError, TypeError, ValueError) as v: except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)
@ -979,7 +976,7 @@ def isIntentSupported(profile, intent, direction):
return 1 return 1
else: else:
return -1 return -1
except (AttributeError, IOError, TypeError, ValueError) as v: except (AttributeError, OSError, TypeError, ValueError) as v:
raise PyCMSError(v) raise PyCMSError(v)

View File

@ -34,7 +34,6 @@ import math
import numbers import numbers
from . import Image, ImageColor from . import Image, ImageColor
from ._util import isStringType
""" """
@ -45,7 +44,7 @@ directly.
""" """
class ImageDraw(object): class ImageDraw:
def __init__(self, im, mode=None): def __init__(self, im, mode=None):
""" """
Create a drawing instance. Create a drawing instance.
@ -107,13 +106,13 @@ class ImageDraw(object):
ink = self.ink ink = self.ink
else: else:
if ink is not None: if ink is not None:
if isStringType(ink): if isinstance(ink, str):
ink = ImageColor.getcolor(ink, self.mode) ink = ImageColor.getcolor(ink, self.mode)
if self.palette and not isinstance(ink, numbers.Number): if self.palette and not isinstance(ink, numbers.Number):
ink = self.palette.getcolor(ink) ink = self.palette.getcolor(ink)
ink = self.draw.draw_ink(ink) ink = self.draw.draw_ink(ink)
if fill is not None: if fill is not None:
if isStringType(fill): if isinstance(fill, str):
fill = ImageColor.getcolor(fill, self.mode) fill = ImageColor.getcolor(fill, self.mode)
if self.palette and not isinstance(fill, numbers.Number): if self.palette and not isinstance(fill, numbers.Number):
fill = self.palette.getcolor(fill) fill = self.palette.getcolor(fill)
@ -314,7 +313,7 @@ class ImageDraw(object):
language=language, language=language,
stroke_width=stroke_width, stroke_width=stroke_width,
*args, *args,
**kwargs **kwargs,
) )
coord = coord[0] + offset[0], coord[1] + offset[1] coord = coord[0] + offset[0], coord[1] + offset[1]
except AttributeError: except AttributeError:
@ -327,7 +326,7 @@ class ImageDraw(object):
language, language,
stroke_width, stroke_width,
*args, *args,
**kwargs **kwargs,
) )
except TypeError: except TypeError:
mask = font.getmask(text) mask = font.getmask(text)

View File

@ -19,25 +19,25 @@
from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath from . import Image, ImageColor, ImageDraw, ImageFont, ImagePath
class Pen(object): class Pen:
def __init__(self, color, width=1, opacity=255): def __init__(self, color, width=1, opacity=255):
self.color = ImageColor.getrgb(color) self.color = ImageColor.getrgb(color)
self.width = width self.width = width
class Brush(object): class Brush:
def __init__(self, color, opacity=255): def __init__(self, color, opacity=255):
self.color = ImageColor.getrgb(color) self.color = ImageColor.getrgb(color)
class Font(object): class Font:
def __init__(self, color, file, size=12): def __init__(self, color, file, size=12):
# FIXME: add support for bitmap fonts # FIXME: add support for bitmap fonts
self.color = ImageColor.getrgb(color) self.color = ImageColor.getrgb(color)
self.font = ImageFont.truetype(file, size) self.font = ImageFont.truetype(file, size)
class Draw(object): class Draw:
def __init__(self, image, size=None, color=None): def __init__(self, image, size=None, color=None):
if not hasattr(image, "im"): if not hasattr(image, "im"):
image = Image.new(image, size, color) image = Image.new(image, size, color)

View File

@ -21,7 +21,7 @@
from . import Image, ImageFilter, ImageStat from . import Image, ImageFilter, ImageStat
class _Enhance(object): class _Enhance:
def enhance(self, factor): def enhance(self, factor):
""" """
Returns an enhanced image. Returns an enhanced image.

View File

@ -56,7 +56,7 @@ def raise_ioerror(error):
message = ERRORS.get(error) message = ERRORS.get(error)
if not message: if not message:
message = "decoder error %d" % error message = "decoder error %d" % error
raise IOError(message + " when reading image file") raise OSError(message + " when reading image file")
# #
@ -148,7 +148,7 @@ class ImageFile(Image.Image):
pixel = Image.Image.load(self) pixel = Image.Image.load(self)
if self.tile is None: if self.tile is None:
raise IOError("cannot load this image") raise OSError("cannot load this image")
if not self.tile: if not self.tile:
return pixel return pixel
@ -206,7 +206,7 @@ class ImageFile(Image.Image):
# we might need to reload the palette data. # we might need to reload the palette data.
if self.palette: if self.palette:
self.palette.dirty = 1 self.palette.dirty = 1
except (AttributeError, EnvironmentError, ImportError): except (AttributeError, OSError, ImportError):
self.map = None self.map = None
self.load_prepare() self.load_prepare()
@ -241,13 +241,13 @@ class ImageFile(Image.Image):
if LOAD_TRUNCATED_IMAGES: if LOAD_TRUNCATED_IMAGES:
break break
else: else:
raise IOError("image file is truncated") raise OSError("image file is truncated")
if not s: # truncated jpeg if not s: # truncated jpeg
if LOAD_TRUNCATED_IMAGES: if LOAD_TRUNCATED_IMAGES:
break break
else: else:
raise IOError( raise OSError(
"image file is truncated " "image file is truncated "
"(%d bytes not processed)" % len(b) "(%d bytes not processed)" % len(b)
) )
@ -325,7 +325,7 @@ class StubImageFile(ImageFile):
def load(self): def load(self):
loader = self._load() loader = self._load()
if loader is None: if loader is None:
raise IOError("cannot find loader for this %s file" % self.format) raise OSError("cannot find loader for this %s file" % self.format)
image = loader.load(self) image = loader.load(self)
assert image is not None assert image is not None
# become the other object (!) # become the other object (!)
@ -337,7 +337,7 @@ class StubImageFile(ImageFile):
raise NotImplementedError("StubImageFile subclass must implement _load") raise NotImplementedError("StubImageFile subclass must implement _load")
class Parser(object): class Parser:
""" """
Incremental image parser. This class implements the standard Incremental image parser. This class implements the standard
feed/close consumer interface. feed/close consumer interface.
@ -414,7 +414,7 @@ class Parser(object):
try: try:
with io.BytesIO(self.data) as fp: with io.BytesIO(self.data) as fp:
im = Image.open(fp) im = Image.open(fp)
except IOError: except OSError:
# traceback.print_exc() # traceback.print_exc()
pass # not enough data pass # not enough data
else: else:
@ -459,9 +459,9 @@ class Parser(object):
self.feed(b"") self.feed(b"")
self.data = self.decoder = None self.data = self.decoder = None
if not self.finished: if not self.finished:
raise IOError("image was incomplete") raise OSError("image was incomplete")
if not self.image: if not self.image:
raise IOError("cannot parse this image") raise OSError("cannot parse this image")
if self.data: if self.data:
# incremental parsing not possible; reopen the file # incremental parsing not possible; reopen the file
# not that we have all data # not that we have all data
@ -517,7 +517,7 @@ def _save(im, fp, tile, bufsize=0):
if s: if s:
break break
if s < 0: if s < 0:
raise IOError("encoder error %d when writing image file" % s) raise OSError("encoder error %d when writing image file" % s)
e.cleanup() e.cleanup()
else: else:
# slight speedup: compress to real file object # slight speedup: compress to real file object
@ -532,7 +532,7 @@ def _save(im, fp, tile, bufsize=0):
else: else:
s = e.encode_to_file(fh, bufsize) s = e.encode_to_file(fh, bufsize)
if s < 0: if s < 0:
raise IOError("encoder error %d when writing image file" % s) raise OSError("encoder error %d when writing image file" % s)
e.cleanup() e.cleanup()
if hasattr(fp, "flush"): if hasattr(fp, "flush"):
fp.flush() fp.flush()
@ -562,7 +562,7 @@ def _safe_read(fp, size):
return b"".join(data) return b"".join(data)
class PyCodecState(object): class PyCodecState:
def __init__(self): def __init__(self):
self.xsize = 0 self.xsize = 0
self.ysize = 0 self.ysize = 0
@ -573,7 +573,7 @@ class PyCodecState(object):
return (self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize) return (self.xoff, self.yoff, self.xoff + self.xsize, self.yoff + self.ysize)
class PyDecoder(object): class PyDecoder:
""" """
Python implementation of a format decoder. Override this class and Python implementation of a format decoder. Override this class and
add the decoding logic in the `decode` method. add the decoding logic in the `decode` method.

View File

@ -14,9 +14,6 @@
# #
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
from __future__ import division
import functools import functools
try: try:
@ -25,7 +22,7 @@ except ImportError: # pragma: no cover
numpy = None numpy = None
class Filter(object): class Filter:
pass pass
@ -498,7 +495,7 @@ class Color3DLUT(MultibandFilter):
r / (size1D - 1), r / (size1D - 1),
g / (size2D - 1), g / (size2D - 1),
b / (size3D - 1), b / (size3D - 1),
*values *values,
) )
else: else:
values = callback(*values) values = callback(*values)

View File

@ -31,13 +31,13 @@ import sys
from io import BytesIO from io import BytesIO
from . import Image from . import Image
from ._util import isDirectory, isPath, py3 from ._util import isDirectory, isPath
LAYOUT_BASIC = 0 LAYOUT_BASIC = 0
LAYOUT_RAQM = 1 LAYOUT_RAQM = 1
class _imagingft_not_installed(object): class _imagingft_not_installed:
# module placeholder # module placeholder
def __getattr__(self, id): def __getattr__(self, id):
raise ImportError("The _imagingft C module is not installed") raise ImportError("The _imagingft C module is not installed")
@ -65,7 +65,7 @@ except ImportError:
# -------------------------------------------------------------------- # --------------------------------------------------------------------
class ImageFont(object): class ImageFont:
"PIL font wrapper" "PIL font wrapper"
def _load_pilfont(self, filename): def _load_pilfont(self, filename):
@ -81,7 +81,7 @@ class ImageFont(object):
if image and image.mode in ("1", "L"): if image and image.mode in ("1", "L"):
break break
else: else:
raise IOError("cannot find glyph data file") raise OSError("cannot find glyph data file")
self.file = fullname self.file = fullname
@ -147,7 +147,7 @@ class ImageFont(object):
# <b>truetype</b> factory function to create font objects. # <b>truetype</b> factory function to create font objects.
class FreeTypeFont(object): class FreeTypeFont:
"FreeType font wrapper (requires _imagingft service)" "FreeType font wrapper (requires _imagingft service)"
def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None): def __init__(self, font=None, size=10, index=0, encoding="", layout_engine=None):
@ -544,7 +544,7 @@ class FreeTypeFont(object):
raise NotImplementedError("FreeType 2.9.1 or greater is required") raise NotImplementedError("FreeType 2.9.1 or greater is required")
class TransposedFont(object): class TransposedFont:
"Wrapper for writing rotated or mirrored text" "Wrapper for writing rotated or mirrored text"
def __init__(self, font, orientation=None): def __init__(self, font, orientation=None):
@ -640,7 +640,7 @@ def truetype(font=None, size=10, index=0, encoding="", layout_engine=None):
try: try:
return freetype(font) return freetype(font)
except IOError: except OSError:
if not isPath(font): if not isPath(font):
raise raise
ttf_filename = os.path.basename(font) ttf_filename = os.path.basename(font)
@ -697,15 +697,12 @@ def load_path(filename):
for directory in sys.path: for directory in sys.path:
if isDirectory(directory): if isDirectory(directory):
if not isinstance(filename, str): if not isinstance(filename, str):
if py3: filename = filename.decode("utf-8")
filename = filename.decode("utf-8")
else:
filename = filename.encode("utf-8")
try: try:
return load(os.path.join(directory, filename)) return load(os.path.join(directory, filename))
except IOError: except OSError:
pass pass
raise IOError("cannot find font file") raise OSError("cannot find font file")
def load_default(): def load_default():

View File

@ -15,15 +15,9 @@
# See the README file for information on usage and redistribution. # See the README file for information on usage and redistribution.
# #
import builtins
from . import Image, _imagingmath from . import Image, _imagingmath
from ._util import py3
try:
import builtins
except ImportError:
import __builtin__
builtins = __builtin__
VERBOSE = 0 VERBOSE = 0
@ -32,7 +26,7 @@ def _isconstant(v):
return isinstance(v, (int, float)) return isinstance(v, (int, float))
class _Operand(object): class _Operand:
"""Wraps an image operand, providing standard operators""" """Wraps an image operand, providing standard operators"""
def __init__(self, im): def __init__(self, im):
@ -101,11 +95,6 @@ class _Operand(object):
# an image is "true" if it contains at least one non-zero pixel # an image is "true" if it contains at least one non-zero pixel
return self.im.getbbox() is not None return self.im.getbbox() is not None
if not py3:
# Provide __nonzero__ for pre-Py3k
__nonzero__ = __bool__
del __bool__
def __abs__(self): def __abs__(self):
return self.apply("abs", self) return self.apply("abs", self)
@ -152,13 +141,6 @@ class _Operand(object):
def __rpow__(self, other): def __rpow__(self, other):
return self.apply("pow", other, self) return self.apply("pow", other, self)
if not py3:
# Provide __div__ and __rdiv__ for pre-Py3k
__div__ = __truediv__
__rdiv__ = __rtruediv__
del __truediv__
del __rtruediv__
# bitwise # bitwise
def __invert__(self): def __invert__(self):
return self.apply("invert", self) return self.apply("invert", self)

View File

@ -17,7 +17,7 @@
_modes = None _modes = None
class ModeDescriptor(object): class ModeDescriptor:
"""Wrapper for mode strings.""" """Wrapper for mode strings."""
def __init__(self, mode, bands, basemode, basetype): def __init__(self, mode, bands, basemode, basetype):

View File

@ -5,8 +5,6 @@
# #
# Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com> # Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
from __future__ import print_function
import re import re
from . import Image, _imagingmorph from . import Image, _imagingmorph
@ -27,7 +25,7 @@ MIRROR_MATRIX = [
# fmt: on # fmt: on
class LutBuilder(object): class LutBuilder:
"""A class for building a MorphLut from a descriptive language """A class for building a MorphLut from a descriptive language
The input patterns is a list of a strings sequences like these:: The input patterns is a list of a strings sequences like these::
@ -178,7 +176,7 @@ class LutBuilder(object):
return self.lut return self.lut
class MorphOp(object): class MorphOp:
"""A class for binary morphological operators""" """A class for binary morphological operators"""
def __init__(self, lut=None, op_name=None, patterns=None): def __init__(self, lut=None, op_name=None, patterns=None):

View File

@ -21,7 +21,6 @@ import functools
import operator import operator
from . import Image from . import Image
from ._util import isStringType
# #
# helpers # helpers
@ -39,7 +38,7 @@ def _border(border):
def _color(color, mode): def _color(color, mode):
if isStringType(color): if isinstance(color, str):
from . import ImageColor from . import ImageColor
color = ImageColor.getcolor(color, mode) color = ImageColor.getcolor(color, mode)
@ -55,7 +54,7 @@ def _lut(image, lut):
lut = lut + lut + lut lut = lut + lut + lut
return image.point(lut) return image.point(lut)
else: else:
raise IOError("not supported for this image mode") raise OSError("not supported for this image mode")
# #

View File

@ -21,7 +21,7 @@ import array
from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile from . import GimpGradientFile, GimpPaletteFile, ImageColor, PaletteFile
class ImagePalette(object): class ImagePalette:
""" """
Color palette for palette mapped images Color palette for palette mapped images
@ -216,6 +216,6 @@ def load(filename):
# traceback.print_exc() # traceback.print_exc()
pass pass
else: else:
raise IOError("cannot load palette") raise OSError("cannot load palette")
return lut # data, rawmode return lut # data, rawmode

View File

@ -20,7 +20,7 @@ import sys
from io import BytesIO from io import BytesIO
from . import Image from . import Image
from ._util import isPath, py3 from ._util import isPath
qt_versions = [["5", "PyQt5"], ["side2", "PySide2"]] qt_versions = [["5", "PyQt5"], ["side2", "PySide2"]]
@ -65,11 +65,7 @@ def fromqimage(im):
im.save(buffer, "ppm") im.save(buffer, "ppm")
b = BytesIO() b = BytesIO()
try: b.write(buffer.data())
b.write(buffer.data())
except TypeError:
# workaround for Python 2
b.write(str(buffer.data()))
buffer.close() buffer.close()
b.seek(0) b.seek(0)
@ -125,10 +121,7 @@ def _toqclass_helper(im):
# handle filename, if given instead of image name # handle filename, if given instead of image name
if hasattr(im, "toUtf8"): if hasattr(im, "toUtf8"):
# FIXME - is this really the best way to do this? # FIXME - is this really the best way to do this?
if py3: im = str(im.toUtf8(), "utf-8")
im = str(im.toUtf8(), "utf-8")
else:
im = unicode(im.toUtf8(), "utf-8") # noqa: F821
if isPath(im): if isPath(im):
im = Image.open(im) im = Image.open(im)

Some files were not shown because too many files have changed in this diff Show More