Added type hints to setup.py

This commit is contained in:
Andrew Murray 2024-09-07 18:25:44 +10:00
parent 93a0bf0276
commit 1105256f2b
2 changed files with 146 additions and 113 deletions

257
setup.py
View File

@ -15,18 +15,20 @@ import struct
import subprocess import subprocess
import sys import sys
import warnings import warnings
from collections.abc import Iterator
from typing import Any
from setuptools import Extension, setup from setuptools import Extension, setup
from setuptools.command.build_ext import build_ext from setuptools.command.build_ext import build_ext
def get_version(): def get_version() -> str:
version_file = "src/PIL/_version.py" version_file = "src/PIL/_version.py"
with open(version_file, encoding="utf-8") as f: with open(version_file, encoding="utf-8") as f:
return f.read().split('"')[1] return f.read().split('"')[1]
configuration = {} configuration: dict[str, list[str]] = {}
PILLOW_VERSION = get_version() PILLOW_VERSION = get_version()
@ -143,7 +145,7 @@ class RequiredDependencyException(Exception):
PLATFORM_MINGW = os.name == "nt" and "GCC" in sys.version PLATFORM_MINGW = os.name == "nt" and "GCC" in sys.version
def _dbg(s, tp=None): def _dbg(s: str, tp: Any = None) -> None:
if DEBUG: if DEBUG:
if tp: if tp:
print(s % tp) print(s % tp)
@ -151,10 +153,13 @@ def _dbg(s, tp=None):
print(s) print(s)
def _find_library_dirs_ldconfig(): def _find_library_dirs_ldconfig() -> list[str]:
# Based on ctypes.util from Python 2 # Based on ctypes.util from Python 2
ldconfig = "ldconfig" if shutil.which("ldconfig") else "/sbin/ldconfig" ldconfig = "ldconfig" if shutil.which("ldconfig") else "/sbin/ldconfig"
args: list[str]
env: dict[str, str]
expr: str
if sys.platform.startswith("linux") or sys.platform.startswith("gnu"): if sys.platform.startswith("linux") or sys.platform.startswith("gnu"):
if struct.calcsize("l") == 4: if struct.calcsize("l") == 4:
machine = os.uname()[4] + "-32" machine = os.uname()[4] + "-32"
@ -184,13 +189,11 @@ def _find_library_dirs_ldconfig():
try: try:
p = subprocess.Popen( p = subprocess.Popen(
args, stderr=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env args, stderr=subprocess.DEVNULL, stdout=subprocess.PIPE, env=env, text=True
) )
except OSError: # E.g. command not found except OSError: # E.g. command not found
return [] return []
[data, _] = p.communicate() data = p.communicate()[0]
if isinstance(data, bytes):
data = data.decode("latin1")
dirs = [] dirs = []
for dll in re.findall(expr, data): for dll in re.findall(expr, data):
@ -200,7 +203,9 @@ def _find_library_dirs_ldconfig():
return dirs return dirs
def _add_directory(path, subdir, where=None): def _add_directory(
path: list[str], subdir: str | None, where: int | None = None
) -> None:
if subdir is None: if subdir is None:
return return
subdir = os.path.realpath(subdir) subdir = os.path.realpath(subdir)
@ -216,7 +221,7 @@ def _add_directory(path, subdir, where=None):
path.insert(where, subdir) path.insert(where, subdir)
def _find_include_file(self, include): def _find_include_file(self: pil_build_ext, include: str) -> int:
for directory in self.compiler.include_dirs: for directory in self.compiler.include_dirs:
_dbg("Checking for include file %s in %s", (include, directory)) _dbg("Checking for include file %s in %s", (include, directory))
if os.path.isfile(os.path.join(directory, include)): if os.path.isfile(os.path.join(directory, include)):
@ -225,7 +230,7 @@ def _find_include_file(self, include):
return 0 return 0
def _find_library_file(self, library): def _find_library_file(self: pil_build_ext, library: str) -> str | None:
ret = self.compiler.find_library_file(self.compiler.library_dirs, library) ret = self.compiler.find_library_file(self.compiler.library_dirs, library)
if ret: if ret:
_dbg("Found library %s at %s", (library, ret)) _dbg("Found library %s at %s", (library, ret))
@ -234,7 +239,7 @@ def _find_library_file(self, library):
return ret return ret
def _find_include_dir(self, dirname, include): def _find_include_dir(self: pil_build_ext, dirname: str, include: str) -> bool | str:
for directory in self.compiler.include_dirs: for directory in self.compiler.include_dirs:
_dbg("Checking for include file %s in %s", (include, directory)) _dbg("Checking for include file %s in %s", (include, directory))
if os.path.isfile(os.path.join(directory, include)): if os.path.isfile(os.path.join(directory, include)):
@ -245,6 +250,7 @@ def _find_include_dir(self, dirname, include):
if os.path.isfile(os.path.join(subdir, include)): if os.path.isfile(os.path.join(subdir, include)):
_dbg("Found %s in %s", (include, subdir)) _dbg("Found %s in %s", (include, subdir))
return subdir return subdir
return False
def _cmd_exists(cmd: str) -> bool: def _cmd_exists(cmd: str) -> bool:
@ -256,7 +262,7 @@ def _cmd_exists(cmd: str) -> bool:
) )
def _pkg_config(name): def _pkg_config(name: str) -> tuple[list[str], list[str]] | None:
command = os.environ.get("PKG_CONFIG", "pkg-config") command = os.environ.get("PKG_CONFIG", "pkg-config")
for keep_system in (True, False): for keep_system in (True, False):
try: try:
@ -283,10 +289,11 @@ def _pkg_config(name):
return libs, cflags return libs, cflags
except Exception: except Exception:
pass pass
return None
class pil_build_ext(build_ext): class pil_build_ext(build_ext):
class feature: class ext_feature:
features = [ features = [
"zlib", "zlib",
"jpeg", "jpeg",
@ -301,25 +308,32 @@ class pil_build_ext(build_ext):
] ]
required = {"jpeg", "zlib"} required = {"jpeg", "zlib"}
vendor = set() vendor: set[str] = set()
def __init__(self): def __init__(self) -> None:
self._settings: dict[str, str | bool | None] = {}
for f in self.features: for f in self.features:
setattr(self, f, None) self.set(f, None)
def require(self, feat): def require(self, feat: str) -> bool:
return feat in self.required return feat in self.required
def want(self, feat): def get(self, feat: str) -> str | bool | None:
return getattr(self, feat) is None return self._settings[feat]
def want_vendor(self, feat): def set(self, feat: str, value: str | bool | None) -> None:
self._settings[feat] = value
def want(self, feat: str) -> bool:
return self._settings[feat] is None
def want_vendor(self, feat: str) -> bool:
return feat in self.vendor return feat in self.vendor
def __iter__(self): def __iter__(self) -> Iterator[str]:
yield from self.features yield from self.features
feature = feature() feature = ext_feature()
user_options = ( user_options = (
build_ext.user_options build_ext.user_options
@ -337,10 +351,10 @@ class pil_build_ext(build_ext):
) )
@staticmethod @staticmethod
def check_configuration(option, value): def check_configuration(option: str, value: str) -> bool | None:
return True if value in configuration.get(option, []) else None return True if value in configuration.get(option, []) else None
def initialize_options(self): def initialize_options(self) -> None:
self.disable_platform_guessing = self.check_configuration( self.disable_platform_guessing = self.check_configuration(
"platform-guessing", "disable" "platform-guessing", "disable"
) )
@ -355,7 +369,7 @@ class pil_build_ext(build_ext):
self.debug = True self.debug = True
self.parallel = configuration.get("parallel", [None])[-1] self.parallel = configuration.get("parallel", [None])[-1]
def finalize_options(self): def finalize_options(self) -> None:
build_ext.finalize_options(self) build_ext.finalize_options(self)
if self.debug: if self.debug:
global DEBUG global DEBUG
@ -363,12 +377,16 @@ class pil_build_ext(build_ext):
if not self.parallel: if not self.parallel:
# If --parallel (or -j) wasn't specified, we want to reproduce the same # If --parallel (or -j) wasn't specified, we want to reproduce the same
# behavior as before, that is, auto-detect the number of jobs. # behavior as before, that is, auto-detect the number of jobs.
try: self.parallel = None
self.parallel = int(
os.environ.get("MAX_CONCURRENCY", min(4, os.cpu_count())) cpu_count = os.cpu_count()
) if cpu_count is not None:
except TypeError: try:
self.parallel = None self.parallel = int(
os.environ.get("MAX_CONCURRENCY", min(4, cpu_count))
)
except TypeError:
pass
for x in self.feature: for x in self.feature:
if getattr(self, f"disable_{x}"): if getattr(self, f"disable_{x}"):
setattr(self.feature, x, False) setattr(self.feature, x, False)
@ -402,7 +420,13 @@ class pil_build_ext(build_ext):
_dbg("Using vendored version of %s", x) _dbg("Using vendored version of %s", x)
self.feature.vendor.add(x) self.feature.vendor.add(x)
def _update_extension(self, name, libraries, define_macros=None, sources=None): def _update_extension(
self,
name: str,
libraries: list[str] | list[str | bool | None],
define_macros: list[tuple[str, str | None]] | None = None,
sources: list[str] | None = None,
) -> None:
for extension in self.extensions: for extension in self.extensions:
if extension.name == name: if extension.name == name:
extension.libraries += libraries extension.libraries += libraries
@ -415,13 +439,13 @@ class pil_build_ext(build_ext):
extension.extra_link_args = ["--stdlib=libc++"] extension.extra_link_args = ["--stdlib=libc++"]
break break
def _remove_extension(self, name): def _remove_extension(self, name: str) -> None:
for extension in self.extensions: for extension in self.extensions:
if extension.name == name: if extension.name == name:
self.extensions.remove(extension) self.extensions.remove(extension)
break break
def get_macos_sdk_path(self): def get_macos_sdk_path(self) -> str | None:
try: try:
sdk_path = ( sdk_path = (
subprocess.check_output(["xcrun", "--show-sdk-path"]) subprocess.check_output(["xcrun", "--show-sdk-path"])
@ -442,9 +466,9 @@ class pil_build_ext(build_ext):
sdk_path = commandlinetools_sdk_path sdk_path = commandlinetools_sdk_path
return sdk_path return sdk_path
def build_extensions(self): def build_extensions(self) -> None:
library_dirs = [] library_dirs: list[str] = []
include_dirs = [] include_dirs: list[str] = []
pkg_config = None pkg_config = None
if _cmd_exists(os.environ.get("PKG_CONFIG", "pkg-config")): if _cmd_exists(os.environ.get("PKG_CONFIG", "pkg-config")):
@ -468,19 +492,22 @@ class pil_build_ext(build_ext):
root = globals()[root_name] root = globals()[root_name]
if root is None and root_name in os.environ: if root is None and root_name in os.environ:
prefix = os.environ[root_name] root_prefix = os.environ[root_name]
root = (os.path.join(prefix, "lib"), os.path.join(prefix, "include")) root = (
os.path.join(root_prefix, "lib"),
os.path.join(root_prefix, "include"),
)
if root is None and pkg_config: if root is None and pkg_config:
if isinstance(lib_name, tuple): if isinstance(lib_name, str):
_dbg(f"Looking for `{lib_name}` using pkg-config.")
root = pkg_config(lib_name)
else:
for lib_name2 in lib_name: for lib_name2 in lib_name:
_dbg(f"Looking for `{lib_name2}` using pkg-config.") _dbg(f"Looking for `{lib_name2}` using pkg-config.")
root = pkg_config(lib_name2) root = pkg_config(lib_name2)
if root: if root:
break break
else:
_dbg(f"Looking for `{lib_name}` using pkg-config.")
root = pkg_config(lib_name)
if isinstance(root, tuple): if isinstance(root, tuple):
lib_root, include_root = root lib_root, include_root = root
@ -660,22 +687,22 @@ class pil_build_ext(build_ext):
_dbg("Looking for zlib") _dbg("Looking for zlib")
if _find_include_file(self, "zlib.h"): if _find_include_file(self, "zlib.h"):
if _find_library_file(self, "z"): if _find_library_file(self, "z"):
feature.zlib = "z" feature.set("zlib", "z")
elif sys.platform == "win32" and _find_library_file(self, "zlib"): elif sys.platform == "win32" and _find_library_file(self, "zlib"):
feature.zlib = "zlib" # alternative name feature.set("zlib", "zlib") # alternative name
if feature.want("jpeg"): if feature.want("jpeg"):
_dbg("Looking for jpeg") _dbg("Looking for jpeg")
if _find_include_file(self, "jpeglib.h"): if _find_include_file(self, "jpeglib.h"):
if _find_library_file(self, "jpeg"): if _find_library_file(self, "jpeg"):
feature.jpeg = "jpeg" feature.set("jpeg", "jpeg")
elif sys.platform == "win32" and _find_library_file(self, "libjpeg"): elif sys.platform == "win32" and _find_library_file(self, "libjpeg"):
feature.jpeg = "libjpeg" # alternative name feature.set("jpeg", "libjpeg") # alternative name
feature.openjpeg_version = None feature.set("openjpeg_version", None)
if feature.want("jpeg2000"): if feature.want("jpeg2000"):
_dbg("Looking for jpeg2000") _dbg("Looking for jpeg2000")
best_version = None best_version: tuple[int, ...] | None = None
best_path = None best_path = None
# Find the best version # Find the best version
@ -705,26 +732,26 @@ class pil_build_ext(build_ext):
# <openjpeg.h> rather than having to cope with the versioned # <openjpeg.h> rather than having to cope with the versioned
# include path # include path
_add_directory(self.compiler.include_dirs, best_path, 0) _add_directory(self.compiler.include_dirs, best_path, 0)
feature.jpeg2000 = "openjp2" feature.set("jpeg2000", "openjp2")
feature.openjpeg_version = ".".join(str(x) for x in best_version) feature.set("openjpeg_version", ".".join(str(x) for x in best_version))
if feature.want("imagequant"): if feature.want("imagequant"):
_dbg("Looking for imagequant") _dbg("Looking for imagequant")
if _find_include_file(self, "libimagequant.h"): if _find_include_file(self, "libimagequant.h"):
if _find_library_file(self, "imagequant"): if _find_library_file(self, "imagequant"):
feature.imagequant = "imagequant" feature.set("imagequant", "imagequant")
elif _find_library_file(self, "libimagequant"): elif _find_library_file(self, "libimagequant"):
feature.imagequant = "libimagequant" feature.set("imagequant", "libimagequant")
if feature.want("tiff"): if feature.want("tiff"):
_dbg("Looking for tiff") _dbg("Looking for tiff")
if _find_include_file(self, "tiff.h"): if _find_include_file(self, "tiff.h"):
if _find_library_file(self, "tiff"): if _find_library_file(self, "tiff"):
feature.tiff = "tiff" feature.set("tiff", "tiff")
if sys.platform in ["win32", "darwin"] and _find_library_file( if sys.platform in ["win32", "darwin"] and _find_library_file(
self, "libtiff" self, "libtiff"
): ):
feature.tiff = "libtiff" feature.set("tiff", "libtiff")
if feature.want("freetype"): if feature.want("freetype"):
_dbg("Looking for freetype") _dbg("Looking for freetype")
@ -745,31 +772,31 @@ class pil_build_ext(build_ext):
freetype_version = 21 freetype_version = 21
break break
if freetype_version: if freetype_version:
feature.freetype = "freetype" feature.set("freetype", "freetype")
if subdir: if subdir:
_add_directory(self.compiler.include_dirs, subdir, 0) _add_directory(self.compiler.include_dirs, subdir, 0)
if feature.freetype and feature.want("raqm"): if feature.get("freetype") and feature.want("raqm"):
if not feature.want_vendor("raqm"): # want system Raqm if not feature.want_vendor("raqm"): # want system Raqm
_dbg("Looking for Raqm") _dbg("Looking for Raqm")
if _find_include_file(self, "raqm.h"): if _find_include_file(self, "raqm.h"):
if _find_library_file(self, "raqm"): if _find_library_file(self, "raqm"):
feature.raqm = "raqm" feature.set("raqm", "raqm")
elif _find_library_file(self, "libraqm"): elif _find_library_file(self, "libraqm"):
feature.raqm = "libraqm" feature.set("raqm", "libraqm")
else: # want to build Raqm from src/thirdparty else: # want to build Raqm from src/thirdparty
_dbg("Looking for HarfBuzz") _dbg("Looking for HarfBuzz")
feature.harfbuzz = None feature.set("harfbuzz", None)
hb_dir = _find_include_dir(self, "harfbuzz", "hb.h") hb_dir = _find_include_dir(self, "harfbuzz", "hb.h")
if hb_dir: if hb_dir:
if isinstance(hb_dir, str): if isinstance(hb_dir, str):
_add_directory(self.compiler.include_dirs, hb_dir, 0) _add_directory(self.compiler.include_dirs, hb_dir, 0)
if _find_library_file(self, "harfbuzz"): if _find_library_file(self, "harfbuzz"):
feature.harfbuzz = "harfbuzz" feature.set("harfbuzz", "harfbuzz")
if feature.harfbuzz: if feature.get("harfbuzz"):
if not feature.want_vendor("fribidi"): # want system FriBiDi if not feature.want_vendor("fribidi"): # want system FriBiDi
_dbg("Looking for FriBiDi") _dbg("Looking for FriBiDi")
feature.fribidi = None feature.set("fribidi", None)
fribidi_dir = _find_include_dir(self, "fribidi", "fribidi.h") fribidi_dir = _find_include_dir(self, "fribidi", "fribidi.h")
if fribidi_dir: if fribidi_dir:
if isinstance(fribidi_dir, str): if isinstance(fribidi_dir, str):
@ -777,19 +804,19 @@ class pil_build_ext(build_ext):
self.compiler.include_dirs, fribidi_dir, 0 self.compiler.include_dirs, fribidi_dir, 0
) )
if _find_library_file(self, "fribidi"): if _find_library_file(self, "fribidi"):
feature.fribidi = "fribidi" feature.set("fribidi", "fribidi")
feature.raqm = True feature.set("raqm", True)
else: # want to build FriBiDi shim from src/thirdparty else: # want to build FriBiDi shim from src/thirdparty
feature.raqm = True feature.set("raqm", True)
if feature.want("lcms"): if feature.want("lcms"):
_dbg("Looking for lcms") _dbg("Looking for lcms")
if _find_include_file(self, "lcms2.h"): if _find_include_file(self, "lcms2.h"):
if _find_library_file(self, "lcms2"): if _find_library_file(self, "lcms2"):
feature.lcms = "lcms2" feature.set("lcms", "lcms2")
elif _find_library_file(self, "lcms2_static"): elif _find_library_file(self, "lcms2_static"):
# alternate Windows name. # alternate Windows name.
feature.lcms = "lcms2_static" feature.set("lcms", "lcms2_static")
if feature.want("webp"): if feature.want("webp"):
_dbg("Looking for webp") _dbg("Looking for webp")
@ -803,17 +830,17 @@ class pil_build_ext(build_ext):
_find_library_file(self, prefix + library) _find_library_file(self, prefix + library)
for library in ("webp", "webpmux", "webpdemux") for library in ("webp", "webpmux", "webpdemux")
): ):
feature.webp = prefix + "webp" feature.set("webp", prefix + "webp")
break break
if feature.want("xcb"): if feature.want("xcb"):
_dbg("Looking for xcb") _dbg("Looking for xcb")
if _find_include_file(self, "xcb/xcb.h"): if _find_include_file(self, "xcb/xcb.h"):
if _find_library_file(self, "xcb"): if _find_library_file(self, "xcb"):
feature.xcb = "xcb" feature.set("xcb", "xcb")
for f in feature: for f in feature:
if not getattr(feature, f) and feature.require(f): if not feature.get(f) and feature.require(f):
if f in ("jpeg", "zlib"): if f in ("jpeg", "zlib"):
raise RequiredDependencyException(f) raise RequiredDependencyException(f)
raise DependencyException(f) raise DependencyException(f)
@ -821,10 +848,11 @@ class pil_build_ext(build_ext):
# #
# core library # core library
libs = self.add_imaging_libs.split() libs: list[str | bool | None] = []
defs = [] libs.extend(self.add_imaging_libs.split())
if feature.tiff: defs: list[tuple[str, str | None]] = []
libs.append(feature.tiff) if feature.get("tiff"):
libs.append(feature.get("tiff"))
defs.append(("HAVE_LIBTIFF", None)) defs.append(("HAVE_LIBTIFF", None))
if sys.platform == "win32": if sys.platform == "win32":
# This define needs to be defined if-and-only-if it was defined # This define needs to be defined if-and-only-if it was defined
@ -832,22 +860,22 @@ class pil_build_ext(build_ext):
# so we have to guess; by default it is defined in all Windows builds. # so we have to guess; by default it is defined in all Windows builds.
# See #4237, #5243, #5359 for more information. # See #4237, #5243, #5359 for more information.
defs.append(("USE_WIN32_FILEIO", None)) defs.append(("USE_WIN32_FILEIO", None))
if feature.jpeg: if feature.get("jpeg"):
libs.append(feature.jpeg) libs.append(feature.get("jpeg"))
defs.append(("HAVE_LIBJPEG", None)) defs.append(("HAVE_LIBJPEG", None))
if feature.jpeg2000: if feature.get("jpeg2000"):
libs.append(feature.jpeg2000) libs.append(feature.get("jpeg2000"))
defs.append(("HAVE_OPENJPEG", None)) defs.append(("HAVE_OPENJPEG", None))
if sys.platform == "win32" and not PLATFORM_MINGW: if sys.platform == "win32" and not PLATFORM_MINGW:
defs.append(("OPJ_STATIC", None)) defs.append(("OPJ_STATIC", None))
if feature.zlib: if feature.get("zlib"):
libs.append(feature.zlib) libs.append(feature.get("zlib"))
defs.append(("HAVE_LIBZ", None)) defs.append(("HAVE_LIBZ", None))
if feature.imagequant: if feature.get("imagequant"):
libs.append(feature.imagequant) libs.append(feature.get("imagequant"))
defs.append(("HAVE_LIBIMAGEQUANT", None)) defs.append(("HAVE_LIBIMAGEQUANT", None))
if feature.xcb: if feature.get("xcb"):
libs.append(feature.xcb) libs.append(feature.get("xcb"))
defs.append(("HAVE_XCB", None)) defs.append(("HAVE_XCB", None))
if sys.platform == "win32": if sys.platform == "win32":
libs.extend(["kernel32", "user32", "gdi32"]) libs.extend(["kernel32", "user32", "gdi32"])
@ -861,22 +889,22 @@ class pil_build_ext(build_ext):
# #
# additional libraries # additional libraries
if feature.freetype: if feature.get("freetype"):
srcs = [] srcs = []
libs = ["freetype"] libs = ["freetype"]
defs = [] defs = []
if feature.raqm: if feature.get("raqm"):
if not feature.want_vendor("raqm"): # using system Raqm if not feature.want_vendor("raqm"): # using system Raqm
defs.append(("HAVE_RAQM", None)) defs.append(("HAVE_RAQM", None))
defs.append(("HAVE_RAQM_SYSTEM", None)) defs.append(("HAVE_RAQM_SYSTEM", None))
libs.append(feature.raqm) libs.append(feature.get("raqm"))
else: # building Raqm from src/thirdparty else: # building Raqm from src/thirdparty
defs.append(("HAVE_RAQM", None)) defs.append(("HAVE_RAQM", None))
srcs.append("src/thirdparty/raqm/raqm.c") srcs.append("src/thirdparty/raqm/raqm.c")
libs.append(feature.harfbuzz) libs.append(feature.get("harfbuzz"))
if not feature.want_vendor("fribidi"): # using system FriBiDi if not feature.want_vendor("fribidi"): # using system FriBiDi
defs.append(("HAVE_FRIBIDI_SYSTEM", None)) defs.append(("HAVE_FRIBIDI_SYSTEM", None))
libs.append(feature.fribidi) libs.append(feature.get("fribidi"))
else: # building FriBiDi shim from src/thirdparty else: # building FriBiDi shim from src/thirdparty
srcs.append("src/thirdparty/fribidi-shim/fribidi.c") srcs.append("src/thirdparty/fribidi-shim/fribidi.c")
self._update_extension("PIL._imagingft", libs, defs, srcs) self._update_extension("PIL._imagingft", libs, defs, srcs)
@ -884,16 +912,17 @@ class pil_build_ext(build_ext):
else: else:
self._remove_extension("PIL._imagingft") self._remove_extension("PIL._imagingft")
if feature.lcms: if feature.get("lcms"):
extra = [] libs = [feature.get("lcms")]
if sys.platform == "win32": if sys.platform == "win32":
extra.extend(["user32", "gdi32"]) libs.extend(["user32", "gdi32"])
self._update_extension("PIL._imagingcms", [feature.lcms] + extra) self._update_extension("PIL._imagingcms", libs)
else: else:
self._remove_extension("PIL._imagingcms") self._remove_extension("PIL._imagingcms")
if feature.webp: webp = feature.get("webp")
libs = [feature.webp, feature.webp + "mux", feature.webp + "demux"] if isinstance(webp, str):
libs = [webp, webp + "mux", webp + "demux"]
self._update_extension("PIL._webp", libs) self._update_extension("PIL._webp", libs)
else: else:
self._remove_extension("PIL._webp") self._remove_extension("PIL._webp")
@ -908,14 +937,14 @@ class pil_build_ext(build_ext):
self.summary_report(feature) self.summary_report(feature)
def summary_report(self, feature): def summary_report(self, feature: ext_feature) -> None:
print("-" * 68) print("-" * 68)
print("PIL SETUP SUMMARY") print("PIL SETUP SUMMARY")
print("-" * 68) print("-" * 68)
print(f"version Pillow {PILLOW_VERSION}") print(f"version Pillow {PILLOW_VERSION}")
v = sys.version.split("[") version = sys.version.split("[")
print(f"platform {sys.platform} {v[0].strip()}") print(f"platform {sys.platform} {version[0].strip()}")
for v in v[1:]: for v in version[1:]:
print(f" [{v.strip()}") print(f" [{v.strip()}")
print("-" * 68) print("-" * 68)
@ -926,16 +955,20 @@ class pil_build_ext(build_ext):
raqm_extra_info += ", FriBiDi shim" raqm_extra_info += ", FriBiDi shim"
options = [ options = [
(feature.jpeg, "JPEG"), (feature.get("jpeg"), "JPEG"),
(feature.jpeg2000, "OPENJPEG (JPEG2000)", feature.openjpeg_version), (
(feature.zlib, "ZLIB (PNG/ZIP)"), feature.get("jpeg2000"),
(feature.imagequant, "LIBIMAGEQUANT"), "OPENJPEG (JPEG2000)",
(feature.tiff, "LIBTIFF"), feature.get("openjpeg_version"),
(feature.freetype, "FREETYPE2"), ),
(feature.raqm, "RAQM (Text shaping)", raqm_extra_info), (feature.get("zlib"), "ZLIB (PNG/ZIP)"),
(feature.lcms, "LITTLECMS2"), (feature.get("imagequant"), "LIBIMAGEQUANT"),
(feature.webp, "WEBP"), (feature.get("tiff"), "LIBTIFF"),
(feature.xcb, "XCB (X protocol)"), (feature.get("freetype"), "FREETYPE2"),
(feature.get("raqm"), "RAQM (Text shaping)", raqm_extra_info),
(feature.get("lcms"), "LITTLECMS2"),
(feature.get("webp"), "WEBP"),
(feature.get("xcb"), "XCB (X protocol)"),
] ]
all = 1 all = 1
@ -964,7 +997,7 @@ class pil_build_ext(build_ext):
print("") print("")
def debug_build(): def debug_build() -> bool:
return hasattr(sys, "gettotalrefcount") or FUZZING_BUILD return hasattr(sys, "gettotalrefcount") or FUZZING_BUILD

View File

@ -36,4 +36,4 @@ deps =
extras = extras =
typing typing
commands = commands =
mypy docs src winbuild Tests {posargs} mypy conftest.py selftest.py setup.py docs src winbuild Tests {posargs}