From 11c9b041f3a8583a7b9bee9a13eb40d25180e516 Mon Sep 17 00:00:00 2001 From: Jeffrey 'Alex' Clark Date: Mon, 1 Jun 2026 08:13:17 -0400 Subject: [PATCH 1/4] Convert setup.py to pyproject.toml Move C extension build logic from setup.py into _custom_build/pillow_ext.py and update the custom build backend to call pillow_ext.run() directly, eliminating the need for an executable setup.py script. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- _custom_build/backend.py | 4 +- setup.py => _custom_build/pillow_ext.py | 121 ++++++++++++------------ 2 files changed, 63 insertions(+), 62 deletions(-) rename setup.py => _custom_build/pillow_ext.py (94%) diff --git a/_custom_build/backend.py b/_custom_build/backend.py index 44ec6efe5c6..e0b217a8233 100644 --- a/_custom_build/backend.py +++ b/_custom_build/backend.py @@ -17,7 +17,9 @@ def run_setup(self, setup_script="setup.py"): for value in values: sys.argv.append(f"--pillow-configuration={key}={value}") - return super().run_setup(setup_script) + import pillow_ext + + pillow_ext.run() def build_wheel( self, wheel_directory, config_settings=None, metadata_directory=None diff --git a/setup.py b/_custom_build/pillow_ext.py similarity index 94% rename from setup.py rename to _custom_build/pillow_ext.py index 6a6bffab6f9..c7ec88b03f6 100644 --- a/setup.py +++ b/_custom_build/pillow_ext.py @@ -27,15 +27,6 @@ configuration: dict[str, list[str]] = {} -# parse configuration from _custom_build/backend.py -while sys.argv[-1].startswith("--pillow-configuration="): - _, key, value = sys.argv.pop().split("=", 2) - configuration.setdefault(key, []).append(value) - -default = int(configuration.get("parallel", ["0"])[-1]) -ParallelCompile("MAX_CONCURRENCY", default).install() - - def get_version() -> str: version_file = "src/PIL/_version.py" with open(version_file, encoding="utf-8") as f: @@ -57,20 +48,6 @@ def get_version() -> str: ZLIB_ROOT = None FUZZING_BUILD = "LIB_FUZZING_ENGINE" in os.environ -if sys.platform == "win32" and sys.version_info >= (3, 16): - import atexit - - atexit.register( - lambda: warnings.warn( - f"Pillow {PILLOW_VERSION} does not support Python " - f"{sys.version_info.major}.{sys.version_info.minor} and does not provide " - "prebuilt Windows binaries. We do not recommend building from source on " - "Windows.", - RuntimeWarning, - ) - ) - - _IMAGING = ("decode", "encode", "map", "display", "outline", "path") _LIB_IMAGING = ( @@ -1073,40 +1050,62 @@ def debug_build() -> bool: return hasattr(sys, "gettotalrefcount") or FUZZING_BUILD -libraries: list[tuple[str, _BuildInfo]] = [ - ("pil_imaging_mode", {"sources": ["src/libImaging/Mode.c"]}), -] +def run() -> None: + global configuration + configuration = {} + while sys.argv and sys.argv[-1].startswith("--pillow-configuration="): + _, key, value = sys.argv.pop().split("=", 2) + configuration.setdefault(key, []).append(value) -files: list[str | os.PathLike[str]] = ["src/_imaging.c"] -files.extend("src/" + src_file + ".c" for src_file in _IMAGING) -files.extend( - os.path.join("src/libImaging", src_file + ".c") for src_file in _LIB_IMAGING -) -ext_modules = [ - Extension("PIL._imaging", files), - Extension("PIL._imagingft", ["src/_imagingft.c"]), - Extension("PIL._imagingcms", ["src/_imagingcms.c"]), - Extension("PIL._webp", ["src/_webp.c"]), - Extension("PIL._avif", ["src/_avif.c"]), - Extension("PIL._imagingtk", ["src/_imagingtk.c", "src/Tk/tkImaging.c"]), - Extension( - "PIL._imagingmath", - ["src/_imagingmath.c"], - libraries=None if sys.platform == "win32" else ["m"], - ), - Extension("PIL._imagingmorph", ["src/_imagingmorph.c"]), -] - - -try: - setup( - cmdclass={"build_ext": pil_build_ext}, - ext_modules=ext_modules, - libraries=libraries, - zip_safe=not (debug_build() or PLATFORM_MINGW), + default = int(configuration.get("parallel", ["0"])[-1]) + ParallelCompile("MAX_CONCURRENCY", default).install() + + if sys.platform == "win32" and sys.version_info >= (3, 16): + import atexit + + atexit.register( + lambda: warnings.warn( + f"Pillow {PILLOW_VERSION} does not support Python " + f"{sys.version_info.major}.{sys.version_info.minor} and does not provide " + "prebuilt Windows binaries. We do not recommend building from source on " + "Windows.", + RuntimeWarning, + ) + ) + + libraries: list[tuple[str, _BuildInfo]] = [ + ("pil_imaging_mode", {"sources": ["src/libImaging/Mode.c"]}), + ] + + files: list[str | os.PathLike[str]] = ["src/_imaging.c"] + files.extend("src/" + src_file + ".c" for src_file in _IMAGING) + files.extend( + os.path.join("src/libImaging", src_file + ".c") for src_file in _LIB_IMAGING ) -except RequiredDependencyException as err: - msg = f""" + ext_modules = [ + Extension("PIL._imaging", files), + Extension("PIL._imagingft", ["src/_imagingft.c"]), + Extension("PIL._imagingcms", ["src/_imagingcms.c"]), + Extension("PIL._webp", ["src/_webp.c"]), + Extension("PIL._avif", ["src/_avif.c"]), + Extension("PIL._imagingtk", ["src/_imagingtk.c", "src/Tk/tkImaging.c"]), + Extension( + "PIL._imagingmath", + ["src/_imagingmath.c"], + libraries=None if sys.platform == "win32" else ["m"], + ), + Extension("PIL._imagingmorph", ["src/_imagingmorph.c"]), + ] + + try: + setup( + cmdclass={"build_ext": pil_build_ext}, + ext_modules=ext_modules, + libraries=libraries, + zip_safe=not (debug_build() or PLATFORM_MINGW), + ) + except RequiredDependencyException as err: + msg = f""" The headers or library files could not be found for {str(err)}, a required dependency when compiling Pillow from source. @@ -1115,14 +1114,14 @@ def debug_build() -> bool: https://pillow.readthedocs.io/en/latest/installation/basic-installation.html """ - sys.stderr.write(msg) - raise RequiredDependencyException(msg) -except DependencyException as err: - msg = f""" + sys.stderr.write(msg) + raise RequiredDependencyException(msg) + except DependencyException as err: + msg = f""" The headers or library files could not be found for {str(err)}, which was requested by the option flag '-C {str(err)}=enable' """ - sys.stderr.write(msg) - raise DependencyException(msg) + sys.stderr.write(msg) + raise DependencyException(msg) From e87b16b06aa8da5ef3fa702e11f90b745c9db673 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 12:15:15 +0000 Subject: [PATCH 2/4] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- _custom_build/pillow_ext.py | 1 + 1 file changed, 1 insertion(+) diff --git a/_custom_build/pillow_ext.py b/_custom_build/pillow_ext.py index c7ec88b03f6..ad4fec4f28f 100644 --- a/_custom_build/pillow_ext.py +++ b/_custom_build/pillow_ext.py @@ -27,6 +27,7 @@ configuration: dict[str, list[str]] = {} + def get_version() -> str: version_file = "src/PIL/_version.py" with open(version_file, encoding="utf-8") as f: From 40ce87d12319c67abeb8e62abcadfa12438109df Mon Sep 17 00:00:00 2001 From: Andrew Murray <3112309+radarhere@users.noreply.github.com> Date: Tue, 2 Jun 2026 22:08:37 +1000 Subject: [PATCH 3/4] Lint fix --- _custom_build/pillow_ext.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/_custom_build/pillow_ext.py b/_custom_build/pillow_ext.py index ad4fec4f28f..3355980e129 100644 --- a/_custom_build/pillow_ext.py +++ b/_custom_build/pillow_ext.py @@ -1067,9 +1067,9 @@ def run() -> None: atexit.register( lambda: warnings.warn( f"Pillow {PILLOW_VERSION} does not support Python " - f"{sys.version_info.major}.{sys.version_info.minor} and does not provide " - "prebuilt Windows binaries. We do not recommend building from source on " - "Windows.", + f"{sys.version_info.major}.{sys.version_info.minor} and does not " + "provide prebuilt Windows binaries. We do not recommend building from " + "source on Windows.", RuntimeWarning, ) ) From 33826c11277bf68334e3721087679ae6841c29fb Mon Sep 17 00:00:00 2001 From: Andrew Murray Date: Tue, 2 Jun 2026 22:30:25 +1000 Subject: [PATCH 4/4] Update references to setup.py --- .github/workflows/wheels.yml | 2 +- docs/installation/building-from-source.rst | 2 +- tox.ini | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 0180d1c1c33..4a3d74f4a1a 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -18,7 +18,7 @@ on: - ".github/generate-sbom.py" - ".github/workflows/wheels*" - "pyproject.toml" - - "setup.py" + - "_custom_build/pillow_ext.py" - "wheels/*" - "winbuild/build_prepare.py" - "winbuild/fribidi.cmake" diff --git a/docs/installation/building-from-source.rst b/docs/installation/building-from-source.rst index ecb892e1fcb..e7405c6d6fc 100644 --- a/docs/installation/building-from-source.rst +++ b/docs/installation/building-from-source.rst @@ -249,7 +249,7 @@ If the prerequisites are installed in the standard library locations for your machine (e.g. :file:`/usr` or :file:`/usr/local`), no additional configuration should be required. If they are installed in a non-standard location, you may need to configure setuptools to use -those locations by editing :file:`setup.py` or +those locations by editing :file:`_custom_build/pillow_ext.py` or :file:`pyproject.toml`, or by adding environment variables on the command line:: diff --git a/tox.ini b/tox.ini index aede5fcdc54..47d42015077 100644 --- a/tox.ini +++ b/tox.ini @@ -36,4 +36,4 @@ skip_install = true deps = -r .ci/requirements-mypy.txt commands = - mypy conftest.py selftest.py setup.py checks docs src winbuild Tests {posargs} + mypy conftest.py selftest.py _custom_build/pillow_ext.py checks docs src winbuild Tests {posargs}