Skip to content

Commit 1bc67c9

Browse files
authored
Merge pull request #4700 from nulano/features-version
2 parents 7b759e1 + 24672a2 commit 1bc67c9

18 files changed

Lines changed: 223 additions & 47 deletions

Tests/test_features.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import io
2+
import re
23

34
import pytest
45
from PIL import features
@@ -21,6 +22,27 @@ def test_check():
2122
assert features.check_feature(feature) == features.check(feature)
2223

2324

25+
def test_version():
26+
# Check the correctness of the convenience function
27+
# and the format of version numbers
28+
29+
def test(name, function):
30+
version = features.version(name)
31+
if not features.check(name):
32+
assert version is None
33+
else:
34+
assert function(name) == version
35+
if name != "PIL":
36+
assert version is None or re.search(r"\d+(\.\d+)*$", version)
37+
38+
for module in features.modules:
39+
test(module, features.version_module)
40+
for codec in features.codecs:
41+
test(codec, features.version_codec)
42+
for feature in features.features:
43+
test(feature, features.version_feature)
44+
45+
2446
@skip_unless_feature("webp")
2547
def test_webp_transparency():
2648
assert features.check("transp_webp") != _webp.WebPDecoderBuggyAlpha()
@@ -37,9 +59,22 @@ def test_webp_anim():
3759
assert features.check("webp_anim") == _webp.HAVE_WEBPANIM
3860

3961

62+
@skip_unless_feature("libjpeg_turbo")
63+
def test_libjpeg_turbo_version():
64+
assert re.search(r"\d+\.\d+\.\d+$", features.version("libjpeg_turbo"))
65+
66+
67+
@skip_unless_feature("libimagequant")
68+
def test_libimagequant_version():
69+
assert re.search(r"\d+\.\d+\.\d+$", features.version("libimagequant"))
70+
71+
4072
def test_check_modules():
4173
for feature in features.modules:
4274
assert features.check_module(feature) in [True, False]
75+
76+
77+
def test_check_codecs():
4378
for feature in features.codecs:
4479
assert features.check_codec(feature) in [True, False]
4580

@@ -64,6 +99,8 @@ def test_unsupported_codec():
6499
# Act / Assert
65100
with pytest.raises(ValueError):
66101
features.check_codec(codec)
102+
with pytest.raises(ValueError):
103+
features.version_codec(codec)
67104

68105

69106
def test_unsupported_module():
@@ -72,6 +109,8 @@ def test_unsupported_module():
72109
# Act / Assert
73110
with pytest.raises(ValueError):
74111
features.check_module(module)
112+
with pytest.raises(ValueError):
113+
features.version_module(module)
75114

76115

77116
def test_pilinfo():

Tests/test_file_icns.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22
import sys
33

44
import pytest
5-
from PIL import IcnsImagePlugin, Image
5+
from PIL import IcnsImagePlugin, Image, features
66

77
from .helper import assert_image_equal, assert_image_similar
88

99
# sample icon file
1010
TEST_FILE = "Tests/images/pillow.icns"
1111

12-
ENABLE_JPEG2K = hasattr(Image.core, "jp2klib_version")
12+
ENABLE_JPEG2K = features.check_codec("jpg_2000")
1313

1414

1515
def test_sanity():

Tests/test_file_jpeg.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from io import BytesIO
44

55
import pytest
6-
from PIL import ExifTags, Image, ImageFile, JpegImagePlugin
6+
from PIL import ExifTags, Image, ImageFile, JpegImagePlugin, features
77

88
from .helper import (
99
assert_image,
@@ -41,7 +41,7 @@ def gen_random_image(self, size, mode="RGB"):
4141
def test_sanity(self):
4242

4343
# internal version number
44-
assert re.search(r"\d+\.\d+$", Image.core.jpeglib_version)
44+
assert re.search(r"\d+\.\d+$", features.version_codec("jpg"))
4545

4646
with Image.open(TEST_FILE) as im:
4747
im.load()

Tests/test_file_jpeg2k.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from io import BytesIO
33

44
import pytest
5-
from PIL import Image, ImageFile, Jpeg2KImagePlugin
5+
from PIL import Image, ImageFile, Jpeg2KImagePlugin, features
66

77
from .helper import (
88
assert_image_equal,
@@ -35,7 +35,7 @@ def roundtrip(im, **options):
3535

3636
def test_sanity():
3737
# Internal version number
38-
assert re.search(r"\d+\.\d+\.\d+$", Image.core.jp2klib_version)
38+
assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("jpg_2000"))
3939

4040
with Image.open("Tests/images/test-card-lossless.jp2") as im:
4141
px = im.load()

Tests/test_file_libtiff.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import itertools
44
import logging
55
import os
6+
import re
67
from collections import namedtuple
78
from ctypes import c_float
89

910
import pytest
10-
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags
11+
from PIL import Image, ImageFilter, TiffImagePlugin, TiffTags, features
1112

1213
from .helper import (
1314
assert_image_equal,
@@ -47,6 +48,9 @@ def _assert_noerr(self, tmp_path, im):
4748

4849

4950
class TestFileLibTiff(LibTiffTestCase):
51+
def test_version(self):
52+
assert re.search(r"\d+\.\d+\.\d+$", features.version_codec("libtiff"))
53+
5054
def test_g4_tiff(self, tmp_path):
5155
"""Test the ordinary file path load path"""
5256

Tests/test_file_png.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from io import BytesIO
44

55
import pytest
6-
from PIL import Image, ImageFile, PngImagePlugin
6+
from PIL import Image, ImageFile, PngImagePlugin, features
77

88
from .helper import (
99
PillowLeakTestCase,
@@ -73,7 +73,7 @@ def get_chunks(self, filename):
7373
def test_sanity(self, tmp_path):
7474

7575
# internal version number
76-
assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", Image.core.zlib_version)
76+
assert re.search(r"\d+\.\d+\.\d+(\.\d+)?$", features.version_codec("zlib"))
7777

7878
test_file = str(tmp_path / "temp.png")
7979

Tests/test_file_webp.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import io
2+
import re
23

34
import pytest
4-
from PIL import Image, WebPImagePlugin
5+
from PIL import Image, WebPImagePlugin, features
56

67
from .helper import (
78
assert_image_similar,
@@ -38,6 +39,7 @@ def setup_method(self):
3839
def test_version(self):
3940
_webp.WebPDecoderVersion()
4041
_webp.WebPDecoderBuggyAlpha()
42+
assert re.search(r"\d+\.\d+\.\d+$", features.version_module("webp"))
4143

4244
def test_read_rgb(self):
4345
"""

Tests/test_imagecms.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from io import BytesIO
55

66
import pytest
7-
from PIL import Image, ImageMode
7+
from PIL import Image, ImageMode, features
88

99
from .helper import assert_image, assert_image_equal, assert_image_similar, hopper
1010

@@ -46,7 +46,7 @@ def test_sanity():
4646
assert list(map(type, v)) == [str, str, str, str]
4747

4848
# internal version number
49-
assert re.search(r"\d+\.\d+$", ImageCms.core.littlecms_version)
49+
assert re.search(r"\d+\.\d+$", features.version_module("littlecms2"))
5050

5151
skip_missing()
5252
i = ImageCms.profileToProfile(hopper(), SRGB, SRGB)

Tests/test_imagefont.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from io import BytesIO
88

99
import pytest
10-
from PIL import Image, ImageDraw, ImageFont
10+
from PIL import Image, ImageDraw, ImageFont, features
1111

1212
from .helper import (
1313
assert_image_equal,
@@ -41,7 +41,7 @@ class TestImageFont:
4141

4242
@classmethod
4343
def setup_class(self):
44-
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
44+
freetype = distutils.version.StrictVersion(features.version_module("freetype2"))
4545

4646
self.metrics = self.METRICS["Default"]
4747
for conditions, metrics in self.METRICS.items():
@@ -68,7 +68,7 @@ def get_font(self):
6868
)
6969

7070
def test_sanity(self):
71-
assert re.search(r"\d+\.\d+\.\d+$", ImageFont.core.freetype2_version)
71+
assert re.search(r"\d+\.\d+\.\d+$", features.version_module("freetype2"))
7272

7373
def test_font_properties(self):
7474
ttf = self.get_font()
@@ -620,7 +620,7 @@ def test_complex_font_settings(self):
620620
def test_variation_get(self):
621621
font = self.get_font()
622622

623-
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
623+
freetype = distutils.version.StrictVersion(features.version_module("freetype2"))
624624
if freetype < "2.9.1":
625625
with pytest.raises(NotImplementedError):
626626
font.get_variation_names()
@@ -692,7 +692,7 @@ def _check_text(self, font, path, epsilon):
692692
def test_variation_set_by_name(self):
693693
font = self.get_font()
694694

695-
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
695+
freetype = distutils.version.StrictVersion(features.version_module("freetype2"))
696696
if freetype < "2.9.1":
697697
with pytest.raises(NotImplementedError):
698698
font.set_variation_by_name("Bold")
@@ -716,7 +716,7 @@ def test_variation_set_by_name(self):
716716
def test_variation_set_by_axes(self):
717717
font = self.get_font()
718718

719-
freetype = distutils.version.StrictVersion(ImageFont.core.freetype2_version)
719+
freetype = distutils.version.StrictVersion(features.version_module("freetype2"))
720720
if freetype < "2.9.1":
721721
with pytest.raises(NotImplementedError):
722722
font.set_variation_by_axes([100])

docs/reference/features.rst

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The :py:mod:`PIL.features` module can be used to detect which Pillow features ar
88

99
.. autofunction:: PIL.features.pilinfo
1010
.. autofunction:: PIL.features.check
11+
.. autofunction:: PIL.features.version
1112
.. autofunction:: PIL.features.get_supported
1213

1314
Modules
@@ -16,45 +17,50 @@ Modules
1617
Support for the following modules can be checked:
1718

1819
* ``pil``: The Pillow core module, required for all functionality.
19-
* ``tkinter``: Tkinter support.
20+
* ``tkinter``: Tkinter support. Version number not available.
2021
* ``freetype2``: FreeType font support via :py:func:`PIL.ImageFont.truetype`.
2122
* ``littlecms2``: LittleCMS 2 support via :py:mod:`PIL.ImageCms`.
2223
* ``webp``: WebP image support.
2324

2425
.. autofunction:: PIL.features.check_module
26+
.. autofunction:: PIL.features.version_module
2527
.. autofunction:: PIL.features.get_supported_modules
2628

2729
Codecs
2830
------
2931

30-
These are only checked during Pillow compilation.
32+
Support for these is only checked during Pillow compilation.
3133
If the required library was uninstalled from the system, the ``pil`` core module may fail to load instead.
34+
Except for ``jpg``, the version number is checked at run-time.
3235

3336
Support for the following codecs can be checked:
3437

35-
* ``jpg``: (compile time) Libjpeg support, required for JPEG based image formats.
38+
* ``jpg``: (compile time) Libjpeg support, required for JPEG based image formats. Only compile time version number is available.
3639
* ``jpg_2000``: (compile time) OpenJPEG support, required for JPEG 2000 image formats.
3740
* ``zlib``: (compile time) Zlib support, required for zlib compressed formats, such as PNG.
3841
* ``libtiff``: (compile time) LibTIFF support, required for TIFF based image formats.
3942

4043
.. autofunction:: PIL.features.check_codec
44+
.. autofunction:: PIL.features.version_codec
4145
.. autofunction:: PIL.features.get_supported_codecs
4246

4347
Features
4448
--------
4549

4650
Some of these are only checked during Pillow compilation.
4751
If the required library was uninstalled from the system, the relevant module may fail to load instead.
52+
Feature version numbers are available only where stated.
4853

4954
Support for the following features can be checked:
5055

51-
* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg.
56+
* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. Compile-time version number is available.
5257
* ``transp_webp``: Support for transparency in WebP images.
5358
* ``webp_mux``: (compile time) Support for EXIF data in WebP images.
5459
* ``webp_anim``: (compile time) Support for animated WebP images.
55-
* ``raqm``: Raqm library, required for ``ImageFont.LAYOUT_RAQM`` in :py:func:`PIL.ImageFont.truetype`.
56-
* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`.
60+
* ``raqm``: Raqm library, required for ``ImageFont.LAYOUT_RAQM`` in :py:func:`PIL.ImageFont.truetype`. Run-time version number is available for Raqm 0.7.0 or newer.
61+
* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`. Run-time version number is available.
5762
* ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library.
5863

5964
.. autofunction:: PIL.features.check_feature
65+
.. autofunction:: PIL.features.version_feature
6066
.. autofunction:: PIL.features.get_supported_features

0 commit comments

Comments
 (0)