Merge pull request #2243 from wiredfool/pr_2192

Drop support for Python 2.6 and 3.2
This commit is contained in:
wiredfool 2016-11-22 12:31:03 +00:00 committed by GitHub
commit b1c816169e
24 changed files with 44 additions and 93 deletions

View File

@ -11,9 +11,7 @@ python:
- "pypy3" - "pypy3"
- 3.5 - 3.5
- 2.7 - 2.7
- 2.6
- "2.7_with_system_site_packages" # For PyQt4 - "2.7_with_system_site_packages" # For PyQt4
- 3.2
- 3.3 - 3.3
- 3.4 - 3.4
- nightly - nightly
@ -24,14 +22,10 @@ install:
- "travis_retry pip install cffi" - "travis_retry pip install cffi"
- "travis_retry pip install nose" - "travis_retry pip install nose"
- "travis_retry pip install check-manifest" - "travis_retry pip install check-manifest"
# Pyroma tests sometimes hang on PyPy and Python 2.6; skip for those # Pyroma tests sometimes hang on PyPy; skip
- if [ $TRAVIS_PYTHON_VERSION != "pypy" && $TRAVIS_PYTHON_VERSION != "2.6" ]; then travis_retry pip install pyroma; fi - if [ $TRAVIS_PYTHON_VERSION != "pypy" ]; then travis_retry pip install pyroma; fi
- if [ "$TRAVIS_PYTHON_VERSION" == "2.6" ]; then travis_retry pip install unittest2; fi - "travis_retry pip install coverage"
# Coverage 4.0 doesn't support Python 3.2
- if [ "$TRAVIS_PYTHON_VERSION" == "3.2" ]; then travis_retry pip install coverage==3.7.1; fi
- if [ "$TRAVIS_PYTHON_VERSION" != "3.2" ]; then travis_retry pip install coverage; fi
# docs only on python 2.7 # docs only on python 2.7
- if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then travis_retry pip install -r requirements.txt ; fi - if [ "$TRAVIS_PYTHON_VERSION" == "2.7" ]; then travis_retry pip install -r requirements.txt ; fi

View File

@ -73,7 +73,7 @@ class BmpImageFile(ImageFile.ImageFile):
read, seek = self.fp.read, self.fp.seek read, seek = self.fp.read, self.fp.seek
if header: if header:
seek(header) seek(header)
file_info = dict() file_info = {}
file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size) file_info['header_size'] = i32(read(4)) # read bmp header size @offset 14 (this is part of the header size)
file_info['direction'] = -1 file_info['direction'] = -1
# --------------------- If requested, read header at a specific position # --------------------- If requested, read header at a specific position

View File

@ -139,7 +139,7 @@ class IcoFile(object):
""" """
Get a list of all available icon sizes and color depths. Get a list of all available icon sizes and color depths.
""" """
return set((h['width'], h['height']) for h in self.entry) return {(h['width'], h['height']) for h in self.entry}
def getimage(self, size, bpp=False): def getimage(self, size, bpp=False):
""" """

View File

@ -408,7 +408,7 @@ def _fixup_dict(src_dict):
except: pass except: pass
return value return value
return dict((k, _fixup(v)) for k, v in src_dict.items()) return {k: _fixup(v) for k, v in src_dict.items()}
def _getexif(self): def _getexif(self):
@ -488,7 +488,7 @@ def _getmp(self):
rawmpentries = mp[0xB002] rawmpentries = mp[0xB002]
for entrynum in range(0, quant): for entrynum in range(0, quant):
unpackedentry = unpack_from( unpackedentry = unpack_from(
'{0}LLLHH'.format(endianness), rawmpentries, entrynum * 16) '{}LLLHH'.format(endianness), rawmpentries, entrynum * 16)
labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1', labels = ('Attribute', 'Size', 'DataOffset', 'EntryNo1',
'EntryNo2') 'EntryNo2')
mpentry = dict(zip(labels, unpackedentry)) mpentry = dict(zip(labels, unpackedentry))

View File

@ -132,7 +132,7 @@ COMPRESSION_INFO = {
34677: "tiff_sgilog24", 34677: "tiff_sgilog24",
} }
COMPRESSION_INFO_REV = dict((v, k) for (k, v) in COMPRESSION_INFO.items()) COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()}
OPEN_INFO = { OPEN_INFO = {
# (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample, # (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample,
@ -294,10 +294,6 @@ class IFDRational(Rational):
return return
elif denominator == 1: elif denominator == 1:
if sys.hexversion < 0x2070000 and isinstance(value, float):
# python 2.6 is different.
self._val = Fraction.from_float(value)
else:
self._val = Fraction(value) self._val = Fraction(value)
else: else:
self._val = Fraction(value, denominator) self._val = Fraction(value, denominator)
@ -592,7 +588,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
TYPES[idx] = name TYPES[idx] = name
size = struct.calcsize("=" + fmt) size = struct.calcsize("=" + fmt)
_load_dispatch[idx] = size, lambda self, data, legacy_api=True: ( _load_dispatch[idx] = size, lambda self, data, legacy_api=True: (
self._unpack("{0}{1}".format(len(data) // size, fmt), data)) self._unpack("{}{}".format(len(data) // size, fmt), data))
_write_dispatch[idx] = lambda self, *values: ( _write_dispatch[idx] = lambda self, *values: (
b"".join(self._pack(fmt, value) for value in values)) b"".join(self._pack(fmt, value) for value in values))
@ -624,7 +620,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
@_register_loader(5, 8) @_register_loader(5, 8)
def load_rational(self, data, legacy_api=True): def load_rational(self, data, legacy_api=True):
vals = self._unpack("{0}L".format(len(data) // 4), data) vals = self._unpack("{}L".format(len(data) // 4), data)
combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b) combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b)
return tuple(combine(num, denom) return tuple(combine(num, denom)
for num, denom in zip(vals[::2], vals[1::2])) for num, denom in zip(vals[::2], vals[1::2]))
@ -644,7 +640,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
@_register_loader(10, 8) @_register_loader(10, 8)
def load_signed_rational(self, data, legacy_api=True): def load_signed_rational(self, data, legacy_api=True):
vals = self._unpack("{0}l".format(len(data) // 4), data) vals = self._unpack("{}l".format(len(data) // 4), data)
combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b) combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b)
return tuple(combine(num, denom) return tuple(combine(num, denom)
for num, denom in zip(vals[::2], vals[1::2])) for num, denom in zip(vals[::2], vals[1::2]))
@ -1518,7 +1514,7 @@ class AppendingTiffWriter:
# JPEGQTables = 519 # JPEGQTables = 519
# JPEGDCTables = 520 # JPEGDCTables = 520
# JPEGACTables = 521 # JPEGACTables = 521
Tags = set((273, 288, 324, 519, 520, 521)) Tags = {273, 288, 324, 519, 520, 521}
def __init__(self, fn, new=False): def __init__(self, fn, new=False):
if hasattr(fn, 'read'): if hasattr(fn, 'read'):

View File

@ -418,13 +418,13 @@ TYPES = {}
# some of these are not in our TAGS_V2 dict and were included from tiff.h # some of these are not in our TAGS_V2 dict and were included from tiff.h
LIBTIFF_CORE = set([255, 256, 257, 258, 259, 262, 263, 266, 274, 277, LIBTIFF_CORE = {255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
278, 280, 281, 340, 341, 282, 283, 284, 286, 287, 278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
296, 297, 321, 320, 338, 32995, 322, 323, 32998, 296, 297, 321, 320, 338, 32995, 322, 323, 32998,
32996, 339, 32997, 330, 531, 530, 301, 532, 333, 32996, 339, 32997, 330, 531, 530, 301, 532, 333,
# as above # as above
269 # this has been in our tests forever, and works 269 # this has been in our tests forever, and works
]) }
LIBTIFF_CORE.remove(320) # Array of short, crashes LIBTIFF_CORE.remove(320) # Array of short, crashes
LIBTIFF_CORE.remove(301) # Array of short, crashes LIBTIFF_CORE.remove(301) # Array of short, crashes

View File

@ -10,10 +10,6 @@ Install::
pip install coverage nose pip install coverage nose
If you're using Python 2.6, there's one additional dependency::
pip install unittest2
Execution Execution
--------- ---------

View File

@ -5,11 +5,7 @@ from __future__ import print_function
import sys import sys
import tempfile import tempfile
import os import os
import unittest
if sys.version_info[:2] <= (2, 6):
import unittest2 as unittest
else:
import unittest
class PillowTestCase(unittest.TestCase): class PillowTestCase(unittest.TestCase):

View File

@ -180,9 +180,9 @@ class TestFileLibTiff(LibTiffTestCase):
# Get the list of the ones that we should be able to write # Get the list of the ones that we should be able to write
core_items = dict((tag, info) for tag, info in [(s, TiffTags.lookup(s)) for s core_items = {tag: info for tag, info in ((s, TiffTags.lookup(s)) for s
in TiffTags.LIBTIFF_CORE] in TiffTags.LIBTIFF_CORE)
if info.type is not None) if info.type is not None}
# Exclude ones that have special meaning # Exclude ones that have special meaning
# that we're already testing them # that we're already testing them

View File

@ -8,7 +8,7 @@ from helper import unittest, PillowTestCase, hopper
from PIL import Image, TiffImagePlugin, TiffTags from PIL import Image, TiffImagePlugin, TiffTags
from PIL.TiffImagePlugin import _limit_rational, IFDRational from PIL.TiffImagePlugin import _limit_rational, IFDRational
tag_ids = dict((info.name, info.value) for info in TiffTags.TAGS_V2.values()) tag_ids = {info.name: info.value for info in TiffTags.TAGS_V2.values()}
class TestFileTiffMetadata(PillowTestCase): class TestFileTiffMetadata(PillowTestCase):

View File

@ -1,5 +1,4 @@
from helper import unittest, PillowTestCase, hopper, py3 from helper import unittest, PillowTestCase, hopper, py3
import sys
class TestImageGetIm(PillowTestCase): class TestImageGetIm(PillowTestCase):
@ -11,13 +10,7 @@ class TestImageGetIm(PillowTestCase):
if py3: if py3:
self.assertIn("PyCapsule", type_repr) self.assertIn("PyCapsule", type_repr)
if sys.hexversion < 0x2070000: self.assertIsInstance(im.im.id, int)
# py2.6 x64, windows
target_types = (int, long)
else:
target_types = (int)
self.assertIsInstance(im.im.id, target_types)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -232,10 +232,10 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
def run_levels_case(self, i): def run_levels_case(self, i):
px = i.load() px = i.load()
for y in range(i.size[1]): for y in range(i.size[1]):
used_colors = set(px[x, y][0] for x in range(i.size[0])) used_colors = {px[x, y][0] for x in range(i.size[0])}
self.assertEqual(256, len(used_colors), self.assertEqual(256, len(used_colors),
'All colors should present in resized image. ' 'All colors should present in resized image. '
'Only {0} on {1} line.'.format(len(used_colors), y)) 'Only {} on {} line.'.format(len(used_colors), y))
@unittest.skip("current implementation isn't precise enough") @unittest.skip("current implementation isn't precise enough")
def test_levels_rgba(self): def test_levels_rgba(self):
@ -270,7 +270,7 @@ class CoreResampleAlphaCorrectTest(PillowTestCase):
for y in range(i.size[1]): for y in range(i.size[1]):
for x in range(i.size[0]): for x in range(i.size[0]):
if px[x, y][-1] != 0 and px[x, y][:-1] != clean_pixel: if px[x, y][-1] != 0 and px[x, y][:-1] != clean_pixel:
message = 'pixel at ({0}, {1}) is differ:\n{2}\n{3}'\ message = 'pixel at ({}, {}) is differ:\n{}\n{}'\
.format(x, y, px[x, y], clean_pixel) .format(x, y, px[x, y], clean_pixel)
self.assertEqual(px[x, y][:3], clean_pixel, message) self.assertEqual(px[x, y][:3], clean_pixel, message)

View File

@ -69,10 +69,10 @@ class TestImagingCoreResize(PillowTestCase):
for f in [Image.LINEAR, Image.BOX, Image.BILINEAR, Image.HAMMING, for f in [Image.LINEAR, Image.BOX, Image.BILINEAR, Image.HAMMING,
Image.BICUBIC, Image.LANCZOS]: Image.BICUBIC, Image.LANCZOS]:
# samples resized with current filter # samples resized with current filter
references = dict( references = {
(name, self.resize(ch, (4, 4), f)) name: self.resize(ch, (4, 4), f)
for name, ch in samples.items() for name, ch in samples.items()
) }
for mode, channels_set in [ for mode, channels_set in [
('RGB', ('blank', 'filled', 'dirty')), ('RGB', ('blank', 'filled', 'dirty')),

View File

@ -29,7 +29,7 @@ class TestToQImage(PillowQtTestCase, PillowTestCase):
self.assertFalse(data.isNull()) self.assertFalse(data.isNull())
# Test saving the file # Test saving the file
tempfile = self.tempfile('temp_{0}.png'.format(mode)) tempfile = self.tempfile('temp_{}.png'.format(mode))
data.save(tempfile) data.save(tempfile)

View File

@ -19,7 +19,7 @@ class TestToQPixmap(PillowQPixmapTestCase, PillowTestCase):
self.assertFalse(data.isNull()) self.assertFalse(data.isNull())
# Test saving the file # Test saving the file
tempfile = self.tempfile('temp_{0}.png'.format(mode)) tempfile = self.tempfile('temp_{}.png'.format(mode))
data.save(tempfile) data.save(tempfile)

View File

@ -75,11 +75,7 @@ if sys.platform.startswith('win32'):
memcpy(bp, ctypes.byref(bf), ctypes.sizeof(bf)) memcpy(bp, ctypes.byref(bf), ctypes.sizeof(bf))
memcpy(bp + ctypes.sizeof(bf), ctypes.byref(bi), bi.biSize) memcpy(bp + ctypes.sizeof(bf), ctypes.byref(bi), bi.biSize)
memcpy(bp + bf.bfOffBits, pixels, bi.biSizeImage) memcpy(bp + bf.bfOffBits, pixels, bi.biSizeImage)
try:
return bytearray(buf) return bytearray(buf)
except ValueError:
# py2.6
return buffer(buf)[:]
class TestImageWinPointers(PillowTestCase): class TestImageWinPointers(PillowTestCase):
def test_pointer(self): def test_pointer(self):

View File

@ -3073,11 +3073,7 @@ _getattr_id(ImagingObject* self, void* closure)
static PyObject* static PyObject*
_getattr_ptr(ImagingObject* self, void* closure) _getattr_ptr(ImagingObject* self, void* closure)
{ {
#if PY_VERSION_HEX >= 0x02070000
return PyCapsule_New(self->image, IMAGING_MAGIC, NULL); return PyCapsule_New(self->image, IMAGING_MAGIC, NULL);
#else
return PyCObject_FromVoidPtrAndDesc(self->image, IMAGING_MAGIC, NULL);
#endif
} }
static PyObject* static PyObject*

View File

@ -15,7 +15,9 @@ Notes
.. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7. .. note:: Pillow < 2.0.0 supports Python versions 2.4, 2.5, 2.6, 2.7.
.. note:: Pillow >= 2.0.0 supports Python versions 2.6, 2.7, 3.2, 3.3, 3.4, 3.5 .. note:: Pillow >= 2.0.0 < 3.5.0 supports Python versions 2.6, 2.7, 3.2, 3.3, 3.4, 3.5
.. note:: Pillow >= 3.5.0 supports Python versions 2.7, 3.3, 3.4, 3.5
Basic Installation Basic Installation
------------------ ------------------

2
py3.h
View File

@ -1,5 +1,5 @@
/* /*
Python3 definition file to consistently map the code to Python 2.6 or Python3 definition file to consistently map the code to Python 2 or
Python 3. Python 3.
PyInt and PyLong were merged into PyLong in Python 3, so all PyInt functions PyInt and PyLong were merged into PyLong in Python 3, so all PyInt functions

View File

@ -74,20 +74,7 @@ def _find_include_file(self, include):
def _find_library_file(self, library): def _find_library_file(self, library):
# Fix for 3.2.x <3.2.4, 3.3.0, shared lib extension is the python shared ret = self.compiler.find_library_file(self.compiler.library_dirs, library)
# lib extension, not the system shared lib extension: e.g. .cpython-33.so
# vs .so. See Python bug http://bugs.python.org/16754
if 'cpython' in self.compiler.shared_lib_extension:
_dbg('stripping cpython from shared library extension %s',
self.compiler.shared_lib_extension)
existing = self.compiler.shared_lib_extension
self.compiler.shared_lib_extension = "." + existing.split('.')[-1]
ret = self.compiler.find_library_file(self.compiler.library_dirs,
library)
self.compiler.shared_lib_extension = existing
else:
ret = self.compiler.find_library_file(self.compiler.library_dirs,
library)
if ret: if ret:
_dbg('Found library %s at %s', (library, ret)) _dbg('Found library %s at %s', (library, ret))
else: else:
@ -147,7 +134,7 @@ class pil_build_ext(build_ext):
features = ['zlib', 'jpeg', 'tiff', 'freetype', 'lcms', 'webp', features = ['zlib', 'jpeg', 'tiff', 'freetype', 'lcms', 'webp',
'webpmux', 'jpeg2000', 'imagequant'] 'webpmux', 'jpeg2000', 'imagequant']
required = set(['jpeg', 'zlib']) required = {'jpeg', 'zlib'}
def __init__(self): def __init__(self):
for f in self.features: for f in self.features:
@ -770,10 +757,8 @@ try:
"Topic :: Multimedia :: Graphics :: Graphics Conversion", "Topic :: Multimedia :: Graphics :: Graphics Conversion",
"Topic :: Multimedia :: Graphics :: Viewers", "Topic :: Multimedia :: Graphics :: Viewers",
"Programming Language :: Python :: 2", "Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7", "Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.2",
"Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.3",
"Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.5",

View File

@ -4,7 +4,7 @@
# and then run "tox" from this directory. # and then run "tox" from this directory.
[tox] [tox]
envlist = py26, py27, py32, py33, py34, py35 envlist = py27, py33, py34, py35
[testenv] [testenv]
commands = commands =

View File

@ -18,9 +18,6 @@ def setup_vms():
% (py, arch, VIRT_BASE, py, arch)) % (py, arch, VIRT_BASE, py, arch))
ret.append("%s%s%s\Scripts\pip.exe install nose" % ret.append("%s%s%s\Scripts\pip.exe install nose" %
(VIRT_BASE, py, arch)) (VIRT_BASE, py, arch))
if py == '26':
ret.append("%s%s%s\Scripts\pip.exe install unittest2" %
(VIRT_BASE, py, arch))
return "\n".join(ret) return "\n".join(ret)

View File

@ -31,7 +31,7 @@ The build routines expect Python to be installed at C:\PythonXX for
Download Python 3.4, install it, and add it to the path. This is the Download Python 3.4, install it, and add it to the path. This is the
Python that we will use to bootstrap the build process. (The download Python that we will use to bootstrap the build process. (The download
routines are using 3.2+ features, and installing 3.4 gives us pip and routines are using 3 features, and installing 3.4 gives us pip and
virtualenv as well, reducing the number of packages that we need to virtualenv as well, reducing the number of packages that we need to
install.) install.)

View File

@ -2,7 +2,7 @@ from fetch import fetch
import os import os
if __name__ == '__main__': if __name__ == '__main__':
for version in ['2.6.6', '2.7.10', '3.2.5', '3.3.5', '3.4.3']: for version in ['2.7.10', '3.3.5', '3.4.3']:
for platform in ['', '.amd64']: for platform in ['', '.amd64']:
for extension in ['', '.asc']: for extension in ['', '.asc']:
fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s' fetch('https://www.python.org/ftp/python/%s/python-%s%s.msi%s'