Merge pull request #2520 from wiredfool/features

Updated Features detection
This commit is contained in:
wiredfool 2017-06-13 13:03:27 +01:00 committed by GitHub
commit 9d46f4f282
5 changed files with 91 additions and 50 deletions

View File

@ -3,7 +3,7 @@
set -e set -e
sudo apt-get update sudo apt-get update
sudo apt-get -qq install libfreetype6-dev liblcms2-dev\ sudo apt-get -qq install libfreetype6-dev liblcms2-dev python-tk \
python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick python-qt4 ghostscript libffi-dev libjpeg-turbo-progs cmake imagemagick
pip install cffi pip install cffi
pip install nose pip install nose

View File

@ -2,45 +2,26 @@ from . import Image
modules = { modules = {
"pil": "PIL._imaging", "pil": "PIL._imaging",
"tkinter": "PIL._imagingtk", "tkinter": "PIL._tkinter_finder",
"freetype2": "PIL._imagingft", "freetype2": "PIL._imagingft",
"littlecms2": "PIL._imagingcms", "littlecms2": "PIL._imagingcms",
"webp": "PIL._webp", "webp": "PIL._webp",
"transp_webp": ("WEBP", "WebPDecoderBuggyAlpha")
} }
def check_module(feature): def check_module(feature):
if feature not in modules: if not (feature in modules):
raise ValueError("Unknown module %s" % feature) raise ValueError("Unknown module %s" % feature)
module = modules[feature] module = modules[feature]
method_to_call = None
if isinstance(module, tuple):
module, method_to_call = module
try: try:
imported_module = __import__(module) imported_module = __import__(module)
except ImportError:
# If a method is being checked, None means that
# rather than the method failing, the module required for the method
# failed to be imported first
return None if method_to_call else False
if method_to_call:
method = getattr(imported_module, method_to_call)
return method() is True
else:
return True return True
except ImportError:
return False
def get_supported_modules(): def get_supported_modules():
supported_modules = [] return [f for f in modules if check_module(f)]
for feature in modules:
if check_module(feature):
supported_modules.append(feature)
return supported_modules
codecs = { codecs = {
"jpg": "jpeg", "jpg": "jpeg",
@ -49,7 +30,6 @@ codecs = {
"libtiff": "libtiff" "libtiff": "libtiff"
} }
def check_codec(feature): def check_codec(feature):
if feature not in codecs: if feature not in codecs:
raise ValueError("Unknown codec %s" % feature) raise ValueError("Unknown codec %s" % feature)
@ -60,8 +40,38 @@ def check_codec(feature):
def get_supported_codecs(): def get_supported_codecs():
supported_codecs = [] return [f for f in codecs if check_codec(f)]
for feature in codecs:
if check_codec(feature): features = {
supported_codecs.append(feature) "webp_mux": ("PIL._webp", 'HAVE_WEBPMUX'),
return supported_codecs "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY"),
}
def check_feature(feature):
if feature not in features:
raise ValueError("Unknown feature %s" % feature)
module, flag = features[feature]
try:
imported_module = __import__(module, fromlist=['PIL'])
return getattr(imported_module, flag)
except ImportError:
return None
def get_supported_features():
return [f for f in features if check_feature(f)]
def check(feature):
return (feature in modules and check_module(feature) or \
feature in codecs and check_codec(feature) or \
feature in features and check_feature(feature))
def get_supported():
ret = get_supported_modules()
ret.extend(get_supported_features())
ret.extend(get_supported_codecs())
return ret

View File

@ -2,19 +2,50 @@ from helper import unittest, PillowTestCase
from PIL import features from PIL import features
try:
from PIL import _webp
HAVE_WEBP = True
except:
HAVE_WEBP = False
class TestFeatures(PillowTestCase): class TestFeatures(PillowTestCase):
def test_check_features(self): def test_check(self):
# Check the correctness of the convenience function
for module in features.modules:
self.assertEqual(features.check_module(module),
features.check(module))
for codec in features.codecs:
self.assertEqual(features.check_codec(codec),
features.check(codec))
for feature in features.features:
self.assertEqual(features.check_feature(feature),
features.check(feature))
@unittest.skipUnless(HAVE_WEBP, True)
def check_webp_transparency(self):
self.assertEqual(features.check('transp_webp'),
not _webp.WebPDecoderBuggyAlpha())
self.assertEqual(features.check('transp_webp'),
_webp.HAVE_TRANSPARENCY)
@unittest.skipUnless(HAVE_WEBP, True)
def check_webp_mux(self):
self.assertEqual(features.check('webp_mux'),
_webp.HAVE_WEBPMUX)
def test_check_modules(self):
for feature in features.modules: for feature in features.modules:
self.assertIn( self.assertIn(features.check_module(feature), [True, False])
features.check_module(feature), [True, False, None])
for feature in features.codecs: for feature in features.codecs:
self.assertIn(features.check_codec(feature), [True, False]) self.assertIn(features.check_codec(feature), [True, False])
def test_supported_features(self): def test_supported_modules(self):
self.assertIsInstance(features.get_supported_modules(), list) self.assertIsInstance(features.get_supported_modules(), list)
self.assertIsInstance(features.get_supported_codecs(), list) self.assertIsInstance(features.get_supported_codecs(), list)
self.assertIsInstance(features.get_supported_features(), list)
self.assertIsInstance(features.get_supported(), list)
def test_unsupported_codec(self): def test_unsupported_codec(self):
# Arrange # Arrange

13
_webp.c
View File

@ -247,8 +247,12 @@ PyObject* WebPDecoderVersion_wrapper(PyObject* self, PyObject* args){
* The version of webp that ships with (0.1.3) Ubuntu 12.04 doesn't handle alpha well. * 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. * Files that are valid with 0.3 are reported as being invalid.
*/ */
int WebPDecoderBuggyAlpha() {
return WebPGetDecoderVersion()==0x0103;
}
PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){ PyObject* WebPDecoderBuggyAlpha_wrapper(PyObject* self, PyObject* args){
return Py_BuildValue("i", WebPGetDecoderVersion()==0x0103); return Py_BuildValue("i", WebPDecoderBuggyAlpha());
} }
static PyMethodDef webpMethods[] = static PyMethodDef webpMethods[] =
@ -268,6 +272,11 @@ void addMuxFlagToModule(PyObject* m) {
#endif #endif
} }
void addTransparencyFlagToModule(PyObject* m) {
PyModule_AddObject(m, "HAVE_TRANSPARENCY",
PyBool_FromLong(!WebPDecoderBuggyAlpha()));
}
#if PY_VERSION_HEX >= 0x03000000 #if PY_VERSION_HEX >= 0x03000000
PyMODINIT_FUNC PyMODINIT_FUNC
@ -284,6 +293,7 @@ PyInit__webp(void) {
m = PyModule_Create(&module_def); m = PyModule_Create(&module_def);
addMuxFlagToModule(m); addMuxFlagToModule(m);
addTransparencyFlagToModule(m);
return m; return m;
} }
#else #else
@ -292,5 +302,6 @@ init_webp(void)
{ {
PyObject* m = Py_InitModule("_webp", webpMethods); PyObject* m = Py_InitModule("_webp", webpMethods);
addMuxFlagToModule(m); addMuxFlagToModule(m);
addTransparencyFlagToModule(m);
} }
#endif #endif

View File

@ -178,25 +178,14 @@ if __name__ == "__main__":
("freetype2", "FREETYPE2"), ("freetype2", "FREETYPE2"),
("littlecms2", "LITTLECMS2"), ("littlecms2", "LITTLECMS2"),
("webp", "WEBP"), ("webp", "WEBP"),
("transp_webp", "Transparent WEBP") ("transp_webp", "Transparent WEBP"),
]: ("webp_mux", "WEBPMUX"),
supported = features.check_module(name)
if supported is None:
# A method was being tested, but the module required
# for the method could not be correctly imported
pass
elif supported:
print("---", feature, "support ok")
else:
print("***", feature, "support not installed")
for name, feature in [
("jpg", "JPEG"), ("jpg", "JPEG"),
("jpg_2000", "OPENJPEG (JPEG2000)"), ("jpg_2000", "OPENJPEG (JPEG2000)"),
("zlib", "ZLIB (PNG/ZIP)"), ("zlib", "ZLIB (PNG/ZIP)"),
("libtiff", "LIBTIFF") ("libtiff", "LIBTIFF")
]: ]:
if features.check_codec(name): if features.check(name):
print("---", feature, "support ok") print("---", feature, "support ok")
else: else:
print("***", feature, "support not installed") print("***", feature, "support not installed")