Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/PIL/features.py: 20%
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
3import collections
4import os
5import sys
6import warnings
7from typing import IO
9import PIL
11from . import Image
12from ._deprecate import deprecate
14modules = {
15 "pil": ("PIL._imaging", "PILLOW_VERSION"),
16 "tkinter": ("PIL._tkinter_finder", "tk_version"),
17 "freetype2": ("PIL._imagingft", "freetype2_version"),
18 "littlecms2": ("PIL._imagingcms", "littlecms_version"),
19 "webp": ("PIL._webp", "webpdecoder_version"),
20 "avif": ("PIL._avif", "libavif_version"),
21}
24def check_module(feature: str) -> bool:
25 """
26 Checks if a module is available.
28 :param feature: The module to check for.
29 :returns: ``True`` if available, ``False`` otherwise.
30 :raises ValueError: If the module is not defined in this version of Pillow.
31 """
32 if feature not in modules:
33 msg = f"Unknown module {feature}"
34 raise ValueError(msg)
36 module, ver = modules[feature]
38 try:
39 __import__(module)
40 return True
41 except ModuleNotFoundError:
42 return False
43 except ImportError as ex:
44 warnings.warn(str(ex))
45 return False
48def version_module(feature: str) -> str | None:
49 """
50 :param feature: The module to check for.
51 :returns:
52 The loaded version number as a string, or ``None`` if unknown or not available.
53 :raises ValueError: If the module is not defined in this version of Pillow.
54 """
55 if not check_module(feature):
56 return None
58 module, ver = modules[feature]
60 return getattr(__import__(module, fromlist=[ver]), ver)
63def get_supported_modules() -> list[str]:
64 """
65 :returns: A list of all supported modules.
66 """
67 return [f for f in modules if check_module(f)]
70codecs = {
71 "jpg": ("jpeg", "jpeglib"),
72 "jpg_2000": ("jpeg2k", "jp2klib"),
73 "zlib": ("zip", "zlib"),
74 "libtiff": ("libtiff", "libtiff"),
75}
78def check_codec(feature: str) -> bool:
79 """
80 Checks if a codec is available.
82 :param feature: The codec to check for.
83 :returns: ``True`` if available, ``False`` otherwise.
84 :raises ValueError: If the codec is not defined in this version of Pillow.
85 """
86 if feature not in codecs:
87 msg = f"Unknown codec {feature}"
88 raise ValueError(msg)
90 codec, lib = codecs[feature]
92 return f"{codec}_encoder" in dir(Image.core)
95def version_codec(feature: str) -> str | None:
96 """
97 :param feature: The codec to check for.
98 :returns:
99 The version number as a string, or ``None`` if not available.
100 Checked at compile time for ``jpg``, run-time otherwise.
101 :raises ValueError: If the codec is not defined in this version of Pillow.
102 """
103 if not check_codec(feature):
104 return None
106 codec, lib = codecs[feature]
108 version = getattr(Image.core, f"{lib}_version")
110 if feature == "libtiff":
111 return version.split("\n")[0].split("Version ")[1]
113 return version
116def get_supported_codecs() -> list[str]:
117 """
118 :returns: A list of all supported codecs.
119 """
120 return [f for f in codecs if check_codec(f)]
123features: dict[str, tuple[str, str | bool, str | None]] = {
124 "webp_anim": ("PIL._webp", True, None),
125 "webp_mux": ("PIL._webp", True, None),
126 "transp_webp": ("PIL._webp", True, 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 if isinstance(flag, bool):
153 deprecate(f'check_feature("{feature}")', 12)
154 try:
155 imported_module = __import__(module, fromlist=["PIL"])
156 if isinstance(flag, bool):
157 return flag
158 return getattr(imported_module, flag)
159 except ModuleNotFoundError:
160 return None
161 except ImportError as ex:
162 warnings.warn(str(ex))
163 return None
166def version_feature(feature: str) -> str | None:
167 """
168 :param feature: The feature to check for.
169 :returns: The version number as a string, or ``None`` if not available.
170 :raises ValueError: If the feature is not defined in this version of Pillow.
171 """
172 if not check_feature(feature):
173 return None
175 module, flag, ver = features[feature]
177 if ver is None:
178 return None
180 return getattr(__import__(module, fromlist=[ver]), ver)
183def get_supported_features() -> list[str]:
184 """
185 :returns: A list of all supported features.
186 """
187 supported_features = []
188 for f, (module, flag, _) in features.items():
189 if flag is True:
190 for feature, (feature_module, _) in modules.items():
191 if feature_module == module:
192 if check_module(feature):
193 supported_features.append(f)
194 break
195 elif check_feature(f):
196 supported_features.append(f)
197 return supported_features
200def check(feature: str) -> bool | None:
201 """
202 :param feature: A module, codec, or feature name.
203 :returns:
204 ``True`` if the module, codec, or feature is available,
205 ``False`` or ``None`` otherwise.
206 """
208 if feature in modules:
209 return check_module(feature)
210 if feature in codecs:
211 return check_codec(feature)
212 if feature in features:
213 return check_feature(feature)
214 warnings.warn(f"Unknown feature '{feature}'.", stacklevel=2)
215 return False
218def version(feature: str) -> str | None:
219 """
220 :param feature:
221 The module, codec, or feature to check for.
222 :returns:
223 The version number as a string, or ``None`` if unknown or not available.
224 """
225 if feature in modules:
226 return version_module(feature)
227 if feature in codecs:
228 return version_codec(feature)
229 if feature in features:
230 return version_feature(feature)
231 return None
234def get_supported() -> list[str]:
235 """
236 :returns: A list of all supported modules, features, and codecs.
237 """
239 ret = get_supported_modules()
240 ret.extend(get_supported_features())
241 ret.extend(get_supported_codecs())
242 return ret
245def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None:
246 """
247 Prints information about this installation of Pillow.
248 This function can be called with ``python3 -m PIL``.
249 It can also be called with ``python3 -m PIL.report`` or ``python3 -m PIL --report``
250 to have "supported_formats" set to ``False``, omitting the list of all supported
251 image file formats.
253 :param out:
254 The output stream to print to. Defaults to ``sys.stdout`` if ``None``.
255 :param supported_formats:
256 If ``True``, a list of all supported image file formats will be printed.
257 """
259 if out is None:
260 out = sys.stdout
262 Image.init()
264 print("-" * 68, file=out)
265 print(f"Pillow {PIL.__version__}", file=out)
266 py_version_lines = sys.version.splitlines()
267 print(f"Python {py_version_lines[0].strip()}", file=out)
268 for py_version in py_version_lines[1:]:
269 print(f" {py_version.strip()}", file=out)
270 print("-" * 68, file=out)
271 print(f"Python executable is {sys.executable or 'unknown'}", file=out)
272 if sys.prefix != sys.base_prefix:
273 print(f"Environment Python files loaded from {sys.prefix}", file=out)
274 print(f"System Python files loaded from {sys.base_prefix}", file=out)
275 print("-" * 68, file=out)
276 print(
277 f"Python Pillow modules loaded from {os.path.dirname(Image.__file__)}",
278 file=out,
279 )
280 print(
281 f"Binary Pillow modules loaded from {os.path.dirname(Image.core.__file__)}",
282 file=out,
283 )
284 print("-" * 68, file=out)
286 for name, feature in [
287 ("pil", "PIL CORE"),
288 ("tkinter", "TKINTER"),
289 ("freetype2", "FREETYPE2"),
290 ("littlecms2", "LITTLECMS2"),
291 ("webp", "WEBP"),
292 ("avif", "AVIF"),
293 ("jpg", "JPEG"),
294 ("jpg_2000", "OPENJPEG (JPEG2000)"),
295 ("zlib", "ZLIB (PNG/ZIP)"),
296 ("libtiff", "LIBTIFF"),
297 ("raqm", "RAQM (Bidirectional Text)"),
298 ("libimagequant", "LIBIMAGEQUANT (Quantization method)"),
299 ("xcb", "XCB (X protocol)"),
300 ]:
301 if check(name):
302 v: str | None = None
303 if name == "jpg":
304 libjpeg_turbo_version = version_feature("libjpeg_turbo")
305 if libjpeg_turbo_version is not None:
306 v = "mozjpeg" if check_feature("mozjpeg") else "libjpeg-turbo"
307 v += " " + libjpeg_turbo_version
308 if v is None:
309 v = version(name)
310 if v is not None:
311 version_static = name in ("pil", "jpg")
312 if name == "littlecms2":
313 # this check is also in src/_imagingcms.c:setup_module()
314 version_static = tuple(int(x) for x in v.split(".")) < (2, 7)
315 t = "compiled for" if version_static else "loaded"
316 if name == "zlib":
317 zlib_ng_version = version_feature("zlib_ng")
318 if zlib_ng_version is not None:
319 v += ", compiled for zlib-ng " + zlib_ng_version
320 elif name == "raqm":
321 for f in ("fribidi", "harfbuzz"):
322 v2 = version_feature(f)
323 if v2 is not None:
324 v += f", {f} {v2}"
325 print("---", feature, "support ok,", t, v, file=out)
326 else:
327 print("---", feature, "support ok", file=out)
328 else:
329 print("***", feature, "support not installed", file=out)
330 print("-" * 68, file=out)
332 if supported_formats:
333 extensions = collections.defaultdict(list)
334 for ext, i in Image.EXTENSION.items():
335 extensions[i].append(ext)
337 for i in sorted(Image.ID):
338 line = f"{i}"
339 if i in Image.MIME:
340 line = f"{line} {Image.MIME[i]}"
341 print(line, file=out)
343 if i in extensions:
344 print(
345 "Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out
346 )
348 features = []
349 if i in Image.OPEN:
350 features.append("open")
351 if i in Image.SAVE:
352 features.append("save")
353 if i in Image.SAVE_ALL:
354 features.append("save_all")
355 if i in Image.DECODERS:
356 features.append("decode")
357 if i in Image.ENCODERS:
358 features.append("encode")
360 print("Features: {}".format(", ".join(features)), file=out)
361 print("-" * 68, file=out)