diff --git a/.appveyor.yml b/.appveyor.yml index 9a2eef76781..36f5bd0ad68 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -10,8 +10,8 @@ environment: TEST_OPTIONS: DEPLOY: YES matrix: - - PYTHON: C:/Python311-x64 - ARCHITECTURE: x64 + - PYTHON: C:/Python311 + ARCHITECTURE: x86 APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 - PYTHON: C:/Python38-x64 ARCHITECTURE: x64 diff --git a/.github/workflows/test-docker.yml b/.github/workflows/test-docker.yml index f22733dc463..36d9c131d0b 100644 --- a/.github/workflows/test-docker.yml +++ b/.github/workflows/test-docker.yml @@ -39,6 +39,7 @@ jobs: centos-stream-8-amd64, centos-stream-9-amd64, debian-11-bullseye-amd64, + debian-12-bookworm-x86, debian-12-bookworm-amd64, fedora-37-amd64, fedora-38-amd64, diff --git a/Tests/32bit_segfault_check.py b/Tests/32bit_segfault_check.py new file mode 100755 index 00000000000..2ff7f908f6d --- /dev/null +++ b/Tests/32bit_segfault_check.py @@ -0,0 +1,8 @@ +#!/usr/bin/env python3 + +import sys + +from PIL import Image + +if sys.maxsize < 2**32: + im = Image.new("L", (999999, 999999), 0) diff --git a/Tests/check_large_memory.py b/Tests/check_large_memory.py index 219788d7bd9..d98f4a694ef 100644 --- a/Tests/check_large_memory.py +++ b/Tests/check_large_memory.py @@ -1,3 +1,5 @@ +import sys + import pytest from PIL import Image @@ -21,6 +23,9 @@ XDIM = 48000 +pytestmark = pytest.mark.skipif(sys.maxsize <= 2**32, reason="requires 64-bit system") + + def _write_png(tmp_path, xdim, ydim): f = str(tmp_path / "temp.png") im = Image.new("L", (xdim, ydim), 0) diff --git a/Tests/check_large_memory_numpy.py b/Tests/check_large_memory_numpy.py index c54894721d0..24cb1f722bf 100644 --- a/Tests/check_large_memory_numpy.py +++ b/Tests/check_large_memory_numpy.py @@ -1,3 +1,5 @@ +import sys + import pytest from PIL import Image @@ -17,6 +19,9 @@ XDIM = 48000 +pytestmark = pytest.mark.skipif(sys.maxsize <= 2**32, reason="requires 64-bit system") + + def _write_png(tmp_path, xdim, ydim): dtype = np.uint8 a = np.zeros((xdim, ydim), dtype=dtype) diff --git a/Tests/test_core_resources.py b/Tests/test_core_resources.py index f2105d6ca6c..9021a9fb36d 100644 --- a/Tests/test_core_resources.py +++ b/Tests/test_core_resources.py @@ -1,3 +1,5 @@ +import sys + import pytest from PIL import Image @@ -108,6 +110,9 @@ def test_set_blocks_max(self): with pytest.raises(ValueError): Image.core.set_blocks_max(-1) + if sys.maxsize < 2**32: + with pytest.raises(ValueError): + Image.core.set_blocks_max(2**29) @pytest.mark.skipif(is_pypy(), reason="Images not collected") def test_set_blocks_max_stats(self): diff --git a/Tests/test_file_webp.py b/Tests/test_file_webp.py index 01b11447ac4..a7b6c735a69 100644 --- a/Tests/test_file_webp.py +++ b/Tests/test_file_webp.py @@ -1,5 +1,6 @@ import io import re +import sys import warnings import pytest @@ -144,6 +145,7 @@ def test_write_unsupported_mode_P(self, tmp_path): self._roundtrip(tmp_path, "P", 50.0) + @pytest.mark.skipif(sys.maxsize <= 2**32, reason="Requires 64-bit system") def test_write_encoding_error_message(self, tmp_path): temp_file = str(tmp_path / "temp.webp") im = Image.new("RGB", (15000, 15000)) diff --git a/Tests/test_image_putdata.py b/Tests/test_image_putdata.py index db5307d2ccb..0e6293349bc 100644 --- a/Tests/test_image_putdata.py +++ b/Tests/test_image_putdata.py @@ -38,7 +38,10 @@ def put(value): assert put(0xFFFFFFFF) == (255, 255, 255, 255) assert put(-1) == (255, 255, 255, 255) assert put(-1) == (255, 255, 255, 255) - assert put(sys.maxsize) == (255, 255, 255, 255) + if sys.maxsize > 2**32: + assert put(sys.maxsize) == (255, 255, 255, 255) + else: + assert put(sys.maxsize) == (255, 255, 255, 127) def test_pypy_performance(): diff --git a/Tests/test_map.py b/Tests/test_map.py index 42b6f7cddab..d816bddaf3d 100644 --- a/Tests/test_map.py +++ b/Tests/test_map.py @@ -1,3 +1,5 @@ +import sys + import pytest from PIL import Image @@ -34,6 +36,7 @@ def test_tobytes(): Image.MAX_IMAGE_PIXELS = max_pixels +@pytest.mark.skipif(sys.maxsize <= 2**32, reason="Requires 64-bit system") def test_ysize(): numpy = pytest.importorskip("numpy", reason="NumPy not installed") diff --git a/codecov.yml b/codecov.yml index 40419979fb4..1ea7974ebbe 100644 --- a/codecov.yml +++ b/codecov.yml @@ -16,6 +16,7 @@ coverage: # Matches 'omit:' in .coveragerc ignore: + - "Tests/32bit_segfault_check.py" - "Tests/bench_cffi_access.py" - "Tests/check_*.py" - "Tests/createfontdatachunk.py" diff --git a/docs/installation.rst b/docs/installation.rst index e29709bff0d..5f3d2c9ef6a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -285,8 +285,11 @@ Many of Pillow's features require external libraries: .. tab:: Windows using MSYS2/MinGW - To build Pillow using MSYS2, make sure you run the **MSYS2 MinGW 64-bit** console, - *not* **MSYS2** directly. + To build Pillow using MSYS2, make sure you run the **MSYS2 MinGW 32-bit** or + **MSYS2 MinGW 64-bit** console, *not* **MSYS2** directly. + + The following instructions target the 64-bit build, for 32-bit + replace all occurrences of ``mingw-w64-x86_64-`` with ``mingw-w64-i686-``. Make sure you have Python and GCC installed:: @@ -336,6 +339,8 @@ Many of Pillow's features require external libraries: pkg install -y python ndk-sysroot clang make \ libjpeg-turbo + This has been tested within the Termux app on ChromeOS, on x86. + Installing ^^^^^^^^^^ @@ -448,7 +453,7 @@ These platforms are built and tested for every change. +----------------------------------+----------------------------+---------------------+ | Debian 11 Bullseye | 3.9 | x86-64 | +----------------------------------+----------------------------+---------------------+ -| Debian 12 Bookworm | 3.11 | x86-64 | +| Debian 12 Bookworm | 3.11 | x86, x86-64 | +----------------------------------+----------------------------+---------------------+ | Fedora 37 | 3.11 | x86-64 | +----------------------------------+----------------------------+---------------------+ @@ -472,6 +477,8 @@ These platforms are built and tested for every change. | Windows Server 2022 | 3.8, 3.9, 3.10, 3.11, | x86-64 | | | 3.12, PyPy3 | | | +----------------------------+---------------------+ +| | 3.11 | x86 | +| +----------------------------+---------------------+ | | 3.9 (MinGW) | x86-64 | | +----------------------------+---------------------+ | | 3.8, 3.9 (Cygwin) | x86-64 | diff --git a/docs/releasenotes/10.0.0.rst b/docs/releasenotes/10.0.0.rst index d33b75e4da3..b5edd0e361b 100644 --- a/docs/releasenotes/10.0.0.rst +++ b/docs/releasenotes/10.0.0.rst @@ -4,11 +4,6 @@ Backwards Incompatible Changes ============================== -32-bit support -^^^^^^^^^^^^^^ - -32-bit architecture is no longer supported and 32-bit wheels are no longer provided. - Categories ^^^^^^^^^^ @@ -129,14 +124,6 @@ Image.coerce_e This undocumented method has been removed. -Deprecations -============ - -TODO -^^^^ - -TODO - API Changes =========== @@ -165,6 +152,11 @@ TODO Other Changes ============= +32-bit wheels +^^^^^^^^^^^^^ + +32-bit wheels are no longer provided. + Support display_jpeg() in IPython ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/setup.py b/setup.py index b647944d3f7..7c1ad6dc5b2 100755 --- a/setup.py +++ b/setup.py @@ -153,13 +153,16 @@ def _find_library_dirs_ldconfig(): ldconfig = "ldconfig" if shutil.which("ldconfig") else "/sbin/ldconfig" if sys.platform.startswith("linux") or sys.platform.startswith("gnu"): - machine = os.uname()[4] + if struct.calcsize("l") == 4: + machine = os.uname()[4] + "-32" + else: + machine = os.uname()[4] + "-64" mach_map = { - "x86_64": "libc6,x86-64", - "ppc64": "libc6,64bit", - "sparc64": "libc6,64bit", - "s390x": "libc6,64bit", - "ia64": "libc6,IA-64", + "x86_64-64": "libc6,x86-64", + "ppc64-64": "libc6,64bit", + "sparc64-64": "libc6,64bit", + "s390x-64": "libc6,64bit", + "ia64-64": "libc6,IA-64", } abi_type = mach_map.get(machine, "libc6") @@ -581,7 +584,10 @@ def build_extensions(self): # user libs are at $PREFIX/lib _add_directory( library_dirs, - os.path.join(os.environ["ANDROID_ROOT"], "lib64"), + os.path.join( + os.environ["ANDROID_ROOT"], + "lib" if struct.calcsize("l") == 4 else "lib64", + ), ) elif sys.platform.startswith("netbsd"): diff --git a/src/libImaging/ImagingUtils.h b/src/libImaging/ImagingUtils.h index f2acabeac1a..0c0c1eda917 100644 --- a/src/libImaging/ImagingUtils.h +++ b/src/libImaging/ImagingUtils.h @@ -30,7 +30,7 @@ /* This is to work around a bug in GCC prior 4.9 in 64 bit mode. GCC generates code with partial dependency which is 3 times slower. See: https://stackoverflow.com/a/26588074/253146 */ -#if defined(__SSE__) && !defined(__NO_INLINE__) && \ +#if defined(__x86_64__) && defined(__SSE__) && !defined(__NO_INLINE__) && \ !defined(__clang__) && defined(GCC_VERSION) && (GCC_VERSION < 40900) static float __attribute__((always_inline)) inline _i2f(int v) { float x; diff --git a/winbuild/build.rst b/winbuild/build.rst index 97df950b348..99dfad3015f 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -27,7 +27,7 @@ Download and install: * `Ninja `_ (optional, use ``--nmake`` if not available; bundled in Visual Studio CMake component) -* x64: `Netwide Assembler (NASM) `_ +* x86/x64: `Netwide Assembler (NASM) `_ Any version of Visual Studio 2017 or newer should be supported, including Visual Studio 2017 Community, or Build Tools for Visual Studio 2019. @@ -42,7 +42,7 @@ Run ``build_prepare.py`` to configure the build:: usage: winbuild\build_prepare.py [-h] [-v] [-d PILLOW_BUILD] [--depends PILLOW_DEPS] - [--architecture {x64,ARM64}] + [--architecture {x86,x64,ARM64}] [--python PYTHON] [--executable EXECUTABLE] [--nmake] [--no-imagequant] [--no-fribidi] @@ -56,7 +56,7 @@ Run ``build_prepare.py`` to configure the build:: --depends PILLOW_DEPS directory used to store cached dependencies (default: 'winbuild\depends') - --architecture {x64,ARM64} + --architecture {x86,x64,ARM64} build architecture (default: same as host Python) --python PYTHON Python install directory (default: use host Python) --executable EXECUTABLE diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index 9984eb1bccc..b9de071a0a5 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -3,6 +3,7 @@ import platform import re import shutil +import struct import subprocess import sys @@ -97,6 +98,7 @@ def cmd_msbuild( SF_PROJECTS = "https://sourceforge.net/projects" architectures = { + "x86": {"vcvars_arch": "x86", "msbuild_arch": "Win32"}, "x64": {"vcvars_arch": "x86_amd64", "msbuild_arch": "x64"}, "ARM64": {"vcvars_arch": "x86_arm64", "msbuild_arch": "ARM64"}, } @@ -609,7 +611,11 @@ def build_pillow(): choices=architectures, default=os.environ.get( "ARCHITECTURE", - "ARM64" if platform.machine() == "ARM64" else "x64", + ( + "ARM64" + if platform.machine() == "ARM64" + else ("x86" if struct.calcsize("P") == 4 else "x64") + ), ), help="build architecture (default: same as host Python)", )