CoolFace
Apppublic

Aluode/PerceptionLabPortable

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