features.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. import collections
  2. import os
  3. import sys
  4. import warnings
  5. import PIL
  6. from . import Image
  7. modules = {
  8. "pil": ("PIL._imaging", "PILLOW_VERSION"),
  9. "tkinter": ("PIL._tkinter_finder", "tk_version"),
  10. "freetype2": ("PIL._imagingft", "freetype2_version"),
  11. "littlecms2": ("PIL._imagingcms", "littlecms_version"),
  12. "webp": ("PIL._webp", "webpdecoder_version"),
  13. }
  14. def check_module(feature):
  15. """
  16. Checks if a module is available.
  17. :param feature: The module to check for.
  18. :returns: ``True`` if available, ``False`` otherwise.
  19. :raises ValueError: If the module is not defined in this version of Pillow.
  20. """
  21. if not (feature in modules):
  22. raise ValueError(f"Unknown module {feature}")
  23. module, ver = modules[feature]
  24. try:
  25. __import__(module)
  26. return True
  27. except ImportError:
  28. return False
  29. def version_module(feature):
  30. """
  31. :param feature: The module to check for.
  32. :returns:
  33. The loaded version number as a string, or ``None`` if unknown or not available.
  34. :raises ValueError: If the module is not defined in this version of Pillow.
  35. """
  36. if not check_module(feature):
  37. return None
  38. module, ver = modules[feature]
  39. if ver is None:
  40. return None
  41. return getattr(__import__(module, fromlist=[ver]), ver)
  42. def get_supported_modules():
  43. """
  44. :returns: A list of all supported modules.
  45. """
  46. return [f for f in modules if check_module(f)]
  47. codecs = {
  48. "jpg": ("jpeg", "jpeglib"),
  49. "jpg_2000": ("jpeg2k", "jp2klib"),
  50. "zlib": ("zip", "zlib"),
  51. "libtiff": ("libtiff", "libtiff"),
  52. }
  53. def check_codec(feature):
  54. """
  55. Checks if a codec is available.
  56. :param feature: The codec to check for.
  57. :returns: ``True`` if available, ``False`` otherwise.
  58. :raises ValueError: If the codec is not defined in this version of Pillow.
  59. """
  60. if feature not in codecs:
  61. raise ValueError(f"Unknown codec {feature}")
  62. codec, lib = codecs[feature]
  63. return codec + "_encoder" in dir(Image.core)
  64. def version_codec(feature):
  65. """
  66. :param feature: The codec to check for.
  67. :returns:
  68. The version number as a string, or ``None`` if not available.
  69. Checked at compile time for ``jpg``, run-time otherwise.
  70. :raises ValueError: If the codec is not defined in this version of Pillow.
  71. """
  72. if not check_codec(feature):
  73. return None
  74. codec, lib = codecs[feature]
  75. version = getattr(Image.core, lib + "_version")
  76. if feature == "libtiff":
  77. return version.split("\n")[0].split("Version ")[1]
  78. return version
  79. def get_supported_codecs():
  80. """
  81. :returns: A list of all supported codecs.
  82. """
  83. return [f for f in codecs if check_codec(f)]
  84. features = {
  85. "webp_anim": ("PIL._webp", "HAVE_WEBPANIM", None),
  86. "webp_mux": ("PIL._webp", "HAVE_WEBPMUX", None),
  87. "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY", None),
  88. "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
  89. "fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"),
  90. "harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"),
  91. "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"),
  92. "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"),
  93. "xcb": ("PIL._imaging", "HAVE_XCB", None),
  94. }
  95. def check_feature(feature):
  96. """
  97. Checks if a feature is available.
  98. :param feature: The feature to check for.
  99. :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown.
  100. :raises ValueError: If the feature is not defined in this version of Pillow.
  101. """
  102. if feature not in features:
  103. raise ValueError(f"Unknown feature {feature}")
  104. module, flag, ver = features[feature]
  105. try:
  106. imported_module = __import__(module, fromlist=["PIL"])
  107. return getattr(imported_module, flag)
  108. except ImportError:
  109. return None
  110. def version_feature(feature):
  111. """
  112. :param feature: The feature to check for.
  113. :returns: The version number as a string, or ``None`` if not available.
  114. :raises ValueError: If the feature is not defined in this version of Pillow.
  115. """
  116. if not check_feature(feature):
  117. return None
  118. module, flag, ver = features[feature]
  119. if ver is None:
  120. return None
  121. return getattr(__import__(module, fromlist=[ver]), ver)
  122. def get_supported_features():
  123. """
  124. :returns: A list of all supported features.
  125. """
  126. return [f for f in features if check_feature(f)]
  127. def check(feature):
  128. """
  129. :param feature: A module, codec, or feature name.
  130. :returns:
  131. ``True`` if the module, codec, or feature is available,
  132. ``False`` or ``None`` otherwise.
  133. """
  134. if feature in modules:
  135. return check_module(feature)
  136. if feature in codecs:
  137. return check_codec(feature)
  138. if feature in features:
  139. return check_feature(feature)
  140. warnings.warn(f"Unknown feature '{feature}'.", stacklevel=2)
  141. return False
  142. def version(feature):
  143. """
  144. :param feature:
  145. The module, codec, or feature to check for.
  146. :returns:
  147. The version number as a string, or ``None`` if unknown or not available.
  148. """
  149. if feature in modules:
  150. return version_module(feature)
  151. if feature in codecs:
  152. return version_codec(feature)
  153. if feature in features:
  154. return version_feature(feature)
  155. return None
  156. def get_supported():
  157. """
  158. :returns: A list of all supported modules, features, and codecs.
  159. """
  160. ret = get_supported_modules()
  161. ret.extend(get_supported_features())
  162. ret.extend(get_supported_codecs())
  163. return ret
  164. def pilinfo(out=None, supported_formats=True):
  165. """
  166. Prints information about this installation of Pillow.
  167. This function can be called with ``python3 -m PIL``.
  168. :param out:
  169. The output stream to print to. Defaults to ``sys.stdout`` if ``None``.
  170. :param supported_formats:
  171. If ``True``, a list of all supported image file formats will be printed.
  172. """
  173. if out is None:
  174. out = sys.stdout
  175. Image.init()
  176. print("-" * 68, file=out)
  177. print(f"Pillow {PIL.__version__}", file=out)
  178. py_version = sys.version.splitlines()
  179. print(f"Python {py_version[0].strip()}", file=out)
  180. for py_version in py_version[1:]:
  181. print(f" {py_version.strip()}", file=out)
  182. print("-" * 68, file=out)
  183. print(
  184. f"Python modules loaded from {os.path.dirname(Image.__file__)}",
  185. file=out,
  186. )
  187. print(
  188. f"Binary modules loaded from {os.path.dirname(Image.core.__file__)}",
  189. file=out,
  190. )
  191. print("-" * 68, file=out)
  192. for name, feature in [
  193. ("pil", "PIL CORE"),
  194. ("tkinter", "TKINTER"),
  195. ("freetype2", "FREETYPE2"),
  196. ("littlecms2", "LITTLECMS2"),
  197. ("webp", "WEBP"),
  198. ("transp_webp", "WEBP Transparency"),
  199. ("webp_mux", "WEBPMUX"),
  200. ("webp_anim", "WEBP Animation"),
  201. ("jpg", "JPEG"),
  202. ("jpg_2000", "OPENJPEG (JPEG2000)"),
  203. ("zlib", "ZLIB (PNG/ZIP)"),
  204. ("libtiff", "LIBTIFF"),
  205. ("raqm", "RAQM (Bidirectional Text)"),
  206. ("libimagequant", "LIBIMAGEQUANT (Quantization method)"),
  207. ("xcb", "XCB (X protocol)"),
  208. ]:
  209. if check(name):
  210. if name == "jpg" and check_feature("libjpeg_turbo"):
  211. v = "libjpeg-turbo " + version_feature("libjpeg_turbo")
  212. else:
  213. v = version(name)
  214. if v is not None:
  215. version_static = name in ("pil", "jpg")
  216. if name == "littlecms2":
  217. # this check is also in src/_imagingcms.c:setup_module()
  218. version_static = tuple(int(x) for x in v.split(".")) < (2, 7)
  219. t = "compiled for" if version_static else "loaded"
  220. if name == "raqm":
  221. for f in ("fribidi", "harfbuzz"):
  222. v2 = version_feature(f)
  223. if v2 is not None:
  224. v += f", {f} {v2}"
  225. print("---", feature, "support ok,", t, v, file=out)
  226. else:
  227. print("---", feature, "support ok", file=out)
  228. else:
  229. print("***", feature, "support not installed", file=out)
  230. print("-" * 68, file=out)
  231. if supported_formats:
  232. extensions = collections.defaultdict(list)
  233. for ext, i in Image.EXTENSION.items():
  234. extensions[i].append(ext)
  235. for i in sorted(Image.ID):
  236. line = f"{i}"
  237. if i in Image.MIME:
  238. line = f"{line} {Image.MIME[i]}"
  239. print(line, file=out)
  240. if i in extensions:
  241. print(
  242. "Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out
  243. )
  244. features = []
  245. if i in Image.OPEN:
  246. features.append("open")
  247. if i in Image.SAVE:
  248. features.append("save")
  249. if i in Image.SAVE_ALL:
  250. features.append("save_all")
  251. if i in Image.DECODERS:
  252. features.append("decode")
  253. if i in Image.ENCODERS:
  254. features.append("encode")
  255. print("Features: {}".format(", ".join(features)), file=out)
  256. print("-" * 68, file=out)