123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- from __future__ import annotations
- import collections
- import os
- import sys
- import warnings
- from typing import IO
- import PIL
- from . import Image
- from ._deprecate import deprecate
- modules = {
- "pil": ("PIL._imaging", "PILLOW_VERSION"),
- "tkinter": ("PIL._tkinter_finder", "tk_version"),
- "freetype2": ("PIL._imagingft", "freetype2_version"),
- "littlecms2": ("PIL._imagingcms", "littlecms_version"),
- "webp": ("PIL._webp", "webpdecoder_version"),
- }
- def check_module(feature: str) -> bool:
- """
- Checks if a module is available.
- :param feature: The module to check for.
- :returns: ``True`` if available, ``False`` otherwise.
- :raises ValueError: If the module is not defined in this version of Pillow.
- """
- if feature not in modules:
- msg = f"Unknown module {feature}"
- raise ValueError(msg)
- module, ver = modules[feature]
- try:
- __import__(module)
- return True
- except ModuleNotFoundError:
- return False
- except ImportError as ex:
- warnings.warn(str(ex))
- return False
- def version_module(feature: str) -> str | None:
- """
- :param feature: The module to check for.
- :returns:
- The loaded version number as a string, or ``None`` if unknown or not available.
- :raises ValueError: If the module is not defined in this version of Pillow.
- """
- if not check_module(feature):
- return None
- module, ver = modules[feature]
- return getattr(__import__(module, fromlist=[ver]), ver)
- def get_supported_modules() -> list[str]:
- """
- :returns: A list of all supported modules.
- """
- return [f for f in modules if check_module(f)]
- codecs = {
- "jpg": ("jpeg", "jpeglib"),
- "jpg_2000": ("jpeg2k", "jp2klib"),
- "zlib": ("zip", "zlib"),
- "libtiff": ("libtiff", "libtiff"),
- }
- def check_codec(feature: str) -> bool:
- """
- Checks if a codec is available.
- :param feature: The codec to check for.
- :returns: ``True`` if available, ``False`` otherwise.
- :raises ValueError: If the codec is not defined in this version of Pillow.
- """
- if feature not in codecs:
- msg = f"Unknown codec {feature}"
- raise ValueError(msg)
- codec, lib = codecs[feature]
- return f"{codec}_encoder" in dir(Image.core)
- def version_codec(feature: str) -> str | None:
- """
- :param feature: The codec to check for.
- :returns:
- The version number as a string, or ``None`` if not available.
- Checked at compile time for ``jpg``, run-time otherwise.
- :raises ValueError: If the codec is not defined in this version of Pillow.
- """
- if not check_codec(feature):
- return None
- codec, lib = codecs[feature]
- version = getattr(Image.core, f"{lib}_version")
- if feature == "libtiff":
- return version.split("\n")[0].split("Version ")[1]
- return version
- def get_supported_codecs() -> list[str]:
- """
- :returns: A list of all supported codecs.
- """
- return [f for f in codecs if check_codec(f)]
- features: dict[str, tuple[str, str | bool, str | None]] = {
- "webp_anim": ("PIL._webp", True, None),
- "webp_mux": ("PIL._webp", True, None),
- "transp_webp": ("PIL._webp", True, None),
- "raqm": ("PIL._imagingft", "HAVE_RAQM", "raqm_version"),
- "fribidi": ("PIL._imagingft", "HAVE_FRIBIDI", "fribidi_version"),
- "harfbuzz": ("PIL._imagingft", "HAVE_HARFBUZZ", "harfbuzz_version"),
- "libjpeg_turbo": ("PIL._imaging", "HAVE_LIBJPEGTURBO", "libjpeg_turbo_version"),
- "zlib_ng": ("PIL._imaging", "HAVE_ZLIBNG", "zlib_ng_version"),
- "libimagequant": ("PIL._imaging", "HAVE_LIBIMAGEQUANT", "imagequant_version"),
- "xcb": ("PIL._imaging", "HAVE_XCB", None),
- }
- def check_feature(feature: str) -> bool | None:
- """
- Checks if a feature is available.
- :param feature: The feature to check for.
- :returns: ``True`` if available, ``False`` if unavailable, ``None`` if unknown.
- :raises ValueError: If the feature is not defined in this version of Pillow.
- """
- if feature not in features:
- msg = f"Unknown feature {feature}"
- raise ValueError(msg)
- module, flag, ver = features[feature]
- if isinstance(flag, bool):
- deprecate(f'check_feature("{feature}")', 12)
- try:
- imported_module = __import__(module, fromlist=["PIL"])
- if isinstance(flag, bool):
- return flag
- return getattr(imported_module, flag)
- except ModuleNotFoundError:
- return None
- except ImportError as ex:
- warnings.warn(str(ex))
- return None
- def version_feature(feature: str) -> str | None:
- """
- :param feature: The feature to check for.
- :returns: The version number as a string, or ``None`` if not available.
- :raises ValueError: If the feature is not defined in this version of Pillow.
- """
- if not check_feature(feature):
- return None
- module, flag, ver = features[feature]
- if ver is None:
- return None
- return getattr(__import__(module, fromlist=[ver]), ver)
- def get_supported_features() -> list[str]:
- """
- :returns: A list of all supported features.
- """
- supported_features = []
- for f, (module, flag, _) in features.items():
- if flag is True:
- for feature, (feature_module, _) in modules.items():
- if feature_module == module:
- if check_module(feature):
- supported_features.append(f)
- break
- elif check_feature(f):
- supported_features.append(f)
- return supported_features
- def check(feature: str) -> bool | None:
- """
- :param feature: A module, codec, or feature name.
- :returns:
- ``True`` if the module, codec, or feature is available,
- ``False`` or ``None`` otherwise.
- """
- if feature in modules:
- return check_module(feature)
- if feature in codecs:
- return check_codec(feature)
- if feature in features:
- return check_feature(feature)
- warnings.warn(f"Unknown feature '{feature}'.", stacklevel=2)
- return False
- def version(feature: str) -> str | None:
- """
- :param feature:
- The module, codec, or feature to check for.
- :returns:
- The version number as a string, or ``None`` if unknown or not available.
- """
- if feature in modules:
- return version_module(feature)
- if feature in codecs:
- return version_codec(feature)
- if feature in features:
- return version_feature(feature)
- return None
- def get_supported() -> list[str]:
- """
- :returns: A list of all supported modules, features, and codecs.
- """
- ret = get_supported_modules()
- ret.extend(get_supported_features())
- ret.extend(get_supported_codecs())
- return ret
- def pilinfo(out: IO[str] | None = None, supported_formats: bool = True) -> None:
- """
- Prints information about this installation of Pillow.
- This function can be called with ``python3 -m PIL``.
- It can also be called with ``python3 -m PIL.report`` or ``python3 -m PIL --report``
- to have "supported_formats" set to ``False``, omitting the list of all supported
- image file formats.
- :param out:
- The output stream to print to. Defaults to ``sys.stdout`` if ``None``.
- :param supported_formats:
- If ``True``, a list of all supported image file formats will be printed.
- """
- if out is None:
- out = sys.stdout
- Image.init()
- print("-" * 68, file=out)
- print(f"Pillow {PIL.__version__}", file=out)
- py_version_lines = sys.version.splitlines()
- print(f"Python {py_version_lines[0].strip()}", file=out)
- for py_version in py_version_lines[1:]:
- print(f" {py_version.strip()}", file=out)
- print("-" * 68, file=out)
- print(f"Python executable is {sys.executable or 'unknown'}", file=out)
- if sys.prefix != sys.base_prefix:
- print(f"Environment Python files loaded from {sys.prefix}", file=out)
- print(f"System Python files loaded from {sys.base_prefix}", file=out)
- print("-" * 68, file=out)
- print(
- f"Python Pillow modules loaded from {os.path.dirname(Image.__file__)}",
- file=out,
- )
- print(
- f"Binary Pillow modules loaded from {os.path.dirname(Image.core.__file__)}",
- file=out,
- )
- print("-" * 68, file=out)
- for name, feature in [
- ("pil", "PIL CORE"),
- ("tkinter", "TKINTER"),
- ("freetype2", "FREETYPE2"),
- ("littlecms2", "LITTLECMS2"),
- ("webp", "WEBP"),
- ("jpg", "JPEG"),
- ("jpg_2000", "OPENJPEG (JPEG2000)"),
- ("zlib", "ZLIB (PNG/ZIP)"),
- ("libtiff", "LIBTIFF"),
- ("raqm", "RAQM (Bidirectional Text)"),
- ("libimagequant", "LIBIMAGEQUANT (Quantization method)"),
- ("xcb", "XCB (X protocol)"),
- ]:
- if check(name):
- v: str | None = None
- if name == "jpg":
- libjpeg_turbo_version = version_feature("libjpeg_turbo")
- if libjpeg_turbo_version is not None:
- v = "libjpeg-turbo " + libjpeg_turbo_version
- if v is None:
- v = version(name)
- if v is not None:
- version_static = name in ("pil", "jpg")
- if name == "littlecms2":
- # this check is also in src/_imagingcms.c:setup_module()
- version_static = tuple(int(x) for x in v.split(".")) < (2, 7)
- t = "compiled for" if version_static else "loaded"
- if name == "zlib":
- zlib_ng_version = version_feature("zlib_ng")
- if zlib_ng_version is not None:
- v += ", compiled for zlib-ng " + zlib_ng_version
- elif name == "raqm":
- for f in ("fribidi", "harfbuzz"):
- v2 = version_feature(f)
- if v2 is not None:
- v += f", {f} {v2}"
- print("---", feature, "support ok,", t, v, file=out)
- else:
- print("---", feature, "support ok", file=out)
- else:
- print("***", feature, "support not installed", file=out)
- print("-" * 68, file=out)
- if supported_formats:
- extensions = collections.defaultdict(list)
- for ext, i in Image.EXTENSION.items():
- extensions[i].append(ext)
- for i in sorted(Image.ID):
- line = f"{i}"
- if i in Image.MIME:
- line = f"{line} {Image.MIME[i]}"
- print(line, file=out)
- if i in extensions:
- print(
- "Extensions: {}".format(", ".join(sorted(extensions[i]))), file=out
- )
- features = []
- if i in Image.OPEN:
- features.append("open")
- if i in Image.SAVE:
- features.append("save")
- if i in Image.SAVE_ALL:
- features.append("save_all")
- if i in Image.DECODERS:
- features.append("decode")
- if i in Image.ENCODERS:
- features.append("encode")
- print("Features: {}".format(", ".join(features)), file=out)
- print("-" * 68, file=out)
|