check run-time version numbers where available, add docs

This commit is contained in:
nulano 2020-06-14 05:35:43 +02:00 committed by Andrew Murray
parent a324f4a466
commit 6c1ff252d6
8 changed files with 118 additions and 33 deletions

View File

@ -8,6 +8,7 @@ The :py:mod:`PIL.features` module can be used to detect which Pillow features ar
.. autofunction:: PIL.features.pilinfo
.. autofunction:: PIL.features.check
.. autofunction:: PIL.features.version
.. autofunction:: PIL.features.get_supported
Modules
@ -16,28 +17,31 @@ Modules
Support for the following modules can be checked:
* ``pil``: The Pillow core module, required for all functionality.
* ``tkinter``: Tkinter support.
* ``tkinter``: Tkinter support. Version number not available.
* ``freetype2``: FreeType font support via :py:func:`PIL.ImageFont.truetype`.
* ``littlecms2``: LittleCMS 2 support via :py:mod:`PIL.ImageCms`.
* ``webp``: WebP image support.
.. autofunction:: PIL.features.check_module
.. autofunction:: PIL.features.version_module
.. autofunction:: PIL.features.get_supported_modules
Codecs
------
These are only checked during Pillow compilation.
Support for these is only checked during Pillow compilation.
If the required library was uninstalled from the system, the ``pil`` core module may fail to load instead.
Except for ``jpg``, the version number is checked at run-time.
Support for the following codecs can be checked:
* ``jpg``: (compile time) Libjpeg support, required for JPEG based image formats.
* ``jpg``: (compile time) Libjpeg support, required for JPEG based image formats. Only compile time version number is available.
* ``jpg_2000``: (compile time) OpenJPEG support, required for JPEG 2000 image formats.
* ``zlib``: (compile time) Zlib support, required for zlib compressed formats, such as PNG.
* ``libtiff``: (compile time) LibTIFF support, required for TIFF based image formats.
.. autofunction:: PIL.features.check_codec
.. autofunction:: PIL.features.version_codec
.. autofunction:: PIL.features.get_supported_codecs
Features
@ -45,16 +49,18 @@ Features
Some of these are only checked during Pillow compilation.
If the required library was uninstalled from the system, the relevant module may fail to load instead.
Feature version numbers are available only where stated.
Support for the following features can be checked:
* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg.
* ``libjpeg_turbo``: (compile time) Whether Pillow was compiled against the libjpeg-turbo version of libjpeg. Compile-time version number is available.
* ``transp_webp``: Support for transparency in WebP images.
* ``webp_mux``: (compile time) Support for EXIF data in WebP images.
* ``webp_anim``: (compile time) Support for animated WebP images.
* ``raqm``: Raqm library, required for ``ImageFont.LAYOUT_RAQM`` in :py:func:`PIL.ImageFont.truetype`.
* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`.
* ``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.
* ``libimagequant``: (compile time) ImageQuant quantization support in :py:func:`PIL.Image.Image.quantize`. Run-time version number is available.
* ``xcb``: (compile time) Support for X11 in :py:func:`PIL.ImageGrab.grab` via the XCB library.
.. autofunction:: PIL.features.check_feature
.. autofunction:: PIL.features.version_feature
.. autofunction:: PIL.features.get_supported_features

View File

@ -8,11 +8,11 @@ import PIL
from . import Image
modules = {
"pil": ("PIL._imaging", None),
"pil": ("PIL._imaging", "PILLOW_VERSION"),
"tkinter": ("PIL._tkinter_finder", None),
"freetype2": ("PIL._imagingft", "freetype2"),
"littlecms2": ("PIL._imagingcms", "littlecms"),
"webp": ("PIL._webp", None),
"freetype2": ("PIL._imagingft", "freetype2_version"),
"littlecms2": ("PIL._imagingcms", "littlecms_version"),
"webp": ("PIL._webp", "webpdecoder_version"),
}
@ -27,7 +27,7 @@ def check_module(feature):
if not (feature in modules):
raise ValueError("Unknown module %s" % feature)
module, lib = modules[feature]
module, ver = modules[feature]
try:
__import__(module)
@ -37,17 +37,21 @@ def check_module(feature):
def version_module(feature):
"""
:param feature: The module to check for.
:returns:
The loaded version number as a string, or ``None`` if unknown or not available.
:raises ValueError: If the module is not defined in this version of Pillow.
"""
if not check_module(feature):
return None
module, lib = modules[feature]
module, ver = modules[feature]
if lib is None:
if ver is None:
return None
attr = lib + "_version"
return getattr(__import__(module, fromlist=[attr]), attr)
return getattr(__import__(module, fromlist=[ver]), ver)
def get_supported_modules():
@ -82,6 +86,13 @@ def check_codec(feature):
def version_codec(feature):
"""
:param feature: The codec to check for.
:returns:
The version number as a string, or ``None`` if not available.
Checked at compile time for ``jpg``, run-time otherwise.
:raises ValueError: If the codec is not defined in this version of Pillow.
"""
if not check_codec(feature):
return None
@ -103,13 +114,13 @@ def get_supported_codecs():
features = {
"webp_anim": ("PIL._webp", "HAVE_WEBPANIM"),
"webp_mux": ("PIL._webp", "HAVE_WEBPMUX"),
"transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY"),
"raqm": ("PIL._imagingft", "HAVE_RAQM"),
"libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO"),
"libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT"),
"xcb": ("PIL._imaging", "HAVE_XCB"),
"webp_anim": ("PIL._webp", "HAVE_WEBPANIM", None),
"webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None),
"transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", None),
"raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
"libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"),
"libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"),
"xcb": ("PIL._imaging", "HAVE_XCB", None),
}
@ -124,7 +135,7 @@ def check_feature(feature):
if feature not in features:
raise ValueError("Unknown feature %s" % feature)
module, flag = features[feature]
module, flag, ver = features[feature]
try:
imported_module = __import__(module, fromlist=["PIL"])
@ -133,6 +144,23 @@ def check_feature(feature):
return None
def version_feature(feature):
"""
:param feature: The feature to check for.
:returns: The version number as a string, or ``None`` if not available.
:raises ValueError: If the feature is not defined in this version of Pillow.
"""
if not check_feature(feature):
return None
module, flag, ver = features[feature]
if ver is None:
return None
return getattr(__import__(module, fromlist=[ver]), ver)
def get_supported_features():
"""
:returns: A list of all supported features.
@ -142,9 +170,9 @@ def get_supported_features():
def check(feature):
"""
:param feature: A module, feature, or codec name.
:param feature: A module, codec, or feature name.
:returns:
``True`` if the module, feature, or codec is available,
``True`` if the module, codec, or feature is available,
``False`` or ``None`` otherwise.
"""
@ -159,10 +187,18 @@ def check(feature):
def version(feature):
"""
:param feature:
The module, codec, or feature to check for.
:returns:
The version number as a string, or ``None`` if unknown or not available.
"""
if feature in modules:
return version_module(feature)
if feature in codecs:
return version_codec(feature)
if feature in features:
return version_feature(feature)
return None
@ -228,12 +264,15 @@ def pilinfo(out=None, supported_formats=True):
("xcb", "XCB (X protocol)"),
]:
if check(name):
v = version(name)
if v is not None:
support = "ok (version {})".format(v)
if name == "jpg" and check_feature("libjpeg_turbo"):
v = "libjpeg-turbo " + version_feature("libjpeg_turbo")
else:
support = "ok"
print("---", feature, "support", support, file=out)
v = version(name)
if v is not None:
t = "compiled for" if name in ("pil", "jpg") else "loaded"
print("---", feature, "support ok,", t, "version", v, file=out)
else:
print("---", feature, "support ok", file=out)
else:
print("***", feature, "support not installed", file=out)
print("-" * 68, file=out)

View File

@ -4168,12 +4168,21 @@ setup_module(PyObject* m) {
#ifdef LIBJPEG_TURBO_VERSION
PyModule_AddObject(m, "HAVE_LIBJPEGTURBO", Py_True);
#define tostr1(a) #a
#define tostr(a) tostr1(a)
PyDict_SetItemString(d, "libjpeg_turbo_version", PyUnicode_FromString(tostr(LIBJPEG_TURBO_VERSION)));
#undef tostr
#undef tostr1
#else
PyModule_AddObject(m, "HAVE_LIBJPEGTURBO", Py_False);
#endif
#ifdef HAVE_LIBIMAGEQUANT
PyModule_AddObject(m, "HAVE_LIBIMAGEQUANT", Py_True);
{
extern const char* ImagingImageQuantVersion(void);
PyDict_SetItemString(d, "imagequant_version", PyUnicode_FromString(ImagingImageQuantVersion()));
}
#else
PyModule_AddObject(m, "HAVE_LIBIMAGEQUANT", Py_False);
#endif

View File

@ -1608,6 +1608,7 @@ static int
setup_module(PyObject* m) {
PyObject *d;
PyObject *v;
int vn;
d = PyModule_GetDict(m);
@ -1622,7 +1623,8 @@ setup_module(PyObject* m) {
d = PyModule_GetDict(m);
v = PyUnicode_FromFormat("%d.%d", LCMS_VERSION / 100, LCMS_VERSION % 100);
vn = cmsGetEncodedCMMversion();
v = PyUnicode_FromFormat("%d.%d", vn / 100, vn % 100);
PyDict_SetItemString(d, "littlecms_version", v);
return 0;

View File

@ -81,6 +81,7 @@ typedef struct {
static PyTypeObject Font_Type;
typedef const char* (*t_raqm_version_string) (void);
typedef bool (*t_raqm_version_atleast)(unsigned int major,
unsigned int minor,
unsigned int micro);
@ -112,6 +113,7 @@ typedef void (*t_raqm_destroy) (raqm_t *rq);
typedef struct {
void* raqm;
int version;
t_raqm_version_string version_string;
t_raqm_version_atleast version_atleast;
t_raqm_create create;
t_raqm_set_text set_text;
@ -173,6 +175,7 @@ setraqm(void)
}
#ifndef _WIN32
p_raqm.version_string = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_string");
p_raqm.version_atleast = (t_raqm_version_atleast)dlsym(p_raqm.raqm, "raqm_version_atleast");
p_raqm.create = (t_raqm_create)dlsym(p_raqm.raqm, "raqm_create");
p_raqm.set_text = (t_raqm_set_text)dlsym(p_raqm.raqm, "raqm_set_text");
@ -206,6 +209,7 @@ setraqm(void)
return 2;
}
#else
p_raqm.version_string = (t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_string");
p_raqm.version_atleast = (t_raqm_version_atleast)GetProcAddress(p_raqm.raqm, "raqm_version_atleast");
p_raqm.create = (t_raqm_create)GetProcAddress(p_raqm.raqm, "raqm_create");
p_raqm.set_text = (t_raqm_set_text)GetProcAddress(p_raqm.raqm, "raqm_set_text");
@ -1251,6 +1255,9 @@ setup_module(PyObject* m) {
setraqm();
v = PyBool_FromLong(!!p_raqm.raqm);
PyDict_SetItemString(d, "HAVE_RAQM", v);
if (p_raqm.version_string) {
PyDict_SetItemString(d, "raqm_version", PyUnicode_FromString(p_raqm.version_string()));
}
return 0;
}

View File

@ -821,6 +821,16 @@ PyObject* WebPDecoderVersion_wrapper() {
return Py_BuildValue("i", WebPGetDecoderVersion());
}
// Version as string
const char*
WebPDecoderVersion_str(void)
{
static char version[20];
int version_number = WebPGetDecoderVersion();
sprintf(version, "%d.%d.%d", version_number >> 16, (version_number >> 8) % 0x100, version_number % 0x100);
return version;
}
/*
* The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well.
* Files that are valid with 0.3 are reported as being invalid.
@ -872,10 +882,13 @@ void addTransparencyFlagToModule(PyObject* m) {
}
static int setup_module(PyObject* m) {
PyObject* d = PyModule_GetDict(m);
addMuxFlagToModule(m);
addAnimFlagToModule(m);
addTransparencyFlagToModule(m);
PyDict_SetItemString(d, "webpdecoder_version", PyUnicode_FromString(WebPDecoderVersion_str()));
#ifdef HAVE_WEBPANIM
/* Ready object types */
if (PyType_Ready(&WebPAnimDecoder_Type) < 0 ||

View File

@ -113,4 +113,13 @@ err:
return result;
}
const char*
ImagingImageQuantVersion(void)
{
static char version[20];
int number = liq_version();
sprintf(version, "%d.%d.%d", number / 10000, (number / 100) % 100, number % 100);
return version;
}
#endif

View File

@ -373,7 +373,7 @@ ImagingZipEncodeCleanup(ImagingCodecState state) {
const char*
ImagingZipVersion(void)
{
return ZLIB_VERSION;
return zlibVersion();
}
#endif