features.py 9.4 KB

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