Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PIL/features.py: 18%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from __future__ import annotations
3__lazy_modules__ = {"collections", "warnings"}
5import collections
6import os
7import sys
8import warnings
10import PIL
12from . import Image
14TYPE_CHECKING = False
15if TYPE_CHECKING:
16 from typing import IO
17modules = {
18 "pil": ("PIL._imaging", "PILLOW_VERSION"),
19 "tkinter": ("PIL._tkinter_finder", "tk_version"),
20 "freetype2": ("PIL._imagingft", "freetype2_version"),
21 "littlecms2": ("PIL._imagingcms", "littlecms_version"),
22 "webp": ("PIL._webp", "webpdecoder_version"),
23 "avif": ("PIL._avif", "libavif_version"),
24}
27def check_module(feature: str) -> bool:
28 """
29 Checks if a module is available.
31 :param feature: The module to check for.
32 :returns: ``True`` if available, ``False`` otherwise.
33 :raises ValueError: If the module is not defined in this version of Pillow.
34 """
35 if feature not in modules:
36 msg = f"Unknown module {feature}"
37 raise ValueError(msg)
39 module, ver = modules[feature]
41 try:
42 __import__(module)
43 return True
44 except ModuleNotFoundError:
45 return False
46 except ImportError as ex:
47 warnings.warn(str(ex))
48 return False
51def version_module(feature: str) -> str | None:
52 """
53 :param feature: The module to check for.
54 :returns:
55 The loaded version number as a string, or ``None`` if unknown or not available.
56 :raises ValueError: If the module is not defined in this version of Pillow.
57 """
58 if not check_module(feature):
59 return None
61 module, ver = modules[feature]
63 return getattr(__import__(module, fromlist=[ver]), ver)
66def get_supported_modules() -> list[str]:
67 """
68 :returns: A list of all supported modules.
69 """
70 return [f for f in modules if check_module(f)]
73codecs = {
74 "jpg": ("jpeg", "jpeglib"),
75 "jpg_2000": ("jpeg2k", "jp2klib"),
76 "zlib": ("zip", "zlib"),
77 "libtiff": ("libtiff", "libtiff"),
78}
81def check_codec(feature: str) -> bool:
82 """
83 Checks if a codec is available.
85 :param feature: The codec to check for.
86 :returns: ``True`` if available, ``False`` otherwise.
87 :raises ValueError: If the codec is not defined in this version of Pillow.
88 """
89 if feature not in codecs:
90 msg = f"Unknown codec {feature}"
91 raise ValueError(msg)
93 codec, lib = codecs[feature]
95 return f"{codec}_encoder" in dir(Image.core)
98def version_codec(feature: str) -> str | None:
99 """
100 :param feature: The codec to check for.
101 :returns:
102 The version number as a string, or ``None`` if not available.
103 Checked at compile time for ``jpg``, run-time otherwise.
104 :raises ValueError: If the codec is not defined in this version of Pillow.
105 """
106 if not check_codec(feature):
107 return None
109 codec, lib = codecs[feature]
111 version = getattr(Image.core, f"{lib}_version")
113 if feature == "libtiff":
114 return version.split("\n")[0].split("Version ")[1]
116 return version
119def get_supported_codecs() -> list[str]:
120 """
121 :returns: A list of all supported codecs.
122 """
123 return [f for f in codecs if check_codec(f)]
126features: dict[str, tuple[str, str, str | None]] = {
127 "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
128 "fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"),
129 "harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"),
130 "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"),
131 "mozjpeg": ("PIL._imaging", "HAVE_MOZJPEG", "libjpeg_turbo_version"),
132 "zlib_ng": ("PIL._imaging", "HAVE_ZLIBNG", "zlib_ng_version"),
133 "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"),
134 "xcb": ("PIL._imaging", "HAVE_XCB", None),
135}
138def check_feature(feature: str) -> bool | None:
139 """
140 Checks if a feature is available.
142 :param feature: The feature to check for.
143 :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown.
144 :raises ValueError: If the feature is not defined in this version of Pillow.
145 """
146 if feature not in features:
147 msg = f"Unknown feature {feature}"
148 raise ValueError(msg)
150 module, flag, ver = features[feature]
152 try:
153 imported_module = __import__(module, fromlist=["PIL"])
154 return getattr(imported_module, flag)
155 except ModuleNotFoundError:
156 return None
157 except ImportError as ex:
158 warnings.warn(str(ex))
159 return None
162def version_feature(feature: str) -> str | None:
163 """
164 :param feature: The feature to check for.
165 :returns: The version number as a string, or ``None`` if not available.
166 :raises ValueError: If the feature is not defined in this version of Pillow.
167 """
168 if not check_feature(feature):
169 return None
171 module, flag, ver = features[feature]
173 if ver is None:
174 return None
176 return getattr(__import__(module, fromlist=[ver]), ver)
179def get_supported_features() -> list[str]:
180 """
181 :returns: A list of all supported features.
182 """
183 return [f for f in features if check_feature(f)]
186def check(feature: str) -> bool | None:
187 """
188 :param feature: A module, codec, or feature name.
189 :returns:
190 ``True`` if the module, codec, or feature is available,
191 ``False`` or ``None`` otherwise.
192 """
194 if feature in modules:
195 return check_module(feature)
196 if feature in codecs:
197 return check_codec(feature)
198 if feature in features:
199 return check_feature(feature)
200 warnings.warn(f"Unknown feature '{feature}'.", stacklevel=2)
201 return False
204def version(feature: str) -> str | None:
205 """
206 :param feature:
207 The module, codec, or feature to check for.
208 :returns:
209 The version number as a string, or ``None`` if unknown or not available.
210 """
211 if feature in modules:
212 return version_module(feature)
213 if feature in codecs:
214 return version_codec(feature)
215 if feature in features:
216 return version_feature(feature)
217 return None
220def get_supported() -> list[str]:
221 """
222 :returns: A list of all supported modules, features, and codecs.
223 """
225 ret = get_supported_modules()
226 ret.extend(get_supported_features())
227 ret.extend(get_supported_codecs())
228 return ret
231def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None:
232 """
233 Prints information about this installation of Pillow.
234 This function can be called with ``python3 -m PIL``.
235 It can also be called with ``python3 -m PIL.report`` or ``python3 -m PIL --report``
236 to have "supported_formats" set to ``False``, omitting the list of all supported
237 image file formats.
239 :param out:
240 The output stream to print to. Defaults to ``sys.stdout`` if ``None``.
241 :param supported_formats:
242 If ``True``, a list of all supported image file formats will be printed.
243 """
245 if out is None:
246 out = sys.stdout
248 Image.init()
250 print("-" * 68, file=out)
251 print(f"Pillow {PIL.__version__}", file=out)
252 py_version_lines = sys.version.splitlines()
253 print(f"Python {py_version_lines[0].strip()}", file=out)
254 for py_version in py_version_lines[1:]:
255 print(f" {py_version.strip()}", file=out)
256 print("-" * 68, file=out)
257 print(f"Python executable is {sys.executable or 'unknown'}", file=out)
258 if sys.prefix != sys.base_prefix:
259 print(f"Environment Python files loaded from {sys.prefix}", file=out)
260 print(f"System Python files loaded from {sys.base_prefix}", file=out)
261 print("-" * 68, file=out)
262 print(
263 f"Python Pillow modules loaded from {os.path.dirname(Image.__file__)}",
264 file=out,
265 )
266 print(
267 f"Binary Pillow modules loaded from {os.path.dirname(Image.core.__file__)}",
268 file=out,
269 )
270 print("-" * 68, file=out)
272 for name, feature in [
273 ("pil", "PIL CORE"),
274 ("tkinter", "TKINTER"),
275 ("freetype2", "FREETYPE2"),
276 ("littlecms2", "LITTLECMS2"),
277 ("webp", "WEBP"),
278 ("avif", "AVIF"),
279 ("jpg", "JPEG"),
280 ("jpg_2000", "OPENJPEG (JPEG2000)"),
281 ("zlib", "ZLIB (PNG/ZIP)"),
282 ("libtiff", "LIBTIFF"),
283 ("raqm", "RAQM (Bidirectional Text)"),
284 ("libimagequant", "LIBIMAGEQUANT (Quantization method)"),
285 ("xcb", "XCB (X protocol)"),
286 ]:
287 if check(name):
288 v: str | None = None
289 if name == "jpg":
290 libjpeg_turbo_version = version_feature("libjpeg_turbo")
291 if libjpeg_turbo_version is not None:
292 v = "mozjpeg" if check_feature("mozjpeg") else "libjpeg-turbo"
293 v += " " + libjpeg_turbo_version
294 if v is None:
295 v = version(name)
296 if v is not None:
297 version_static = name in ("pil", "jpg")
298 if name == "littlecms2":
299 # this check is also in src/_imagingcms.c:setup_module()
300 version_static = tuple(int(x) for x in v.split(".")) < (2, 7)
301 t = "compiled for" if version_static else "loaded"
302 if name == "zlib":
303 zlib_ng_version = version_feature("zlib_ng")
304 if zlib_ng_version is not None:
305 v += ", compiled for zlib-ng " + zlib_ng_version
306 elif name == "raqm":
307 for f in ("fribidi", "harfbuzz"):
308 v2 = version_feature(f)
309 if v2 is not None:
310 v += f", {f} {v2}"
311 print("---", feature, "support ok,", t, v, file=out)
312 else:
313 print("---", feature, "support ok", file=out)
314 else:
315 print("***", feature, "support not installed", file=out)
316 print("-" * 68, file=out)
318 if supported_formats:
319 extensions = collections.defaultdict(list)
320 for ext, i in Image.EXTENSION.items():
321 extensions[i].append(ext)
323 for i in sorted(Image.ID):
324 line = f"{i}"
325 if i in Image.MIME:
326 line = f"{line} {Image.MIME[i]}"
327 print(line, file=out)
329 if i in extensions:
330 print(
331 "Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out
332 )
334 features = []
335 if i in Image.OPEN:
336 features.append("open")
337 if i in Image.SAVE:
338 features.append("save")
339 if i in Image.SAVE_ALL:
340 features.append("save_all")
341 if i in Image.DECODERS:
342 features.append("decode")
343 if i in Image.ENCODERS:
344 features.append("encode")
346 print("Features: {}".format(", ".join(features)), file=out)
347 print("-" * 68, file=out)